Add runner args abstraction and fix shm on macOS/BSD (#134)

* add runner args abstraction to allow different args patterns in various terminal apps; fix macOS shm IPC

* extend platform support from Linux to XDG desktop

* update build docs for Unix

* improve terminal args pattern migration for macOS
This commit is contained in:
Cyano Hao 2023-09-05 19:14:08 +08:00 committed by GitHub
parent 18aa239b40
commit 7cf5d21b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1150 additions and 246 deletions

View File

@ -198,11 +198,33 @@ To build with VS 2017 or later in Command Prompt:
"%JOM%" install "%JOM%" install
``` ```
# Linux # Linux and Other freedesktop.org-conforming (XDG) Desktop Systems
- Install gcc and qt5 General steps:
- Optionally install fcitx5-qt for building with static version of Qt
- Open `Red_Panda_CPP.pro` with Qt Creator - Install recent version of GCC (≥ 7) or Clang (≥ 6) that supports C++17.
- Install Qt 5 (≥ 5.12) Base, SVG and Tools modules, including both libraries and development files.
- Optionally install fcitx5-qt for building with static Qt library.
- Optionally install Qt Creator for development.
For build only:
1. Configure:
```bash
qmake PREFIX=/usr/local /path/to/src/Red_Panda_CPP.pro
```
2. Make:
```bash
make -j$(nproc)
```
3. Install:
```bash
sudo make install
```
For development:
1. Open `Red_Panda_CPP.pro` with Qt Creator
qmake variables: qmake variables:
- `PREFIX`: default to `/usr/local`. It should be set to `/usr` or `/opt/redpanda-cpp` when packaging. - `PREFIX`: default to `/usr/local`. It should be set to `/usr` or `/opt/redpanda-cpp` when packaging.
@ -296,7 +318,7 @@ Enter `RedPandaIDE` to launch RedPanda C++.
Note that makepkg checks out HEAD of the repo, so any change should be committed before building. Note that makepkg checks out HEAD of the repo, so any change should be committed before building.
## AppImage ## Linux AppImage
1. Install dependency: Docker or Podman. 1. Install dependency: Docker or Podman.
@ -336,9 +358,9 @@ Note that makepkg checks out HEAD of the repo, so any change should be committed
It is possible to build Red Panda C++ for foreign architectures using targets native toolchains with QEMU user space emulation. It is possible to build Red Panda C++ for foreign architectures using targets native toolchains with QEMU user space emulation.
Note: Always run emulated native build **in containers**. Mixing architectures may kill your system. Note: Always run emulated native build **in containers or jails**. Mixing architectures may kill your system.
For Linux host, install statically linked QEMU user space emulator (package name is likely `qemu-user-static`) and make sure that binfmt support is enabled. For Linux or BSD host, install statically linked QEMU user space emulator (package name is likely `qemu-user-static`) and make sure that binfmt support is enabled.
For Windows host, Docker and Podman should have QEMU user space emulation enabled. If not, For Windows host, Docker and Podman should have QEMU user space emulation enabled. If not,
* For Docker: * For Docker:
@ -350,3 +372,38 @@ For Windows host, Docker and Podman should have QEMU user space emulation enable
wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /proc/sys/fs/binfmt_misc/register wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /proc/sys/fs/binfmt_misc/register
wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-riscv64-static.conf /proc/sys/fs/binfmt_misc/register wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-riscv64-static.conf /proc/sys/fs/binfmt_misc/register
``` ```
# macOS
## Qt.io Qt Library
Prerequisites:
0. macOS 10.13 or later.
1. Install Xcode Command Line Tools:
```zsh
xcode-select --install
```
2. Install Qt with online installer from [Qt.io](https://www.qt.io/download-qt-installer-oss).
- Select the library (in _Qt_ group, _Qt 5.15.2_ subgroup, check _macOS_).
Build:
1. Set related variables:
```bash
SRC_DIR="~/redpanda-src"
BUILD_DIR="~/redpanda-build"
INSTALL_DIR="~/redpanda-pkg"
```
2. Navigate to build directory:
```bash
rm -rf "$BUILD_DIR" # optional for clean build
mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR"
```
3. Configure, build and install:
```bash
~/Qt/5.15.2/clang_64/bin/qmake PREFIX="$INSTALL_DIR" "$SRC_DIR/Red_Panda_CPP.pro"
make -j$(sysctl -n hw.logicalcpu)
make install
~/Qt/5.15.2/clang_64/bin/macdeployqt "$INSTALL_DIR/bin/RedPandaIDE.app"
```

View File

@ -198,12 +198,33 @@
"%JOM%" install "%JOM%" install
``` ```
# Linux # Linux 和其他符合 freedesktop.orgXDG规范的桌面系统
步骤: 通用步骤:
- 安装 gcc 和 qt5开发相关包
- 如果使用静态版本的 Qt 编译,还要安装 fcitx5-qt - 安装支持 C++17 的 GCC≥ 7或 Clang≥ 6
- 使用qtcreator打开Red_Panda_CPP.pro文件 - 安装 Qt 5≥ 5.12Base、SVG、Tools 模块,包括库和开发文件。
- 如果使用静态版本的 Qt 编译,还要安装 fcitx5-qt。
- 根据需要,安装 Qt Creator 用于开发。
仅构建:
1. 配置:
```bash
qmake PREFIX=/usr/local /path/to/src/Red_Panda_CPP.pro
```
2. 构建:
```bash
make -j$(nproc)
```
3. 安装:
```bash
sudo make install
```
开发:
1. 使用 Qt Creator 打开 `Red_Panda_CPP.pro` 文件。
qmake 变量: qmake 变量:
- `PREFIX`:默认值是 `/usr/local`。打包时应该定义为 `/usr``/opt/redpanda-cpp` - `PREFIX`:默认值是 `/usr/local`。打包时应该定义为 `/usr``/opt/redpanda-cpp`
@ -297,7 +318,7 @@ Windows 宿主的额外要求:
注意makepkg 签出此存储库的 HEAD因此构建之前务必提交所有变更。 注意makepkg 签出此存储库的 HEAD因此构建之前务必提交所有变更。
## AppImage ## Linux AppImage
1. 安装依赖包Docker 或 Podman。 1. 安装依赖包Docker 或 Podman。
@ -337,9 +358,9 @@ Windows 宿主的额外要求:
可以借助 QEMU 用户空间模拟,运行目标架构的本机工具链,来构建小熊猫 C++。 可以借助 QEMU 用户空间模拟,运行目标架构的本机工具链,来构建小熊猫 C++。
注意:始终**在容器中**运行模拟本机构建,因为混用不同架构的程序和库可能会损坏系统。 注意:始终**在容器或 jail 中**运行模拟本机构建,因为混用不同架构的程序和库可能会损坏系统。
对于 Linux 宿主,需要安装静态链接的 QEMU 用户空间模拟器(包名通常为 `qemu-user-static`)并确认已经启用 binfmt 支持。 对于 Linux 或 BSD 宿主,需要安装静态链接的 QEMU 用户空间模拟器(包名通常为 `qemu-user-static`)并确认已经启用 binfmt 支持。
对于 Windows 宿主Docker 和 Podman 应该已经启用了 QEMU 用户空间模拟。如果没有启用, 对于 Windows 宿主Docker 和 Podman 应该已经启用了 QEMU 用户空间模拟。如果没有启用,
* Docker * Docker
@ -351,3 +372,38 @@ Windows 宿主的额外要求:
wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /proc/sys/fs/binfmt_misc/register wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /proc/sys/fs/binfmt_misc/register
wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-riscv64-static.conf /proc/sys/fs/binfmt_misc/register wsl -d podman-machine-default sudo cp /usr/lib/binfmt.d/qemu-riscv64-static.conf /proc/sys/fs/binfmt_misc/register
``` ```
# macOS
## Qt.io 的 Qt 库
前置条件:
0. macOS 10.13 或更高版本。
1. 安装 Xcode 命令行工具:
```bash
xcode-select --install
```
2. 用 [Qt.io](https://www.qt.io/download-qt-installer-oss) 或[镜像站](https://mirrors.sjtug.sjtu.edu.cn/docs/qt)的在线安装器安装 Qt。
- 选中 Qt 库“Qt” 组下的 “Qt 5.15.2” 小组,勾选 “macOS”
构建:
1. 设置相关变量:
```bash
SRC_DIR="~/redpanda-src"
BUILD_DIR="~/redpanda-build"
INSTALL_DIR="~/redpanda-pkg"
```
2. 定位到构建目录:
```bash
rm -rf "$BUILD_DIR" # 根据需要进行全新构建
mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR"
```
3. 配置、构建、安装:
```bash
~/Qt/5.15.2/clang_64/bin/qmake PREFIX="$INSTALL_DIR" "$SRC_DIR/Red_Panda_CPP.pro"
make -j$(sysctl -n hw.logicalcpu)
make install
~/Qt/5.15.2/clang_64/bin/macdeployqt "$INSTALL_DIR/bin/RedPandaIDE.app"
```

View File

@ -29,10 +29,6 @@ contains(QMAKE_HOST.arch, x86_64):{
} }
macos: { macos: {
# This package needs to be installed via homebrew before we can compile it
INCLUDEPATH += \
/opt/homebrew/opt/icu4c/include
QT += gui-private QT += gui-private
ICON = ../macos/RedPandaIDE.icns ICON = ../macos/RedPandaIDE.icns
@ -131,6 +127,7 @@ SOURCES += \
settingsdialog/editortooltipswidget.cpp \ settingsdialog/editortooltipswidget.cpp \
settingsdialog/environmentfolderswidget.cpp \ settingsdialog/environmentfolderswidget.cpp \
settingsdialog/environmentperformancewidget.cpp \ settingsdialog/environmentperformancewidget.cpp \
settingsdialog/environmentprogramswidget.cpp \
settingsdialog/environmentshortcutwidget.cpp \ settingsdialog/environmentshortcutwidget.cpp \
settingsdialog/executorproblemsetwidget.cpp \ settingsdialog/executorproblemsetwidget.cpp \
settingsdialog/formattergeneralwidget.cpp \ settingsdialog/formattergeneralwidget.cpp \
@ -254,6 +251,7 @@ HEADERS += \
settingsdialog/editortooltipswidget.h \ settingsdialog/editortooltipswidget.h \
settingsdialog/environmentfolderswidget.h \ settingsdialog/environmentfolderswidget.h \
settingsdialog/environmentperformancewidget.h \ settingsdialog/environmentperformancewidget.h \
settingsdialog/environmentprogramswidget.h \
settingsdialog/environmentshortcutwidget.h \ settingsdialog/environmentshortcutwidget.h \
settingsdialog/executorproblemsetwidget.h \ settingsdialog/executorproblemsetwidget.h \
settingsdialog/formattergeneralwidget.h \ settingsdialog/formattergeneralwidget.h \
@ -349,6 +347,7 @@ FORMS += \
settingsdialog/editortooltipswidget.ui \ settingsdialog/editortooltipswidget.ui \
settingsdialog/environmentfolderswidget.ui \ settingsdialog/environmentfolderswidget.ui \
settingsdialog/environmentperformancewidget.ui \ settingsdialog/environmentperformancewidget.ui \
settingsdialog/environmentprogramswidget.ui \
settingsdialog/environmentshortcutwidget.ui \ settingsdialog/environmentshortcutwidget.ui \
settingsdialog/executorproblemsetwidget.ui \ settingsdialog/executorproblemsetwidget.ui \
settingsdialog/formattergeneralwidget.ui \ settingsdialog/formattergeneralwidget.ui \
@ -469,21 +468,18 @@ win32: {
unix: { unix: {
HEADERS += \ HEADERS += \
settingsdialog/formatterpathwidget.h \ settingsdialog/formatterpathwidget.h
settingsdialog/environmentprogramswidget.h
SOURCES += \ SOURCES += \
settingsdialog/formatterpathwidget.cpp \ settingsdialog/formatterpathwidget.cpp
settingsdialog/environmentprogramswidget.cpp
FORMS += \ FORMS += \
settingsdialog/formatterpathwidget.ui \ settingsdialog/formatterpathwidget.ui
settingsdialog/environmentprogramswidget.ui
} }
linux: { linux: {
LIBS+= \ # legacy glibc compatibility -- modern Unices have all components in `libc.so`
-lrt LIBS += -lrt
_LINUX_STATIC_IME_PLUGIN = $$(LINUX_STATIC_IME_PLUGIN) _LINUX_STATIC_IME_PLUGIN = $$(LINUX_STATIC_IME_PLUGIN)
equals(_LINUX_STATIC_IME_PLUGIN, "ON") { equals(_LINUX_STATIC_IME_PLUGIN, "ON") {

View File

@ -62,11 +62,13 @@ void AutolinkManager::load()
file.write(content); file.write(content);
file.close(); file.close();
preFile.close(); preFile.close();
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_MACOS)
QFile preFile(":/config/autolink-linux.json"); return;
#else // XDG desktop
QFile preFile(":/config/autolink-xdg.json");
if (!preFile.open(QFile::ReadOnly)) { if (!preFile.open(QFile::ReadOnly)) {
throw FileError(QObject::tr("Can't open file '%1' for read.") throw FileError(QObject::tr("Can't open file '%1' for read.")
.arg(":/config/autolink-linux.json")); .arg(":/config/autolink-xdg.json"));
} }
QByteArray content=preFile.readAll(); QByteArray content=preFile.readAll();
if (!file.open(QFile::WriteOnly|QFile::Truncate)) { if (!file.open(QFile::WriteOnly|QFile::Truncate)) {
@ -76,8 +78,6 @@ void AutolinkManager::load()
file.write(content); file.write(content);
file.close(); file.close();
preFile.close(); preFile.close();
#else
return;
#endif #endif
} }
if (file.open(QFile::ReadOnly)) { if (file.open(QFile::ReadOnly)) {

View File

@ -177,9 +177,12 @@ void CompilerInfo::prepareCompilerOptions()
sl.append(QPair<QString,QString>("Strong","-strong")); sl.append(QPair<QString,QString>("Strong","-strong"));
sl.append(QPair<QString,QString>("All","-all")); sl.append(QPair<QString,QString>("All","-all"));
addOption(CC_CMD_OPT_STACK_PROTECTOR , QObject::tr("Check for stack smashing attacks (-fstack-protector)"), groupName, false, false, true, "-fstack-protector", CompilerOptionType::Choice, sl); addOption(CC_CMD_OPT_STACK_PROTECTOR , QObject::tr("Check for stack smashing attacks (-fstack-protector)"), groupName, false, false, true, "-fstack-protector", CompilerOptionType::Choice, sl);
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) #if defined(Q_OS_UNIX)
sl.clear(); sl.clear();
sl.append(QPair<QString,QString>("Address","address")); sl.append(QPair<QString,QString>("Address","address"));
# ifdef __aarch64__
sl.append(QPair<QString,QString>("Hardware-assisted Address","hwaddress"));
# endif
sl.append(QPair<QString,QString>("Thread","thread")); sl.append(QPair<QString,QString>("Thread","thread"));
sl.append(QPair<QString,QString>("Leak","leak")); sl.append(QPair<QString,QString>("Leak","leak"));
sl.append(QPair<QString,QString>("Undefined","undefined")); sl.append(QPair<QString,QString>("Undefined","undefined"));

View File

@ -31,6 +31,9 @@
#include <QMessageBox> #include <QMessageBox>
#include <QUuid> #include <QUuid>
#include "projectcompiler.h" #include "projectcompiler.h"
#ifdef Q_OS_MACOS
#include <sys/posix_shm.h>
#endif
enum RunProgramFlag { enum RunProgramFlag {
RPF_PAUSE_CONSOLE = 0x0001, RPF_PAUSE_CONSOLE = 0x0001,
@ -255,20 +258,32 @@ void CompilerManager::run(
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (consoleFlag!=0) { if (consoleFlag!=0) {
QString sharedMemoryId = QUuid::createUuid().toString(); QString sharedMemoryId = QUuid::createUuid().toString();
QString newArguments = QString(" %1 %2 \"%3\" %4") QString consolePauserPath = includeTrailingPathDelimiter(pSettings->dirs().appDir()) + CONSOLE_PAUSER;
.arg(consoleFlag) QStringList execArgs = QStringList{
.arg(sharedMemoryId,localizePath(filename)).arg(arguments); consolePauserPath,
QString::number(consoleFlag),
sharedMemoryId,
localizePath(filename)
} + splitProcessCommand(arguments);
auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
pSettings->environment().terminalPathForExec(),
pSettings->environment().terminalArgumentsPattern(),
execArgs
);
//delete when thread finished //delete when thread finished
execRunner = new ExecutableRunner(includeTrailingPathDelimiter(pSettings->dirs().appDir())+CONSOLE_PAUSER,newArguments,workDir); execRunner = new ExecutableRunner(filename, args, workDir);
execRunner->setShareMemoryId(sharedMemoryId); execRunner->setShareMemoryId(sharedMemoryId);
mTempFileOwner = std::move(fileOwner);
} else { } else {
//delete when thread finished //delete when thread finished
execRunner = new ExecutableRunner(filename,arguments,workDir); execRunner = new ExecutableRunner(filename,splitProcessCommand(arguments),workDir);
} }
#else #else
QString newArguments; QStringList execArgs;
QString sharedMemoryId = "/r"+QUuid::createUuid().toString(QUuid::StringFormat::Id128); QString sharedMemoryId = "/r"+QUuid::createUuid().toString(QUuid::StringFormat::Id128);
#ifdef Q_OS_MACOS
sharedMemoryId = sharedMemoryId.mid(0, PSHMNAMLEN);
#endif
if (consoleFlag!=0) { if (consoleFlag!=0) {
QString consolePauserPath=includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+"consolepauser"; QString consolePauserPath=includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+"consolepauser";
if (!fileExists(consolePauserPath)) { if (!fileExists(consolePauserPath)) {
@ -280,31 +295,40 @@ void CompilerManager::run(
} }
if (redirectInput) { if (redirectInput) {
newArguments = QString(" -e \"%1\" %2 %3 \"%4\" \"%5\" %6") execArgs = QStringList{
.arg(consolePauserPath) consolePauserPath,
.arg(consoleFlag) QString::number(consoleFlag),
.arg(sharedMemoryId) sharedMemoryId,
.arg(escapeSpacesInString(redirectInputFilename)) redirectInputFilename,
.arg(localizePath(escapeSpacesInString(filename))) localizePath(filename),
.arg(arguments); } + splitProcessCommand(arguments);
} else { } else {
newArguments = QString(" -e \"%1\" %2 %3 \"%4\" %5") execArgs = QStringList{
.arg(consolePauserPath) consolePauserPath,
.arg(consoleFlag) QString::number(consoleFlag),
.arg(sharedMemoryId,localizePath(escapeSpacesInString(filename))).arg(arguments); sharedMemoryId,
localizePath(filename),
} + splitProcessCommand(arguments);
} }
} else { } else {
newArguments = QString(" -e \"%1\" %2") execArgs = QStringList{
.arg(localizePath(escapeSpacesInString(filename))).arg(arguments); localizePath(filename),
} + splitProcessCommand(arguments);
} }
execRunner = new ExecutableRunner(pSettings->environment().terminalPathForExec(),newArguments,workDir); auto [filename, args, fileOwner] = wrapCommandForTerminalEmulator(
pSettings->environment().terminalPathForExec(),
pSettings->environment().terminalArgumentsPattern(),
execArgs
);
execRunner = new ExecutableRunner(filename, args, workDir);
execRunner->setShareMemoryId(sharedMemoryId); execRunner->setShareMemoryId(sharedMemoryId);
mTempFileOwner = std::move(fileOwner);
#endif #endif
execRunner->setStartConsole(true); execRunner->setStartConsole(true);
} else { } else {
//delete when thread finished //delete when thread finished
execRunner = new ExecutableRunner(filename,arguments,workDir); execRunner = new ExecutableRunner(filename,splitProcessCommand(arguments),workDir);
} }
if (redirectInput) { if (redirectInput) {
execRunner->setRedirectInput(true); execRunner->setRedirectInput(true);
execRunner->setRedirectInputFilename(redirectInputFilename); execRunner->setRedirectInputFilename(redirectInputFilename);
@ -347,7 +371,7 @@ void CompilerManager::doRunProblem(const QString &filename, const QString &argum
if (mRunner!=nullptr) { if (mRunner!=nullptr) {
return; return;
} }
OJProblemCasesRunner * execRunner = new OJProblemCasesRunner(filename,arguments,workDir,problemCases); OJProblemCasesRunner * execRunner = new OJProblemCasesRunner(filename,splitProcessCommand(arguments),workDir,problemCases);
mRunner = execRunner; mRunner = execRunner;
if (pSettings->executor().enableCaseLimit()) { if (pSettings->executor().enableCaseLimit()) {
execRunner->setExecTimeout(pSettings->executor().caseTimeout()); execRunner->setExecTimeout(pSettings->executor().caseTimeout());
@ -380,6 +404,7 @@ void CompilerManager::stopRun()
mRunner->stop(); mRunner->stop();
disconnect(mRunner, &Runner::finished, this ,&CompilerManager::onRunnerTerminated); disconnect(mRunner, &Runner::finished, this ,&CompilerManager::onRunnerTerminated);
mRunner=nullptr; mRunner=nullptr;
mTempFileOwner=nullptr;
} }
} }
@ -395,6 +420,7 @@ void CompilerManager::stopPausing()
disconnect(mRunner, &Runner::finished, this ,&CompilerManager::onRunnerTerminated); disconnect(mRunner, &Runner::finished, this ,&CompilerManager::onRunnerTerminated);
mRunner->stop(); mRunner->stop();
mRunner=nullptr; mRunner=nullptr;
mTempFileOwner=nullptr;
} }
} }
@ -428,6 +454,7 @@ void CompilerManager::onRunnerTerminated()
{ {
QMutexLocker locker(&mRunnerMutex); QMutexLocker locker(&mRunnerMutex);
mRunner=nullptr; mRunner=nullptr;
mTempFileOwner=nullptr;
} }
void CompilerManager::onRunnerPausing() void CompilerManager::onRunnerPausing()
@ -438,6 +465,7 @@ void CompilerManager::onRunnerPausing()
disconnect(mRunner, &Runner::runErrorOccurred, pMainWindow ,&MainWindow::onRunErrorOccured); disconnect(mRunner, &Runner::runErrorOccurred, pMainWindow ,&MainWindow::onRunErrorOccured);
connect(this, &CompilerManager::signalStopAllRunners, mRunner, &Runner::stop); connect(this, &CompilerManager::signalStopAllRunners, mRunner, &Runner::stop);
mRunner=nullptr; mRunner=nullptr;
mTempFileOwner=nullptr;
} }
void CompilerManager::onCompileIssue(PCompileIssue issue) void CompilerManager::onCompileIssue(PCompileIssue issue)

View File

@ -20,6 +20,7 @@
#include <QObject> #include <QObject>
#include <QMutex> #include <QMutex>
#include "qt_utils/utils.h" #include "qt_utils/utils.h"
#include "../utils.h"
#include "../common.h" #include "../common.h"
class Runner; class Runner;
@ -96,6 +97,7 @@ private:
int mSyntaxCheckIssueCount; int mSyntaxCheckIssueCount;
Compiler* mBackgroundSyntaxChecker; Compiler* mBackgroundSyntaxChecker;
Runner* mRunner; Runner* mRunner;
TemporaryFileOwner mTempFileOwner;
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
QRecursiveMutex mCompileMutex; QRecursiveMutex mCompileMutex;
QRecursiveMutex mBackgroundSyntaxCheckMutex; QRecursiveMutex mBackgroundSyntaxCheckMutex;

View File

@ -23,7 +23,7 @@
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <QUuid> #include <QUuid>
#include <windows.h> #include <windows.h>
#elif defined(Q_OS_LINUX) #else
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@ -32,7 +32,7 @@
#endif #endif
ExecutableRunner::ExecutableRunner(const QString &filename, const QString &arguments, const QString &workDir ExecutableRunner::ExecutableRunner(const QString &filename, const QStringList &arguments, const QString &workDir
,QObject* parent): ,QObject* parent):
Runner(filename,arguments,workDir,parent), Runner(filename,arguments,workDir,parent),
mRedirectInput(false), mRedirectInput(false),
@ -110,7 +110,7 @@ void ExecutableRunner::run()
mProcess = std::make_shared<QProcess>(); mProcess = std::make_shared<QProcess>();
mProcess->setProgram(mFilename); mProcess->setProgram(mFilename);
mProcess->setArguments(splitProcessCommand(mArguments)); mProcess->setArguments(mArguments);
//qDebug()<<splitProcessCommand(mArguments); //qDebug()<<splitProcessCommand(mArguments);
mProcess->setWorkingDirectory(mWorkDir); mProcess->setWorkingDirectory(mWorkDir);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@ -162,7 +162,7 @@ void ExecutableRunner::run()
} }
} }
} }
#elif defined(Q_OS_LINUX) #else
int BUF_SIZE=1024; int BUF_SIZE=1024;
char* pBuf=nullptr; char* pBuf=nullptr;
int fd_shm = shm_open(mShareMemoryId.toLocal8Bit().data(),O_RDWR | O_CREAT,S_IRWXU); int fd_shm = shm_open(mShareMemoryId.toLocal8Bit().data(),O_RDWR | O_CREAT,S_IRWXU);
@ -214,7 +214,6 @@ void ExecutableRunner::run()
} }
break; break;
} }
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
if (mStartConsole && !mPausing && pBuf) { if (mStartConsole && !mPausing && pBuf) {
if (strncmp(pBuf,"FINISHED",sizeof("FINISHED"))==0) { if (strncmp(pBuf,"FINISHED",sizeof("FINISHED"))==0) {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -226,7 +225,7 @@ void ExecutableRunner::run()
hSharedMemory = INVALID_HANDLE_VALUE; hSharedMemory = INVALID_HANDLE_VALUE;
CloseHandle(hSharedMemory); CloseHandle(hSharedMemory);
} }
#elif defined(Q_OS_LINUX) #else
if (pBuf) { if (pBuf) {
munmap(pBuf,BUF_SIZE); munmap(pBuf,BUF_SIZE);
pBuf = nullptr; pBuf = nullptr;
@ -240,14 +239,13 @@ void ExecutableRunner::run()
emit pausingForFinish(); emit pausingForFinish();
} }
} }
#endif
} }
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (pBuf) if (pBuf)
UnmapViewOfFile(pBuf); UnmapViewOfFile(pBuf);
if (hSharedMemory!=INVALID_HANDLE_VALUE && hSharedMemory!=NULL) if (hSharedMemory!=INVALID_HANDLE_VALUE && hSharedMemory!=NULL)
CloseHandle(hSharedMemory); CloseHandle(hSharedMemory);
#elif defined(Q_OS_LINUX) #else
if (pBuf) { if (pBuf) {
munmap(pBuf,BUF_SIZE); munmap(pBuf,BUF_SIZE);
} }

View File

@ -25,7 +25,7 @@ class ExecutableRunner : public Runner
{ {
Q_OBJECT Q_OBJECT
public: public:
ExecutableRunner(const QString& filename, const QString& arguments, const QString& workDir, ExecutableRunner(const QString& filename, const QStringList& arguments, const QString& workDir,
QObject* parent = nullptr); QObject* parent = nullptr);
ExecutableRunner(const ExecutableRunner&)=delete; ExecutableRunner(const ExecutableRunner&)=delete;
ExecutableRunner& operator=(const ExecutableRunner&)=delete; ExecutableRunner& operator=(const ExecutableRunner&)=delete;

View File

@ -25,7 +25,7 @@
#endif #endif
OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir, OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QStringList& arguments, const QString& workDir,
const QVector<POJProblemCase>& problemCases, QObject *parent): const QVector<POJProblemCase>& problemCases, QObject *parent):
Runner(filename,arguments,workDir,parent), Runner(filename,arguments,workDir,parent),
mExecTimeout(0), mExecTimeout(0),
@ -37,7 +37,7 @@ OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QStrin
setWaitForFinishTime(100); setWaitForFinishTime(100);
} }
OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir, OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QStringList& arguments, const QString& workDir,
POJProblemCase problemCase, QObject *parent): POJProblemCase problemCase, QObject *parent):
Runner(filename,arguments,workDir,parent), Runner(filename,arguments,workDir,parent),
mExecTimeout(0), mExecTimeout(0),
@ -64,7 +64,7 @@ void OJProblemCasesRunner::runCase(int index,POJProblemCase problemCase)
QElapsedTimer elapsedTimer; QElapsedTimer elapsedTimer;
bool execTimeouted = false; bool execTimeouted = false;
process.setProgram(mFilename); process.setProgram(mFilename);
process.setArguments(splitProcessCommand(mArguments)); process.setArguments(mArguments);
process.setWorkingDirectory(mWorkDir); process.setWorkingDirectory(mWorkDir);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString path = env.value("PATH"); QString path = env.value("PATH");

View File

@ -25,10 +25,10 @@ class OJProblemCasesRunner : public Runner
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir, explicit OJProblemCasesRunner(const QString& filename, const QStringList& arguments, const QString& workDir,
const QVector<POJProblemCase>& problemCases, const QVector<POJProblemCase>& problemCases,
QObject *parent = nullptr); QObject *parent = nullptr);
explicit OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir, explicit OJProblemCasesRunner(const QString& filename, const QStringList& arguments, const QString& workDir,
POJProblemCase problemCase, POJProblemCase problemCase,
QObject *parent = nullptr); QObject *parent = nullptr);
OJProblemCasesRunner(const OJProblemCasesRunner&)=delete; OJProblemCasesRunner(const OJProblemCasesRunner&)=delete;

View File

@ -17,7 +17,7 @@
#include "runner.h" #include "runner.h"
#include <QDebug> #include <QDebug>
Runner::Runner(const QString &filename, const QString &arguments, const QString &workDir Runner::Runner(const QString &filename, const QStringList &arguments, const QString &workDir
,QObject *parent) : QThread(parent), ,QObject *parent) : QThread(parent),
mPausing(false), mPausing(false),
mStop(false), mStop(false),

View File

@ -23,7 +23,7 @@ class Runner : public QThread
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit Runner(const QString& filename, const QString& arguments, const QString& workDir, QObject *parent = nullptr); explicit Runner(const QString& filename, const QStringList& arguments, const QString& workDir, QObject *parent = nullptr);
Runner(const Runner&)=delete; Runner(const Runner&)=delete;
Runner operator=(const Runner&)=delete; Runner operator=(const Runner&)=delete;
@ -48,7 +48,7 @@ protected:
bool mPausing; bool mPausing;
bool mStop; bool mStop;
QString mFilename; QString mFilename;
QString mArguments; QStringList mArguments; // without argv[0]
QString mWorkDir; QString mWorkDir;
int mWaitForFinishTime; int mWaitForFinishTime;
}; };

View File

@ -2,6 +2,6 @@
<qresource prefix="/config"> <qresource prefix="/config">
<file alias="autolink.json">resources/autolink.json</file> <file alias="autolink.json">resources/autolink.json</file>
<file alias="codesnippets.json">resources/codesnippets.json</file> <file alias="codesnippets.json">resources/codesnippets.json</file>
<file alias="autolink-linux.json">resources/autolink-linux.json</file> <file alias="autolink-xdg.json">resources/autolink-xdg.json</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -248,6 +248,23 @@ int main(int argc, char *argv[])
qputenv("QT_QPA_PLATFORM", "windows:darkmode=2"); qputenv("QT_QPA_PLATFORM", "windows:darkmode=2");
#endif #endif
#ifdef Q_OS_MACOS
// in macOS GUI apps, `/usr/local/bin` is not in PATH by default
// follow the Unix way by prepending it to `/usr/bin`
{
QStringList pathList = getExecutableSearchPaths();
if (!pathList.contains("/usr/local/bin")) {
auto idxUsrBin = pathList.indexOf("/usr/bin");
if (idxUsrBin >= 0)
pathList.insert(idxUsrBin, "/usr/local/bin");
else
pathList.append("/usr/local/bin");
}
QString newPath = pathList.join(PATH_SEPARATOR);
qputenv("PATH", newPath.toUtf8());
}
#endif
QApplication app(argc, argv); QApplication app(argc, argv);
app.setAttribute(Qt::AA_UseHighDpiPixmaps); app.setAttribute(Qt::AA_UseHighDpiPixmaps);

View File

@ -1,4 +1,12 @@
[ [
{
"header": "fmt/core.h",
"links": "-lfmt"
},
{
"header": "math.h",
"links": "-lm"
},
{ {
"execUseUTF8": true, "execUseUTF8": true,
"header": "raylib.h", "header": "raylib.h",
@ -11,5 +19,13 @@
{ {
"header": "rturtle.h", "header": "rturtle.h",
"links": "-lrturtle" "links": "-lrturtle"
},
{
"header": "thread",
"links": "-lpthread"
},
{
"header": "threads.h",
"links": "-lpthread"
} }
] ]

View File

@ -203,13 +203,13 @@ QString Settings::Dirs::appResourceDir() const
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
return appDir(); return appDir();
#elif defined(Q_OS_LINUX)
// in AppImage PREFIX is not true, resolve from relative path
const static QString absoluteResourceDir(QDir(appDir()).absoluteFilePath("../share/" APP_NAME));
return absoluteResourceDir;
#elif defined(Q_OS_MACOS) #elif defined(Q_OS_MACOS)
// return QApplication::instance()->applicationDirPath(); // return QApplication::instance()->applicationDirPath();
return ""; return "";
#else // XDG desktop
// in AppImage or tarball PREFIX is not true, resolve from relative path
const static QString absoluteResourceDir(QDir(appDir()).absoluteFilePath("../share/" APP_NAME));
return absoluteResourceDir;
#endif #endif
} }
@ -218,13 +218,13 @@ QString Settings::Dirs::appLibexecDir() const
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
return appDir(); return appDir();
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_MACOS)
// in AppImage LIBEXECDIR is not true, resolve from relative path return QApplication::instance()->applicationDirPath();
#else // XDG desktop
// in AppImage or tarball LIBEXECDIR is not true, resolve from relative path
const static QString relativeLibExecDir(QDir(PREFIX "/bin").relativeFilePath(LIBEXECDIR "/" APP_NAME)); const static QString relativeLibExecDir(QDir(PREFIX "/bin").relativeFilePath(LIBEXECDIR "/" APP_NAME));
const static QString absoluteLibExecDir(QDir(appDir()).absoluteFilePath(relativeLibExecDir)); const static QString absoluteLibExecDir(QDir(appDir()).absoluteFilePath(relativeLibExecDir));
return absoluteLibExecDir; return absoluteLibExecDir;
#elif defined(Q_OS_MACOS)
return QApplication::instance()->applicationDirPath();
#endif #endif
} }
@ -1473,16 +1473,16 @@ void Settings::Editor::doLoad()
mRightEdgeLineColor = colorValue("right_edge_line_color",Qt::yellow); mRightEdgeLineColor = colorValue("right_edge_line_color",Qt::yellow);
//Editor font //Editor font
#ifdef Q_OS_WIN mFontName = stringValue("font_name",DEFAULT_MONO_FONT);
mFontName = stringValue("font_name","consolas"); QString defaultCjkFontName = CJK_MONO_FONT_SC;
mNonAsciiFontName = stringValue("non_ascii_font_name","consolas"); QString defaultLocaleName = QLocale::system().name();
#elif defined(Q_OS_MACOS) if (defaultLocaleName == "zh_TW")
mFontName = stringValue("font_name","Menlo"); defaultCjkFontName = CJK_MONO_FONT_TC;
mNonAsciiFontName = stringValue("non_ascii_font_name","PingFang SC"); else if (defaultLocaleName == "ja_JP")
#else defaultCjkFontName = CJK_MONO_FONT_J;
mFontName = stringValue("font_name","Dejavu Sans Mono"); else if (defaultLocaleName == "ko_KR")
mNonAsciiFontName = stringValue("non_ascii_font_name","Dejavu Sans Mono"); defaultCjkFontName = CJK_MONO_FONT_K;
#endif mNonAsciiFontName = stringValue("non_ascii_font_name",defaultCjkFontName);
mFontSize = intValue("font_size",12); mFontSize = intValue("font_size",12);
mFontOnlyMonospaced = boolValue("font_only_monospaced",true); mFontOnlyMonospaced = boolValue("font_only_monospaced",true);
mLineSpacing = doubleValue("line_spacing",1.0); mLineSpacing = doubleValue("line_spacing",1.0);
@ -1504,11 +1504,7 @@ void Settings::Editor::doLoad()
mGutterLineNumbersStartZero = boolValue("gutter_line_numbers_start_zero",false); mGutterLineNumbersStartZero = boolValue("gutter_line_numbers_start_zero",false);
mGutterUseCustomFont = boolValue("gutter_use_custom_font",false); mGutterUseCustomFont = boolValue("gutter_use_custom_font",false);
#ifdef Q_OS_WIN mGutterFontName = stringValue("gutter_font_name",DEFAULT_MONO_FONT);
mGutterFontName = stringValue("gutter_font_name","consolas");
#else
mGutterFontName = stringValue("gutter_font_name","Dejavu Sans Mono");
#endif
mGutterFontSize = intValue("gutter_font_size",12); mGutterFontSize = intValue("gutter_font_size",12);
mGutterFontOnlyMonospaced = boolValue("gutter_font_only_monospaced",true); mGutterFontOnlyMonospaced = boolValue("gutter_font_only_monospaced",true);
@ -2999,11 +2995,11 @@ static void setDebugOptions(Settings::PCompilerSet pSet, bool enableAsan = false
pSet->setCompileOption(CC_CMD_OPT_USE_PIPE, COMPILER_OPTION_ON); pSet->setCompileOption(CC_CMD_OPT_USE_PIPE, COMPILER_OPTION_ON);
if (enableAsan) { if (enableAsan) {
#ifdef __aarch64__
pSet->setCompileOption(CC_CMD_OPT_ADDRESS_SANITIZER, "hwaddress");
#else
pSet->setCompileOption(CC_CMD_OPT_ADDRESS_SANITIZER, "address"); pSet->setCompileOption(CC_CMD_OPT_ADDRESS_SANITIZER, "address");
// pSet->setCustomCompileParams("-fsanitize=address"); #endif
// pSet->setUseCustomCompileParams(true);
// pSet->setCustomLinkParams("-fsanitize=address");
// pSet->setUseCustomLinkParams(true);
} }
//Some windows gcc don't correctly support this //Some windows gcc don't correctly support this
//pSet->setCompileOption(CC_CMD_OPT_STACK_PROTECTOR, "-strong"); //pSet->setCompileOption(CC_CMD_OPT_STACK_PROTECTOR, "-strong");
@ -3064,8 +3060,8 @@ bool Settings::CompilerSets::addSets(const QString &folder, const QString& c_pro
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
# if defined(__x86_64__) || __SIZEOF_POINTER__ == 4 # if defined(__x86_64__) || defined(__aarch64__) || __SIZEOF_POINTER__ == 4
mDefaultIndex = (int)mList.size() - 1; // x86-64 Linux or 32-bit Unix, default to "debug with ASan" mDefaultIndex = (int)mList.size() - 1; // x86-64, AArch64 Linux or 32-bit Unix, default to "debug with ASan"
# else # else
mDefaultIndex = (int)mList.size() - 2; // other Unix, where ASan can be very slow, default to "debug" mDefaultIndex = (int)mList.size() - 2; // other Unix, where ASan can be very slow, default to "debug"
# endif # endif
@ -3576,17 +3572,20 @@ void Settings::Environment::doLoad()
{ {
//Appearance //Appearance
mTheme = stringValue("theme","dark"); mTheme = stringValue("theme","dark");
QString defaultFontName = "Segoe UI"; QString defaultFontName = DEFAULT_UI_FONT;
QString defaultLocaleName = QLocale::system().name(); QString defaultLocaleName = QLocale::system().name();
if (defaultLocaleName == "zh_CN") { {
QString fontName; QString fontName;
#ifdef Q_OS_WINDOWS if (defaultLocaleName == "zh_CN")
fontName = "Microsoft Yahei"; fontName = CJK_UI_FONT_SC;
#elif defined(Q_OS_MACOS) else if (defaultLocaleName == "zh_TW")
fontName = "PingFang SC"; fontName = CJK_UI_FONT_TC;
#elif defined(Q_OS_LINUX) else if (defaultLocaleName == "ja_JP")
fontName = "Noto Sans CJK"; fontName = CJK_UI_FONT_J;
#endif else if (defaultLocaleName == "ko_KR")
fontName = CJK_UI_FONT_K;
else
fontName = DEFAULT_UI_FONT;
QFont font(fontName); QFont font(fontName);
if (font.exactMatch()) { if (font.exactMatch()) {
defaultFontName = fontName; defaultFontName = fontName;
@ -3608,58 +3607,85 @@ void Settings::Environment::doLoad()
if (!fileExists(mDefaultOpenFolder)) { if (!fileExists(mDefaultOpenFolder)) {
mDefaultOpenFolder = QDir::currentPath(); mDefaultOpenFolder = QDir::currentPath();
} }
#ifdef Q_OS_LINUX
#define SYSTEM_TERMINAL(term) "/usr/bin/" #term, "/usr/local/bin/" #term using AP = TerminalEmulatorArgumentsPattern;
const static QString terminals[] { struct TerminalSearchItem {
QString appName;
AP argsPattern;
};
#ifdef Q_OS_WINDOWS
const TerminalSearchItem terminals[] {
/* explicitly installed terminals */
/* system */
{"conhost.exe", AP::ImplicitSystem}, // dummy for system default
/* will not actually be searched, just a list for users who dig into here */
{"conhost.exe", AP::MinusMinusAppendArgs}, // yes, it accepts GNU-style (--) arguments
{"wt.exe", AP::MinusMinusAppendArgs}, // generally okay, but “Test” does not work
{"alacritty.exe", AP::MinusEAppendArgs}, // GPU-accelerated
{"C:/Program Files/Alacritty/alacritty.exe", AP::MinusEAppendArgs},
{"C:/Program Files/konsole/bin/konsole.exe", AP::MinusEAppendArgs}, // generally okay, but “Test” does not work
{"C:/Program Files/Git/usr/bin/mintty.exe", AP::MinusEAppendArgs}, // Git Mintty
{"C:/msys64/usr/bin/mintty.exe", AP::MinusEAppendArgs}, // MSYS2 Mintty
};
#else
const TerminalSearchItem terminals[] {
/* modern, specialized or stylized terminal -- user who installed them are likely to prefer them */ /* modern, specialized or stylized terminal -- user who installed them are likely to prefer them */
SYSTEM_TERMINAL(alacritty), // GPU-accelerated {"alacritty", AP::MinusEAppendArgs}, // GPU-accelerated
SYSTEM_TERMINAL(kitty), // GPU-accelerated {"kitty", AP::MinusEAppendArgs}, // GPU-accelerated
SYSTEM_TERMINAL(wayst), // GPU-accelerated {"wayst", AP::MinusEAppendArgs}, // GPU-accelerated
SYSTEM_TERMINAL(tilix), // tiling
SYSTEM_TERMINAL(cool-retro-term), // old CRT style
/* default terminal for DE */ {"coreterminal", AP::MinusEAppendCommandLine}, // lightweighted
SYSTEM_TERMINAL(konsole), // KDE {"kermit", AP::MinusEAppendCommandLine}, // lightweighted
SYSTEM_TERMINAL(deepin-terminal), // DDE {"roxterm", AP::MinusEAppendCommandLine}, // lightweighted
SYSTEM_TERMINAL(qterminal), // LXQt {"sakura", AP::MinusEAppendCommandLine}, // lightweighted
SYSTEM_TERMINAL(lxterminal), // LXDE {"termit", AP::MinusEAppendArgs}, // Lua scripting
{"termite", AP::MinusEAppendCommandLine}, // tiling, keyboard-centric
{"tilix", AP::MinusEAppendArgs}, // tiling
{"cool-retro-term", AP::MinusEAppendArgs}, // old CRT style
/* default terminal for XDG DE -- macOS user who installed them are likely to prefer them */
{"deepin-terminal", AP::MinusEAppendArgs}, // DDE
{"konsole", AP::MinusEAppendArgs}, // KDE
{"gnome-terminal", AP::MinusMinusAppendArgs}, // GNOME
{"io.elementary.terminal", AP::MinusEAppendCommandLine}, // Pantheon (elementary OS)
{"lxterminal", AP::MinusEAppendArgs}, // LXDE
{"mate-terminal", AP::MinusXAppendArgs}, // MATE
{"qterminal", AP::MinusEAppendArgs}, // LXQt
{"terminator", AP::MinusXAppendArgs}, // tiling, also seen in SBC images
{"terminology", AP::MinusEAppendCommandLine}, // Enlightenment
{"xfce4-terminal", AP::MinusXAppendArgs}, // Xfce
/* bundled terminal in AppImage */ /* bundled terminal in AppImage */
"alacritty", {"./alacritty", AP::MinusEAppendArgs},
/* compatible, with minor issue */ /* compatible, with minor issue */
SYSTEM_TERMINAL(kgx), // GNOME Console, confirm to quit {"kgx", AP::MinusMinusAppendArgs}, // GNOME Console, confirm to quit
SYSTEM_TERMINAL(coreterminal), // not so conforming when parsing args
SYSTEM_TERMINAL(sakura), // not so conforming when parsing args
/* compatible, without out-of-box hidpi support */ /* compatible, without out-of-box hidpi support on Linux */
SYSTEM_TERMINAL(mlterm), {"mlterm", AP::MinusEAppendArgs},
SYSTEM_TERMINAL(st), {"st", AP::MinusEAppendArgs},
SYSTEM_TERMINAL(terminology), // also not so conforming when parsing args {"urxvt", AP::MinusEAppendArgs},
SYSTEM_TERMINAL(urxvt), {"xterm", AP::MinusEAppendArgs},
SYSTEM_TERMINAL(xterm), {"zutty", AP::MinusEAppendArgs},
SYSTEM_TERMINAL(zutty),
/* macOS system */
{"/Applications/iTerm.app/Contents/MacOS/iTerm2", AP::WriteCommandLineToTempFileThenTempFilename},
{"/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", AP::WriteCommandLineToTempFileThenTempFilename},
{"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", AP::WriteCommandLineToTempFileThenTempFilename},
/* fallbacks */ /* fallbacks */
SYSTEM_TERMINAL(foot), // Wayland only {"foot", AP::MinusEAppendArgs}, // Wayland only
SYSTEM_TERMINAL(x-terminal-emulator), // Debian alternatives
/* parameter incompatible */ /* parameter incompatible */
// "gnome-terminal", // "guake", // drop down
// "guake", // "hyper", // no execute support
// "hyper", // "liri-terminal", // no execute support
// "io.elementary.terminal", // "station", // no execute support
// "kermit", // "tilda", // drop down
// "liri-terminal",
// "mate-terminal",
// "roxterm",
// "station",
// "terminator",
// "termite",
// "tilda",
// "xfce4-terminal",
// "yakuake",
/* incompatible -- other */ /* incompatible -- other */
// "aterm", // AUR broken, unable to test // "aterm", // AUR broken, unable to test
@ -3667,22 +3693,57 @@ void Settings::Environment::doLoad()
// "rxvt", // no unicode support // "rxvt", // no unicode support
// "shellinabox", // AUR broken, unable to test // "shellinabox", // AUR broken, unable to test
}; };
#undef SYSTEM_TERMINAL #endif
auto checkAndSetTerminalPath = [this](QString terminalPath) -> bool { auto checkAndSetTerminalPath = [this](const TerminalSearchItem &searchItem) -> bool {
QDir appDir(pSettings->dirs().appDir()); #define DO_CHECK_AND_SET do { \
QString absoluteTerminalPath = appDir.absoluteFilePath(terminalPath); if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) { \
QFileInfo termPathInfo(absoluteTerminalPath); mTerminalPath = searchItem.appName; \
if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) { mTerminalArgumentsPattern = searchItem.argsPattern; \
mTerminalPath = terminalPath; return true; \
return true; } \
} else { } while (0)
return false;
switch (getPathUnixExecSemantics(searchItem.appName)) {
case UnixExecSemantics::Absolute: {
QFileInfo termPathInfo(searchItem.appName);
DO_CHECK_AND_SET;
break;
} }
case UnixExecSemantics::RelativeToCwd: {
QDir appDir(pSettings->dirs().appDir());
QString absoluteTerminalPath = appDir.absoluteFilePath(searchItem.appName);
QFileInfo termPathInfo(absoluteTerminalPath);
DO_CHECK_AND_SET;
break;
}
case UnixExecSemantics::SearchInPath: {
auto pathList = getExecutableSearchPaths();
for (auto &dir: pathList) {
QString absoluteTerminalPath = QDir(dir).absoluteFilePath(searchItem.appName);
QFileInfo termPathInfo(absoluteTerminalPath);
DO_CHECK_AND_SET;
}
break;
}
}
return false;
}; };
#undef DO_CHECK_AND_SET
// check saved terminal path // check saved terminal path
if (!checkAndSetTerminalPath(stringValue("terminal_path", ""))) { QString savedTerminalPath = stringValue("terminal_path", "");
int savedArgsPattern_ = intValue("terminal_arguments_pattern",
#ifdef Q_OS_MACOS
// macOS: old versions have set Terminal.app as default terminal
// fallback to temp file to work with Terminal.app for smooth migration
int(AP::WriteCommandLineToTempFileThenTempFilename)
#else
int(AP::MinusEAppendArgs) // Linux: keep old behaviour
#endif
);
AP savedArgsPattern = static_cast<AP>(savedArgsPattern_);
if (!checkAndSetTerminalPath(TerminalSearchItem{savedTerminalPath, savedArgsPattern})) {
// if saved terminal path is invalid, try determing terminal from our list // if saved terminal path is invalid, try determing terminal from our list
for (auto terminal: terminals) { for (auto terminal: terminals) {
if (checkAndSetTerminalPath(terminal)) if (checkAndSetTerminalPath(terminal))
@ -3691,11 +3752,6 @@ void Settings::Environment::doLoad()
} }
mAStylePath = includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+"astyle"; mAStylePath = includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+"astyle";
#elif defined(Q_OS_MACOS)
mTerminalPath = stringValue("terminal_path",
"/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal");
mAStylePath = includeTrailingPathDelimiter(pSettings->dirs().appLibexecDir())+"astyle";
#endif
mHideNonSupportFilesInFileView=boolValue("hide_non_support_files_file_view",true); mHideNonSupportFilesInFileView=boolValue("hide_non_support_files_file_view",true);
mOpenFilesInSingleInstance = boolValue("open_files_in_single_instance",false); mOpenFilesInSingleInstance = boolValue("open_files_in_single_instance",false);
} }
@ -3757,13 +3813,11 @@ QString Settings::Environment::terminalPath() const
QString Settings::Environment::terminalPathForExec() const QString Settings::Environment::terminalPathForExec() const
{ {
#ifdef Q_OS_LINUX if (getPathUnixExecSemantics(mTerminalPath) == UnixExecSemantics::RelativeToCwd) {
// `mTerminalPath` can be reletive (bundled terminal in AppImage). QDir appDir(pSettings->dirs().appDir());
QDir appDir(pSettings->dirs().appDir()); return appDir.absoluteFilePath(mTerminalPath);
return appDir.absoluteFilePath(mTerminalPath); } else
#else return mTerminalPath;
return mTerminalPath;
#endif
} }
void Settings::Environment::setTerminalPath(const QString &terminalPath) void Settings::Environment::setTerminalPath(const QString &terminalPath)
@ -3781,6 +3835,16 @@ void Settings::Environment::setAStylePath(const QString &aStylePath)
mAStylePath = aStylePath; mAStylePath = aStylePath;
} }
TerminalEmulatorArgumentsPattern Settings::Environment::terminalArgumentsPattern() const
{
return mTerminalArgumentsPattern;
}
void Settings::Environment::setTerminalArgumentsPattern(const TerminalEmulatorArgumentsPattern &argsPattern)
{
mTerminalArgumentsPattern = argsPattern;
}
bool Settings::Environment::useCustomIconSet() const bool Settings::Environment::useCustomIconSet() const
{ {
return mUseCustomIconSet; return mUseCustomIconSet;
@ -3845,10 +3909,9 @@ void Settings::Environment::doSave()
saveValue("current_folder",mCurrentFolder); saveValue("current_folder",mCurrentFolder);
saveValue("default_open_folder",mDefaultOpenFolder); saveValue("default_open_folder",mDefaultOpenFolder);
#ifndef Q_OS_WIN
saveValue("terminal_path",mTerminalPath); saveValue("terminal_path",mTerminalPath);
saveValue("terminal_arguments_pattern",int(mTerminalArgumentsPattern));
saveValue("asyle_path",mAStylePath); saveValue("asyle_path",mAStylePath);
#endif
saveValue("hide_non_support_files_file_view",mHideNonSupportFilesInFileView); saveValue("hide_non_support_files_file_view",mHideNonSupportFilesInFileView);
saveValue("open_files_in_single_instance",mOpenFilesInSingleInstance); saveValue("open_files_in_single_instance",mOpenFilesInSingleInstance);
@ -4111,13 +4174,7 @@ void Settings::Executor::doLoad()
mProblemCaseValidateType =(ProblemCaseValidateType)intValue("problem_case_validate_type", (int)ProblemCaseValidateType::Exact); mProblemCaseValidateType =(ProblemCaseValidateType)intValue("problem_case_validate_type", (int)ProblemCaseValidateType::Exact);
mRedirectStderrToToolLog = boolValue("redirect_stderr_to_toollog", false); mRedirectStderrToToolLog = boolValue("redirect_stderr_to_toollog", false);
#ifdef Q_OS_WIN mCaseEditorFontName = stringValue("case_editor_font_name",DEFAULT_MONO_FONT);
mCaseEditorFontName = stringValue("case_editor_font_name","consolas");
#elif defined(Q_OS_MACOS)
mCaseEditorFontName = stringValue("case_editor_font_name", "Menlo");
#else
mCaseEditorFontName = stringValue("case_editor_font_name","Dejavu Sans Mono");
#endif
mCaseEditorFontSize = intValue("case_editor_font_size",11); mCaseEditorFontSize = intValue("case_editor_font_size",11);
mCaseEditorFontOnlyMonospaced = boolValue("case_editor_font_only_monospaced",true); mCaseEditorFontOnlyMonospaced = boolValue("case_editor_font_only_monospaced",true);
int case_timeout = intValue("case_timeout", -1); int case_timeout = intValue("case_timeout", -1);

View File

@ -575,6 +575,9 @@ public:
QString AStylePath() const; QString AStylePath() const;
void setAStylePath(const QString &aStylePath); void setAStylePath(const QString &aStylePath);
TerminalEmulatorArgumentsPattern terminalArgumentsPattern() const;
void setTerminalArgumentsPattern(const TerminalEmulatorArgumentsPattern &argsPattern);
bool useCustomIconSet() const; bool useCustomIconSet() const;
void setUseCustomIconSet(bool newUseCustomIconSet); void setUseCustomIconSet(bool newUseCustomIconSet);
@ -606,6 +609,7 @@ public:
QString mDefaultOpenFolder; QString mDefaultOpenFolder;
QString mTerminalPath; QString mTerminalPath;
QString mAStylePath; QString mAStylePath;
TerminalEmulatorArgumentsPattern mTerminalArgumentsPattern;
bool mHideNonSupportFilesInFileView; bool mHideNonSupportFilesInFileView;
bool mOpenFilesInSingleInstance; bool mOpenFilesInSingleInstance;
// _Base interface // _Base interface

View File

@ -19,6 +19,7 @@
#include "../settings.h" #include "../settings.h"
#include "../iconsmanager.h" #include "../iconsmanager.h"
#include "../systemconsts.h" #include "../systemconsts.h"
#include "../compiler/executablerunner.h"
#include <QFileDialog> #include <QFileDialog>
@ -27,6 +28,16 @@ EnvironmentProgramsWidget::EnvironmentProgramsWidget(const QString& name, const
ui(new Ui::EnvironmentProgramsWidget) ui(new Ui::EnvironmentProgramsWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
QFont monoFont(DEFAULT_MONO_FONT);
ui->rbImplicitSystem->setFont(monoFont);
ui->rbMinusEAppendArgs->setFont(monoFont);
ui->rbMinusXAppendArgs->setFont(monoFont);
ui->rbMinusMinusAppendArgs->setFont(monoFont);
ui->rbMinusEAppendCommandLine->setFont(monoFont);
ui->rbWriteCommandLineToTempFileThenTempFilename->setFont(monoFont);
#ifndef Q_OS_MACOS
hideMacosSpecificPattern();
#endif
} }
EnvironmentProgramsWidget::~EnvironmentProgramsWidget() EnvironmentProgramsWidget::~EnvironmentProgramsWidget()
@ -34,14 +45,62 @@ EnvironmentProgramsWidget::~EnvironmentProgramsWidget()
delete ui; delete ui;
} }
void EnvironmentProgramsWidget::hideMacosSpecificPattern()
{
ui->rbWriteCommandLineToTempFileThenTempFilename->setVisible(false);
ui->rbWriteCommandLineToTempFileThenTempFilename->setEnabled(false);
ui->pbWriteCommandLineToTempFileThenTempFilename->setVisible(false);
ui->pbWriteCommandLineToTempFileThenTempFilename->setEnabled(false);
}
void EnvironmentProgramsWidget::testTerminal(const TerminalEmulatorArgumentsPattern &pattern)
{
auto [filename, arguments, fileOwner] = wrapCommandForTerminalEmulator(ui->txtTerminal->text(), pattern, {defaultShell(), "-c", "echo hello; sleep 3"});
ExecutableRunner runner(filename, arguments, "", nullptr);
runner.start();
runner.wait();
}
void EnvironmentProgramsWidget::doLoad() void EnvironmentProgramsWidget::doLoad()
{ {
ui->txtTerminal->setText(pSettings->environment().terminalPath()); ui->txtTerminal->setText(pSettings->environment().terminalPath());
switch (pSettings->environment().terminalArgumentsPattern()) {
case TerminalEmulatorArgumentsPattern::ImplicitSystem:
ui->rbImplicitSystem->setChecked(true);
break;
case TerminalEmulatorArgumentsPattern::MinusEAppendArgs:
ui->rbMinusEAppendArgs->setChecked(true);
break;
case TerminalEmulatorArgumentsPattern::MinusXAppendArgs:
ui->rbMinusXAppendArgs->setChecked(true);
break;
case TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs:
ui->rbMinusMinusAppendArgs->setChecked(true);
break;
case TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine:
ui->rbMinusEAppendCommandLine->setChecked(true);
break;
case TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename:
ui->rbWriteCommandLineToTempFileThenTempFilename->setChecked(true);
break;
}
} }
void EnvironmentProgramsWidget::doSave() void EnvironmentProgramsWidget::doSave()
{ {
pSettings->environment().setTerminalPath(ui->txtTerminal->text()); pSettings->environment().setTerminalPath(ui->txtTerminal->text());
if (ui->rbImplicitSystem->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::ImplicitSystem);
if (ui->rbMinusEAppendArgs->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusEAppendArgs);
if (ui->rbMinusXAppendArgs->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusXAppendArgs);
if (ui->rbMinusMinusAppendArgs->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs);
if (ui->rbMinusEAppendCommandLine->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine);
if (ui->rbWriteCommandLineToTempFileThenTempFilename->isChecked())
pSettings->environment().setTerminalArgumentsPattern(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename);
pSettings->environment().save(); pSettings->environment().save();
} }
@ -61,3 +120,62 @@ void EnvironmentProgramsWidget::on_btnChooseTerminal_clicked()
ui->txtTerminal->setText(filename); ui->txtTerminal->setText(filename);
} }
} }
void EnvironmentProgramsWidget::on_txtTerminal_textChanged(const QString &terminalPath)
{
QString terminalPathForExec;
if (getPathUnixExecSemantics(terminalPath) == UnixExecSemantics::RelativeToCwd) {
QDir appDir(pSettings->dirs().appDir());
terminalPathForExec = appDir.absoluteFilePath(terminalPath);
} else
terminalPathForExec = terminalPath;
QString terminalPathEscaped = escapeArgument(terminalPathForExec, true);
QString shell = defaultShell();
QStringList execArgs{shell, "-c", "echo hello; sleep 3"};
auto displayCommand = [this, &execArgs](const TerminalEmulatorArgumentsPattern &pattern) {
auto [filename, arguments, fileOwner] = wrapCommandForTerminalEmulator(ui->txtTerminal->text(), pattern, execArgs);
for (auto &arg : arguments)
arg = escapeArgument(arg, false);
return escapeArgument(filename, true) + " " + arguments.join(' ');
};
ui->rbImplicitSystem->setText(displayCommand(TerminalEmulatorArgumentsPattern::ImplicitSystem));
ui->rbMinusEAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusEAppendArgs));
ui->rbMinusXAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusXAppendArgs));
ui->rbMinusMinusAppendArgs->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs));
ui->rbMinusEAppendCommandLine->setText(displayCommand(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine));
if (ui->rbWriteCommandLineToTempFileThenTempFilename->isEnabled())
ui->rbWriteCommandLineToTempFileThenTempFilename->setText(displayCommand(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename));
}
void EnvironmentProgramsWidget::on_pbImplicitSystem_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::ImplicitSystem);
}
void EnvironmentProgramsWidget::on_pbMinusEAppendArgs_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::MinusEAppendArgs);
}
void EnvironmentProgramsWidget::on_pbMinusXAppendArgs_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::MinusXAppendArgs);
}
void EnvironmentProgramsWidget::on_pbMinusMinusAppendArgs_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs);
}
void EnvironmentProgramsWidget::on_pbMinusEAppendCommandLine_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine);
}
void EnvironmentProgramsWidget::on_pbWriteCommandLineToTempFileThenTempFilename_clicked()
{
testTerminal(TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename);
}

View File

@ -18,6 +18,7 @@
#define ENVIRONMENTPROGRAMSWIDGET_H #define ENVIRONMENTPROGRAMSWIDGET_H
#include "settingswidget.h" #include "settingswidget.h"
#include "utils.h"
namespace Ui { namespace Ui {
class EnvironmentProgramsWidget; class EnvironmentProgramsWidget;
@ -30,6 +31,10 @@ class EnvironmentProgramsWidget : public SettingsWidget
public: public:
explicit EnvironmentProgramsWidget(const QString& name, const QString& group, QWidget *parent = nullptr); explicit EnvironmentProgramsWidget(const QString& name, const QString& group, QWidget *parent = nullptr);
~EnvironmentProgramsWidget(); ~EnvironmentProgramsWidget();
void hideMacosSpecificPattern();
private:
void testTerminal(const TerminalEmulatorArgumentsPattern &pattern);
private: private:
Ui::EnvironmentProgramsWidget *ui; Ui::EnvironmentProgramsWidget *ui;
@ -41,6 +46,13 @@ protected:
void updateIcons(const QSize &size) override; void updateIcons(const QSize &size) override;
private slots: private slots:
void on_btnChooseTerminal_clicked(); void on_btnChooseTerminal_clicked();
void on_txtTerminal_textChanged(const QString &terminalPath);
void on_pbImplicitSystem_clicked();
void on_pbMinusEAppendArgs_clicked();
void on_pbMinusXAppendArgs_clicked();
void on_pbMinusMinusAppendArgs_clicked();
void on_pbMinusEAppendCommandLine_clicked();
void on_pbWriteCommandLineToTempFileThenTempFilename_clicked();
}; };
#endif // ENVIRONMENTPROGRAMSWIDGET_H #endif // ENVIRONMENTPROGRAMSWIDGET_H

View File

@ -35,7 +35,141 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="1" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Terminal emulator arguments pattern</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QRadioButton" name="rbImplicitSystem">
<property name="text">
<string>sh -c &quot;echo hello; sleep 3&quot;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pbImplicitSystem">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="rbMinusEAppendArgs">
<property name="text">
<string>term -e sh -c &quot;echo hello; sleep 3&quot;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pbMinusEAppendArgs">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="rbMinusXAppendArgs">
<property name="text">
<string>term -x sh -c &quot;echo hello; sleep 3&quot;</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pbMinusXAppendArgs">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="rbMinusMinusAppendArgs">
<property name="text">
<string>term -- sh -c &quot;echo hello; sleep 3&quot;</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="pbMinusMinusAppendArgs">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QRadioButton" name="rbMinusEAppendCommandLine">
<property name="text">
<string>term -e &quot;sh -c \&quot;echo hello; sleep 3\&quot;&quot;</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="pbMinusEAppendCommandLine">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QRadioButton" name="rbWriteCommandLineToTempFileThenTempFilename">
<property name="text">
<string>term /tmp/redpanda_XXXXXX.command</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="pbWriteCommandLineToTempFileThenTempFilename">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QLabel" name="lExpectedBahavior0">
<property name="text">
<string>On clicking “Test” for the correct pattern, the terminal emulator</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="3">
<widget class="QLabel" name="lExpectedBahavior1">
<property name="text">
<string>• pops up;</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="3">
<widget class="QLabel" name="lExpectedBahavior2">
<property name="text">
<string>• shows “hello”; and</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="3">
<widget class="QLabel" name="lExpectedBahavior3">
<property name="text">
<string>• quits in 3 seconds.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>

View File

@ -21,12 +21,15 @@
#include "../systemconsts.h" #include "../systemconsts.h"
#include <QFileDialog> #include <QFileDialog>
#include <QJsonDocument>
#include <QJsonArray>
ExecutorGeneralWidget::ExecutorGeneralWidget(const QString& name, const QString& group, QWidget *parent): ExecutorGeneralWidget::ExecutorGeneralWidget(const QString& name, const QString& group, QWidget *parent):
SettingsWidget(name,group,parent), SettingsWidget(name,group,parent),
ui(new Ui::ExecutorGeneralWidget) ui(new Ui::ExecutorGeneralWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->txtParsedArgsInJson->setFont(QFont(DEFAULT_MONO_FONT));
} }
ExecutorGeneralWidget::~ExecutorGeneralWidget() ExecutorGeneralWidget::~ExecutorGeneralWidget()
@ -73,3 +76,11 @@ void ExecutorGeneralWidget::updateIcons(const QSize &/*size*/)
pIconsManager->setIcon(ui->btnBrowse,IconsManager::ACTION_FILE_OPEN_FOLDER); pIconsManager->setIcon(ui->btnBrowse,IconsManager::ACTION_FILE_OPEN_FOLDER);
} }
void ExecutorGeneralWidget::on_txtExecuteParamaters_textChanged(const QString &commandLine)
{
QStringList parsed = splitProcessCommand(commandLine);
QJsonArray obj = QJsonArray::fromStringList(parsed);
ui->txtParsedArgsInJson->setText(QJsonDocument{obj}.toJson());
}

View File

@ -43,6 +43,8 @@ private slots:
void on_btnBrowse_clicked(); void on_btnBrowse_clicked();
// SettingsWidget interface // SettingsWidget interface
void on_txtExecuteParamaters_textChanged(const QString &commandLine);
protected: protected:
void updateIcons(const QSize &size) override; void updateIcons(const QSize &size) override;
}; };

View File

@ -55,10 +55,21 @@
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QLineEdit" name="txtExecuteParamaters"/> <widget class="QLineEdit" name="txtExecuteParamaters"/>
</item> </item>
<item>
<widget class="QLabel" name="labelParseArgsInJson">
<property name="text">
<string>Parsed argv array (represented in JSON):</string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="txtParsedArgsInJson">
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -75,7 +86,6 @@
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -88,7 +98,6 @@
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -101,7 +110,6 @@
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>

View File

@ -36,6 +36,7 @@
#include "environmentshortcutwidget.h" #include "environmentshortcutwidget.h"
#include "environmentfolderswidget.h" #include "environmentfolderswidget.h"
#include "environmentperformancewidget.h" #include "environmentperformancewidget.h"
#include "environmentprogramswidget.h"
#include "executorgeneralwidget.h" #include "executorgeneralwidget.h"
#include "executorproblemsetwidget.h" #include "executorproblemsetwidget.h"
#include "debuggeneralwidget.h" #include "debuggeneralwidget.h"
@ -58,8 +59,7 @@
#include "environmentfileassociationwidget.h" #include "environmentfileassociationwidget.h"
#include "projectversioninfowidget.h" #include "projectversioninfowidget.h"
#endif #endif
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // XDG desktop
#include "environmentprogramswidget.h"
#include "formatterpathwidget.h" #include "formatterpathwidget.h"
#endif #endif
#include <QDebug> #include <QDebug>
@ -156,10 +156,8 @@ PSettingsDialog SettingsDialog::optionDialog()
widget = new EnvironmentShortcutWidget(tr("Shortcuts"),tr("Environment")); widget = new EnvironmentShortcutWidget(tr("Shortcuts"),tr("Environment"));
dialog->addWidget(widget); dialog->addWidget(widget);
#ifdef Q_OS_LINUX
widget = new EnvironmentProgramsWidget(tr("Terminal"),tr("Environment")); widget = new EnvironmentProgramsWidget(tr("Terminal"),tr("Environment"));
dialog->addWidget(widget); dialog->addWidget(widget);
#endif
widget = new EnvironmentPerformanceWidget(tr("Performance"),tr("Environment")); widget = new EnvironmentPerformanceWidget(tr("Performance"),tr("Environment"));
dialog->addWidget(widget); dialog->addWidget(widget);
@ -230,7 +228,7 @@ PSettingsDialog SettingsDialog::optionDialog()
widget = new FormatterGeneralWidget(tr("General"),tr("Code Formatter")); widget = new FormatterGeneralWidget(tr("General"),tr("Code Formatter"));
dialog->addWidget(widget); dialog->addWidget(widget);
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) // XDG desktop
widget = new FormatterPathWidget(tr("Program"),tr("Code Formatter")); widget = new FormatterPathWidget(tr("Program"),tr("Code Formatter"));
dialog->addWidget(widget); dialog->addWidget(widget);
#endif #endif

View File

@ -22,7 +22,6 @@
#define APP_SETTSINGS_FILENAME "redpandacpp.ini" #define APP_SETTSINGS_FILENAME "redpandacpp.ini"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#define CONSOLE_PAUSER "consolepauser.exe" #define CONSOLE_PAUSER "consolepauser.exe"
#define ASSEMBLER "nasm.exe"
#define GCC_PROGRAM "gcc.exe" #define GCC_PROGRAM "gcc.exe"
#define GPP_PROGRAM "g++.exe" #define GPP_PROGRAM "g++.exe"
#define GDB_PROGRAM "gdb.exe" #define GDB_PROGRAM "gdb.exe"
@ -40,9 +39,8 @@
#define SDCC_PROGRAM "sdcc.exe" #define SDCC_PROGRAM "sdcc.exe"
#define PACKIHX_PROGRAM "packihx.exe" #define PACKIHX_PROGRAM "packihx.exe"
#define MAKEBIN_PROGRAM "makebin.exe" #define MAKEBIN_PROGRAM "makebin.exe"
#elif defined(Q_OS_LINUX) #else // Unix
#define CONSOLE_PAUSER "consolepauser" #define CONSOLE_PAUSER "consolepauser"
#define ASSEMBLER "nasm"
#define GCC_PROGRAM "gcc" #define GCC_PROGRAM "gcc"
#define GPP_PROGRAM "g++" #define GPP_PROGRAM "g++"
#define GDB_PROGRAM "gdb" #define GDB_PROGRAM "gdb"
@ -61,28 +59,6 @@
#define SDCC_PROGRAM "sdcc" #define SDCC_PROGRAM "sdcc"
#define PACKIHX_PROGRAM "packihx" #define PACKIHX_PROGRAM "packihx"
#define MAKEBIN_PROGRAM "makebin" #define MAKEBIN_PROGRAM "makebin"
#elif defined(Q_OS_MACOS)
#define CONSOLE_PAUSER "consolepauser"
#define ASSEMBLER "nasm"
#define GCC_PROGRAM "gcc"
#define GPP_PROGRAM "g++"
#define GDB_PROGRAM "gdb"
#define GDB_SERVER_PROGRAM "gdbserver"
#define GDB32_PROGRAM "gdb32"
#define MAKE_PROGRAM "make"
#define WINDRES_PROGRAM ""
#define CLEAN_PROGRAM "rm -rf"
#define CPP_PROGRAM "cpp"
#define GIT_PROGRAM "git"
#define CLANG_PROGRAM "clang"
#define CLANG_CPP_PROGRAM "clang++"
#define LLDB_MI_PROGRAM "lldb-mi"
#define LLDB_SERVER_PROGRAM "lldb-server"
#define SDCC_PROGRAM "sdcc"
#define PACKIHX_PROGRAM "packihx"
#define MAKEBIN_PROGRAM "makebin"
#else
#error "Only support windows, Linux and MacOS now!"
#endif #endif
#define DEV_PROJECT_EXT "dev" #define DEV_PROJECT_EXT "dev"
@ -128,7 +104,7 @@
# define MAKEFILE_NAME "makefile.win" # define MAKEFILE_NAME "makefile.win"
# define XMAKEFILE_NAME "xmake.lua" # define XMAKEFILE_NAME "xmake.lua"
# define ALL_FILE_WILDCARD "*.*" # define ALL_FILE_WILDCARD "*.*"
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS) #else // Unix
# define PATH_SENSITIVITY Qt::CaseSensitive # define PATH_SENSITIVITY Qt::CaseSensitive
# define PATH_SEPARATOR ":" # define PATH_SEPARATOR ":"
# define LINE_BREAKER "\n" # define LINE_BREAKER "\n"
@ -142,8 +118,6 @@
# define MAKEFILE_NAME "makefile" # define MAKEFILE_NAME "makefile"
# define XMAKEFILE_NAME "xmake.lua" # define XMAKEFILE_NAME "xmake.lua"
# define ALL_FILE_WILDCARD "*" # define ALL_FILE_WILDCARD "*"
#else
#error "Only support windows, linux and macos now!"
#endif #endif
#define SDCC_IHX_SUFFIX "ihx" #define SDCC_IHX_SUFFIX "ihx"
@ -151,6 +125,40 @@
#define SDCC_HEX_SUFFIX "hex" #define SDCC_HEX_SUFFIX "hex"
#define SDCC_REL_SUFFIX "rel" #define SDCC_REL_SUFFIX "rel"
#if defined(Q_OS_WIN)
# define DEFAULT_UI_FONT "Segoe UI"
# define CJK_UI_FONT_SC "Microsoft YaHei UI"
# define CJK_UI_FONT_TC "Microsoft JhengHei UI"
# define CJK_UI_FONT_J "Yu Gothic UI"
# define CJK_UI_FONT_K "Malgun Gothic"
# define DEFAULT_MONO_FONT "Consolas"
# define CJK_MONO_FONT_SC "Microsoft YaHei"
# define CJK_MONO_FONT_TC "Microsoft JhengHei"
# define CJK_MONO_FONT_J "Yu Gothic"
# define CJK_MONO_FONT_K "Malgun Gothic"
#elif defined(Q_OS_MACOS)
# define DEFAULT_UI_FONT "Helvetica Neue"
# define CJK_UI_FONT_SC "PingFang SC"
# define CJK_UI_FONT_TC "PingFang TC"
# define CJK_UI_FONT_J "Hiragino Sans"
# define CJK_UI_FONT_K "Apple SD Gothic Neo"
# define DEFAULT_MONO_FONT "Menlo"
# define CJK_MONO_FONT_SC CJK_UI_FONT_SC
# define CJK_MONO_FONT_TC CJK_UI_FONT_TC
# define CJK_MONO_FONT_J CJK_UI_FONT_J
# define CJK_MONO_FONT_K CJK_UI_FONT_K
#else // XDG desktop
# define DEFAULT_UI_FONT "Sans" // use fontconfig default
# define CJK_UI_FONT_SC "Noto Sans CJK SC"
# define CJK_UI_FONT_TC "Noto Sans CJK TC"
# define CJK_UI_FONT_J "Noto Sans CJK JP"
# define CJK_UI_FONT_K "Noto Sans CJK KR"
# define DEFAULT_MONO_FONT "Monospace" // use fontconfig default
# define CJK_MONO_FONT_SC CJK_UI_FONT_SC // intentionally: the "Mono" version is not stricly monospaced either, and has less weights
# define CJK_MONO_FONT_TC CJK_UI_FONT_TC
# define CJK_MONO_FONT_J CJK_UI_FONT_J
# define CJK_MONO_FONT_K CJK_UI_FONT_K
#endif
class SystemConsts class SystemConsts
{ {

View File

@ -1958,6 +1958,54 @@
<source>All files (%1)</source> <source>All files (%1)</source>
<translation>Todos os arquivos (%1)</translation> <translation>Todos os arquivos (%1)</translation>
</message> </message>
<message>
<source>Terminal emulator arguments pattern</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Test</source>
<translation type="unfinished">Testar</translation>
</message>
<message>
<source>term -e sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -x sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -- sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -e &quot;sh -c \&quot;echo hello; sleep 3\&quot;&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On clicking Test for the correct pattern, the terminal emulator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> pops up;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> shows hello; and</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> quits in 3 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term /tmp/redpanda_XXXXXX.command</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EnvironmentShortcutModel</name> <name>EnvironmentShortcutModel</name>
@ -2058,6 +2106,10 @@
<source>All files (%1)</source> <source>All files (%1)</source>
<translation>Todos os arquivos (%1)</translation> <translation>Todos os arquivos (%1)</translation>
</message> </message>
<message>
<source>Parsed argv array (represented in JSON):</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExecutorProblemSetWidget</name> <name>ExecutorProblemSetWidget</name>

View File

@ -2689,7 +2689,72 @@ Are you really want to continue?</oldsource>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../settingsdialog/environmentprogramswidget.cpp" line="57"/> <location filename="../settingsdialog/environmentprogramswidget.ui" line="41"/>
<source>Terminal emulator arguments pattern</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="54"/>
<source>sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="74"/>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="95"/>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="116"/>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="137"/>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="158"/>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="179"/>
<source>Test</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="88"/>
<source>term -e sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="109"/>
<source>term -x sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="130"/>
<source>term -- sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="151"/>
<source>term -e &quot;sh -c \&quot;echo hello; sleep 3\&quot;&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="172"/>
<source>term /tmp/redpanda_XXXXXX.command</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="186"/>
<source>On clicking Test for the correct pattern, the terminal emulator</source>
<translation> </translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="193"/>
<source> pops up;</source>
<translation> </translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="200"/>
<source> shows hello; and</source>
<translation> hello</translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.ui" line="207"/>
<source> quits in 3 seconds.</source>
<translation> 3 </translation>
</message>
<message>
<location filename="../settingsdialog/environmentprogramswidget.cpp" line="109"/>
<source>Choose Terminal Program</source> <source>Choose Terminal Program</source>
<translation></translation> <translation></translation>
</message> </message>
@ -2798,6 +2863,11 @@ Are you really want to continue?</oldsource>
<source>Parameters to pass to your program</source> <source>Parameters to pass to your program</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<location filename="../settingsdialog/executorgeneralwidget.ui" line="65"/>
<source>Parsed argv array (represented in JSON):</source>
<translation>argv JSON </translation>
</message>
<message> <message>
<location filename="../settingsdialog/executorgeneralwidget.ui" line="68"/> <location filename="../settingsdialog/executorgeneralwidget.ui" line="68"/>
<source>Redirect input to the following file:</source> <source>Redirect input to the following file:</source>

View File

@ -1791,6 +1791,54 @@
<source>All files (%1)</source> <source>All files (%1)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Terminal emulator arguments pattern</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Test</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -e sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -x sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -- sh -c &quot;echo hello; sleep 3&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term -e &quot;sh -c \&quot;echo hello; sleep 3\&quot;&quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On clicking Test for the correct pattern, the terminal emulator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> pops up;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> shows hello; and</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> quits in 3 seconds.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>term /tmp/redpanda_XXXXXX.command</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EnvironmentShortcutModel</name> <name>EnvironmentShortcutModel</name>
@ -1891,6 +1939,10 @@
<source>All files (%1)</source> <source>All files (%1)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Parsed argv array (represented in JSON):</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ExecutorProblemSetWidget</name> <name>ExecutorProblemSetWidget</name>

View File

@ -447,7 +447,7 @@ void executeFile(const QString &fileName, const QString &params, const QString &
{ {
ExecutableRunner* runner=new ExecutableRunner( ExecutableRunner* runner=new ExecutableRunner(
fileName, fileName,
params, splitProcessCommand(params),
workingDir); workingDir);
runner->connect(runner, &QThread::finished, runner->connect(runner, &QThread::finished,
[runner,tempFile](){ [runner,tempFile](){
@ -589,3 +589,190 @@ QColor alphaBlend(const QColor &lower, const QColor &upper) {
int(lower.blue() * wl + upper.blue() * wu) int(lower.blue() * wl + upper.blue() * wu)
); );
} }
UnixExecSemantics getPathUnixExecSemantics(const QString &path)
{
QFileInfo pathInfo(path);
if (pathInfo.isRelative()) {
if (path.contains('/'))
return UnixExecSemantics::RelativeToCwd;
else
return UnixExecSemantics::SearchInPath;
} else
return UnixExecSemantics::Absolute;
}
QStringList getExecutableSearchPaths()
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QString path = env.value("PATH");
QStringList pathList = path.split(PATH_SEPARATOR);
return pathList;
}
QString escapeArgument(const QString &arg, [[maybe_unused]] bool isFirstArg)
{
auto argContainsOneOf = [&arg](auto... ch) { return (arg.contains(ch) || ...); };
#ifdef Q_OS_WINDOWS
// See https://stackoverflow.com/questions/31838469/how-do-i-convert-argv-to-lpcommandline-parameter-of-createprocess ,
// and https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way .
// TODO: investigate whether we need escaping for cmd.
if (!arg.isEmpty() && !argContainsOneOf(' ', '\t', '\n', '\v', '"'))
return arg;
QString result = "\"";
for (auto it = arg.begin(); ; ++it) {
int nBackslash = 0;
while (it != arg.end() && *it == '\\') {
++it;
++nBackslash;
}
if (it == arg.end()) {
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
result.append(QString('\\').repeated(nBackslash * 2));
break;
} else if (*it == '"') {
// Escape all backslashes and the following double quotation mark.
result.append(QString('\\').repeated(nBackslash * 2 + 1));
result.push_back(*it);
} else {
// Backslashes aren't special here.
result.append(QString('\\').repeated(nBackslash));
result.push_back(*it);
}
}
result.push_back('"');
return result;
#else
/* be speculative, but keep readability.
* ref. https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html
*/
if (arg.isEmpty())
return R"("")";
/* POSIX say the following reserved words (may) have special meaning:
* !, {, }, case, do, done, elif, else, esac, fi, for, if, in, then, until, while,
* [[, ]], function, select,
* only if used as the _first word_ of a command (or somewhere we dot not care).
*/
const static QSet<QString> reservedWord{
"!", "{", "}", "case", "do", "done", "elif", "else", "esac", "fi", "for", "if", "in", "then", "until", "while",
"[[", "]]", "function", "select",
};
if (isFirstArg && reservedWord.contains(arg))
return QString(R"("%1")").arg(arg);
/* POSIX say “shall quote”:
* '|', '&', ';', '<', '>', '(', ')', '$', '`', '\\', '"', '\'', ' ', '\t', '\n';
* and may need to be quoted:
* '*', '?', '[', '#', '~', '=', '%'.
* among which may need to be quoted there are 4 kinds:
* - wildcards '*', '?', '[' are danger anywhere (handle it as if shall quote);
* - comment '#', home '~', is danger at first char in any word;
* - (environment) variable '=' is danger at any char in first word;
* - foreground '%' is danger at first char in first word.
* although not mentioned by POSIX, bashs brace expansion '{', '}' are also danger anywhere.
*/
bool isDoubleQuotingDanger = argContainsOneOf('$', '`', '\\', '"');
bool isSingleQuotingDanger = arg.contains('\'');
bool isDangerAnyChar = isDoubleQuotingDanger || isSingleQuotingDanger || argContainsOneOf(
'|', '&', ';', '<', '>', '(', ')', ' ', '\t', '\n',
'*', '?', '[',
'{', '}'
);
bool isDangerFirstChar = (arg[0] == '#') || (arg[0] == '~');
if (isFirstArg) {
isDangerAnyChar = isDangerAnyChar || arg.contains('=');
isDangerFirstChar = isDangerFirstChar || (arg[0] == '%');
}
// a “safe” string
if (!isDangerAnyChar && !isDangerFirstChar)
return arg;
// prefer more-common double quoting
if (!isDoubleQuotingDanger)
return QString(R"("%1")").arg(arg);
// and then check the opportunity of single quoting
if (!isSingleQuotingDanger)
return QString("'%1'").arg(arg);
// escaping is necessary
// use double quoting since its less tricky
QString result = "\"";
for (auto ch : arg) {
if (ch == '$' || ch == '`' || ch == '\\' || ch == '"')
result.push_back('\\');
result.push_back(ch);
}
result.push_back('"');
return result;
/* single quoting, which is roughly raw string, is possible and quite simple in programming:
* 1. replace each single quote with `'\''`, which contains
* - a single quote to close quoting,
* - an escaped single quote representing the single quote itself, and
* - a single quote to open quoting again;
* 2. enclose the string with a pair of single quotes.
* e.g. `o'clock` => `'o'\''clock'`, really tricky and hard to read.
*/
#endif
}
auto wrapCommandForTerminalEmulator(const QString &terminal, const TerminalEmulatorArgumentsPattern &argsPattern, const QStringList &argsWithArgv0)
-> std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>>
{
switch (argsPattern) {
case TerminalEmulatorArgumentsPattern::ImplicitSystem:
default: {
return {argsWithArgv0[0], argsWithArgv0.mid(1), nullptr};
}
case TerminalEmulatorArgumentsPattern::MinusEAppendArgs: {
return {terminal, QStringList{"-e"} + argsWithArgv0, nullptr};
}
case TerminalEmulatorArgumentsPattern::MinusXAppendArgs: {
return {terminal, QStringList{"-x"} + argsWithArgv0, nullptr};
}
case TerminalEmulatorArgumentsPattern::MinusMinusAppendArgs: {
return {terminal, QStringList{"--"} + argsWithArgv0, nullptr};
}
case TerminalEmulatorArgumentsPattern::MinusEAppendCommandLine: {
QStringList escapedArgs;
for (int i = 0; i < argsWithArgv0.length(); i++) {
auto &arg = argsWithArgv0[i];
auto escaped = escapeArgument(arg, i == 0);
escapedArgs.append(escaped);
}
return {terminal, QStringList{"-e", escapedArgs.join(' ')}, nullptr};
}
case TerminalEmulatorArgumentsPattern::WriteCommandLineToTempFileThenTempFilename: {
auto fileOwner = std::make_unique<QTemporaryFile>(QDir::tempPath() + "/redpanda_XXXXXX.command");
if (fileOwner->open()) {
QStringList escapedArgs;
for (int i = 0; i < argsWithArgv0.length(); i++) {
auto &arg = argsWithArgv0[i];
auto escaped = escapeArgument(arg, i == 0);
escapedArgs.append(escaped);
}
fileOwner->write(escapedArgs.join(' ').toUtf8());
fileOwner->write(QString('\n').toUtf8());
fileOwner->flush();
}
QFile(fileOwner->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
return {terminal, QStringList{fileOwner->fileName()}, std::move(fileOwner)};
}
}
}
QString defaultShell()
{
#ifdef Q_OS_WINDOWS
return "powershell.exe";
#else
return "sh";
#endif
}

View File

@ -25,6 +25,7 @@
#include <memory> #include <memory>
#include <QThread> #include <QThread>
#include <QProcessEnvironment> #include <QProcessEnvironment>
#include <QTemporaryFile>
#define SI_NO_CONVERSION #define SI_NO_CONVERSION
#include "SimpleIni.h" #include "SimpleIni.h"
#include "qt_utils/utils.h" #include "qt_utils/utils.h"
@ -34,6 +35,7 @@
using SimpleIni = CSimpleIniA; using SimpleIni = CSimpleIniA;
using PSimpleIni = std::shared_ptr<SimpleIni>; using PSimpleIni = std::shared_ptr<SimpleIni>;
using TemporaryFileOwner = std::unique_ptr<QTemporaryFile>;
enum class FileType{ enum class FileType{
GAS, // GNU assembler source file (.s) GAS, // GNU assembler source file (.s)
@ -113,6 +115,22 @@ enum class ProblemCaseValidateType {
IgnoreSpaces IgnoreSpaces
}; };
enum class UnixExecSemantics {
Absolute,
RelativeToCwd,
SearchInPath,
};
enum class TerminalEmulatorArgumentsPattern {
ImplicitSystem = 0, // bash -c "echo hello, world; sleep 3"
MinusEAppendArgs, // term -e bash -c "echo hello, world; sleep 3" # xterm-compatible
MinusXAppendArgs, // term -x bash -c "echo hello, world; sleep 3" # some VTE-based
MinusMinusAppendArgs, // term -- bash -c "echo hello, world; sleep 3" # gnome-terminal, kgx
MinusEAppendCommandLine, // term -e "bash -c \"echo hello, world; sleep 3\"" # some lightweighted; alternative form for VTE-based
WriteCommandLineToTempFileThenTempFilename = 6226700, // macOS Terminal.app and iTerm2.app; 6226700 is how you dial “macOS00”
};
FileType getFileType(const QString& filename); FileType getFileType(const QString& filename);
QStringList splitProcessCommand(const QString& cmd); QStringList splitProcessCommand(const QString& cmd);
@ -165,4 +183,15 @@ void saveComboHistory(QComboBox* cb,const QString& text);
QColor alphaBlend(const QColor &lower, const QColor &upper); QColor alphaBlend(const QColor &lower, const QColor &upper);
UnixExecSemantics getPathUnixExecSemantics(const QString &path);
QStringList getExecutableSearchPaths();
QString escapeArgument(const QString &arg, bool isFirstArg);
auto wrapCommandForTerminalEmulator(const QString &terminal, const TerminalEmulatorArgumentsPattern &argsPattern, const QStringList &argsWithArgv0)
-> std::tuple<QString, QStringList, std::unique_ptr<QTemporaryFile>>;
QString defaultShell();
#endif // UTILS_H #endif // UTILS_H

View File

@ -25,7 +25,7 @@ void GitManager::createRepository(const QString &folder)
contents.append("*.o"); contents.append("*.o");
contents.append("*.exe"); contents.append("*.exe");
contents.append("*.layout"); contents.append("*.layout");
#ifdef Q_OS_LINUX #ifdef Q_OS_UNIX
contents.append("*."); contents.append("*.");
#endif #endif
@ -593,7 +593,7 @@ QString GitManager::runGit(const QString& workingFolder, const QStringList &args
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
env.insert("PATH",pSettings->dirs().appDir()); env.insert("PATH",pSettings->dirs().appDir());
env.insert("GIT_ASKPASS",includeTrailingPathDelimiter(pSettings->dirs().appDir())+"redpanda-win-git-askpass.exe"); env.insert("GIT_ASKPASS",includeTrailingPathDelimiter(pSettings->dirs().appDir())+"redpanda-win-git-askpass.exe");
#elif defined(Q_OS_LINUX) #else // Unix
env.insert(QProcessEnvironment::systemEnvironment()); env.insert(QProcessEnvironment::systemEnvironment());
env.insert("LANG","en"); env.insert("LANG","en");
env.insert("LANGUAGE","en"); env.insert("LANGUAGE","en");

View File

@ -56,15 +56,7 @@ QSynEdit::QSynEdit(QWidget *parent) : QAbstractScrollArea(parent),
mPaintLock = 0; mPaintLock = 0;
mPainterLock = 0; mPainterLock = 0;
mPainting = false; mPainting = false;
#ifdef Q_OS_WIN mFontDummy = QFont("monospace",14);
mFontDummy = QFont("Consolas",12);
#elif defined(Q_OS_LINUX)
mFontDummy = QFont("terminal",14);
#elif defined(Q_OS_MACOS)
mFontDummy = QFont("Menlo", 14);
#else
#error "Not supported!"
#endif
mFontDummy.setStyleStrategy(QFont::PreferAntialias); mFontDummy.setStyleStrategy(QFont::PreferAntialias);
mDocument = std::make_shared<Document>(mFontDummy, mFontDummy, this); mDocument = std::make_shared<Document>(mFontDummy, mFontDummy, this);
//fPlugins := TList.Create; //fPlugins := TList.Create;

View File

@ -108,7 +108,7 @@ const QSet<QString> ASMSyntaxer::ATTDirectives {
".seh_setframe",".seh_stackalloc",".seh_pushreg", ".seh_setframe",".seh_stackalloc",".seh_pushreg",
".seh_savereg",".seh_savemm",".seh_savexmm", ".seh_savereg",".seh_savemm",".seh_savexmm",
".seh_pushframe",".seh_scope", ".seh_pushframe",".seh_scope",
#elif defined(Q_OS_LINUX) #else // Unix
".cfi_sections",".cfi_startproc",".cfi_endproc", ".cfi_sections",".cfi_startproc",".cfi_endproc",
".cfi_personality",".cfi_personality_id",".cfi_fde_data", ".cfi_personality",".cfi_personality_id",".cfi_fde_data",
".cfi_lsda",".cfi_inline_lsda",".cfi_def_cfa", ".cfi_lsda",".cfi_inline_lsda",".cfi_def_cfa",

View File

@ -22,6 +22,7 @@ using std::string;
using std::vector; using std::vector;
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@ -184,15 +185,11 @@ int main(int argc, char** argv) {
//todo: handle error //todo: handle error
printf("shm open failed %d:%s\n",errno,strerror(errno)); printf("shm open failed %d:%s\n",errno,strerror(errno));
} else { } else {
if (ftruncate(fd_shm,BUF_SIZE)==-1){ // `ftruncate` has already done in RedPandaIDE
printf("ftruncate failed %d:%s\n",errno,strerror(errno)); pBuf = (char*)mmap(NULL,BUF_SIZE,PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm,0);
//todo: set size error if (pBuf == MAP_FAILED) {
} else { printf("mmap failed %d:%s\n",errno,strerror(errno));
pBuf = (char*)mmap(NULL,BUF_SIZE,PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm,0); pBuf = nullptr;
if (pBuf == MAP_FAILED) {
printf("mmap failed %d:%s\n",errno,strerror(errno));
pBuf = nullptr;
}
} }
} }