From 83914d53785b2f7cc445429bf798bae9970c60d8 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Fri, 8 Dec 2023 23:56:06 +0100 Subject: [PATCH] Linux: resolve procps symbols at runtime This is done to be able to support both libprocps and libproc2. Either of them or neither might be available on all platforms and this should not prevent the plugin from being used at all. --- CMakeLists.txt | 29 ++-- src/linux/advanced-scene-switcher-nix.cpp | 157 +++++++++++++++++----- 2 files changed, 134 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f2a3f10..c5d8c48e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -464,27 +464,24 @@ else() find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h) find_path(PROCPS2_INCLUDE_DIR NAMES libproc2/pids.h) - find_library(PROCPS_LIBRARY NAMES procps) - find_library(PROCPS2_LIBRARY NAMES proc2) - if(PROCPS_INCLUDE_DIR AND PROCPS_LIBRARY) + if(PROCPS_INCLUDE_DIR) message(STATUS "${PROJECT_NAME} using procps") set(PROC_INCLUDE_DIR "${PROCPS_INCLUDE_DIR}") - set(PROC_LIBRARY "${PROCPS_LIBRARY}") - target_compile_definitions(${LIB_NAME} PRIVATE USE_PROCPS) - elseif(PROCPS2_INCLUDE_DIR AND PROCPS2_LIBRARY) + target_compile_definitions(${LIB_NAME} PRIVATE PROCPS_AVAILABLE) + set(PROCESS_CONDITION_SUPPORTED 1) + endif() + if(PROCPS2_INCLUDE_DIR) message(STATUS "${PROJECT_NAME} using libproc2") set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}") - set(PROC_LIBRARY "${PROCPS2_LIBRARY}") - else() - message( - FATAL_ERROR - "found neither procps nor libproc2! - please either set ... - PROCPS_INCLUDE_DIR and PROCPS_LIBRARY - ... or ... - PROCPS2_INCLUDE_DIR and PROCPS2_LIBRARY") + target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE) + set(PROCESS_CONDITION_SUPPORTED 1) + endif() + if(NOT DEFINED PROCESS_CONDITION_SUPPORTED) + message( + WARNING + "found neither procps nor libproc2! Process condition will not be functional!" + ) endif() - target_link_libraries(${LIB_NAME} PRIVATE "${PROC_LIBRARY}") target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}") find_package(CURL) diff --git a/src/linux/advanced-scene-switcher-nix.cpp b/src/linux/advanced-scene-switcher-nix.cpp index 25c8be51..323a50fe 100644 --- a/src/linux/advanced-scene-switcher-nix.cpp +++ b/src/linux/advanced-scene-switcher-nix.cpp @@ -1,5 +1,6 @@ #include "platform-funcs.hpp" #include "hotkey.hpp" +#include "log-helper.hpp" #include #include @@ -25,9 +26,10 @@ #include #include #include -#ifdef USE_PROCPS +#ifdef PROCPS_AVAILABLE #include -#else +#endif +#ifdef PROCPS2_AVAILABLE #include #endif #include @@ -53,6 +55,31 @@ std::chrono::high_resolution_clock::time_point lastMouseLeftClickTime{}; std::chrono::high_resolution_clock::time_point lastMouseMiddleClickTime{}; std::chrono::high_resolution_clock::time_point lastMouseRightClickTime{}; +static QLibrary *libprocps = nullptr; +#ifdef PROCPS_AVAILABLE +typedef PROCTAB *(*openproc_func)(int flags); +typedef void (*closeproc_func)(PROCTAB *PT); +typedef proc_t *(*readproc_func)(PROCTAB *__restrict const PT, + proc_t *__restrict p); +static openproc_func openproc_ = nullptr; +static closeproc_func closeproc_ = nullptr; +static readproc_func readproc_ = nullptr; +#endif +static bool libprocpsSupported = false; + +static QLibrary *libproc2 = nullptr; +#ifdef PROCPS2_AVAILABLE +typedef int (*procps_pids_new_func)(struct pids_info **info, + enum pids_item *items, int numitems); +typedef struct pids_stack *(*procps_pids_get_func)(struct pids_info *info, + enum pids_fetch_type which); +typedef int (*procps_pids_unref_func)(struct pids_info **info); +static procps_pids_new_func procps_pids_new_ = nullptr; +static procps_pids_get_func procps_pids_get_ = nullptr; +static procps_pids_unref_func procps_pids_unref_ = nullptr; +#endif +static bool libprocps2Supported = false; + Display *disp() { if (!xdisplay) { @@ -338,46 +365,57 @@ std::optional GetTextInWindow(const std::string &) return {}; } -#ifdef USE_PROCPS -void GetProcessList(QStringList &processes) +static void getProcessListProcps(QStringList &processes) { - processes.clear(); - PROCTAB *proc = openproc(PROC_FILLSTAT); +#ifdef PROCPS_AVAILABLE + PROCTAB *proc = openproc_(PROC_FILLSTAT); proc_t proc_info; memset(&proc_info, 0, sizeof(proc_info)); - while (readproc(proc, &proc_info) != NULL) { + while (readproc_(proc, &proc_info) != NULL) { QString procName(proc_info.cmd); if (!procName.isEmpty() && !processes.contains(procName)) { processes << procName; } } - closeproc(proc); + closeproc_(proc); +#endif } -#else -void GetProcessList(QStringList &processes) + +static void getProcessListProcps2(QStringList &processes) { - processes.clear(); +#ifdef PROCPS2_AVAILABLE struct pids_info *info = NULL; struct pids_stack *stack; enum pids_item Items[] = { PIDS_CMD, }; - if (procps_pids_new(&info, Items, sizeof(Items) / sizeof(Items[0])) < + if (procps_pids_new_(&info, Items, sizeof(Items) / sizeof(Items[0])) < 0) { return; } - - while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) { + while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) { auto cmd = PIDS_VAL(0, str, stack, info); QString procName(cmd); if (!procName.isEmpty() && !processes.contains(procName)) { processes << procName; } } - procps_pids_unref(&info); -} + procps_pids_unref_(&info); #endif +} + +void GetProcessList(QStringList &processes) +{ + processes.clear(); + if (libprocpsSupported) { + getProcessListProcps(processes); + return; + } + if (libprocps2Supported) { + getProcessListProcps2(processes); + } +} long getForegroundProcessPid() { @@ -633,6 +671,58 @@ void PressKeys(const std::vector keys, int duration) XFlush(display); } +static void initXtst() +{ + libXtstHandle = new QLibrary("libXtst", nullptr); + pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent"); + int _; + canSimulateKeyPresses = pressFunc && + XQueryExtension(disp(), "XTEST", &_, &_, &_); +} + +static void initXss() +{ + libXssHandle = new QLibrary("libXss", nullptr); + allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve( + "XScreenSaverAllocInfo"); + querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve( + "XScreenSaverQueryInfo"); + int _; + canGetIdleTime = allocSSFunc && querySSFunc && + XQueryExtension(disp(), ScreenSaverName, &_, &_, &_); +} + +static void initProcps() +{ +#ifdef PROCPS_AVAILABLE + libprocps = new QLibrary("libprocps", nullptr); + openproc_ = (openproc_func)libprocps->resolve("openproc"); + closeproc_ = (closeproc_func)libprocps->resolve("closeproc"); + readproc_ = (readproc_func)libprocps->resolve("readproc"); + if (openproc_ && closeproc_ && readproc_) { + libprocpsSupported = true; + blog(LOG_INFO, "libprocps symbols resolved successfully!"); + } +#endif +} + +static void initProc2() +{ +#ifdef PROCPS2_AVAILABLE + libproc2 = new QLibrary("libproc2", nullptr); + procps_pids_new_ = + (procps_pids_new_func)libproc2->resolve("procps_pids_new"); + procps_pids_get_ = + (procps_pids_get_func)libproc2->resolve("procps_pids_get"); + procps_pids_unref_ = + (procps_pids_unref_func)libproc2->resolve("procps_pids_unref"); + if (procps_pids_new_ && procps_pids_get_ && procps_pids_unref_) { + libprocps2Supported = true; + blog(LOG_INFO, "libproc2 symbols resolved successfully!"); + } +#endif +} + void PlatformInit() { auto display = disp(); @@ -640,31 +730,26 @@ void PlatformInit() return; } - libXtstHandle = new QLibrary("libXtst", nullptr); - pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent"); - int _; - canSimulateKeyPresses = pressFunc && - XQueryExtension(disp(), "XTEST", &_, &_, &_); + initXtst(); + initXss(); + initProcps(); + initProc2(); +} - libXssHandle = new QLibrary("libXss", nullptr); - allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve( - "XScreenSaverAllocInfo"); - querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve( - "XScreenSaverQueryInfo"); - canGetIdleTime = allocSSFunc && querySSFunc && - XQueryExtension(disp(), ScreenSaverName, &_, &_, &_); +static void cleanupHelper(QLibrary *lib) +{ + if (lib) { + delete lib; + lib = nullptr; + } } void PlatformCleanup() { - if (libXtstHandle) { - delete libXtstHandle; - libXtstHandle = nullptr; - } - if (libXssHandle) { - delete libXssHandle; - libXssHandle = nullptr; - } + cleanupHelper(libXtstHandle); + cleanupHelper(libXssHandle); + cleanupHelper(libprocps); + cleanupHelper(libproc2); cleanupDisplay(); }