自Gtk +版本3.22起,'gdk_screen_get_active_window()'已弃用

Col*_*nan 6 gtk3

根据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`
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Col*_*nan 3

(2017 年 3 月 11 日编辑,通过在打开时关闭显示来消除内存泄漏)

\n\n

(编辑 3/6/17 以初始化 get_top_window 中的 s)

\n\n

(编辑 12/24 以提供 X11 的完整答案,并标记为正确答案,直到有人找到通用解决方案)。这是我在 github 上重写/重构silentcast 应用程序(之前只是一系列使用 yad 作为 UI 的 bash 脚本)的一部分,尽管我还没有将任何 Gtk 代码放在 github 上。

\n\n

我下面的“正确答案”允许您实际获取活动的 GdkWindow、它的几何图形和范围,或者带有子项的活动 X11 窗口及其几何图形。

\n\n

正确答案

\n\n

(请注意,它仅适用于 X11,因此应包含并针对 gtk/gtkx.h 进行编译,而不是 gtk/gtk.h)

\n\n

.h 文件

\n\n
/*\n *  Filename: SC_X11_get_active_window.h \n *  App Name: Silentcast <https://github.com/colinkeenan/silentcast>\n *  Copyright \xc2\xa9 2016, 2017 Colin N Keenan <colinnkeenan@gmail.com>\n * \n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 3 of the License, or\n *  (at your option) any later version.\n *  \n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU Library General Public License for more details.\n *  \n *  You should have received a copy of the GNU General Public License\n *  along with this program. If not, see <http://www.gnu.org/licenses/>.\n * \n *  Description: defines some custom X11 error messags and exposes 3 functions:\n *  SC_get_active_gdkwindow (...), SC_get_geomeotry_for (...), \n *  and SC_get_active_windows_and_geometry (...)\n */\n\n#include <gtk/gtkx.h>\n\n#define SC_X11_ERROR0 "                                      \\n"\n#define SC_X11_ERROR1 "Failed to connect to X server.\\n"\n#define SC_X11_ERROR2 "x11 error trying to get focused window\\n"\n#define SC_X11_ERROR3 "X11 reports no focused window\\n"\n#define SC_X11_ERROR4 "X11 error trying to get top window\\n"\n\n#define D_ERR 1\n#define FOCUS_ERR1 2\n#define FOCUS_ERR2 3\n#define TOP_ERR 4\n#define UKN_ERR 5\n\n#define SC_X11_E1 D_ERR\n#define SC_X11_E2 FOCUS_ERR1\n#define SC_X11_E3 FOCUS_ERR2\n#define SC_X11_E4 TOP_ERR\n#define SC_X11_E0 UKN_ERR\n\nunsigned int SC_get_active_X11window (Window *w, Window* *w_children, ssize_t *n);\n\ngboolean SC_get_active_gdkwindow (Window aw, Window *aw_children, ssize_t n, GdkWindow* *gdkwindow);\n\nvoid SC_get_geometry_for (Window aw, Window *aw_children, ssize_t n, int *x, int *y, \n        unsigned int *width, unsigned int *height, GdkRectangle *extents, GdkWindow* *gdkwindow); \n\ngboolean SC_get_active_windows_and_geometry (Window *aw, Window* *aw_children, ssize_t *n,\n        int *x, int *y, unsigned int *width, unsigned int *height, GdkRectangle *extents, GdkWindow* *gdkwindow); \n
Run Code Online (Sandbox Code Playgroud)\n\n

.c 文件

\n\n
/*\n *  Filename: SC_X11_get_active_window.c \n *  App Name: Silentcast <https://github.com/colinkeenan/silentcast>\n *  Copyright \xc2\xa9 2016 Colin N Keenan <colinnkeenan@gmail.com>\n * \n *  This program is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 3 of the License, or\n *  (at your option) any later version.\n *  \n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU Library General Public License for more details.\n *  \n *  You should have received a copy of the GNU General Public License\n *  along with this program. If not, see <http://www.gnu.org/licenses/>.\n * \n *  Description: adapted from "get the active window on X window system" \n *               https://gist.github.com/kui/2622504\n *               to get Gdk geometry of the active window, both the\n *               inner window and the extents\n */\n\n\n#include "SC_X11_get_active_window.h"\n\nBool xerror = False;\n\nstatic int handle_error (Display* display, XErrorEvent* error) {\n  xerror = True;\n  return 1;\n}\n\nstatic int get_focus_window (Display* d, Window *w) {\n  int revert_to;\n\n  XGetInputFocus (d, w, &revert_to);\n  if (xerror) return FOCUS_ERR1; //X error trying to get focused window\n  else if (w == None) return FOCUS_ERR2; //no focused window\n  else return 0;\n}\n\nstatic int get_top_window (Display* d, Window start, Window *w, Window* *w_children, ssize_t *n) {\n  Window parent = start, root = None, *children = NULL;\n  *w = start; \n  unsigned int nchildren;\n  Status s = XQueryTree (d, *w, &root, &parent, &children, &nchildren), s_prev;\n\n  /* ultimately trying to get *w and *w_children */\n  while (parent != root && !xerror) {\n\n    *w = parent; //previous parent\n    s_prev = s; //previous status of XQueryTree\n    if (s_prev) {\n      *w_children = children; //previous children\n      *n = nchildren; //previous number of children\n    }\n\n    s = XQueryTree (d, *w, &root, &parent, &children, &nchildren);\n    /* When parent == root, the previous "parent" is the top window.\n     * Save the children of the top window too, but XFree all other\n     * children.\n     */\n    if (parent != root) {\n    // parent is not root, so previous parent wasn\'t top window, so don\'t need it\'s children\n      if (s_prev) XFree (*w_children); \n    } else \n      if (s) XFree (children); // don\'t keep the children of root either\n  }\n  if (xerror) return TOP_ERR;\n  else return 0;\n}\n\nunsigned int \nSC_get_active_X11window (Window *w, Window* *w_children, ssize_t *n)\n{\n  Display* d = NULL;\n  unsigned int e = 0;\n\n  XSetErrorHandler (handle_error);\n  d = XOpenDisplay (NULL); \n  if (d == NULL) { \n    return D_ERR; \n  } else {\n    /* set w to the focused window */\n    e = get_focus_window (d, w); \n    if (e) { //if error\n      XCloseDisplay (d);\n      return e;\n    }\n    /* get_top_window will set w to the top focused window (active window) */\n    e = get_top_window (d, *w, w, w_children, n); \n    if (e) { //if error\n      XCloseDisplay (d);\n      return e;\n    }\n    XCloseDisplay(d);\n  } \n\n  return 0; //no error\n}\n\n/* SC_get_active_gdkwindow (...) tries to match a GdkWindow to one of the passed X11\n * windows (supposed to be the active X11 window and it\'s n children), and returns\n * TRUE if such a match is found, FALSE if not\n */\ngboolean\nSC_get_active_gdkwindow (Window aw, Window *aw_children, ssize_t n, GdkWindow* *gdkwindow) {\n  ssize_t i = 0;\n  GdkWindow *dwindow = NULL;\n  GdkScreen *screen = NULL;\n  GList *gl_item = NULL, *gl = NULL;\n  gboolean active_window_found = FALSE;\n\n\n  screen = gdk_screen_get_default ();\n  if (screen != NULL) { \n    /* Go through all windows known to Gtk and check XID against active X11 window, aw. */\n    gl = gdk_screen_get_window_stack (screen);\n    for (gl_item = g_list_first (gl); !active_window_found && gl_item != NULL; gl_item = gl_item->next) { \n\n      dwindow = gl_item->data;\n\n      if (gdk_x11_window_get_xid (dwindow) == aw) active_window_found = TRUE;\n      else for (i = 0; i < n; i++)  //aw didn\'t match this dwindow, so check all of aw_children\n        if (gdk_x11_window_get_xid (dwindow) == aw_children[i]) active_window_found = TRUE;\n\n      if (!active_window_found) g_object_unref (dwindow);\n      else *gdkwindow = dwindow;\n    } \n    g_list_free (gl);\n  }\n  return active_window_found;\n}\n\n/* SC_get_geometry_for (...) trys to get the Gdk geometry for the GdkWindow\n * matching the passed X11 window with children, getting both the internal\n * window geometry and it\'s extents (title-bar/frame). If can\'t get Gdk info\n * will get the X11 geometry, setting both inner and extents geometry to\n * the same values. \n */\n\nvoid\nSC_get_geometry_for (Window aw, Window *aw_children, ssize_t n, GdkRectangle *win_rect, GdkRectangle *extents, GdkWindow* *dwindow) {\n  unsigned int bwidth = 0, depth = 0, width, height;\n  int x, y;\n  Window root = 0;\n\n  if (SC_get_active_gdkwindow (aw, aw_children, n, dwindow)) {\n    gdk_window_get_frame_extents (*dwindow, extents); //{top-left corner, width & height} of title-bar/borders\n    gdk_window_get_origin(*dwindow, &x, &y); //top-left corner of interior window (not title bar/borders)\n    width = gdk_window_get_width (*dwindow); //width of interior window\n    height = gdk_window_get_height (*dwindow); //height of interior window\n    win_rect->x = x;\n    win_rect->y = y;\n    win_rect->width = (int) width;\n    win_rect->height = (int) height;\n  } else {\n    fprintf (stderr, "Failed to get GdkWindow. Falling back on X11 geometry of active window, saved as both extents and interior geometry.");\n    Display* d = XOpenDisplay (NULL); \n    if (d) {\n      XGetGeometry (d, aw, &root, &x, &y, &width, &height, &bwidth, &depth);\n      XCloseDisplay (d);\n      extents->x = x;\n      extents->y = y;\n      extents->width = (int) width;\n      extents->height = (int) height;\n    }\n  }\n}\n\n/* SC_get_active_windows_and_geometry (...) calls get_active_x11window (...) to get the active X11 window\n * and it\'s children, then calls SC_get_geometry_for (...) to get geometry (hopefully Gdk) that matches\n */\ngboolean\nSC_get_active_windows_and_geometry (Window *aw, Window* *aw_children, ssize_t *n, \n    GdkRectangle *win_rect, GdkRectangle *extents, GdkWindow* *dwindow) {\n\n  switch (SC_get_active_X11window(aw, aw_children, n)) {  get aw, aw_children, and n (number of children)\ncase 0: SC_get_geometry_for (*aw, *aw_children, *n, win_rect, extents, dwindow); return TRUE; \ncase SC_X11_E1: fprintf (stderr, SC_X11_ERROR1); break;\ncase SC_X11_E2: fprintf (stderr, SC_X11_ERROR2); break;\ncase SC_X11_E3: fprintf (stderr, SC_X11_ERROR3); break;\ncase SC_X11_E4: fprintf (stderr, SC_X11_ERROR4); break;\n  }     \n  return FALSE; //failed to get active window due to X11 error\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我之前的答案通常得到正确的几何形状,但不是窗口

\n\n

我已经改编了“在 X 窗口系统上获取活动窗口” https://gist.github.com/kui/2622504中的代码,以与问题中的示例配合使用。我把它变成了图书馆。我没有将其标记为正确答案,因为这是我编写的第一个库文件,而且我对 Gtk 也是完全陌生的。我也没有太多编写 C 代码的经验。最后,正确答案应该包括 X11、Wayland 和 MIR 的库。我很高兴看到一个答案,包括我的改进库和缺少的两个库。

\n\n

编译如下:

\n\n
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\n
Run Code Online (Sandbox Code Playgroud)\n\n

X11_get_active_window_geometry.h

\n\n
#include <X11/Xlib.h>\n\n#define SC_X11_ERROR0 "Uknown error from get_actve_window_geometry.\\n"\n#define SC_X11_ERROR1 "Failed to connect to X server.\\n"\n#define SC_X11_ERROR2 "x11 error trying to get focused window\\n"\n#define SC_X11_ERROR3 "X11 reports no focused window\\n"\n#define SC_X11_ERROR4 "X11 error trying to get top window\\n"\n#define SC_X11_ERROR5 "X11 error trying to get the active-window geometry.\\n"\n\n#define D_ERR 1\n#define FOCUS_ERR1 2\n#define FOCUS_ERR2 3\n#define TOP_ERR 4\n#define GEOM_ERR 5\n\n#define SC_X11_E1 D_ERR\n#define SC_X11_E2 FOCUS_ERR1\n#define SC_X11_E3 FOCUS_ERR2\n#define SC_X11_E4 TOP_ERR\n#define SC_X11_E5 GEOM_ERR\n\nunsigned int get_active_window_geometry (int *x, int *y, unsigned int *width, unsigned int *height); \n
Run Code Online (Sandbox Code Playgroud)\n\n

X11_get_active_window_geometry.c

\n\n
#include "X11_get_active_window_geometry.h"\n\nBool xerror = False;\n\nstatic int handle_error (Display* display, XErrorEvent* error) {\n  xerror = True;\n  return 1;\n}\n\nstatic int get_focus_window (Display* d, Window *w) {\n  int revert_to;\n\n  XGetInputFocus (d, w, &revert_to);\n    if (xerror) return FOCUS_ERR1; //X error trying to get focused window\n    else if (w == None) return FOCUS_ERR2; //no focused window\n    else return 0;\n}\n\nstatic int get_top_window (Display* d, Window start, Window *w){\n    Window parent = start, root = None, *children;\n  *w = start; \n  unsigned int nchildren;\n  Status s;\n\n  while (parent != root && !xerror) {\n    *w = parent;\n    s = XQueryTree (d, *w, &root, &parent, &children, &nchildren);\n\n    if (s)\n      XFree (children);\n  }\n    if (xerror) return TOP_ERR;\n    else return 0;\n}\n\nunsigned int get_active_window_geometry (int *x, int *y, \n        unsigned int *width, unsigned int *height) \n{\n    Display* d = NULL;\n    Window root, w;\n  unsigned int bwidth = 0, depth = 0, e = 0;\n\n  XSetErrorHandler (handle_error);\n  d = XOpenDisplay (NULL);\n    if (d == NULL) { \n        return D_ERR;   \n    } else {\n        e = get_focus_window (d,&w); //get focused window w\n        if (e) return e;\n    e = get_top_window (d, w, &w); //get top focused window w (the active window)\n        if (e) return e;\n        XGetGeometry (d, w, &root, x, y, width, height, &bwidth, &depth);\n        if (xerror) return GEOM_ERR;\n    } \n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

get_active_window.c

\n\n
#include <gtk/gtk.h>\n#include "X11_get_active_window_geometry.h"\n\nstatic void\nactivate (GtkApplication* app,\n          gpointer        user_data)\n{\n  GtkWidget *window = NULL, *text_view;\n  GtkTextBuffer *buffer;\n    unsigned int width = 0, height = 0, widtha = 0, heighta = 0, iwidtha = 0, iheighta = 0;\n    int x = 0, y = 0, xa = 0, ya = 0, ixa =0, iya = 0;\n  GdkRectangle extents= { 0, 0, 0, 0 };\n    char char_x[5], char_y[5], char_width[5], char_height[5]; \n    GdkScreen *screen;\n    GdkWindow *dwindow;\n    GList *gl_item = NULL, *gl = NULL;\n\n  window = gtk_application_window_new (app);\n    screen = gtk_window_get_screen (GTK_WINDOW(window));\n  buffer = gtk_text_buffer_new (NULL);\n  text_view = gtk_text_view_new_with_buffer (buffer);\n  gtk_container_add (GTK_CONTAINER (window), text_view);\n\n#define ADD_TEXT(STRING) gtk_text_buffer_insert_at_cursor (buffer,STRING,-1)\n#define ADD_INT(CHAR_INT,INT) snprintf (CHAR_INT, 5, "%d", INT); ADD_TEXT(CHAR_INT);\n#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");\n\n    /* get active window geometry using X11 and handle error, if any*/\n    switch (get_active_window_geometry(&xa, &ya, &widtha, &heighta)) { \ncase 0:\n            ADD_TEXT("GEOMETRY FOR ACTIVE WINDOW USING X11\\n");\n            ADD_GEOMETRY_TEXT(xa, ya, widtha, heighta);\n            ADD_TEXT("\\n");\n            break;\ncase SC_X11_E1:\n            ADD_TEXT(SC_X11_ERROR1);\n            break;\ncase SC_X11_E2:\n            ADD_TEXT(SC_X11_ERROR2);\n            break;\ncase SC_X11_E3:\n            ADD_TEXT(SC_X11_ERROR3);\n            break;\ncase SC_X11_E4:\n            ADD_TEXT(SC_X11_ERROR4);\n            break;\ncase SC_X11_E5:\n            ADD_TEXT(SC_X11_ERROR5);\n            break;\ndefault:\n            ADD_TEXT(SC_X11_ERROR0);\n    }           \n\n    /* get window geometry for all windows using Gtk and identify the active one by comparison with X11 result*/\n    if (screen != NULL) { \n        ADD_TEXT("GEOMETRY FOR ALL WINDOWS USING Gtk:\\n\\n");\n        gl = gdk_screen_get_window_stack (screen);\n        for (gl_item = g_list_first (gl); gl_item != NULL; gl_item = gl_item->next) { \n            dwindow=gl_item->data;\n      gdk_window_get_frame_extents (dwindow, &extents); //{top-left corner, width & height} of title-bar/borders\n            ADD_TEXT("Entirety of Window: ");\n            ADD_GEOMETRY_TEXT(extents.x, extents.y, extents.width, extents.height);\n            gdk_window_get_origin(dwindow, &x, &y); //top-left corner of interior window (not title bar/borders)\n            width = gdk_window_get_width (dwindow); //width of interior window\n            height = gdk_window_get_height (dwindow); //height of interior window\n            ADD_TEXT("Interior of Window: ");\n            ADD_GEOMETRY_TEXT(x, y, width, height);\n            ADD_TEXT("\\n");\n            /*If extents matches active window geometry, save interior window geometry */\n            if (extents.x == xa && extents.y == ya && extents.width == widtha && extents.height == heighta) {\n                ixa = x; iya = y; iwidtha = width; iheighta = height;\n            }\n            g_object_unref (dwindow);\n        }; \n        g_list_free (gl);\n        ADD_TEXT("MATCHING THE ACTIVE WINDOW REPORTED BY X11 WITH THE GTK WINDOW GEOMETRIES:\\n");\n        ADD_TEXT("Entirety of Active Window: ");\n        ADD_GEOMETRY_TEXT(xa, ya, widtha, heighta);\n        ADD_TEXT("Interior of Active Window: ");\n        ADD_GEOMETRY_TEXT(ixa, iya, iwidtha, iheighta);\n    } else {\n        ADD_TEXT("Failed to get default screen.\\n");\n    }\n\n  gtk_widget_show_all (window);\n}\n\nint\nmain (int    argc,\n      char **argv)\n{\n  GtkApplication *app;\n  int status;\n\n  app = gtk_application_new ("com.github.colinkeenan.silentcast", G_APPLICATION_FLAGS_NONE);\n  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);\n  status = g_application_run (G_APPLICATION (app), argc, argv);\n  g_object_unref (app);\n\n  return status;\n}\n
Run Code Online (Sandbox Code Playgroud)\n