1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * xstuff.c: assorted X11-specific routines for pho.
4  *
5  * Most of this comes from
6  * http://www.opensource.apple.com/source/gcc/gcc-5483/libjava/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkWindowPeer.c
7  *
8  * To build as a standalone demo:
9 gcc `pkg-config --cflags --libs gtk+-2.0` -o winman -DSTANDALONE=1 winman.c
10  */
11 
12 #include <gtk/gtk.h>
13 #include <gdk/gdkx.h>
14 
15 static Atom extents_atom = 0;
16 
17 static Bool
property_notify_predicate(Display * xdisplay,XEvent * event,XPointer window_id)18 property_notify_predicate (Display *xdisplay __attribute__((unused)),
19                            XEvent  *event,
20                            XPointer window_id)
21 {
22   unsigned long *window = (unsigned long *) window_id;
23 
24   if (event->xany.type == PropertyNotify
25       && event->xany.window == *window
26       && event->xproperty.atom == extents_atom)
27     return True;
28   else
29     return False;
30 }
31 
32 /* Requests that the window manager set window's
33    _NET_FRAME_EXTENTS property. */
34 void
request_frame_extents(GtkWidget * window)35 request_frame_extents (GtkWidget *window)
36 {
37   const char *request_str = "_NET_REQUEST_FRAME_EXTENTS";
38   GdkAtom request_extents = gdk_atom_intern (request_str, FALSE);
39 
40   /* Check if the current window manager supports
41      _NET_REQUEST_FRAME_EXTENTS. */
42   if (gdk_net_wm_supports (request_extents))
43     {
44       GdkDisplay *display = gtk_widget_get_display (window);
45       Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
46 
47       GdkWindow *root_window = gdk_get_default_root_window ();
48       Window xroot_window = GDK_WINDOW_XID (root_window);
49 
50       Atom extents_request_atom =
51           gdk_x11_get_xatom_by_name_for_display (display, request_str);
52 
53       XEvent xevent;
54       XEvent notify_xevent;
55 
56       /* This doesn't work if window==GWin */
57       unsigned long window_id = GDK_WINDOW_XID (GDK_DRAWABLE(window->window));
58 
59       if (!extents_atom)
60       {
61           const char *extents_str = "_NET_FRAME_EXTENTS";
62           extents_atom =
63               gdk_x11_get_xatom_by_name_for_display (display, extents_str);
64       }
65 
66       xevent.xclient.type = ClientMessage;
67       xevent.xclient.message_type = extents_request_atom;
68       xevent.xclient.display = xdisplay;
69       xevent.xclient.window = window_id;
70       xevent.xclient.format = 32;
71       xevent.xclient.data.l[0] = 0;
72       xevent.xclient.data.l[1] = 0;
73       xevent.xclient.data.l[2] = 0;
74       xevent.xclient.data.l[3] = 0;
75       xevent.xclient.data.l[4] = 0;
76 
77       XSendEvent (xdisplay, xroot_window, False,
78 		  (SubstructureRedirectMask | SubstructureNotifyMask),
79                   &xevent);
80 
81       XIfEvent(xdisplay, &notify_xevent,
82 	       property_notify_predicate, (XPointer) &window_id);
83     }
84 }
85 
86 #ifdef STANDALONE
87 void
88 window_get_frame_extents (GtkWidget *window, int *width, int *height);
89 
90 int gDebug = 1;
91 
HandleExpose(GtkWidget * widget,GdkEventExpose * event)92 static gint HandleExpose(GtkWidget* widget, GdkEventExpose* event)
93 {
94     gint width, height;
95     gdk_drawable_get_size(widget->window, &width, &height);
96 
97     printf("HandleExpose: area %dx%d +%d+%d in window %dx%d\n",
98            event->area.width, event->area.height,
99            event->area.x, event->area.y,
100            width, height);
101     if (event->area.x != 0 || event->area.y != 0) {
102         if (event->area.width != width || event->area.height != height)
103             printf("*** Expose different from window size!\n");
104     }
105 }
106 
main(int argc,char ** argv)107 int main(int argc, char** argv)
108 {
109     int gMonitorWidth, gMonitorHeight;
110 
111     gint sFrameWidth = -1;
112     gint sFrameHeight = -1;
113     static GdkColor sBlack;
114 
115     GtkWidget *gWin = 0;
116     static GtkWidget *sDrawingArea = 0;
117 
118     gtk_init(&argc, &argv);
119     gMonitorWidth = gdk_screen_width();
120     gMonitorHeight = gdk_screen_height();
121 
122     gWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
123 
124 //    gtk_signal_connect(GTK_OBJECT(gWin), "key_press_event",
125 //                       (GtkSignalFunc)HandleGlobalKeys, 0);
126     sDrawingArea = gtk_drawing_area_new();
127     gtk_container_add(GTK_CONTAINER(gWin), sDrawingArea);
128     gtk_widget_show(sDrawingArea);
129 
130     gtk_drawing_area_size(GTK_DRAWING_AREA(sDrawingArea),
131                           gMonitorWidth, gMonitorHeight);
132     gtk_window_unfullscreen(GTK_WINDOW(gWin));
133 
134     gtk_signal_connect(GTK_OBJECT(sDrawingArea), "expose_event",
135                        (GtkSignalFunc)HandleExpose, 0);
136     gtk_widget_show(gWin);
137 
138     int width, height;
139     window_get_frame_extents (gWin, &sFrameWidth, &sFrameHeight);
140     printf("Frame size: %d, %d\n", sFrameWidth, sFrameHeight);
141 
142     gtk_window_resize(GTK_WINDOW(gWin),
143                       gMonitorWidth-sFrameWidth, gMonitorHeight-sFrameHeight);
144     printf("Resizing after frame extents to %dx%d\n",
145            gMonitorWidth-sFrameWidth, gMonitorHeight-sFrameHeight);
146 }
147 #endif
148 
149 /* Try to find the size of the window decorations,
150  * using _NET_REQUEST_FRAME_EXTENTS
151  * gdk_window_get_frame_extents doesn't work until after the
152  * window is realized, by which time it's too late.
153  * This method works once the window is mapped but not realized.
154  */
155 void
window_get_frame_extents(GtkWidget * window,int * width,int * height)156 window_get_frame_extents (GtkWidget *window, int *width, int *height)
157 {
158   unsigned long *extents = NULL;
159   extern int gDebug;
160 
161   /* I'm not sure whether is_visible or is_viewable is better here.
162    * is_viewable requires that the window and its ancestors be mapped;
163    * is_visible, just the window. Unfortunately, there's no documentation
164    * on what's needed to make gdk_window_get_frame_extents() work.
165    */
166   if (gdk_window_is_viewable(window->window)) {
167       GdkRectangle rect;
168       gint winwidth, winheight;
169 
170       gdk_drawable_get_size(window->window, &winwidth, &winheight);
171       gdk_window_get_frame_extents(window->window, &rect);
172       *width = rect.width - winwidth;
173       *height = rect.height - winheight;
174 
175       if (gDebug)
176           printf("get_frame_extents from visible window: %dx%d\n",
177                  *width, *height);
178       return;
179   }
180 
181   /* Otherwise, the window isn't visible, so maybe we'll be able to
182    * get the X property (which just hangs if the window already exists).
183    */
184   if (gDebug)
185       printf("Window isn't visible; requestiong NET_FRAME_EXTENTS\n");
186 
187   /* Guess frame extents in case _NET_FRAME_EXTENTS is not
188      supported. */
189   *height = 29;
190   *width = 12;
191 
192   /* Request that the window manager set window's
193      _NET_FRAME_EXTENTS property.
194   */
195   request_frame_extents (window);
196 
197   /* Attempt to retrieve window's frame extents. */
198   if (gdk_property_get (window->window,
199                         gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
200                         gdk_atom_intern ("CARDINAL", FALSE),
201                         0,
202                         sizeof (unsigned long) * 4,
203                         FALSE,
204                         NULL,
205                         NULL,
206                         NULL,
207                         (guchar**)&extents))
208     {
209       *width = extents[0] + extents[1];
210       *height = extents[2] + extents[3];
211     }
212 }
213