不久前,我用 C 语言编写了一个脚本,该脚本使用 Windows API 函数 EnumWindows、SetWindowPos 和 SetForegroundWindow 以我通常想要的特定布局自动排列窗口(按标题)。
这些功能是否有 Linux 等价物?我将使用 Kubuntu,因此特定于 KDE 和/或特定于 Ubuntu 的解决方案都可以。
最好的方法是在窗口管理器本身(如果您的支持扩展)或使用旨在支持“寻呼机”的协议和提示(寻呼机 = 任何执行窗口组织或导航的非窗口管理器进程)。
EWMH 规范包括一个 _NET_MOVERESIZE_WINDOW 设计供寻呼机使用。http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#id2731465
原始 Xlib 或 Xcb 非常粗糙,但有一个名为 libwnck 的库专门设计用于执行您所说的那种事情。(我很早以前就编写了原始库,但它一直由其他人维护。)即使您不使用它,也请阅读代码以了解如何做事。我不确定 KDE 可能与 KDE 风格的 API 等效。
不需要使用任何 KDE 或 GNOME 或特定于发行版的东西,因为所需的东西都在 EWMH 中说明了。也就是说,对于某些窗口管理器,将其作为扩展程序可能比编写单独的应用程序更容易。
在我看来,直接使用老式的 X 调用当然可以工作,但是如果你想解决所有的错误和极端情况,有很多细节需要处理,需要大量的专业知识,所以使用 WM 扩展 API 或寻呼机图书馆将是我的建议。
@andrewdotn 在那里有一个很好的答案,但是您也可以通过使用 XQueryTree 从显示的根窗口开始遍历树并使用 XFetchName 获取窗口名称,然后使用 XMoveWindow 移动它来相当简单地做到这一点。这是一个示例,它将列出所有窗口,如果有任何被称为“xeyes”,它们将被移动到左上角。像大多数 X 程序一样,它还有更多功能,这可能应该调用 XGetWindowProperty 来获取 _NET_WM_NAME 扩展窗口管理器属性,但该示例可以作为初学者使用。编译gcc -Wall -g -o demo demo.c -lX11
#include <X11/Xlib.h>
#include <stdio.h>
#include <string.h>
static int
EnumWindows(Display *display, Window window, int depth)
{
Window parent, *children;
unsigned int count = 0;
int r = 1, n = 0;
char *name = NULL;
XFetchName(display, window, &name);
for (n = 0; n < depth; ++n) putchar(' ');
printf("%08x %s\n", (int)window, name?name:"(null)");
if (name && strcmp("xeyes", name) == 0) {
XMoveWindow(display, window, 5, 5);
}
if (name) XFree(name);
if (XQueryTree(display, window, &window, &parent, &children, &count) == 0) {
fprintf(stderr, "error: XQueryTree error\n");
return 0;
}
for (n = 0; r && n < count; ++n) {
r = EnumWindows(display, children[n], depth+1);
}
XFree(children);
return r;
}
int
main(int argc, char *const argv[])
{
Display *display = NULL;
if ((display = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "error: cannot connect to X server\n");
return 1;
}
EnumWindows(display, DefaultRootWindow(display), 0);
XCloseDisplay(display);
return 0;
}
是的,您可以使用 X Windows 协议执行此操作。这是一个非常低级的协议,因此需要一些工作。您可以使用xcb_query_tree
找到要操作的窗口,然后使用 移动它xcb_configure_window
。此页面提供了有关如何执行此操作的一些详细信息。有一个关于使用这些函数所来自的库的基本教程,但你可能想在谷歌上找到更好的。
这可能看起来令人生畏,但也不算太糟糕。这是一个 50 行的 C 程序,它将所有 xterms 向右移动 10px:
#include <stdio.h>
#include <string.h>
#include <xcb/xcb.h>
void handle(xcb_connection_t* connection, xcb_window_t window) {
xcb_query_tree_reply_t *tree = xcb_query_tree_reply(connection,
xcb_query_tree(connection, window), NULL);
xcb_window_t *children = xcb_query_tree_children(tree);
for (int i = 0; i < xcb_query_tree_children_length(tree); i++) {
xcb_get_property_reply_t *class_reply = xcb_get_property_reply(
connection,
xcb_get_property(connection, 0, children[i], XCB_ATOM_WM_CLASS,
XCB_ATOM_STRING, 0, 512), NULL);
char* class = (char*)xcb_get_property_value(class_reply);
class[xcb_get_property_value_length(class_reply)] = '\0';
if (!strcmp(class, "xterm")) {
/* Get geometry relative to parent window */
xcb_get_geometry_reply_t* geom = xcb_get_geometry_reply(
connection,
xcb_get_geometry(connection, window),
NULL);
/* Move 10 pixels right */
uint32_t values[] = {geom->x + 10};
xcb_configure_window(connection, children[i],
XCB_CONFIG_WINDOW_X, values);
}
/* Recurse down window tree */
handle(connection, children[i]);
}
}
int main() {
xcb_connection_t *connection;
const xcb_setup_t *setup;
connection = xcb_connect(NULL, NULL);
setup = xcb_get_setup(connection);
xcb_screen_iterator_t screen = xcb_setup_roots_iterator(setup);
handle(connection, screen.data->root);
return 0;
}
没有错误检查或内存管理,它可以做的非常有限。但是它应该很容易更新为执行您想要的程序,或者通过添加命令行选项将其转换为通用帮助程序以指定要在哪些窗口上操作以及在它们上执行哪些操作。
看起来您并不是专门在代码中寻找解决方案,而是在桌面环境中寻找解决方案,您需要查看在此类桌面环境中处理窗口放置的窗口管理器之一。
KDE 的KWin 的窗口属性
Compiz (GNOME) 在 CompizConfig 设置管理器应用程序中有“窗口规则”和“放置窗口”。参见例如这里
尽管 Openbox 链接到本页底部的 GUI 工具,但似乎很难做到正确。
直接使用 X 的问题在于 X 本身对您的桌面环境(面板、快捷方式等)一无所知,您必须手动进行补偿。
在谷歌搜索之后,我很惊讶 KDE 是唯一一个有简单方法来做到这一点的人。