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
```
# Linux
# Linux and Other freedesktop.org-conforming (XDG) Desktop Systems
- Install gcc and qt5
- Optionally install fcitx5-qt for building with static version of Qt
- Open `Red_Panda_CPP.pro` with Qt Creator
General steps:
- 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:
- `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.
## AppImage
## Linux AppImage
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.
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 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-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
```
# Linux
# Linux 和其他符合 freedesktop.orgXDG规范的桌面系统
步骤:
- 安装 gcc 和 qt5开发相关包
- 如果使用静态版本的 Qt 编译,还要安装 fcitx5-qt
- 使用qtcreator打开Red_Panda_CPP.pro文件
通用步骤:
- 安装支持 C++17 的 GCC≥ 7或 Clang≥ 6
- 安装 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 变量:
- `PREFIX`:默认值是 `/usr/local`。打包时应该定义为 `/usr``/opt/redpanda-cpp`
@ -297,7 +318,7 @@ Windows 宿主的额外要求:
注意makepkg 签出此存储库的 HEAD因此构建之前务必提交所有变更。
## AppImage
## Linux AppImage
1. 安装依赖包Docker 或 Podman。
@ -337,9 +358,9 @@ Windows 宿主的额外要求:
可以借助 QEMU 用户空间模拟,运行目标架构的本机工具链,来构建小熊猫 C++。
注意:始终**在容器中**运行模拟本机构建,因为混用不同架构的程序和库可能会损坏系统。
注意:始终**在容器或 jail 中**运行模拟本机构建,因为混用不同架构的程序和库可能会损坏系统。
对于 Linux 宿主,需要安装静态链接的 QEMU 用户空间模拟器(包名通常为 `qemu-user-static`)并确认已经启用 binfmt 支持。
对于 Linux 或 BSD 宿主,需要安装静态链接的 QEMU 用户空间模拟器(包名通常为 `qemu-user-static`)并确认已经启用 binfmt 支持。
对于 Windows 宿主Docker 和 Podman 应该已经启用了 QEMU 用户空间模拟。如果没有启用,
* 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-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: {
# This package needs to be installed via homebrew before we can compile it
INCLUDEPATH += \
/opt/homebrew/opt/icu4c/include
QT += gui-private
ICON = ../macos/RedPandaIDE.icns
@ -131,6 +127,7 @@ SOURCES += \
settingsdialog/editortooltipswidget.cpp \
settingsdialog/environmentfolderswidget.cpp \
settingsdialog/environmentperformancewidget.cpp \
settingsdialog/environmentprogramswidget.cpp \
settingsdialog/environmentshortcutwidget.cpp \
settingsdialog/executorproblemsetwidget.cpp \
settingsdialog/formattergeneralwidget.cpp \
@ -254,6 +251,7 @@ HEADERS += \
settingsdialog/editortooltipswidget.h \
settingsdialog/environmentfolderswidget.h \
settingsdialog/environmentperformancewidget.h \
settingsdialog/environmentprogramswidget.h \
settingsdialog/environmentshortcutwidget.h \
settingsdialog/executorproblemsetwidget.h \
settingsdialog/formattergeneralwidget.h \
@ -349,6 +347,7 @@ FORMS += \
settingsdialog/editortooltipswidget.ui \
settingsdialog/environmentfolderswidget.ui \
settingsdialog/environmentperformancewidget.ui \
settingsdialog/environmentprogramswidget.ui \
settingsdialog/environmentshortcutwidget.ui \
settingsdialog/executorproblemsetwidget.ui \
settingsdialog/formattergeneralwidget.ui \
@ -469,21 +468,18 @@ win32: {
unix: {
HEADERS += \
settingsdialog/formatterpathwidget.h \
settingsdialog/environmentprogramswidget.h
settingsdialog/formatterpathwidget.h
SOURCES += \
settingsdialog/formatterpathwidget.cpp \
settingsdialog/environmentprogramswidget.cpp
settingsdialog/formatterpathwidget.cpp
FORMS += \
settingsdialog/formatterpathwidget.ui \
settingsdialog/environmentprogramswidget.ui
settingsdialog/formatterpathwidget.ui
}
linux: {
LIBS+= \
-lrt
# legacy glibc compatibility -- modern Unices have all components in `libc.so`
LIBS += -lrt
_LINUX_STATIC_IME_PLUGIN = $$(LINUX_STATIC_IME_PLUGIN)
equals(_LINUX_STATIC_IME_PLUGIN, "ON") {

View File

@ -62,11 +62,13 @@ void AutolinkManager::load()
file.write(content);
file.close();
preFile.close();
#elif defined(Q_OS_LINUX)
QFile preFile(":/config/autolink-linux.json");
#elif defined(Q_OS_MACOS)
return;
#else // XDG desktop
QFile preFile(":/config/autolink-xdg.json");
if (!preFile.open(QFile::ReadOnly)) {
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();
if (!file.open(QFile::WriteOnly|QFile::Truncate)) {
@ -76,8 +78,6 @@ void AutolinkManager::load()
file.write(content);
file.close();
preFile.close();
#else
return;
#endif
}
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>("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);
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
#if defined(Q_OS_UNIX)
sl.clear();
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>("Leak","leak"));
sl.append(QPair<QString,QString>("Undefined","undefined"));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,10 +25,10 @@ class OJProblemCasesRunner : public Runner
{
Q_OBJECT
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,
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,
QObject *parent = nullptr);
OJProblemCasesRunner(const OJProblemCasesRunner&)=delete;

View File

@ -17,7 +17,7 @@
#include "runner.h"
#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),
mPausing(false),
mStop(false),

View File

@ -23,7 +23,7 @@ class Runner : public QThread
{
Q_OBJECT
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 operator=(const Runner&)=delete;
@ -48,7 +48,7 @@ protected:
bool mPausing;
bool mStop;
QString mFilename;
QString mArguments;
QStringList mArguments; // without argv[0]
QString mWorkDir;
int mWaitForFinishTime;
};

View File

@ -2,6 +2,6 @@
<qresource prefix="/config">
<file alias="autolink.json">resources/autolink.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>
</RCC>

View File

@ -248,6 +248,23 @@ int main(int argc, char *argv[])
qputenv("QT_QPA_PLATFORM", "windows:darkmode=2");
#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);
app.setAttribute(Qt::AA_UseHighDpiPixmaps);

View File

@ -1,4 +1,12 @@
[
{
"header": "fmt/core.h",
"links": "-lfmt"
},
{
"header": "math.h",
"links": "-lm"
},
{
"execUseUTF8": true,
"header": "raylib.h",
@ -11,5 +19,13 @@
{
"header": "rturtle.h",
"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
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)
// return QApplication::instance()->applicationDirPath();
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
}
@ -218,13 +218,13 @@ QString Settings::Dirs::appLibexecDir() const
{
#ifdef Q_OS_WIN
return appDir();
#elif defined(Q_OS_LINUX)
// in AppImage LIBEXECDIR is not true, resolve from relative path
#elif defined(Q_OS_MACOS)
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 absoluteLibExecDir(QDir(appDir()).absoluteFilePath(relativeLibExecDir));
return absoluteLibExecDir;
#elif defined(Q_OS_MACOS)
return QApplication::instance()->applicationDirPath();
#endif
}
@ -1473,16 +1473,16 @@ void Settings::Editor::doLoad()
mRightEdgeLineColor = colorValue("right_edge_line_color",Qt::yellow);
//Editor font
#ifdef Q_OS_WIN
mFontName = stringValue("font_name","consolas");
mNonAsciiFontName = stringValue("non_ascii_font_name","consolas");
#elif defined(Q_OS_MACOS)
mFontName = stringValue("font_name","Menlo");
mNonAsciiFontName = stringValue("non_ascii_font_name","PingFang SC");
#else
mFontName = stringValue("font_name","Dejavu Sans Mono");
mNonAsciiFontName = stringValue("non_ascii_font_name","Dejavu Sans Mono");
#endif
mFontName = stringValue("font_name",DEFAULT_MONO_FONT);
QString defaultCjkFontName = CJK_MONO_FONT_SC;
QString defaultLocaleName = QLocale::system().name();
if (defaultLocaleName == "zh_TW")
defaultCjkFontName = CJK_MONO_FONT_TC;
else if (defaultLocaleName == "ja_JP")
defaultCjkFontName = CJK_MONO_FONT_J;
else if (defaultLocaleName == "ko_KR")
defaultCjkFontName = CJK_MONO_FONT_K;
mNonAsciiFontName = stringValue("non_ascii_font_name",defaultCjkFontName);
mFontSize = intValue("font_size",12);
mFontOnlyMonospaced = boolValue("font_only_monospaced",true);
mLineSpacing = doubleValue("line_spacing",1.0);
@ -1504,11 +1504,7 @@ void Settings::Editor::doLoad()
mGutterLineNumbersStartZero = boolValue("gutter_line_numbers_start_zero",false);
mGutterUseCustomFont = boolValue("gutter_use_custom_font",false);
#ifdef Q_OS_WIN
mGutterFontName = stringValue("gutter_font_name","consolas");
#else
mGutterFontName = stringValue("gutter_font_name","Dejavu Sans Mono");
#endif
mGutterFontName = stringValue("gutter_font_name",DEFAULT_MONO_FONT);
mGutterFontSize = intValue("gutter_font_size",12);
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);
if (enableAsan) {
#ifdef __aarch64__
pSet->setCompileOption(CC_CMD_OPT_ADDRESS_SANITIZER, "hwaddress");
#else
pSet->setCompileOption(CC_CMD_OPT_ADDRESS_SANITIZER, "address");
// pSet->setCustomCompileParams("-fsanitize=address");
// pSet->setUseCustomCompileParams(true);
// pSet->setCustomLinkParams("-fsanitize=address");
// pSet->setUseCustomLinkParams(true);
#endif
}
//Some windows gcc don't correctly support this
//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
# if defined(__x86_64__) || __SIZEOF_POINTER__ == 4
mDefaultIndex = (int)mList.size() - 1; // x86-64 Linux or 32-bit Unix, default to "debug with ASan"
# if defined(__x86_64__) || defined(__aarch64__) || __SIZEOF_POINTER__ == 4
mDefaultIndex = (int)mList.size() - 1; // x86-64, AArch64 Linux or 32-bit Unix, default to "debug with ASan"
# else
mDefaultIndex = (int)mList.size() - 2; // other Unix, where ASan can be very slow, default to "debug"
# endif
@ -3576,17 +3572,20 @@ void Settings::Environment::doLoad()
{
//Appearance
mTheme = stringValue("theme","dark");
QString defaultFontName = "Segoe UI";
QString defaultFontName = DEFAULT_UI_FONT;
QString defaultLocaleName = QLocale::system().name();
if (defaultLocaleName == "zh_CN") {
{
QString fontName;
#ifdef Q_OS_WINDOWS
fontName = "Microsoft Yahei";
#elif defined(Q_OS_MACOS)
fontName = "PingFang SC";
#elif defined(Q_OS_LINUX)
fontName = "Noto Sans CJK";
#endif
if (defaultLocaleName == "zh_CN")
fontName = CJK_UI_FONT_SC;
else if (defaultLocaleName == "zh_TW")
fontName = CJK_UI_FONT_TC;
else if (defaultLocaleName == "ja_JP")
fontName = CJK_UI_FONT_J;
else if (defaultLocaleName == "ko_KR")
fontName = CJK_UI_FONT_K;
else
fontName = DEFAULT_UI_FONT;
QFont font(fontName);
if (font.exactMatch()) {
defaultFontName = fontName;
@ -3608,58 +3607,85 @@ void Settings::Environment::doLoad()
if (!fileExists(mDefaultOpenFolder)) {
mDefaultOpenFolder = QDir::currentPath();
}
#ifdef Q_OS_LINUX
#define SYSTEM_TERMINAL(term) "/usr/bin/" #term, "/usr/local/bin/" #term
const static QString terminals[] {
using AP = TerminalEmulatorArgumentsPattern;
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 */
SYSTEM_TERMINAL(alacritty), // GPU-accelerated
SYSTEM_TERMINAL(kitty), // GPU-accelerated
SYSTEM_TERMINAL(wayst), // GPU-accelerated
SYSTEM_TERMINAL(tilix), // tiling
SYSTEM_TERMINAL(cool-retro-term), // old CRT style
{"alacritty", AP::MinusEAppendArgs}, // GPU-accelerated
{"kitty", AP::MinusEAppendArgs}, // GPU-accelerated
{"wayst", AP::MinusEAppendArgs}, // GPU-accelerated
/* default terminal for DE */
SYSTEM_TERMINAL(konsole), // KDE
SYSTEM_TERMINAL(deepin-terminal), // DDE
SYSTEM_TERMINAL(qterminal), // LXQt
SYSTEM_TERMINAL(lxterminal), // LXDE
{"coreterminal", AP::MinusEAppendCommandLine}, // lightweighted
{"kermit", AP::MinusEAppendCommandLine}, // lightweighted
{"roxterm", AP::MinusEAppendCommandLine}, // lightweighted
{"sakura", AP::MinusEAppendCommandLine}, // lightweighted
{"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 */
"alacritty",
{"./alacritty", AP::MinusEAppendArgs},
/* compatible, with minor issue */
SYSTEM_TERMINAL(kgx), // GNOME Console, confirm to quit
SYSTEM_TERMINAL(coreterminal), // not so conforming when parsing args
SYSTEM_TERMINAL(sakura), // not so conforming when parsing args
{"kgx", AP::MinusMinusAppendArgs}, // GNOME Console, confirm to quit
/* compatible, without out-of-box hidpi support */
SYSTEM_TERMINAL(mlterm),
SYSTEM_TERMINAL(st),
SYSTEM_TERMINAL(terminology), // also not so conforming when parsing args
SYSTEM_TERMINAL(urxvt),
SYSTEM_TERMINAL(xterm),
SYSTEM_TERMINAL(zutty),
/* compatible, without out-of-box hidpi support on Linux */
{"mlterm", AP::MinusEAppendArgs},
{"st", AP::MinusEAppendArgs},
{"urxvt", AP::MinusEAppendArgs},
{"xterm", AP::MinusEAppendArgs},
{"zutty", AP::MinusEAppendArgs},
/* 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 */
SYSTEM_TERMINAL(foot), // Wayland only
SYSTEM_TERMINAL(x-terminal-emulator), // Debian alternatives
{"foot", AP::MinusEAppendArgs}, // Wayland only
/* parameter incompatible */
// "gnome-terminal",
// "guake",
// "hyper",
// "io.elementary.terminal",
// "kermit",
// "liri-terminal",
// "mate-terminal",
// "roxterm",
// "station",
// "terminator",
// "termite",
// "tilda",
// "xfce4-terminal",
// "yakuake",
// "guake", // drop down
// "hyper", // no execute support
// "liri-terminal", // no execute support
// "station", // no execute support
// "tilda", // drop down
/* incompatible -- other */
// "aterm", // AUR broken, unable to test
@ -3667,22 +3693,57 @@ void Settings::Environment::doLoad()
// "rxvt", // no unicode support
// "shellinabox", // AUR broken, unable to test
};
#undef SYSTEM_TERMINAL
#endif
auto checkAndSetTerminalPath = [this](QString terminalPath) -> bool {
QDir appDir(pSettings->dirs().appDir());
QString absoluteTerminalPath = appDir.absoluteFilePath(terminalPath);
QFileInfo termPathInfo(absoluteTerminalPath);
if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) {
mTerminalPath = terminalPath;
return true;
} else {
return false;
auto checkAndSetTerminalPath = [this](const TerminalSearchItem &searchItem) -> bool {
#define DO_CHECK_AND_SET do { \
if (termPathInfo.isFile() && termPathInfo.isReadable() && termPathInfo.isExecutable()) { \
mTerminalPath = searchItem.appName; \
mTerminalArgumentsPattern = searchItem.argsPattern; \
return true; \
} \
} while (0)
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
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
for (auto terminal: terminals) {
if (checkAndSetTerminalPath(terminal))
@ -3691,11 +3752,6 @@ void Settings::Environment::doLoad()
}
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);
mOpenFilesInSingleInstance = boolValue("open_files_in_single_instance",false);
}
@ -3757,13 +3813,11 @@ QString Settings::Environment::terminalPath() const
QString Settings::Environment::terminalPathForExec() const
{
#ifdef Q_OS_LINUX
// `mTerminalPath` can be reletive (bundled terminal in AppImage).
if (getPathUnixExecSemantics(mTerminalPath) == UnixExecSemantics::RelativeToCwd) {
QDir appDir(pSettings->dirs().appDir());
return appDir.absoluteFilePath(mTerminalPath);
#else
} else
return mTerminalPath;
#endif
}
void Settings::Environment::setTerminalPath(const QString &terminalPath)
@ -3781,6 +3835,16 @@ void Settings::Environment::setAStylePath(const QString &aStylePath)
mAStylePath = aStylePath;
}
TerminalEmulatorArgumentsPattern Settings::Environment::terminalArgumentsPattern() const
{
return mTerminalArgumentsPattern;
}
void Settings::Environment::setTerminalArgumentsPattern(const TerminalEmulatorArgumentsPattern &argsPattern)
{
mTerminalArgumentsPattern = argsPattern;
}
bool Settings::Environment::useCustomIconSet() const
{
return mUseCustomIconSet;
@ -3845,10 +3909,9 @@ void Settings::Environment::doSave()
saveValue("current_folder",mCurrentFolder);
saveValue("default_open_folder",mDefaultOpenFolder);
#ifndef Q_OS_WIN
saveValue("terminal_path",mTerminalPath);
saveValue("terminal_arguments_pattern",int(mTerminalArgumentsPattern));
saveValue("asyle_path",mAStylePath);
#endif
saveValue("hide_non_support_files_file_view",mHideNonSupportFilesInFileView);
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);
mRedirectStderrToToolLog = boolValue("redirect_stderr_to_toollog", false);
#ifdef Q_OS_WIN
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
mCaseEditorFontName = stringValue("case_editor_font_name",DEFAULT_MONO_FONT);
mCaseEditorFontSize = intValue("case_editor_font_size",11);
mCaseEditorFontOnlyMonospaced = boolValue("case_editor_font_only_monospaced",true);
int case_timeout = intValue("case_timeout", -1);

View File

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

View File

@ -19,6 +19,7 @@
#include "../settings.h"
#include "../iconsmanager.h"
#include "../systemconsts.h"
#include "../compiler/executablerunner.h"
#include <QFileDialog>
@ -27,6 +28,16 @@ EnvironmentProgramsWidget::EnvironmentProgramsWidget(const QString& name, const
ui(new Ui::EnvironmentProgramsWidget)
{
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()
@ -34,14 +45,62 @@ EnvironmentProgramsWidget::~EnvironmentProgramsWidget()
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()
{
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()
{
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();
}
@ -61,3 +120,62 @@ void EnvironmentProgramsWidget::on_btnChooseTerminal_clicked()
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
#include "settingswidget.h"
#include "utils.h"
namespace Ui {
class EnvironmentProgramsWidget;
@ -30,6 +31,10 @@ class EnvironmentProgramsWidget : public SettingsWidget
public:
explicit EnvironmentProgramsWidget(const QString& name, const QString& group, QWidget *parent = nullptr);
~EnvironmentProgramsWidget();
void hideMacosSpecificPattern();
private:
void testTerminal(const TerminalEmulatorArgumentsPattern &pattern);
private:
Ui::EnvironmentProgramsWidget *ui;
@ -41,6 +46,13 @@ protected:
void updateIcons(const QSize &size) override;
private slots:
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

View File

@ -35,7 +35,141 @@
</property>
</widget>
</item>
<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">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -21,12 +21,15 @@
#include "../systemconsts.h"
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonArray>
ExecutorGeneralWidget::ExecutorGeneralWidget(const QString& name, const QString& group, QWidget *parent):
SettingsWidget(name,group,parent),
ui(new Ui::ExecutorGeneralWidget)
{
ui->setupUi(this);
ui->txtParsedArgsInJson->setFont(QFont(DEFAULT_MONO_FONT));
}
ExecutorGeneralWidget::~ExecutorGeneralWidget()
@ -73,3 +76,11 @@ void ExecutorGeneralWidget::updateIcons(const QSize &/*size*/)
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();
// SettingsWidget interface
void on_txtExecuteParamaters_textChanged(const QString &commandLine);
protected:
void updateIcons(const QSize &size) override;
};

View File

@ -55,10 +55,21 @@
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="txtExecuteParamaters"/>
</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>
</widget>
</item>
@ -75,7 +86,6 @@
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -88,7 +98,6 @@
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -101,7 +110,6 @@
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>

View File

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

View File

@ -22,7 +22,6 @@
#define APP_SETTSINGS_FILENAME "redpandacpp.ini"
#ifdef Q_OS_WIN
#define CONSOLE_PAUSER "consolepauser.exe"
#define ASSEMBLER "nasm.exe"
#define GCC_PROGRAM "gcc.exe"
#define GPP_PROGRAM "g++.exe"
#define GDB_PROGRAM "gdb.exe"
@ -40,9 +39,8 @@
#define SDCC_PROGRAM "sdcc.exe"
#define PACKIHX_PROGRAM "packihx.exe"
#define MAKEBIN_PROGRAM "makebin.exe"
#elif defined(Q_OS_LINUX)
#else // Unix
#define CONSOLE_PAUSER "consolepauser"
#define ASSEMBLER "nasm"
#define GCC_PROGRAM "gcc"
#define GPP_PROGRAM "g++"
#define GDB_PROGRAM "gdb"
@ -61,28 +59,6 @@
#define SDCC_PROGRAM "sdcc"
#define PACKIHX_PROGRAM "packihx"
#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
#define DEV_PROJECT_EXT "dev"
@ -128,7 +104,7 @@
# define MAKEFILE_NAME "makefile.win"
# define XMAKEFILE_NAME "xmake.lua"
# define ALL_FILE_WILDCARD "*.*"
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
#else // Unix
# define PATH_SENSITIVITY Qt::CaseSensitive
# define PATH_SEPARATOR ":"
# define LINE_BREAKER "\n"
@ -142,8 +118,6 @@
# define MAKEFILE_NAME "makefile"
# define XMAKEFILE_NAME "xmake.lua"
# define ALL_FILE_WILDCARD "*"
#else
#error "Only support windows, linux and macos now!"
#endif
#define SDCC_IHX_SUFFIX "ihx"
@ -151,6 +125,40 @@
#define SDCC_HEX_SUFFIX "hex"
#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
{

View File

@ -1958,6 +1958,54 @@
<source>All files (%1)</source>
<translation>Todos os arquivos (%1)</translation>
</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>
<name>EnvironmentShortcutModel</name>
@ -2058,6 +2106,10 @@
<source>All files (%1)</source>
<translation>Todos os arquivos (%1)</translation>
</message>
<message>
<source>Parsed argv array (represented in JSON):</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExecutorProblemSetWidget</name>

View File

@ -2689,7 +2689,72 @@ Are you really want to continue?</oldsource>
<translation></translation>
</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>
<translation></translation>
</message>
@ -2798,6 +2863,11 @@ Are you really want to continue?</oldsource>
<source>Parameters to pass to your program</source>
<translation></translation>
</message>
<message>
<location filename="../settingsdialog/executorgeneralwidget.ui" line="65"/>
<source>Parsed argv array (represented in JSON):</source>
<translation>argv JSON </translation>
</message>
<message>
<location filename="../settingsdialog/executorgeneralwidget.ui" line="68"/>
<source>Redirect input to the following file:</source>

View File

@ -1791,6 +1791,54 @@
<source>All files (%1)</source>
<translation type="unfinished"></translation>
</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>
<name>EnvironmentShortcutModel</name>
@ -1891,6 +1939,10 @@
<source>All files (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Parsed argv array (represented in JSON):</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExecutorProblemSetWidget</name>

View File

@ -447,7 +447,7 @@ void executeFile(const QString &fileName, const QString &params, const QString &
{
ExecutableRunner* runner=new ExecutableRunner(
fileName,
params,
splitProcessCommand(params),
workingDir);
runner->connect(runner, &QThread::finished,
[runner,tempFile](){
@ -589,3 +589,190 @@ QColor alphaBlend(const QColor &lower, const QColor &upper) {
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 <QThread>
#include <QProcessEnvironment>
#include <QTemporaryFile>
#define SI_NO_CONVERSION
#include "SimpleIni.h"
#include "qt_utils/utils.h"
@ -34,6 +35,7 @@
using SimpleIni = CSimpleIniA;
using PSimpleIni = std::shared_ptr<SimpleIni>;
using TemporaryFileOwner = std::unique_ptr<QTemporaryFile>;
enum class FileType{
GAS, // GNU assembler source file (.s)
@ -113,6 +115,22 @@ enum class ProblemCaseValidateType {
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);
QStringList splitProcessCommand(const QString& cmd);
@ -165,4 +183,15 @@ void saveComboHistory(QComboBox* cb,const QString& text);
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

View File

@ -25,7 +25,7 @@ void GitManager::createRepository(const QString &folder)
contents.append("*.o");
contents.append("*.exe");
contents.append("*.layout");
#ifdef Q_OS_LINUX
#ifdef Q_OS_UNIX
contents.append("*.");
#endif
@ -593,7 +593,7 @@ QString GitManager::runGit(const QString& workingFolder, const QStringList &args
#ifdef Q_OS_WIN
env.insert("PATH",pSettings->dirs().appDir());
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("LANG","en");
env.insert("LANGUAGE","en");

View File

@ -56,15 +56,7 @@ QSynEdit::QSynEdit(QWidget *parent) : QAbstractScrollArea(parent),
mPaintLock = 0;
mPainterLock = 0;
mPainting = false;
#ifdef Q_OS_WIN
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 = QFont("monospace",14);
mFontDummy.setStyleStrategy(QFont::PreferAntialias);
mDocument = std::make_shared<Document>(mFontDummy, mFontDummy, this);
//fPlugins := TList.Create;

View File

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

View File

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