1 /**
2 * Copyright (C) 2004 Billy Biggs <vektor@dumbterm.net>.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdk.h>
29 #include <gdk/gdkx.h>
30 #include <X11/Xlib.h>
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 #ifdef ENABLE_NLS
35 # define _(string) gettext (string)
36 # include "gettext.h"
37 #else
38 # define _(string) string
39 #endif
40 #include "ewmhview.h"
41 #include "xresview.h"
42 #include "xwinview.h"
43
44 static Atom net_wm_state;
45 static Atom net_wm_state_above;
46 static int atoms_loaded = 0;
47
48 /**
49 * Loads any atoms we will be using. Kinda pointless since we just use
50 * these once.
51 */
load_atoms(Display * dpy)52 static void load_atoms( Display *dpy )
53 {
54 static char *atom_names[] = {
55 "_NET_WM_STATE",
56 "_NET_WM_STATE_ABOVE"
57 };
58 Atom atoms_return[ 2 ];
59
60 if( atoms_loaded ) return;
61 XInternAtoms( dpy, atom_names, 2, False, atoms_return );
62 net_wm_state = atoms_return[ 0 ];
63 net_wm_state_above = atoms_return[ 1 ];
64 atoms_loaded = 1;
65 }
66
67 /**
68 * This hacky code sets us to be always-on-top for supporting window
69 * managers. There is a call for this in Gtk+ 2.4, but I would like to
70 * be backwards compatible. We set this even if the WM does not support
71 * it since we don't mind if the feature is not available.
72 */
set_state_above(GdkWindow * gdkwin)73 static void set_state_above( GdkWindow *gdkwin )
74 {
75 Display *dpy = gdk_x11_get_default_xdisplay();
76 Window wm_window = gdk_x11_drawable_get_xid( gdkwin );
77 XEvent ev;
78
79 load_atoms( dpy );
80
81 ev.type = ClientMessage;
82 ev.xclient.window = wm_window;
83 ev.xclient.message_type = net_wm_state;
84 ev.xclient.format = 32;
85 ev.xclient.data.l[ 0 ] = 1;
86 ev.xclient.data.l[ 1 ] = net_wm_state_above;
87 ev.xclient.data.l[ 2 ] = 0;
88
89 XSendEvent( dpy, DefaultRootWindow( dpy ), False,
90 SubstructureNotifyMask|SubstructureRedirectMask, &ev );
91 }
92
93
94 static ewmhview_t *ev;
95 static xwinview_t *wv;
96 static xresview_t *rv;
97 static int windowlist[ 4096 ];
98 static int numwindows = 0;
99 static int lasttop = -1;
100 static int paused = 0;
101
check_events(gpointer data)102 static gboolean check_events( gpointer data )
103 {
104 Window real_root, win;
105 Display *dpy;
106 int i;
107
108 /* Don't update anything if we are paused. */
109 if( paused ) {
110 return TRUE;
111 }
112
113 /* Query the list of windows underneath the pointer. */
114 dpy = gdk_x11_get_default_xdisplay();
115 real_root = gdk_x11_get_default_root_xwindow();
116 numwindows = 0;
117 win = real_root;
118 while( win ) {
119 Window root, child;
120 int x, y, rx, ry;
121 unsigned int mask;
122
123 windowlist[ numwindows++ ] = win;
124 XQueryPointer( dpy, win, &root, &child, &rx, &ry, &x, &y, &mask );
125
126 win = child;
127 }
128
129 /* Don't update the tree if we have seen this list before. */
130 if( windowlist[ numwindows - 1 ] == lasttop ) {
131 return TRUE;
132 }
133 lasttop = windowlist[ numwindows - 1 ];
134
135 /* We're about to start asking for windows that may not exist.
136 * Let's make sure we don't give ourselves a BadWindow error.
137 */
138 gdk_error_trap_push ();
139
140 /* Generate information for the new list. */
141 ewmhview_clear( ev, dpy, real_root );
142 xwinview_clear( wv, dpy, real_root );
143 xresview_clear( rv, dpy, real_root );
144 for( i = 0; i < numwindows; i++ ) {
145 xwinview_load( wv, dpy, windowlist[ i ], real_root );
146 ewmhview_load( ev, dpy, windowlist[ i ], real_root );
147 xresview_load( rv, dpy, windowlist[ i ], real_root );
148 }
149
150 /* We're done with the possible causes of BadWindow errors.
151 * We need to flush the event queue to make sure the errors
152 * arrive (i.e., they are asynchronous). We could do
153 * something to handle the errors, but it's probably okay to
154 * just wait for the next time this function is called.
155 */
156 gdk_flush ();
157 gdk_error_trap_pop ();
158
159 return TRUE;
160 }
161
162 /**
163 * This can obviously be improved.
164 */
about_dialog(GtkWindow * parent)165 static void about_dialog( GtkWindow *parent )
166 {
167 GtkWidget *dialog;
168
169 dialog = gtk_message_dialog_new( GTK_WINDOW( parent ),
170 GTK_DIALOG_DESTROY_WITH_PARENT,
171 GTK_MESSAGE_INFO,
172 GTK_BUTTONS_CLOSE,
173 PACKAGE_STRING "\n\n" PACKAGE_BUGREPORT );
174
175 g_signal_connect( dialog, "response",
176 G_CALLBACK( gtk_widget_destroy ), 0 );
177 gtk_widget_show( dialog );
178 }
179
180 /**
181 * Cheap callback for menu items.
182 */
menuitem_cb(gpointer callback_data,guint callback_action,GtkWidget * widget)183 static void menuitem_cb( gpointer callback_data, guint callback_action,
184 GtkWidget *widget )
185 {
186
187 if( !strcmp( gtk_item_factory_path_from_widget( widget ),
188 "<main>/Help/About" ) ) {
189 about_dialog( (GtkWindow *) callback_data );
190 } else if( !strcmp( gtk_item_factory_path_from_widget( widget ),
191 "<main>/Commands/Pause" ) ) {
192 paused = !paused;
193 } else if( !strcmp( gtk_item_factory_path_from_widget( widget ),
194 "<main>/File/Quit" ) ) {
195 gtk_main_quit();
196 }
197 }
198
199 static GtkItemFactoryEntry menu_items[] =
200 {
201 { "/_File", NULL, 0, 0, "<Branch>" },
202 { "/File/_Quit", "<control>Q", menuitem_cb, 0, "<StockItem>", GTK_STOCK_QUIT },
203
204 { "/_Commands", NULL, 0, 0, "<Branch>" },
205 { "/Commands/_Pause", "<control>P", menuitem_cb, 0 },
206
207 { "/_Help", NULL, 0, 0, "<Branch>" },
208 { "/Help/_About", NULL, menuitem_cb, 0 },
209 };
210
211 /**
212 * Window destroy.
213 */
on_destroy(GtkWidget * widget,gpointer data)214 static void on_destroy( GtkWidget *widget, gpointer data )
215 {
216 gtk_main_quit();
217 }
218
main(int argc,char ** argv)219 int main( int argc, char **argv )
220 {
221 GtkWidget *window;
222 GtkWidget *vbox;
223 GtkWidget *notebook;
224 GtkAccelGroup *accel_group;
225 GtkItemFactory *item_factory;
226
227 gtk_init( &argc, &argv );
228
229 window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
230 gtk_window_set_title( GTK_WINDOW( window ), _("X Window Information") );
231
232 vbox = gtk_vbox_new( FALSE, 0 );
233 gtk_container_add( GTK_CONTAINER( window ), vbox );
234
235 g_signal_connect( G_OBJECT( window ), "destroy",
236 G_CALLBACK( on_destroy ), 0 );
237
238 accel_group = gtk_accel_group_new();
239 gtk_window_add_accel_group( GTK_WINDOW( window ), accel_group );
240 g_object_unref( accel_group );
241
242 item_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>",
243 accel_group );
244
245 g_object_ref( item_factory );
246 gtk_object_sink( GTK_OBJECT( item_factory ) );
247 g_object_set_data_full( G_OBJECT( window ), "<main>",
248 item_factory, (GDestroyNotify) g_object_unref );
249
250 gtk_item_factory_create_items( item_factory, G_N_ELEMENTS( menu_items ),
251 menu_items, window );
252 gtk_box_pack_start( GTK_BOX( vbox ),
253 gtk_item_factory_get_widget( item_factory, "<main>" ),
254 FALSE, FALSE, 0 );
255
256 notebook = gtk_notebook_new();
257 gtk_box_pack_start( GTK_BOX( vbox ), notebook,
258 TRUE, TRUE, 0 );
259
260 wv = xwinview_new();
261 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ),
262 xwinview_get_widget( wv ),
263 gtk_label_new( _("Window List") ) );
264
265 ev = ewmhview_new();
266 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ),
267 ewmhview_get_widget( ev ),
268 gtk_label_new( _("Window Manager Hints") ) );
269
270 rv = xresview_new();
271 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ),
272 xresview_get_widget( rv ),
273 gtk_label_new( _("Server Resources") ) );
274
275 g_timeout_add( 50, check_events, window );
276
277 gtk_widget_show_all( window );
278 set_state_above( ((GtkWidget *) window)->window );
279
280 gtk_main();
281 return 0;
282 }
283
284