'gdk_screen_get_active_window()'自Gtk +版本3.22

时间:2016-12-07 21:44:05

标签: 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`

使用上面的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;
}

2 个答案:

答案 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的良好替代品。我现在真的不知道该告诉你什么。