(edit 3/11/17 to eliminate memory leaks when closing the display each time you open)
(edit 3/6/17 to initialize s in get_top_window)
(edit 12/24 to get the full answer for X11 and mark it as the correct answer until someone has a common solution). This is part of my work of rewriting / refactoring my silentcast application (previously just a series of bash scripts using yad for the UI) on github, although I haven't put any of this Gtk code on github yet.
My “Correct Answer” below allows you to actually get the active GdkWindow, its geometry and extents, or the active X11 window with children and geometry.
Correct answer
(note that it only applies to X11, so it should include and compile in gtk / gtkx.h, not gtk / gtk.h)
.h file
#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 file
#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 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 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 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 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 }
My previous answer, which usually got the correct geometry, but not a window
I adapted the code from "get active window in windows system X" https://gist.github.com/kui/2622504 to work with my example in the question. I turned it into a library. I don’t think this is the right answer, because this is the first library file I have ever written, and I am completely new to Gtk. I also don't have much experience writing C code. Finally, the correct answer should include libraries for X11, Wayland and MIR. I would be glad to see the answer, including my library with improvements + two missing libraries.
Compile below:
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; }