Linux получает уведомление об изменении окон в графическом интерфейсе
В linux возможно ли получать уведомления, когда текущее приложение GUI изменяется? Я пишу приложение, которое отслеживает, как долго пользователь остается в каждом приложении с графическим интерфейсом (для каждого процесса, а не внутри одного процесса), и нуждается в некотором способе доступа к этой информации. Я делаю это в C++.
Вот что я нашел до сих пор:
xprop -id $(xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/{print $NF}') | awk '/_NET_WM_PID\(CARDINAL\)/{print $NF}'
Это распечатывает pid приложения, которое в настоящее время сосредоточено, но потребовало бы, чтобы я вытягивал это так часто. Я бы предпочел не тянуть, но я буду, если придется. Также предполагается, что все графические интерфейсы проходят через x11, что, возможно, не является необоснованным предположением, но не является полностью переносимым.
Другой подход - написать общий объект, который подключается к различным функциям графического интерфейса, а затем изменить файл ld.so.preload хост-системы для загрузки этого общего объекта в каждый процесс. Это предполагает, что все приложения графического интерфейса используют динамически связанные графические библиотеки. Я также должен написать хуки для каждой графической библиотеки, чтобы обеспечить полное покрытие. И, исследуя GTK (я тестирую систему под управлением Gnome), я не нашел ни одной функции, которая вызывается для оконных переключателей. Я не выглядел очень сильно, хотя.
Есть ли способ получать уведомления через x11 для такого рода вещей? Или другие графические библиотеки в этом отношении?
Редактировать:
Хорошо, это то, что у меня есть, основываясь на коде @Andrey:
#include <X11/Xlib.h>
#include <cstring>
#include <iostream>
using namespace std;
pid_t get_window_pid( Display * d, Window& w );
int main()
{
Display * d;
Window w;
XEvent e;
d = XOpenDisplay( 0 );
if ( !d ) {
cerr << "Could not open display" << endl;
return 1;
}
w = DefaultRootWindow( d );
XSelectInput( d, w, PropertyChangeMask );
pid_t window_pid;
for ( ;; ) {
XNextEvent( d, &e );
if ( e.type == PropertyNotify ) {
if ( !strcmp( XGetAtomName( d, e.xproperty.atom ), "_NET_ACTIVE_WINDOW" ) ) {
window_pid = get_window_pid( d, w );
cout << window_pid << endl;
}
}
}
return 0;
}
pid_t get_window_pid( Display * d, Window& w )
{
Atom atom = XInternAtom( d, "_NET_WM_PID", true );
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *prop;
int status;
status = XGetWindowProperty(
d, w, atom, 0, 1024,
false, AnyPropertyType,
&actual_type,
&actual_format, &nitems,
&bytes_after,
&prop
);
if ( status || !prop )
return -1;
return prop[1] * 256 + prop[0];
}
Но get_window_pid
всегда возвращает -1, даже используя xprop -id $(xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/{print $NF}') | awk '/_NET_WM_PID\(CARDINAL\)/{print $NF}'
правильно возвращает pid активного окна. Что я делаю неправильно?
3 ответа
Пример в JavaScript с использованием node-x11:
var x11 = require('x11');
x11.createClient(function(err, display) {
var X = display.client;
X.ChangeWindowAttributes(display.screen[0].root, { eventMask: x11.eventMask.PropertyChange });
X.on('event', function(ev) {
if(ev.name == 'PropertyNotify') {
X.GetAtomName(ev.atom, function(err, name) {
if (name == '_NET_ACTIVE_WINDOW') {
X.GetProperty(0, ev.window, ev.atom, X.atoms.WINDOW, 0, 4, function(err, prop) {
console.log('New active window:' + prop.data.readUInt32LE(0));
});
}
});
}
});
});
Наконец то я понял.
скомпилировать: g++ ./a.cpp -lX11
#include <X11/Xlib.h>
#include <cstring>
#include <iostream>
#define MAXSTR 1000
using namespace std;
Display* display;
unsigned char *prop;
void check_status(int status, Window window)
{
if (status == BadWindow)
{
printf("window id # 0x%lx does not exists!", window);
}
if (status != Success)
{
printf("XGetWindowProperty failed!");
}
}
unsigned char *get_string_property(const char *property_name, Window window)
{
Atom actual_type, filter_atom;
int actual_format, status;
unsigned long nitems, bytes_after;
filter_atom = XInternAtom(display, property_name, True);
status = XGetWindowProperty(display, window, filter_atom, 0, MAXSTR, False, AnyPropertyType,
&actual_type, &actual_format, &nitems, &bytes_after, &prop);
check_status(status, window);
return prop;
}
unsigned long get_long_property(const char *property_name, Window window)
{
if (window == 0)
return 0;
get_string_property(property_name, window);
unsigned long long_property = static_cast<unsigned long>(prop[0] + (prop[1] << 8) + (prop[2] << 16) + (prop[3] << 24));
return long_property;
}
unsigned long getActiveWindowPID(Window root_window)
{
unsigned long window;
window = get_long_property("_NET_ACTIVE_WINDOW", root_window);
return get_long_property(("_NET_WM_PID"), window);
}
int main()
{
Display * d;
Window w;
XEvent e;
d = XOpenDisplay( 0 );
if ( !d ) {
cerr << "Could not open display" << endl;
return 1;
}
display = d;
w = DefaultRootWindow( d );
XSelectInput( d, w, PropertyChangeMask );
pid_t window_pid;
for ( ;; ) {
XNextEvent( d, &e );
if ( e.type == PropertyNotify ) {
if ( !strcmp( XGetAtomName( d, e.xproperty.atom ), "_NET_ACTIVE_WINDOW" ) ) {
window_pid = getActiveWindowPID(w );
cout << window_pid << endl;
}
}
}
return 0;
}
Просто хочу добавить еще один возможный ответ
есть инструментdevilspie
( https://www.nongnu.org/devilspie2/ ), который создан специально для этого. Он запускает серию сценариев lua, запускаемых при запуске приложения, получении фокуса или размытии, а затем запуске процесса.
Все это основано на lua, но вы можете легко запустить сторонний инструмент, например, я использовалos.execute("my bash script")