From c90f0bc3132a368b9a9c394116286c09962c33b8 Mon Sep 17 00:00:00 2001 From: Cyano Hao Date: Wed, 17 Apr 2024 12:49:37 +0800 Subject: [PATCH] Add support for weston-terminal (#384) * add support for weston-terminal * move NonExclusiveTemporaryFileOwner to utils * move wrapCommandForTerminalEmulator to utils --- RedPandaIDE/compiler/compilermanager.h | 3 +- RedPandaIDE/debugger/debugger.cpp | 2 +- RedPandaIDE/resources/terminal-unix.json | 11 ++ RedPandaIDE/settings.cpp | 80 ------------- RedPandaIDE/settings.h | 5 - .../environmentprogramswidget.cpp | 2 +- .../environmentprogramswidget.h | 3 +- RedPandaIDE/utils.cpp | 111 ++++++++++++++++++ RedPandaIDE/utils.h | 20 +++- 9 files changed, 147 insertions(+), 90 deletions(-) diff --git a/RedPandaIDE/compiler/compilermanager.h b/RedPandaIDE/compiler/compilermanager.h index 9b5ae731..12eeb755 100644 --- a/RedPandaIDE/compiler/compilermanager.h +++ b/RedPandaIDE/compiler/compilermanager.h @@ -22,6 +22,7 @@ #include "qt_utils/utils.h" #include "../utils.h" #include "../common.h" +#include "settings.h" enum RunProgramFlag { RPF_PAUSE_CONSOLE = 0x0001, @@ -103,7 +104,7 @@ private: int mSyntaxCheckIssueCount; Compiler* mBackgroundSyntaxChecker; Runner* mRunner; - TemporaryFileOwner mTempFileOwner; + PNonExclusiveTemporaryFileOwner mTempFileOwner; #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) QRecursiveMutex mCompileMutex; QRecursiveMutex mBackgroundSyntaxCheckMutex; diff --git a/RedPandaIDE/debugger/debugger.cpp b/RedPandaIDE/debugger/debugger.cpp index 1ee4db8a..5315ee41 100644 --- a/RedPandaIDE/debugger/debugger.cpp +++ b/RedPandaIDE/debugger/debugger.cpp @@ -2360,7 +2360,7 @@ void DebugTarget::run() } + mArguments; QString cmd; QStringList arguments; - std::unique_ptr fileOwner; + PNonExclusiveTemporaryFileOwner fileOwner; #ifdef Q_OS_WIN if (pSettings->environment().useCustomTerminal()) { std::tie(cmd, arguments, fileOwner) = wrapCommandForTerminalEmulator( diff --git a/RedPandaIDE/resources/terminal-unix.json b/RedPandaIDE/resources/terminal-unix.json index 8ce5be29..83cea967 100644 --- a/RedPandaIDE/resources/terminal-unix.json +++ b/RedPandaIDE/resources/terminal-unix.json @@ -125,6 +125,17 @@ } ] }, + { + "group": "Special Purpose", + "terminals": [ + { + "name": "Wayland Terminal", + "path": "weston-terminal", + "argsPattern": "$term --shell $tmpfile.sh", + "comment": "integration with Weston compositor, for minimal Live CD/USB" + } + ] + }, { "group": "With minor issue", "terminals": [ diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index 2da33900..c48261dd 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -6545,83 +6545,3 @@ void Settings::Languages::setNoDebugDirectivesWhenGenerateASM(bool newNoDebugDir { mNoDebugDirectivesWhenGenerateASM = newNoDebugDirectivesWhenGenerateASM; } - -std::tuple> wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0) -{ - QStringList wrappedArgs; - std::unique_ptr temproryFile; - for (const QString &patternItem : argsPattern) { - if (patternItem == "$term") - wrappedArgs.append(terminal); - else if (patternItem == "$integrated_term") - wrappedArgs.append(includeTrailingPathDelimiter(pSettings->dirs().appDir())+terminal); - else if (patternItem == "$argv") - wrappedArgs.append(payloadArgsWithArgv0); - else if (patternItem == "$command" || patternItem == "$unix_command") { - // “$command” is for compatibility; previously used on multiple Unix terms - QStringList escapedArgs; - for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { - auto &arg = payloadArgsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); - escapedArgs.append(escaped); - } - wrappedArgs.push_back(escapedArgs.join(' ')); - } else if (patternItem == "$dos_command") { - QStringList escapedArgs; - for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { - auto &arg = payloadArgsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); - escapedArgs.append(escaped); - } - wrappedArgs.push_back(escapedArgs.join(' ')); - } else if (patternItem == "$lpCommandLine") { - QStringList escapedArgs; - for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { - auto &arg = payloadArgsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCreateProcess); - escapedArgs.append(escaped); - } - wrappedArgs.push_back(escapedArgs.join(' ')); - } else if (patternItem == "$tmpfile" || patternItem == "$tmpfile.command") { - // “$tmpfile” is for compatibility; previously used on macOS Terminal.app - temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); - if (temproryFile->open()) { - QStringList escapedArgs; - for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { - auto &arg = payloadArgsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); - escapedArgs.append(escaped); - } - temproryFile->write(escapedArgs.join(' ').toUtf8()); - temproryFile->write("\n"); - temproryFile->flush(); - QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); - } - wrappedArgs.push_back(temproryFile->fileName()); - } else if (patternItem == "$tmpfile.bat") { - temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.bat"); - if (temproryFile->open()) { - QStringList escapedArgs; - for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { - auto &arg = payloadArgsWithArgv0[i]; - auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); - escapedArgs.append(escaped); - } - temproryFile->write(escapedArgs.join(' ').toLocal8Bit()); - temproryFile->write("\r\n"); - temproryFile->flush(); - QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); - } - wrappedArgs.push_back(temproryFile->fileName()); - } else - wrappedArgs.push_back(patternItem); - } - if (wrappedArgs.empty()) - return {QString(""), QStringList{}, std::move(temproryFile)}; - return {wrappedArgs[0], wrappedArgs.mid(1), std::move(temproryFile)}; -} - -std::tuple> wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0) -{ - return wrapCommandForTerminalEmulator(terminal, parseArguments(argsPattern, Settings::Environment::terminalArgsPatternMagicVariables(), false), payloadArgsWithArgv0); -} diff --git a/RedPandaIDE/settings.h b/RedPandaIDE/settings.h index eac55611..d1b00adf 100644 --- a/RedPandaIDE/settings.h +++ b/RedPandaIDE/settings.h @@ -1615,11 +1615,6 @@ private: Languages mLanguages; }; - -std::tuple> wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0); - -std::tuple> wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0); - extern Settings* pSettings; #endif // SETTINGS_H diff --git a/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp b/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp index 5aa55f9b..59cf9343 100644 --- a/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp +++ b/RedPandaIDE/settingsdialog/environmentprogramswidget.cpp @@ -44,7 +44,7 @@ EnvironmentProgramsWidget::~EnvironmentProgramsWidget() } auto EnvironmentProgramsWidget::resolveExecArguments(const QString &terminalPath, const QString &argsPattern) - -> std::tuple> + -> std::tuple { return wrapCommandForTerminalEmulator(terminalPath, argsPattern, platformCommandForTerminalArgsPreview()); } diff --git a/RedPandaIDE/settingsdialog/environmentprogramswidget.h b/RedPandaIDE/settingsdialog/environmentprogramswidget.h index ce7b80c2..81996e14 100644 --- a/RedPandaIDE/settingsdialog/environmentprogramswidget.h +++ b/RedPandaIDE/settingsdialog/environmentprogramswidget.h @@ -17,6 +17,7 @@ #ifndef ENVIRONMENTPROGRAMSWIDGET_H #define ENVIRONMENTPROGRAMSWIDGET_H +#include "settings.h" #include "settingswidget.h" #include "utils.h" @@ -34,7 +35,7 @@ public: private: auto resolveExecArguments(const QString &terminalPath, const QString &argsPatter) - -> std::tuple>; + -> std::tuple; void updateCommandPreview(const QString &terminalPath, const QString &argsPatter); void autoDetectAndUpdateArgumentsPattern(const QString &terminalPath); diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index f1e2ef06..4cf84fe2 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -14,6 +14,8 @@ #include "parser/cppparser.h" #include "compiler/executablerunner.h" #include +#include "utils/escape.h" +#include "utils/parsearg.h" #ifdef Q_OS_WIN #include #include @@ -25,6 +27,22 @@ using pIsWow64Process2_t = BOOL (WINAPI *)( ); #endif +NonExclusiveTemporaryFileOwner::NonExclusiveTemporaryFileOwner(std::unique_ptr &tempFile) : + filename(tempFile ? tempFile->fileName() : QString()) +{ + if (tempFile) { + tempFile->flush(); + tempFile->setAutoRemove(false); + tempFile = nullptr; + } +} + +NonExclusiveTemporaryFileOwner::~NonExclusiveTemporaryFileOwner() +{ + if (!filename.isEmpty()) + QFile::remove(filename); +} + FileType getFileType(const QString &filename) { if (filename.endsWith(".s",PATH_SENSITIVITY)) { @@ -635,3 +653,96 @@ QByteArray stringToByteArray(const QString &content, bool isUTF8) else return content.toLocal8Bit(); } + +std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0) +{ + QStringList wrappedArgs; + std::unique_ptr temproryFile; + for (const QString &patternItem : argsPattern) { + if (patternItem == "$term") + wrappedArgs.append(terminal); + else if (patternItem == "$integrated_term") + wrappedArgs.append(includeTrailingPathDelimiter(pSettings->dirs().appDir())+terminal); + else if (patternItem == "$argv") + wrappedArgs.append(payloadArgsWithArgv0); + else if (patternItem == "$command" || patternItem == "$unix_command") { + // “$command” is for compatibility; previously used on multiple Unix terms + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); + escapedArgs.append(escaped); + } + wrappedArgs.push_back(escapedArgs.join(' ')); + } else if (patternItem == "$dos_command") { + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); + escapedArgs.append(escaped); + } + wrappedArgs.push_back(escapedArgs.join(' ')); + } else if (patternItem == "$lpCommandLine") { + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCreateProcess); + escapedArgs.append(escaped); + } + wrappedArgs.push_back(escapedArgs.join(' ')); + } else if (patternItem == "$tmpfile" || patternItem == "$tmpfile.command") { + // “$tmpfile” is for compatibility; previously used on macOS Terminal.app + temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); + if (temproryFile->open()) { + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); + escapedArgs.append(escaped); + } + temproryFile->write(escapedArgs.join(' ').toUtf8()); + temproryFile->write("\n"); + QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); + } + wrappedArgs.push_back(temproryFile->fileName()); + } else if (patternItem == "$tmpfile.sh") { + temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); + if (temproryFile->open()) { + QStringList escapedArgs = {"exec"}; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, false, EscapeArgumentRule::BourneAgainShellPretty); + escapedArgs.append(escaped); + } + temproryFile->write("#!/bin/sh\n"); + temproryFile->write(escapedArgs.join(' ').toUtf8()); + temproryFile->write("\n"); + QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); + } + wrappedArgs.push_back(temproryFile->fileName()); + } else if (patternItem == "$tmpfile.bat") { + temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.bat"); + if (temproryFile->open()) { + QStringList escapedArgs; + for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { + auto &arg = payloadArgsWithArgv0[i]; + auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); + escapedArgs.append(escaped); + } + temproryFile->write(escapedArgs.join(' ').toLocal8Bit()); + temproryFile->write("\r\n"); + QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); + } + wrappedArgs.push_back(temproryFile->fileName()); + } else + wrappedArgs.push_back(patternItem); + } + if (wrappedArgs.empty()) + return {QString(""), QStringList{}, std::make_unique(temproryFile)}; + return {wrappedArgs[0], wrappedArgs.mid(1), std::make_unique(temproryFile)}; +} + +std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0) +{ + return wrapCommandForTerminalEmulator(terminal, parseArguments(argsPattern, Settings::Environment::terminalArgsPatternMagicVariables(), false), payloadArgsWithArgv0); +} diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index d3763d4f..4a9a2981 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -35,7 +35,6 @@ using SimpleIni = CSimpleIniA; using PSimpleIni = std::shared_ptr; -using TemporaryFileOwner = std::unique_ptr; enum class FileType{ GAS, // GNU assembler source file (.s) @@ -116,6 +115,21 @@ enum class ProblemCaseValidateType { IgnoreSpaces }; +struct NonExclusiveTemporaryFileOwner { + const QString filename; + + // take ownership + explicit NonExclusiveTemporaryFileOwner(std::unique_ptr &tempFile); + + NonExclusiveTemporaryFileOwner(const NonExclusiveTemporaryFileOwner &) = delete; + NonExclusiveTemporaryFileOwner(NonExclusiveTemporaryFileOwner &&) = delete; + NonExclusiveTemporaryFileOwner& operator=(const NonExclusiveTemporaryFileOwner &) = delete; + NonExclusiveTemporaryFileOwner& operator=(NonExclusiveTemporaryFileOwner &&) = delete; + ~NonExclusiveTemporaryFileOwner(); +}; + +using PNonExclusiveTemporaryFileOwner = std::unique_ptr; + FileType getFileType(const QString& filename); bool programHasConsole(const QString& filename); @@ -180,4 +194,8 @@ QByteArray stringToByteArray(const QString& content, bool isUTF8); #define __builtin_unreachable() (__assume(0)) #endif +std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0); + +std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0); + #endif // UTILS_H