Linux 获取有关重点 gui 窗口更改的通知

Posted

技术标签:

【中文标题】Linux 获取有关重点 gui 窗口更改的通知【英文标题】:Linux get notification on focused gui window change 【发布时间】:2013-11-07 15:53:32 【问题描述】:

在 linux 中,是否可以在当前关注的 GUI 应用程序更改时收到通知?我正在编写一个应用程序来跟踪用户在每个 GUI 应用程序上停留的时间(每个进程,而不是一个进程内),并且需要某种方式来访问这些信息。我正在用 C++ 做这个。


这是我到目前为止发现的:
xprop -id $(xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/print $NF') | awk '/_NET_WM_PID\(CARDINAL\)/print $NF'

这会打印出当前关注的应用程序的 pid,但需要我经常拉动。我宁愿不拉,但如果必须,我会。它还假设所有 GUI 都通过 x11,这可能不是一个不合理的假设,但也不是完全可移植的。

另一种方法是编写一个与各种 gui 函数挂钩的共享对象,然后修改主机系统的 ld.so.preload 文件以在每个进程中加载​​此共享对象。这假设所有 gui 应用程序都使用动态链接的图形库。我还必须为每个图形库编写挂钩以确保完全覆盖。在研究 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。我做错了什么?

【问题讨论】:

get_window_pid 使用根窗口而不是事件中指定的窗口。 get_window_pid 必须像这样声明:get_window_pid(e.xproperty.display, e.xproperty.window, e.xproperty.atom); 并像这样调用:pid_t get_window_pid( Display * d, Window w, Atom atom) 【参考方案1】:

使用 node-x11 的 javascript 示例:

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));
          );
        
      );
    
  );
);

【讨论】:

这会拦截其他窗口的消息吗?或者这是一个特定程序如何处理自己的活动窗口更改?如果是后者,它让我知道如何在注入的共享对象中使用它(我必须找出相应的 c++ 代码。) 它拦截根窗口属性的变化。属性更改的发起者是您的窗口管理器(如果它现在符合standards.freedesktop.org/wm-spec/wm-spec-latest.html,则此方法将不起作用) 如果有人需要这个的 Python 实现,我在this answer 写了一个。【参考方案2】:

我终于明白了。 编译: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;


【讨论】:

以上是关于Linux 获取有关重点 gui 窗口更改的通知的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Scintilla 获取有关插入符号移动的通知

获取有关 cgroup 进程更改的通知?

Delphi中有关窗口绘制

如何获取有关数据库列更改的通知

获取有关 FaxOut RingCentral API 的传真状态更改的通知

获取有关 linux 中审计事件的通知