根据https://developer.gnome.org/gdk3/stable/GdkScreen.html#gdk-screen-get-active-window,
gdk_screen_get_active_window自版本3.22起已被弃用,不应在新编写的代码中使用。
但是,应该使用什么呢? (这是许多已弃用的GdkScreen功能之一。)
具体来说,我如何获得活动窗口的位置和几何?
编辑12/10/16:经过几天调查后,我得出结论,这个问题的答案在developer.gnome.org之外。可能需要直接针对X11,wayland和mir编写单独的代码。
对于它的价值,下面是get_window-areas.c
,我已经写过了在不使用弃用函数的情况下探索Gtk中可以找到的内容。似乎没有办法获得窗口标题或活动状态;所以,我无法复制@ theGtknerd使用不稳定Wnck库的答案的功能。
我只是在学习Gtk,所以我非常感谢你提出的改进意见。我从空窗口代码https://developer.gnome.org/gtk3/stable/gtk-getting-started.html#id-1.2.3.5开始,向其中添加了带缓冲区的textview,然后将有关每个窗口的几何和位置的信息插入到文本缓冲区中。
gcc `pkg-config --cflags gtk+-3.0` -o get_window-areas get_window-areas.c `pkg-config --libs gtk+-3.0`
使用上面的get_window-areas.c
命令在下面编译gcc
。
#include <gtk/gtk.h>
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window = NULL;
GtkWidget *text_view;
GtkTextBuffer *buffer;
int x = 0, y = 0, width = 0, height = 0;
char char_x[5], char_y[5], char_width[5], char_height[5];
GdkScreen *screen;
GdkWindow *dwindow;
GList *gl_item = NULL, *gl = NULL;
window = gtk_application_window_new (app);
screen = gtk_window_get_screen (GTK_WINDOW(window));
buffer = gtk_text_buffer_new (NULL);
text_view = gtk_text_view_new_with_buffer (buffer);
gtk_container_add (GTK_CONTAINER (window), text_view);
if(screen != NULL)
{
gl = gdk_screen_get_window_stack(screen);
for (gl_item = g_list_first(gl); gl_item != NULL; gl_item = gl_item->next)
{
dwindow=gl_item->data;
gdk_window_get_root_origin(dwindow, &x, &y);
width = gdk_window_get_width(dwindow);
height = gdk_window_get_height(dwindow);
g_object_unref(dwindow);
snprintf (char_x, 5, "%d", x);
snprintf (char_y, 5, "%d", y);
snprintf (char_width, 5, "%d", width);
snprintf (char_height, 5, "%d", height);
gtk_text_buffer_insert_at_cursor(buffer,char_width,-1);
gtk_text_buffer_insert_at_cursor(buffer,"x", -1);
gtk_text_buffer_insert_at_cursor(buffer,char_height,-1);
gtk_text_buffer_insert_at_cursor(buffer," at (", -1);
gtk_text_buffer_insert_at_cursor(buffer,char_x, -1);
gtk_text_buffer_insert_at_cursor(buffer,",", -1);
gtk_text_buffer_insert_at_cursor(buffer,char_y,-1);
gtk_text_buffer_insert_at_cursor(buffer,")\n", -1);
};
g_list_free (gl);
}
else {gtk_text_buffer_insert_at_cursor(buffer, "Failed to get default screen.\n", -1);}
gtk_widget_show_all (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("com.github.colinkeenan.silentcast", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
答案 0 :(得分:3)
(编辑3/11/17以通过在打开时关闭显示来消除内存泄漏)
(编辑3/6/17以在get_top_window中初始化s)
(编辑12/24以提供X11的完整答案,并标记为正确的答案,直到某人有一般解决方案)。这是我在github上重写/重构我的silentcast应用程序(以前只是使用yad for UI的一系列bash脚本)的一部分,尽管我还没有在github上放置任何这个Gtk代码。
下面的“正确答案”允许您实际获得活动的GdkWindow,它的几何形状和范围,或带有子项的活动X11窗口,以及它的几何。
正确答案
(请注意,它仅适用于X11,因此应包含并编译gtk / gtkx.h,而不是gtk / gtk.h)
.h文件
/*
* Filename: SC_X11_get_active_window.h
* App Name: Silentcast <https://github.com/colinkeenan/silentcast>
* Copyright © 2016, 2017 Colin N Keenan <colinnkeenan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Description: defines some custom X11 error messags and exposes 3 functions:
* SC_get_active_gdkwindow (...), SC_get_geomeotry_for (...),
* and SC_get_active_windows_and_geometry (...)
*/
#include <gtk/gtkx.h>
#define SC_X11_ERROR0 " \n"
#define SC_X11_ERROR1 "Failed to connect to X server.\n"
#define SC_X11_ERROR2 "x11 error trying to get focused window\n"
#define SC_X11_ERROR3 "X11 reports no focused window\n"
#define SC_X11_ERROR4 "X11 error trying to get top window\n"
#define D_ERR 1
#define FOCUS_ERR1 2
#define FOCUS_ERR2 3
#define TOP_ERR 4
#define UKN_ERR 5
#define SC_X11_E1 D_ERR
#define SC_X11_E2 FOCUS_ERR1
#define SC_X11_E3 FOCUS_ERR2
#define SC_X11_E4 TOP_ERR
#define SC_X11_E0 UKN_ERR
unsigned int SC_get_active_X11window (Window *w, Window* *w_children, ssize_t *n);
gboolean SC_get_active_gdkwindow (Window aw, Window *aw_children, ssize_t n, GdkWindow* *gdkwindow);
void SC_get_geometry_for (Window aw, Window *aw_children, ssize_t n, int *x, int *y,
unsigned int *width, unsigned int *height, GdkRectangle *extents, GdkWindow* *gdkwindow);
gboolean SC_get_active_windows_and_geometry (Window *aw, Window* *aw_children, ssize_t *n,
int *x, int *y, unsigned int *width, unsigned int *height, GdkRectangle *extents, GdkWindow* *gdkwindow);
.c文件
/*
* Filename: SC_X11_get_active_window.c
* App Name: Silentcast <https://github.com/colinkeenan/silentcast>
* Copyright © 2016 Colin N Keenan <colinnkeenan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Description: adapted from "get the active window on X window system"
* https://gist.github.com/kui/2622504
* to get Gdk geometry of the active window, both the
* inner window and the extents
*/
#include "SC_X11_get_active_window.h"
Bool xerror = False;
static int handle_error (Display* display, XErrorEvent* error) {
xerror = True;
return 1;
}
static int get_focus_window (Display* d, Window *w) {
int revert_to;
XGetInputFocus (d, w, &revert_to);
if (xerror) return FOCUS_ERR1; //X error trying to get focused window
else if (w == None) return FOCUS_ERR2; //no focused window
else return 0;
}
static int get_top_window (Display* d, Window start, Window *w, Window* *w_children, ssize_t *n) {
Window parent = start, root = None, *children = NULL;
*w = start;
unsigned int nchildren;
Status s = XQueryTree (d, *w, &root, &parent, &children, &nchildren), s_prev;
/* ultimately trying to get *w and *w_children */
while (parent != root && !xerror) {
*w = parent; //previous parent
s_prev = s; //previous status of XQueryTree
if (s_prev) {
*w_children = children; //previous children
*n = nchildren; //previous number of children
}
s = XQueryTree (d, *w, &root, &parent, &children, &nchildren);
/* When parent == root, the previous "parent" is the top window.
* Save the children of the top window too, but XFree all other
* children.
*/
if (parent != root) {
// parent is not root, so previous parent wasn't top window, so don't need it's children
if (s_prev) XFree (*w_children);
} else
if (s) XFree (children); // don't keep the children of root either
}
if (xerror) return TOP_ERR;
else return 0;
}
unsigned int
SC_get_active_X11window (Window *w, Window* *w_children, ssize_t *n)
{
Display* d = NULL;
unsigned int e = 0;
XSetErrorHandler (handle_error);
d = XOpenDisplay (NULL);
if (d == NULL) {
return D_ERR;
} else {
/* set w to the focused window */
e = get_focus_window (d, w);
if (e) { //if error
XCloseDisplay (d);
return e;
}
/* get_top_window will set w to the top focused window (active window) */
e = get_top_window (d, *w, w, w_children, n);
if (e) { //if error
XCloseDisplay (d);
return e;
}
XCloseDisplay(d);
}
return 0; //no error
}
/* SC_get_active_gdkwindow (...) tries to match a GdkWindow to one of the passed X11
* windows (supposed to be the active X11 window and it's n children), and returns
* TRUE if such a match is found, FALSE if not
*/
gboolean
SC_get_active_gdkwindow (Window aw, Window *aw_children, ssize_t n, GdkWindow* *gdkwindow) {
ssize_t i = 0;
GdkWindow *dwindow = NULL;
GdkScreen *screen = NULL;
GList *gl_item = NULL, *gl = NULL;
gboolean active_window_found = FALSE;
screen = gdk_screen_get_default ();
if (screen != NULL) {
/* Go through all windows known to Gtk and check XID against active X11 window, aw. */
gl = gdk_screen_get_window_stack (screen);
for (gl_item = g_list_first (gl); !active_window_found && gl_item != NULL; gl_item = gl_item->next) {
dwindow = gl_item->data;
if (gdk_x11_window_get_xid (dwindow) == aw) active_window_found = TRUE;
else for (i = 0; i < n; i++) //aw didn't match this dwindow, so check all of aw_children
if (gdk_x11_window_get_xid (dwindow) == aw_children[i]) active_window_found = TRUE;
if (!active_window_found) g_object_unref (dwindow);
else *gdkwindow = dwindow;
}
g_list_free (gl);
}
return active_window_found;
}
/* SC_get_geometry_for (...) trys to get the Gdk geometry for the GdkWindow
* matching the passed X11 window with children, getting both the internal
* window geometry and it's extents (title-bar/frame). If can't get Gdk info
* will get the X11 geometry, setting both inner and extents geometry to
* the same values.
*/
void
SC_get_geometry_for (Window aw, Window *aw_children, ssize_t n, GdkRectangle *win_rect, GdkRectangle *extents, GdkWindow* *dwindow) {
unsigned int bwidth = 0, depth = 0, width, height;
int x, y;
Window root = 0;
if (SC_get_active_gdkwindow (aw, aw_children, n, dwindow)) {
gdk_window_get_frame_extents (*dwindow, extents); //{top-left corner, width & height} of title-bar/borders
gdk_window_get_origin(*dwindow, &x, &y); //top-left corner of interior window (not title bar/borders)
width = gdk_window_get_width (*dwindow); //width of interior window
height = gdk_window_get_height (*dwindow); //height of interior window
win_rect->x = x;
win_rect->y = y;
win_rect->width = (int) width;
win_rect->height = (int) height;
} else {
fprintf (stderr, "Failed to get GdkWindow. Falling back on X11 geometry of active window, saved as both extents and interior geometry.");
Display* d = XOpenDisplay (NULL);
if (d) {
XGetGeometry (d, aw, &root, &x, &y, &width, &height, &bwidth, &depth);
XCloseDisplay (d);
extents->x = x;
extents->y = y;
extents->width = (int) width;
extents->height = (int) height;
}
}
}
/* SC_get_active_windows_and_geometry (...) calls get_active_x11window (...) to get the active X11 window
* and it's children, then calls SC_get_geometry_for (...) to get geometry (hopefully Gdk) that matches
*/
gboolean
SC_get_active_windows_and_geometry (Window *aw, Window* *aw_children, ssize_t *n,
GdkRectangle *win_rect, GdkRectangle *extents, GdkWindow* *dwindow) {
switch (SC_get_active_X11window(aw, aw_children, n)) { get aw, aw_children, and n (number of children)
case 0: SC_get_geometry_for (*aw, *aw_children, *n, win_rect, extents, dwindow); return TRUE;
case SC_X11_E1: fprintf (stderr, SC_X11_ERROR1); break;
case SC_X11_E2: fprintf (stderr, SC_X11_ERROR2); break;
case SC_X11_E3: fprintf (stderr, SC_X11_ERROR3); break;
case SC_X11_E4: fprintf (stderr, SC_X11_ERROR4); break;
}
return FALSE; //failed to get active window due to X11 error
}
我以前的答案,通常得到正确的几何,但不是窗口
我修改了“在X窗口系统上获取活动窗口”https://gist.github.com/kui/2622504的代码,以便在问题中使用我的示例。我把它变成了一个图书馆。我没有将此标记为正确的答案,因为这是我写过的第一个库文件,我也是Gtk的新手。我也没有太多编写C代码的经验。最后,正确答案应包括X11,Wayland和MIR的库。我很高兴看到一个答案,包括我的图书馆有改进+缺少两个图书馆。
编译如下:
gcc `pkg-config --cflags gtk+-3.0` -o get_window-areas X11_get_active_window_geometry.c get_window-areas.c `pkg-config --libs gtk+-3.0` -lX11
X11_get_active_window_geometry.h
#include <X11/Xlib.h>
#define SC_X11_ERROR0 "Uknown error from get_actve_window_geometry.\n"
#define SC_X11_ERROR1 "Failed to connect to X server.\n"
#define SC_X11_ERROR2 "x11 error trying to get focused window\n"
#define SC_X11_ERROR3 "X11 reports no focused window\n"
#define SC_X11_ERROR4 "X11 error trying to get top window\n"
#define SC_X11_ERROR5 "X11 error trying to get the active-window geometry.\n"
#define D_ERR 1
#define FOCUS_ERR1 2
#define FOCUS_ERR2 3
#define TOP_ERR 4
#define GEOM_ERR 5
#define SC_X11_E1 D_ERR
#define SC_X11_E2 FOCUS_ERR1
#define SC_X11_E3 FOCUS_ERR2
#define SC_X11_E4 TOP_ERR
#define SC_X11_E5 GEOM_ERR
unsigned int get_active_window_geometry (int *x, int *y, unsigned int *width, unsigned int *height);
X11_get_active_window_geometry.c
#include "X11_get_active_window_geometry.h"
Bool xerror = False;
static int handle_error (Display* display, XErrorEvent* error) {
xerror = True;
return 1;
}
static int get_focus_window (Display* d, Window *w) {
int revert_to;
XGetInputFocus (d, w, &revert_to);
if (xerror) return FOCUS_ERR1; //X error trying to get focused window
else if (w == None) return FOCUS_ERR2; //no focused window
else return 0;
}
static int get_top_window (Display* d, Window start, Window *w){
Window parent = start, root = None, *children;
*w = start;
unsigned int nchildren;
Status s;
while (parent != root && !xerror) {
*w = parent;
s = XQueryTree (d, *w, &root, &parent, &children, &nchildren);
if (s)
XFree (children);
}
if (xerror) return TOP_ERR;
else return 0;
}
unsigned int get_active_window_geometry (int *x, int *y,
unsigned int *width, unsigned int *height)
{
Display* d = NULL;
Window root, w;
unsigned int bwidth = 0, depth = 0, e = 0;
XSetErrorHandler (handle_error);
d = XOpenDisplay (NULL);
if (d == NULL) {
return D_ERR;
} else {
e = get_focus_window (d,&w); //get focused window w
if (e) return e;
e = get_top_window (d, w, &w); //get top focused window w (the active window)
if (e) return e;
XGetGeometry (d, w, &root, x, y, width, height, &bwidth, &depth);
if (xerror) return GEOM_ERR;
}
return 0;
}
get_active_window.c
#include <gtk/gtk.h>
#include "X11_get_active_window_geometry.h"
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window = NULL, *text_view;
GtkTextBuffer *buffer;
unsigned int width = 0, height = 0, widtha = 0, heighta = 0, iwidtha = 0, iheighta = 0;
int x = 0, y = 0, xa = 0, ya = 0, ixa =0, iya = 0;
GdkRectangle extents= { 0, 0, 0, 0 };
char char_x[5], char_y[5], char_width[5], char_height[5];
GdkScreen *screen;
GdkWindow *dwindow;
GList *gl_item = NULL, *gl = NULL;
window = gtk_application_window_new (app);
screen = gtk_window_get_screen (GTK_WINDOW(window));
buffer = gtk_text_buffer_new (NULL);
text_view = gtk_text_view_new_with_buffer (buffer);
gtk_container_add (GTK_CONTAINER (window), text_view);
#define ADD_TEXT(STRING) gtk_text_buffer_insert_at_cursor (buffer,STRING,-1)
#define ADD_INT(CHAR_INT,INT) snprintf (CHAR_INT, 5, "%d", INT); ADD_TEXT(CHAR_INT);
#define ADD_GEOMETRY_TEXT(X,Y,WIDTH,HEIGHT) ADD_INT(char_width, WIDTH); ADD_TEXT("x"); ADD_INT(char_height, HEIGHT); ADD_TEXT(" at ("); ADD_INT(char_x, X); ADD_TEXT(","); ADD_INT(char_y, Y); ADD_TEXT(")\n");
/* get active window geometry using X11 and handle error, if any*/
switch (get_active_window_geometry(&xa, &ya, &widtha, &heighta)) {
case 0:
ADD_TEXT("GEOMETRY FOR ACTIVE WINDOW USING X11\n");
ADD_GEOMETRY_TEXT(xa, ya, widtha, heighta);
ADD_TEXT("\n");
break;
case SC_X11_E1:
ADD_TEXT(SC_X11_ERROR1);
break;
case SC_X11_E2:
ADD_TEXT(SC_X11_ERROR2);
break;
case SC_X11_E3:
ADD_TEXT(SC_X11_ERROR3);
break;
case SC_X11_E4:
ADD_TEXT(SC_X11_ERROR4);
break;
case SC_X11_E5:
ADD_TEXT(SC_X11_ERROR5);
break;
default:
ADD_TEXT(SC_X11_ERROR0);
}
/* get window geometry for all windows using Gtk and identify the active one by comparison with X11 result*/
if (screen != NULL) {
ADD_TEXT("GEOMETRY FOR ALL WINDOWS USING Gtk:\n\n");
gl = gdk_screen_get_window_stack (screen);
for (gl_item = g_list_first (gl); gl_item != NULL; gl_item = gl_item->next) {
dwindow=gl_item->data;
gdk_window_get_frame_extents (dwindow, &extents); //{top-left corner, width & height} of title-bar/borders
ADD_TEXT("Entirety of Window: ");
ADD_GEOMETRY_TEXT(extents.x, extents.y, extents.width, extents.height);
gdk_window_get_origin(dwindow, &x, &y); //top-left corner of interior window (not title bar/borders)
width = gdk_window_get_width (dwindow); //width of interior window
height = gdk_window_get_height (dwindow); //height of interior window
ADD_TEXT("Interior of Window: ");
ADD_GEOMETRY_TEXT(x, y, width, height);
ADD_TEXT("\n");
/*If extents matches active window geometry, save interior window geometry */
if (extents.x == xa && extents.y == ya && extents.width == widtha && extents.height == heighta) {
ixa = x; iya = y; iwidtha = width; iheighta = height;
}
g_object_unref (dwindow);
};
g_list_free (gl);
ADD_TEXT("MATCHING THE ACTIVE WINDOW REPORTED BY X11 WITH THE GTK WINDOW GEOMETRIES:\n");
ADD_TEXT("Entirety of Active Window: ");
ADD_GEOMETRY_TEXT(xa, ya, widtha, heighta);
ADD_TEXT("Interior of Active Window: ");
ADD_GEOMETRY_TEXT(ixa, iya, iwidtha, iheighta);
} else {
ADD_TEXT("Failed to get default screen.\n");
}
gtk_widget_show_all (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("com.github.colinkeenan.silentcast", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
答案 1 :(得分:2)
这是获取活动窗口并打印其几何体的Python代码。
#!/usr/bin/python
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck
screen = Wnck.Screen.get_default()
screen.force_update() # recommended per Wnck documentation
# loop all windows
for window in screen.get_windows():
if window.is_active() == True:
print (window.get_geometry())
window_name = window.get_name()
print (window_name)
# clean up Wnck (saves resources, check documentation)
window = None
screen = None
Wnck.shutdown()
文档为https://developer.gnome.org/libwnck/stable/WnckWindow.html。
编辑:我正在尝试使用以下代码编译C:
gcc `pkg-config --cflags --libs libwnck-3.0` -o wnck wnck.c
我收到错误:
/usr/include/libwnck-3.0/libwnck/window.h:30:2: error: #error "libwnck should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform"
有一种解决方法,但我不确定Wnck是GdkScreen的良好替代品。我现在真的不知道该告诉你什么。