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 窗口更改的通知的主要内容,如果未能解决你的问题,请参考以下文章