Quantcast
Channel: Active questions tagged ubuntu - Stack Overflow
Viewing all articles
Browse latest Browse all 5956

How to get active application headers in Linux?

$
0
0

I have the following task: I need to receive information about the application the user is currently working with once every 4 seconds. You need to know the window title, the process path in the system, and which host the application is sending data to.

I wrote (using stackowrflow articles) the following code:

file ActiveApplication.hpp

//// Created by tania on 13.05.24.//#ifndef NFLDMODULE_ACTIVEAPPLICATION_HPP#define NFLDMODULE_ACTIVEAPPLICATION_HPP#include <Poco/SharedPtr.h>#include <Poco/Logger.h>#include <X11/Xatom.h>#include <X11/Xlib.h>#include <tasks/window/WindowInfo.h>#include <list>#include <array>using namespace tasks::window;class ActiveApplication {public:    /* конструкторкласса         @param logger тип Poco::Logger - ссылкана Poco::Logger дляведениялоговвнутрикласса         * */    explicit ActiveApplication(Poco::Logger& logger);    // деструкторкласса    ~ActiveApplication() = default;    /* методполучающийнужнуюнаминформациюизактивногоокна         @return Poco::SharedPtr<WindowInfo> - возвращаетумныйуказательнаструктурухранящуюсведенияобокне         * */    Poco::SharedPtr<WindowInfo> getActiveWindowInfo();    /* методполучающийокноверхнегоуровня         @return X11::Window - возвращаетодноизоткрытыхоконприложений         * */    Poco::SharedPtr<WindowInfo> getTopWindow();private:    /**Приватныйметодполучающийнекотороесвойствоокна        @param xWindow указательнатип Window из xLib - корневоеокно         @param xPropertyType тип Atom из xLib - типсвойствакотороенужнополучить         @param propertyName тип const char* - имясвойствакотороенужнополучить        @return const char* - массивбайсодержащийинформациюобокне        */    unsigned char* getProperty(Window xWindow, Atom xPropertyType, const char* propertyName);    std::string getWindowTitle(Window& xWindow);    int getPidWindow(Window& xWindow);    std::string getPathApp(int pid);    // указательнаглавныйдисплейОС, скоторогомыбудемполучатьокнаприложений    Display* xDisplay_{};    // ссылканалоггеркоторыйведётунаслогирование    Poco::Logger& logger_;    // периодпокоторомумыповторяемдействияв runTask    const int32_t sleep_period_{4000}; // 4 секунды};#endif    //NFLDMODULE_ACTIVEAPPLICATION_HPP

file ActiveApplication.cpp

//// Created by tania on 13.05.24.//#include "ActiveApplication.hpp"ActiveApplication::ActiveApplication(Poco::Logger& logger)    : logger_(logger){};Poco::SharedPtr<WindowInfo> ActiveApplication::getActiveWindowInfo(){    setenv("DISPLAY", ":0", 1);    xDisplay_ = ::XOpenDisplay(NULL);    Window activeWindow = NULL;    unsigned char* property = getProperty(::XDefaultRootWindow(xDisplay_), XA_WINDOW, "_NET_ACTIVE_WINDOW");    if (property != nullptr)    {        activeWindow = *(reinterpret_cast<Window*>(property));    } else    {        logger_.information("error get property _NET_ACTIVE_WINDOW");        ::XCloseDisplay(xDisplay_);        return nullptr;    }    // получимнужныенамданныеобактивномокнеприложениязаполнимнашуструктуруэтиданными    Poco::SharedPtr<WindowInfo> windowInfo = Poco::makeShared<WindowInfo>();    // заголовококна    windowInfo->name = this->getWindowTitle(activeWindow);    windowInfo->pid  = this->getPidWindow(activeWindow);    windowInfo->path = this->getPathApp(windowInfo->pid);    ::XCloseDisplay(xDisplay_);    return windowInfo;}Poco::SharedPtr<WindowInfo> ActiveApplication::getTopWindow(){    xDisplay_ = ::XOpenDisplay(NULL);    Poco::SharedPtr<WindowInfo> windowInfo{nullptr};    Atom prop_NET_CLIENT_LIST = XInternAtom(xDisplay_,"_NET_CLIENT_LIST",False);    unsigned long len, remain;    unsigned char *output;    int i,form;    Atom type;    Window *list;    if (Success == XGetWindowProperty(xDisplay_,XDefaultRootWindow(xDisplay_),prop_NET_CLIENT_LIST,0,1024,False,XA_WINDOW,&type,&form,&len,&remain,&output))    {        list = (Window*)output;        std::cout<<"len = "<<(int)len<<std::endl;        const int count = (int)len;        long item_user_time {1000000};        int index{-1};        for (i=0;i<count;i++)        {            std::cout << "i = " << i << std::endl;            unsigned char* property = getProperty(::XDefaultRootWindow(xDisplay_), XA_WINDOW, "_NET_WM_USER_TIME");            if (property != nullptr)            {                unsigned long long_property = static_cast<unsigned long>(property[0] + (property[1] << 8) + (property[2] << 16) + (property[3] << 24));                logger_.information("user_time=[%hu]", long_property);                if (long_property < item_user_time)                {                    item_user_time = long_property;                    index = i;                }            }        }        if (index >= 0)        {            // получимнужныенамданныеобактивномокнеприложениязаполнимнашуструктуруэтиданными            windowInfo = Poco::makeShared<WindowInfo>();            // заголовококна            windowInfo->name = this->getWindowTitle(list[index]);            windowInfo->pid  = this->getPidWindow(list[index]);            windowInfo->path = this->getPathApp(windowInfo->pid);        }    }    ::XCloseDisplay(xDisplay_);    return windowInfo;}// методполучающийнекотороесвойствоокнаunsigned char* ActiveApplication::getProperty(Window xWindow, Atom xPropertyType, const char* propertyName){    Atom xProperty = ::XInternAtom(xDisplay_,propertyName,False);    if (xProperty == None)    {        logger_.information("error get property - %s",propertyName);        return nullptr;    }    Atom xActualType = None;    int actualFormat = 0;    unsigned long  itemsNumber     = 0;    unsigned long  remainigBytes   = 0;    unsigned char *pProperty       = NULL;    const long     maxPropertySize = 4096;    try {        int result = ::XGetWindowProperty(xDisplay_,                                          xWindow,                                          xProperty,                                          0,                                          maxPropertySize / 4,                                          False,                                          xPropertyType,&xActualType,&actualFormat,&itemsNumber,&remainigBytes,&pProperty);        if (result != Success) {            logger_.information("error get property window - %s", propertyName);            return nullptr;        }    }    catch (std::exception& ex)    {        logger_.critical("Critical error in ActiveApplication::getProperty: %s", std::string(ex.what()));    }    return  pProperty;}std::string ActiveApplication::getWindowTitle(Window& xWindow){    unsigned char* netwmName = getProperty(xWindow,                                           ::XInternAtom(xDisplay_, "UTF8_STRING", False),"_NET_WM_NAME");    if (netwmName == nullptr)            netwmName = getProperty(xWindow,                                ::XInternAtom(xDisplay_, "UTF8_STRING", False),"WM_NAME");    if (netwmName != nullptr)        return std::string((char*)netwmName);    else        return "";}int ActiveApplication::getPidWindow(Window& xWindow){    unsigned char* wmPid = getProperty(xWindow,                                       AnyPropertyType,"_NET_WM_PID");    if (wmPid != nullptr)    {        // PID процесса, которомупринадлежитэтоокно        unsigned long long_property = static_cast<unsigned long>(wmPid[0] + (wmPid[1] << 8) + (wmPid[2] << 16) + (wmPid[3] << 24));        return (int)long_property;    }    return -1;}std::string ActiveApplication::getPathApp(int pid){    //получимпутькисполняемомуфайлупроцесса, которомупринадлежитактивноеокно    char path[32];    sprintf(path, "/proc/%d/exe", pid);    // вызовсистемнойфункции Linux, возвращающейпутьвсистемедоисполняемогофайла    return realpath(path,NULL);}

file main.cpp

#include <Poco/ConsoleChannel.h>#include <Poco/PatternFormatter.h>#include <Poco/FormattingChannel.h>#include <Poco/AutoPtr.h>#include <Poco/Util/LayeredConfiguration.h>#include <Poco/Util/JSONConfiguration.h>#include <tasks/process/ProcessTask.hpp>#include <tasks/screenshot/ScreenshotTask.hpp>#include <tasks/schedule/ScheduleTask.hpp>#include <tasks/window/WindowTask.hpp>#include <storage/DbContext.hpp>#include <net/NetClient.hpp>#include "ActiveApplication.hpp"using namespace storage::entities;{    std::filesystem::path configs_root_path("/etc/opt");    auto configs_dir_path = configs_root_path / "nf-agent" / "platform.json";    if (std::filesystem::exists(configs_dir_path)) {        auto json_config = Poco::makeAuto<Poco::Util::JSONConfiguration>();        if (std::filesystem::is_regular_file(configs_dir_path)) {            json_config->load(configs_dir_path.string());        }        auto config = Poco::makeAuto<Poco::Util::LayeredConfiguration>();        config->add(json_config);        auto api_url = config->getString("api.baseUrl");        auto api_key = config->getString("api.key");        //creating environment for modules        auto api_json = Poco::makeShared<Poco::JSON::Object>();        api_json->set("key", api_key);        api_json->set("baseUrl", api_url);        auto module_env_json = Poco::makeShared<Poco::JSON::Object>();        module_env_json->set("api", api_json);        auto with_env_key = Poco::makeShared<Poco::JSON::Object>();        with_env_key->set("env", module_env_json);        auto json_module_config = Poco::makeAuto<Poco::Util::JSONConfiguration>(with_env_key);        configModule->add(json_module_config);    }}void initLogger(){    // Инициализациялоггера    Poco::AutoPtr<Poco::PatternFormatter> pattern_formatter(new Poco::PatternFormatter());    pattern_formatter->setProperty(Poco::PatternFormatter::PROP_PATTERN, "%L%Y-%m-%d %L%H:%M:%S [%s-%I] [%p] %t");    Poco::AutoPtr<Poco::ConsoleChannel> console_channel(new Poco::ConsoleChannel);    Poco::AutoPtr<Poco::FormattingChannel> log_console_channel(            new Poco::FormattingChannel(pattern_formatter, console_channel));    Poco::Logger::root().setChannel(log_console_channel);    // log_channel#ifdef _DEBUG    Poco::Logger::root().setLevel(Poco::Message::Priority::PRIO_TRACE);#else    Poco::Logger::root().setLevel(Poco::Message::Priority::PRIO_INFORMATION);#endif}int main(){    initLogger();    Poco::Logger& logger(Poco::Logger::get("LDModuleTest"));    ActiveApplication* activeWindow = new ActiveApplication(logger);    auto window = activeWindow->getTopWindow();    logger.information("top window: %s", window->name);    std::this_thread::sleep_for(std::chrono::seconds (3600));    return 0;}

My class does a good job of getting the window titles. But it only works if the default X11 graphical shell is selected. When testing on ubuntu and kubuntu I have different performance metrics. Everything works well on kubuntu, since Xorg is used by default. But on Ubuntu 24 the code did not work; the application crashed all the time when trying to access the xlib library (even though I link it statically). We began to understand why this behavior occurred. We tried to execute the command in the console to get the active window through the _NET_ACTIVE_WINDOW properties and got an error:

enter image description here

After a little googling, we found an answer on one forum that new versions of ubuntu use the wayland graphical shell by default - https://superuser.com/questions/1170344/why-does-xprop-root-net-active-window-give-me-0x0-for-some-windows

We were faced with a task: is it possible to somehow abstract from shell types and obtain window titles regardless of the graphics subsystem used? Suddenly, in a month a new subsystem will be released and it turns out that we will have to write our own implementation for it. Does anyone know a universal method? Maybe I can somehow implement my task at the OS kernel level? Maybe it’s possible, like antiviruses, to connect to the application launch event in the system? And intercept information about him? Can anyone suggest a solution?


Viewing all articles
Browse latest Browse all 5956

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>