Linux: port the emulator to a working state on Linux (#117)

Linux: port the emulator to a working state
This commit is contained in:
Stefanos Costa
2026-07-29 01:25:58 +03:00
committed by GitHub
parent 3dc793d859
commit 2b9cba457e
30 changed files with 2320 additions and 381 deletions
+59
View File
@@ -32,6 +32,9 @@ set_target_properties(launcher PROPERTIES AUTOUIC ON AUTOMOC ON AUTORCC ON)
if(WIN32 AND NOT KYTY_CLANG_CL)
set_target_properties(launcher PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS}")
elseif(LINUX)
# Use the same linker as kyty_emulator.
set_target_properties(launcher PROPERTIES LINK_FLAGS "${KYTY_LD_OPTIONS}")
endif()
add_dependencies(launcher KytyGitVersion)
@@ -79,6 +82,62 @@ if(WIN32)
--no-translations
)
")
elseif(LINUX)
# Bundle the Qt runtime and plugins.
set_target_properties(launcher PROPERTIES
INSTALL_RPATH "$ORIGIN/lib"
BUILD_WITH_INSTALL_RPATH FALSE
LINK_FLAGS "${KYTY_LD_OPTIONS} -Wl,--disable-new-dtags")
# Support distribution and official Qt layouts.
get_target_property(kyty_qt_core_lib Qt6::Core IMPORTED_LOCATION_RELEASE)
if(NOT kyty_qt_core_lib)
get_target_property(kyty_qt_core_lib Qt6::Core IMPORTED_LOCATION)
endif()
get_filename_component(kyty_qt_libdir "${kyty_qt_core_lib}" DIRECTORY)
set(kyty_qt_plugins_dir "")
foreach(kyty_candidate
"${kyty_qt_libdir}/qt6/plugins"
"${kyty_qt_libdir}/../plugins"
"${Qt6_DIR}/../../../plugins")
if(IS_DIRECTORY "${kyty_candidate}/platforms")
get_filename_component(kyty_qt_plugins_dir "${kyty_candidate}" ABSOLUTE)
break()
endif()
endforeach()
if(kyty_qt_plugins_dir)
message(STATUS "Qt plugins for launcher deployment: ${kyty_qt_plugins_dir}")
set(kyty_qt_plugin_files "")
foreach(kyty_qt_plugin platforms tls imageformats xcbglintegrations wayland-shell-integration)
if(IS_DIRECTORY "${kyty_qt_plugins_dir}/${kyty_qt_plugin}")
file(GLOB kyty_qt_plugin_group
"${kyty_qt_plugins_dir}/${kyty_qt_plugin}/*.so"
"${kyty_qt_plugins_dir}/${kyty_qt_plugin}/*.so.*")
list(APPEND kyty_qt_plugin_files ${kyty_qt_plugin_group})
install(DIRECTORY "${kyty_qt_plugins_dir}/${kyty_qt_plugin}" DESTINATION "plugins")
endif()
endforeach()
install(CODE "
set(kyty_plugin_files \"${kyty_qt_plugin_files}\")
file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES \"${CMAKE_CURRENT_BINARY_DIR}/${launcher_name}\"
LIBRARIES \${kyty_plugin_files}
RESOLVED_DEPENDENCIES_VAR kyty_resolved
POST_INCLUDE_REGEXES \"libQt6\" \"libicu\"
POST_EXCLUDE_REGEXES \".*\")
foreach(kyty_dep \${kyty_resolved})
file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/lib\"
TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES \"\${kyty_dep}\")
endforeach()
")
install(CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/qt.conf\" \"[Paths]\\nPlugins = plugins\\n\")
")
else()
message(WARNING "Qt plugin directory not found; the installed launcher will need a system Qt")
endif()
endif()
+70 -4
View File
@@ -45,8 +45,6 @@ constexpr char EMULATOR_EXE[] = "kyty_emulator";
#if defined(_WIN32)
constexpr char CMD_EXE[] = "cmd.exe";
#elif defined(__linux__)
constexpr char GNOME[] = "gnome-terminal";
constexpr char XTERM[] = "xterm";
constexpr char KYTY_BASH_FILE[] = "kyty_run.sh";
#endif
#if defined(_WIN32)
@@ -265,6 +263,57 @@ static bool CreateBashScript(const QString& interpreter, const QStringList& args
}
return false;
}
// Find a terminal and its command separator.
static bool FindTerminal(QString* program, QStringList* prefix) {
struct TerminalSpec {
const char* executable;
const char* separator; // nullptr when the command follows immediately
};
static const TerminalSpec candidates[] = {
{"x-terminal-emulator", "-e"}, {"gnome-terminal", "--"}, {"konsole", "-e"},
{"xfce4-terminal", "-x"}, {"mate-terminal", "--"}, {"tilix", "-e"},
{"alacritty", "-e"}, {"kitty", nullptr}, {"foot", nullptr},
{"wezterm", "-e"}, {"urxvt", "-e"}, {"xterm", "-e"},
};
const auto try_candidate = [program, prefix](const QString& executable, const char* separator) {
const auto resolved = QStandardPaths::findExecutable(executable);
if (resolved.isEmpty()) {
return false;
}
*program = resolved;
prefix->clear();
if (separator != nullptr) {
*prefix << QString::fromLatin1(separator);
}
return true;
};
if (const auto from_env = qEnvironmentVariable("TERMINAL"); !from_env.isEmpty()) {
// Reuse the known separator for an explicit terminal.
const auto env_name = QFileInfo(from_env).fileName();
const char* separator = "-e";
for (const auto& candidate: candidates) {
if (env_name == QLatin1String(candidate.executable)) {
separator = candidate.separator;
break;
}
}
if (try_candidate(from_env, separator)) {
return true;
}
}
for (const auto& candidate: candidates) {
if (try_candidate(QString::fromLatin1(candidate.executable), candidate.separator)) {
return true;
}
}
return false;
}
#endif
void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
@@ -289,8 +338,16 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
}
{
process->setProgram(GNOME);
process->setArguments({"--", "bash", "-c", bash_file_name});
QString terminal;
QStringList terminal_prefix;
if (FindTerminal(&terminal, &terminal_prefix)) {
process->setProgram(terminal);
process->setArguments(terminal_prefix + QStringList {"bash", "-c", bash_file_name});
} else {
// Run without a terminal as a fallback.
process->setProgram(QStringLiteral("bash"));
process->setArguments({QStringLiteral("-c"), bash_file_name});
}
}
#elif defined(_WIN32)
{
@@ -319,6 +376,15 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) {
});
#endif
process->start();
#if !defined(_WIN32)
// Report immediate launch failures.
if (!process->waitForStarted(5000)) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to start:\n%1\n\n%2")
.arg(process->program(), process->errorString()));
return;
}
#endif
process->waitForFinished(100);
}