X11 如何使用 xcb restore/raise 另一个应用程序 window?
X11 How to restore/raise another application window using xcb?
我正在向我的 C 应用程序中写入一个 window 列表,显示所有顶级 windows,包括阴影、最小化和其他桌面。我想恢复未映射(最小化)windows,提高 window,并在选择一个时切换到 window 的 desktop/workspace。
我过去使用 Xlib 写过一些东西来完成这个。我之前使用 XSendEvent() 发送类型为 _NET_ACTIVE_WINDOW 的 ClientMessage 事件,然后是 XMapRaised(),它工作得相当好,但并不完美。
我现在正在重写应用程序,并决定将 XCB 用于 window 列表代码而不是 Xlib,并希望创建一个更好、更高效的实现。 XCB 没有等同于 XMapRaised() 并且 xcb_map_window() 似乎对我不起作用。我找到了大量关于创建和配置新的 windows 的文档,但很少有用于实现 window 管理器或实用程序(如寻呼机、图标框、任务栏等)的文档。生成的XCB 的文档对于某些功能的实际作用也相当模糊。如果有人知道对此有价值的其他文档,那也很好。
编辑:
我在一个小型实用程序中复制了一部分使用 Xlib 的旧代码,它确实完成了我想要的大部分工作,而且似乎始终如一地工作:
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
int main(int argc, char **argv) {
Display *display = XOpenDisplay("");
Window rootwin = XDefaultRootWindow(display);
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return 0;
}
Window window = (Window)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%lx\n", window);
Atom ActiveWindowAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.window = window;
xev.xclient.message_type = ActiveWindowAtom;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1U;
xev.xclient.data.l[1] = 1U;
xev.xclient.data.l[2] = 0U;
xev.xclient.data.l[3] = 0U;
xev.xclient.data.l[4] = 0U;
XSendEvent(display, rootwin, False, SubstructureRedirectMask, &xev);
XMapRaised(display, window);
XCloseDisplay(display);
return 0;
}
...然后我解压了 libX11 源代码并确认它只是包装了 XCB 函数,但是包装器使用了一些 libX11 数据结构而不是 XCB 中定义的数据结构,并且使用了太多的宏很难确切地看到发生了什么。这是我想出的 XCB 等价物,但它似乎不起作用:
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
#include "atom_cache.h"
int main(int argc, char **argv) {
xcb_connection_t *connection;
const xcb_setup_t *setup;
xcb_screen_iterator_t screen_iter;
xcb_screen_t *screen;
xcb_window_t rootwin, window;
xcb_void_cookie_t void_cookie;
xcb_client_message_event_t client_message_event;
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return 0;
}
window = (xcb_window_t)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%x\n", window);
connection = xcb_connect(NULL, NULL);
setup = xcb_get_setup(connection);
screen_iter = xcb_setup_roots_iterator(setup);
screen = screen_iter.data;
rootwin = screen->root;
// send _net_active_window request
client_message_event.response_type = XCB_CLIENT_MESSAGE;
client_message_event.format = 32;
client_message_event.sequence = 0;
client_message_event.window = window;
client_message_event.type = get_atom(connection, "_NET_ACTIVE_WINDOW");
client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
client_message_event.data.data32[1] = 1UL; // timestamp
client_message_event.data.data32[2] = 0UL; // my currently active window?
client_message_event.data.data32[3] = 0UL;
client_message_event.data.data32[4] = 0UL;
void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);
uint32_t values[] = { XCB_STACK_MODE_ABOVE };
xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
xcb_map_window(connection, window);
xcb_flush(connection);
xcb_disconnect(connection);
return 0;
}
我还在想办法解决这个问题。
我在问题末尾 post 编写的代码似乎是正确的,除了 atom_cache 中有一个小错误。我还重新排序了映射 window、提高 window 并使其成为活动 window 的序列。我在想也许我应该 post github 上的这些解决方案作为 xcb 示例。我已经编写了几个这样的小 cli 实用程序来与 xserver 和 window 管理器进行交互。也许它们会对其他人有用...
xcb_switchto.c:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
int main(int argc, char **argv) {
xcb_connection_t *connection;
xcb_window_t rootwin, window;
xcb_intern_atom_cookie_t atom_cookie;
xcb_intern_atom_reply_t *atom_reply;
xcb_atom_t net_active_window;
xcb_query_tree_cookie_t qtree_cookie;
xcb_query_tree_reply_t *qtree_reply;
xcb_void_cookie_t void_cookie;
xcb_client_message_event_t client_message_event;
xcb_generic_error_t *err;
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return EXIT_FAILURE;
}
window = (xcb_window_t)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%x\n", window);
// connect to X server
connection = xcb_connect(NULL, NULL);
// get _NET_ACTIVE_WINDOW atom from X server
atom_cookie = xcb_intern_atom(connection, 0, 18, "_NET_ACTIVE_WINDOW");
if (atom_reply = xcb_intern_atom_reply(connection, atom_cookie, &err)) {
net_active_window = atom_reply->atom;
free(atom_reply);
} else if (err) {
printf("xcb_intern_atom failed with error code %d\n", err->error_code);
return EXIT_FAILURE;
}
// get the window's root window. can there really be more than one?
qtree_cookie = xcb_query_tree(connection, window);
if (qtree_reply = xcb_query_tree_reply(connection, qtree_cookie, &err)) {
rootwin = qtree_reply->root;
} else if (err) {
printf("xcb_query_tree failed with error code %d\n", err->error_code);
return EXIT_FAILURE;
}
// map the window first
xcb_map_window(connection, window);
// now raise (restack) the window
uint32_t values[] = { XCB_STACK_MODE_ABOVE };
xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
// and, finally, make it the active window
client_message_event.response_type = XCB_CLIENT_MESSAGE;
client_message_event.format = 32;
client_message_event.sequence = 0;
client_message_event.window = window;
client_message_event.type = net_active_window;
client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
client_message_event.data.data32[1] = 1UL; // timestamp
client_message_event.data.data32[2] = 0UL; // currently active window (none)
client_message_event.data.data32[3] = 0UL;
client_message_event.data.data32[4] = 0UL;
void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);
// probably unnecessary
xcb_flush(connection);
// close the connection
xcb_disconnect(connection);
return EXIT_SUCCESS;
}
这里有一个与使用 1 作为时间戳有关的小问题。查询 window 以获得 _NET_WM_USER_TIME 并使用该值可能会更好,因为有时每次使用 1 时它都会被忽略。我也不确定你怎么可能拥有多个根 window,但我仍然认为最好询问服务器它在哪个根 window 上。也许是非 xinerama 多头显示器...无论如何,问题解决了。
我正在向我的 C 应用程序中写入一个 window 列表,显示所有顶级 windows,包括阴影、最小化和其他桌面。我想恢复未映射(最小化)windows,提高 window,并在选择一个时切换到 window 的 desktop/workspace。
我过去使用 Xlib 写过一些东西来完成这个。我之前使用 XSendEvent() 发送类型为 _NET_ACTIVE_WINDOW 的 ClientMessage 事件,然后是 XMapRaised(),它工作得相当好,但并不完美。
我现在正在重写应用程序,并决定将 XCB 用于 window 列表代码而不是 Xlib,并希望创建一个更好、更高效的实现。 XCB 没有等同于 XMapRaised() 并且 xcb_map_window() 似乎对我不起作用。我找到了大量关于创建和配置新的 windows 的文档,但很少有用于实现 window 管理器或实用程序(如寻呼机、图标框、任务栏等)的文档。生成的XCB 的文档对于某些功能的实际作用也相当模糊。如果有人知道对此有价值的其他文档,那也很好。
编辑:
我在一个小型实用程序中复制了一部分使用 Xlib 的旧代码,它确实完成了我想要的大部分工作,而且似乎始终如一地工作:
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
int main(int argc, char **argv) {
Display *display = XOpenDisplay("");
Window rootwin = XDefaultRootWindow(display);
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return 0;
}
Window window = (Window)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%lx\n", window);
Atom ActiveWindowAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.window = window;
xev.xclient.message_type = ActiveWindowAtom;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1U;
xev.xclient.data.l[1] = 1U;
xev.xclient.data.l[2] = 0U;
xev.xclient.data.l[3] = 0U;
xev.xclient.data.l[4] = 0U;
XSendEvent(display, rootwin, False, SubstructureRedirectMask, &xev);
XMapRaised(display, window);
XCloseDisplay(display);
return 0;
}
...然后我解压了 libX11 源代码并确认它只是包装了 XCB 函数,但是包装器使用了一些 libX11 数据结构而不是 XCB 中定义的数据结构,并且使用了太多的宏很难确切地看到发生了什么。这是我想出的 XCB 等价物,但它似乎不起作用:
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
#include "atom_cache.h"
int main(int argc, char **argv) {
xcb_connection_t *connection;
const xcb_setup_t *setup;
xcb_screen_iterator_t screen_iter;
xcb_screen_t *screen;
xcb_window_t rootwin, window;
xcb_void_cookie_t void_cookie;
xcb_client_message_event_t client_message_event;
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return 0;
}
window = (xcb_window_t)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%x\n", window);
connection = xcb_connect(NULL, NULL);
setup = xcb_get_setup(connection);
screen_iter = xcb_setup_roots_iterator(setup);
screen = screen_iter.data;
rootwin = screen->root;
// send _net_active_window request
client_message_event.response_type = XCB_CLIENT_MESSAGE;
client_message_event.format = 32;
client_message_event.sequence = 0;
client_message_event.window = window;
client_message_event.type = get_atom(connection, "_NET_ACTIVE_WINDOW");
client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
client_message_event.data.data32[1] = 1UL; // timestamp
client_message_event.data.data32[2] = 0UL; // my currently active window?
client_message_event.data.data32[3] = 0UL;
client_message_event.data.data32[4] = 0UL;
void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);
uint32_t values[] = { XCB_STACK_MODE_ABOVE };
xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
xcb_map_window(connection, window);
xcb_flush(connection);
xcb_disconnect(connection);
return 0;
}
我还在想办法解决这个问题。
我在问题末尾 post 编写的代码似乎是正确的,除了 atom_cache 中有一个小错误。我还重新排序了映射 window、提高 window 并使其成为活动 window 的序列。我在想也许我应该 post github 上的这些解决方案作为 xcb 示例。我已经编写了几个这样的小 cli 实用程序来与 xserver 和 window 管理器进行交互。也许它们会对其他人有用...
xcb_switchto.c:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
int main(int argc, char **argv) {
xcb_connection_t *connection;
xcb_window_t rootwin, window;
xcb_intern_atom_cookie_t atom_cookie;
xcb_intern_atom_reply_t *atom_reply;
xcb_atom_t net_active_window;
xcb_query_tree_cookie_t qtree_cookie;
xcb_query_tree_reply_t *qtree_reply;
xcb_void_cookie_t void_cookie;
xcb_client_message_event_t client_message_event;
xcb_generic_error_t *err;
if (argc < 2)
{
printf("usage: %s windowid\n", argv[0]);
return EXIT_FAILURE;
}
window = (xcb_window_t)strtoul(argv[1], NULL, 0);
printf("switch to window: 0x%x\n", window);
// connect to X server
connection = xcb_connect(NULL, NULL);
// get _NET_ACTIVE_WINDOW atom from X server
atom_cookie = xcb_intern_atom(connection, 0, 18, "_NET_ACTIVE_WINDOW");
if (atom_reply = xcb_intern_atom_reply(connection, atom_cookie, &err)) {
net_active_window = atom_reply->atom;
free(atom_reply);
} else if (err) {
printf("xcb_intern_atom failed with error code %d\n", err->error_code);
return EXIT_FAILURE;
}
// get the window's root window. can there really be more than one?
qtree_cookie = xcb_query_tree(connection, window);
if (qtree_reply = xcb_query_tree_reply(connection, qtree_cookie, &err)) {
rootwin = qtree_reply->root;
} else if (err) {
printf("xcb_query_tree failed with error code %d\n", err->error_code);
return EXIT_FAILURE;
}
// map the window first
xcb_map_window(connection, window);
// now raise (restack) the window
uint32_t values[] = { XCB_STACK_MODE_ABOVE };
xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
// and, finally, make it the active window
client_message_event.response_type = XCB_CLIENT_MESSAGE;
client_message_event.format = 32;
client_message_event.sequence = 0;
client_message_event.window = window;
client_message_event.type = net_active_window;
client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
client_message_event.data.data32[1] = 1UL; // timestamp
client_message_event.data.data32[2] = 0UL; // currently active window (none)
client_message_event.data.data32[3] = 0UL;
client_message_event.data.data32[4] = 0UL;
void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);
// probably unnecessary
xcb_flush(connection);
// close the connection
xcb_disconnect(connection);
return EXIT_SUCCESS;
}
这里有一个与使用 1 作为时间戳有关的小问题。查询 window 以获得 _NET_WM_USER_TIME 并使用该值可能会更好,因为有时每次使用 1 时它都会被忽略。我也不确定你怎么可能拥有多个根 window,但我仍然认为最好询问服务器它在哪个根 window 上。也许是非 xinerama 多头显示器...无论如何,问题解决了。