]> icculus.org git repositories - dana/openbox.git/blob - openbox/screen.c
only preserve focus for normal omnipresent windows
[dana/openbox.git] / openbox / screen.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    screen.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "xerror.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "startupnotify.h"
27 #include "moveresize.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "client.h"
31 #include "session.h"
32 #include "frame.h"
33 #include "event.h"
34 #include "focus.h"
35 #include "popup.h"
36 #include "extensions.h"
37 #include "render/render.h"
38 #include "gettext.h"
39
40 #include <X11/Xlib.h>
41 #ifdef HAVE_UNISTD_H
42 #  include <sys/types.h>
43 #  include <unistd.h>
44 #endif
45 #include <assert.h>
46
47 /*! The event mask to grab on the root window */
48 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
49                         EnterWindowMask | LeaveWindowMask | \
50                         SubstructureRedirectMask | FocusChangeMask | \
51                         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
52
53 static gboolean screen_validate_layout(ObDesktopLayout *l);
54 static gboolean replace_wm();
55
56 guint    screen_num_desktops;
57 guint    screen_num_monitors;
58 guint    screen_desktop;
59 guint    screen_last_desktop;
60 Size     screen_physical_size;
61 gboolean screen_showing_desktop;
62 ObDesktopLayout screen_desktop_layout;
63 gchar  **screen_desktop_names;
64 Window   screen_support_win;
65 Time     screen_desktop_user_time = CurrentTime;
66
67 static Rect  **area; /* array of desktop holding array of xinerama areas */
68 static Rect  *monitor_area;
69
70 static ObPagerPopup *desktop_cycle_popup;
71
72 static gboolean replace_wm()
73 {
74     gchar *wm_sn;
75     Atom wm_sn_atom;
76     Window current_wm_sn_owner;
77     Time timestamp;
78
79     wm_sn = g_strdup_printf("WM_S%d", ob_screen);
80     wm_sn_atom = XInternAtom(ob_display, wm_sn, FALSE);
81     g_free(wm_sn);
82
83     current_wm_sn_owner = XGetSelectionOwner(ob_display, wm_sn_atom);
84     if (current_wm_sn_owner == screen_support_win)
85         current_wm_sn_owner = None;
86     if (current_wm_sn_owner) {
87         if (!ob_replace_wm) {
88             g_message(_("A window manager is already running on screen %d"),
89                       ob_screen);
90             return FALSE;
91         }
92         xerror_set_ignore(TRUE);
93         xerror_occured = FALSE;
94
95         /* We want to find out when the current selection owner dies */
96         XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask);
97         XSync(ob_display, FALSE);
98
99         xerror_set_ignore(FALSE);
100         if (xerror_occured)
101             current_wm_sn_owner = None;
102     }
103
104     {
105         /* Generate a timestamp */
106         XEvent event;
107
108         XSelectInput(ob_display, screen_support_win, PropertyChangeMask);
109
110         XChangeProperty(ob_display, screen_support_win,
111                         prop_atoms.wm_class, prop_atoms.string,
112                         8, PropModeAppend, NULL, 0);
113         XWindowEvent(ob_display, screen_support_win,
114                      PropertyChangeMask, &event);
115
116         XSelectInput(ob_display, screen_support_win, NoEventMask);
117
118         timestamp = event.xproperty.time;
119     }
120
121     XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
122                        timestamp);
123
124     if (XGetSelectionOwner(ob_display, wm_sn_atom) != screen_support_win) {
125         g_message(_("Could not acquire window manager selection on screen %d"),
126                   ob_screen);
127         return FALSE;
128     }
129
130     /* Wait for old window manager to go away */
131     if (current_wm_sn_owner) {
132       XEvent event;
133       gulong wait = 0;
134       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
135
136       while (wait < timeout) {
137           if (XCheckWindowEvent(ob_display, current_wm_sn_owner,
138                                 StructureNotifyMask, &event) &&
139               event.type == DestroyNotify)
140               break;
141           g_usleep(G_USEC_PER_SEC / 10);
142           wait += G_USEC_PER_SEC / 10;
143       }
144
145       if (wait >= timeout) {
146           g_message(_("The WM on screen %d is not exiting"), ob_screen);
147           return FALSE;
148       }
149     }
150
151     /* Send client message indicating that we are now the WM */
152     prop_message(RootWindow(ob_display, ob_screen), prop_atoms.manager,
153                  timestamp, wm_sn_atom, screen_support_win, 0,
154                  SubstructureNotifyMask);
155
156     return TRUE;
157 }
158
159 gboolean screen_annex()
160 {
161     XSetWindowAttributes attrib;
162     pid_t pid;
163     gint i, num_support;
164     Atom *prop_atoms_start, *wm_supported_pos;
165     gulong *supported;
166
167     /* create the netwm support window */
168     attrib.override_redirect = TRUE;
169     screen_support_win = XCreateWindow(ob_display,
170                                        RootWindow(ob_display, ob_screen),
171                                        -100, -100, 1, 1, 0,
172                                        CopyFromParent, InputOutput,
173                                        CopyFromParent,
174                                        CWOverrideRedirect, &attrib);
175     XMapWindow(ob_display, screen_support_win);
176     XLowerWindow(ob_display, screen_support_win);
177
178     if (!replace_wm()) {
179         XDestroyWindow(ob_display, screen_support_win);
180         return FALSE;
181     }
182
183     xerror_set_ignore(TRUE);
184     xerror_occured = FALSE;
185     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
186                  ROOT_EVENTMASK);
187     xerror_set_ignore(FALSE);
188     if (xerror_occured) {
189         g_message(_("A window manager is already running on screen %d"),
190                   ob_screen);
191
192         XDestroyWindow(ob_display, screen_support_win);
193         return FALSE;
194     }
195
196     screen_set_root_cursor();
197
198     /* set the OPENBOX_PID hint */
199     pid = getpid();
200     PROP_SET32(RootWindow(ob_display, ob_screen),
201                openbox_pid, cardinal, pid);
202
203     /* set supporting window */
204     PROP_SET32(RootWindow(ob_display, ob_screen),
205                net_supporting_wm_check, window, screen_support_win);
206
207     /* set properties on the supporting window */
208     PROP_SETS(screen_support_win, net_wm_name, "Openbox");
209     PROP_SET32(screen_support_win, net_supporting_wm_check,
210                window, screen_support_win);
211
212     /* set the _NET_SUPPORTED_ATOMS hint */
213
214     /* this is all the atoms after net_supported in the prop_atoms struct */
215     prop_atoms_start = (Atom*)&prop_atoms;
216     wm_supported_pos = (Atom*)&(prop_atoms.net_supported);
217     num_support = sizeof(prop_atoms) / sizeof(Atom) -
218         (wm_supported_pos - prop_atoms_start) - 1;
219     i = 0;
220     supported = g_new(gulong, num_support);
221     supported[i++] = prop_atoms.net_supporting_wm_check;
222     supported[i++] = prop_atoms.net_wm_full_placement;
223     supported[i++] = prop_atoms.net_current_desktop;
224     supported[i++] = prop_atoms.net_number_of_desktops;
225     supported[i++] = prop_atoms.net_desktop_geometry;
226     supported[i++] = prop_atoms.net_desktop_viewport;
227     supported[i++] = prop_atoms.net_active_window;
228     supported[i++] = prop_atoms.net_workarea;
229     supported[i++] = prop_atoms.net_client_list;
230     supported[i++] = prop_atoms.net_client_list_stacking;
231     supported[i++] = prop_atoms.net_desktop_names;
232     supported[i++] = prop_atoms.net_close_window;
233     supported[i++] = prop_atoms.net_desktop_layout;
234     supported[i++] = prop_atoms.net_showing_desktop;
235     supported[i++] = prop_atoms.net_wm_name;
236     supported[i++] = prop_atoms.net_wm_visible_name;
237     supported[i++] = prop_atoms.net_wm_icon_name;
238     supported[i++] = prop_atoms.net_wm_visible_icon_name;
239     supported[i++] = prop_atoms.net_wm_desktop;
240     supported[i++] = prop_atoms.net_wm_strut;
241     supported[i++] = prop_atoms.net_wm_strut_partial;
242     supported[i++] = prop_atoms.net_wm_icon;
243     supported[i++] = prop_atoms.net_wm_icon_geometry;
244     supported[i++] = prop_atoms.net_wm_window_type;
245     supported[i++] = prop_atoms.net_wm_window_type_desktop;
246     supported[i++] = prop_atoms.net_wm_window_type_dock;
247     supported[i++] = prop_atoms.net_wm_window_type_toolbar;
248     supported[i++] = prop_atoms.net_wm_window_type_menu;
249     supported[i++] = prop_atoms.net_wm_window_type_utility;
250     supported[i++] = prop_atoms.net_wm_window_type_splash;
251     supported[i++] = prop_atoms.net_wm_window_type_dialog;
252     supported[i++] = prop_atoms.net_wm_window_type_normal;
253     supported[i++] = prop_atoms.net_wm_allowed_actions;
254     supported[i++] = prop_atoms.net_wm_action_move;
255     supported[i++] = prop_atoms.net_wm_action_resize;
256     supported[i++] = prop_atoms.net_wm_action_minimize;
257     supported[i++] = prop_atoms.net_wm_action_shade;
258     supported[i++] = prop_atoms.net_wm_action_maximize_horz;
259     supported[i++] = prop_atoms.net_wm_action_maximize_vert;
260     supported[i++] = prop_atoms.net_wm_action_fullscreen;
261     supported[i++] = prop_atoms.net_wm_action_change_desktop;
262     supported[i++] = prop_atoms.net_wm_action_close;
263     supported[i++] = prop_atoms.net_wm_action_above;
264     supported[i++] = prop_atoms.net_wm_action_below;
265     supported[i++] = prop_atoms.net_wm_state;
266     supported[i++] = prop_atoms.net_wm_state_modal;
267     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
268     supported[i++] = prop_atoms.net_wm_state_maximized_horz;
269     supported[i++] = prop_atoms.net_wm_state_shaded;
270     supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
271     supported[i++] = prop_atoms.net_wm_state_skip_pager;
272     supported[i++] = prop_atoms.net_wm_state_hidden;
273     supported[i++] = prop_atoms.net_wm_state_fullscreen;
274     supported[i++] = prop_atoms.net_wm_state_above;
275     supported[i++] = prop_atoms.net_wm_state_below;
276     supported[i++] = prop_atoms.net_wm_state_demands_attention;
277     supported[i++] = prop_atoms.net_moveresize_window;
278     supported[i++] = prop_atoms.net_wm_moveresize;
279     supported[i++] = prop_atoms.net_wm_user_time;
280     supported[i++] = prop_atoms.net_wm_user_time_window;
281     supported[i++] = prop_atoms.net_frame_extents;
282     supported[i++] = prop_atoms.net_request_frame_extents;
283     supported[i++] = prop_atoms.net_restack_window;
284     supported[i++] = prop_atoms.net_startup_id;
285 #ifdef SYNC
286     supported[i++] = prop_atoms.net_wm_sync_request;
287     supported[i++] = prop_atoms.net_wm_sync_request_counter;
288 #endif
289
290     supported[i++] = prop_atoms.kde_wm_change_state;
291     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
292     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
293
294     supported[i++] = prop_atoms.ob_wm_action_undecorate;
295     supported[i++] = prop_atoms.ob_wm_state_undecorated;
296     supported[i++] = prop_atoms.openbox_pid;
297     supported[i++] = prop_atoms.ob_config;
298     supported[i++] = prop_atoms.ob_control;
299     g_assert(i == num_support);
300
301     PROP_SETA32(RootWindow(ob_display, ob_screen),
302                 net_supported, atom, supported, num_support);
303     g_free(supported);
304
305     return TRUE;
306 }
307
308 void screen_startup(gboolean reconfig)
309 {
310     gchar **names = NULL;
311     guint32 d;
312     gboolean namesexist = FALSE;
313
314     desktop_cycle_popup = pager_popup_new(FALSE);
315     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
316
317     if (reconfig) {
318         /* update the pager popup's width */
319         pager_popup_text_width_to_strings(desktop_cycle_popup,
320                                           screen_desktop_names,
321                                           screen_num_desktops);
322         return;
323     }
324
325     /* get the initial size */
326     screen_resize();
327
328     /* have names already been set for the desktops? */
329     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
330                    net_desktop_names, utf8, &names))
331     {
332         g_strfreev(names);
333         namesexist = TRUE;
334     }
335
336     /* if names don't exist and we have session names, set those.
337        do this stuff BEFORE setting the number of desktops, because that
338        will create default names for them
339     */
340     if (!namesexist && session_desktop_names != NULL) {
341         guint i, numnames;
342         GSList *it;
343
344         /* get the desktop names */
345         numnames = g_slist_length(session_desktop_names);
346         names = g_new(gchar*, numnames + 1);
347         names[numnames] = NULL;
348         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
349             names[i] = g_strdup(it->data);
350
351         /* set the root window property */
352         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
353
354         g_strfreev(names);
355     }
356
357     /* set the number of desktops, if it's not already set.
358
359        this will also set the default names from the config file up for
360        desktops that don't have names yet */
361     screen_num_desktops = 0;
362     if (PROP_GET32(RootWindow(ob_display, ob_screen),
363                    net_number_of_desktops, cardinal, &d))
364         screen_set_num_desktops(d);
365     /* restore from session if possible */
366     else if (session_num_desktops)
367         screen_set_num_desktops(session_num_desktops);
368     else
369         screen_set_num_desktops(config_desktops_num);
370
371     screen_desktop = screen_num_desktops;  /* something invalid */
372     /* start on the current desktop when a wm was already running */
373     if (PROP_GET32(RootWindow(ob_display, ob_screen),
374                    net_current_desktop, cardinal, &d) &&
375         d < screen_num_desktops)
376     {
377         screen_set_desktop(d, FALSE);
378     } else if (session_desktop >= 0)
379         screen_set_desktop(MIN((guint)session_desktop,
380                                screen_num_desktops), FALSE);
381     else
382         screen_set_desktop(MIN(config_screen_firstdesk,
383                                screen_num_desktops) - 1, FALSE);
384     screen_last_desktop = screen_desktop;
385
386     /* don't start in showing-desktop mode */
387     screen_showing_desktop = FALSE;
388     PROP_SET32(RootWindow(ob_display, ob_screen),
389                net_showing_desktop, cardinal, screen_showing_desktop);
390
391     if (session_desktop_layout_present &&
392         screen_validate_layout(&session_desktop_layout))
393     {
394         screen_desktop_layout = session_desktop_layout;
395     }
396     else
397         screen_update_layout();
398 }
399
400 void screen_shutdown(gboolean reconfig)
401 {
402     Rect **r;
403
404     pager_popup_free(desktop_cycle_popup);
405
406     if (reconfig)
407         return;
408
409     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
410                  NoEventMask);
411
412     /* we're not running here no more! */
413     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
414     /* not without us */
415     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
416     /* don't keep this mode */
417     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
418
419     XDestroyWindow(ob_display, screen_support_win);
420
421     g_strfreev(screen_desktop_names);
422     screen_desktop_names = NULL;
423
424     for (r = area; *r; ++r)
425         g_free(*r);
426     g_free(area);
427     area = NULL;
428 }
429
430 void screen_resize()
431 {
432     static gint oldw = 0, oldh = 0;
433     gint w, h;
434     GList *it;
435     gulong geometry[2];
436
437     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
438     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
439
440     if (w == oldw && h == oldh) return;
441
442     oldw = w; oldh = h;
443
444     /* Set the _NET_DESKTOP_GEOMETRY hint */
445     screen_physical_size.width = geometry[0] = w;
446     screen_physical_size.height = geometry[1] = h;
447     PROP_SETA32(RootWindow(ob_display, ob_screen),
448                 net_desktop_geometry, cardinal, geometry, 2);
449
450     if (ob_state() == OB_STATE_STARTING)
451         return;
452
453     screen_update_areas();
454     dock_configure();
455
456     for (it = client_list; it; it = g_list_next(it))
457         client_move_onscreen(it->data, FALSE);
458 }
459
460 void screen_set_num_desktops(guint num)
461 {
462     guint old;
463     gulong *viewport;
464     GList *it;
465
466     g_assert(num > 0);
467
468     if (screen_num_desktops == num) return;
469
470     old = screen_num_desktops;
471     screen_num_desktops = num;
472     PROP_SET32(RootWindow(ob_display, ob_screen),
473                net_number_of_desktops, cardinal, num);
474
475     /* set the viewport hint */
476     viewport = g_new0(gulong, num * 2);
477     PROP_SETA32(RootWindow(ob_display, ob_screen),
478                 net_desktop_viewport, cardinal, viewport, num * 2);
479     g_free(viewport);
480
481     /* the number of rows/columns will differ */
482     screen_update_layout();
483
484     /* move windows on desktops that will no longer exist! */
485     for (it = client_list; it; it = g_list_next(it)) {
486         ObClient *c = it->data;
487         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
488             client_set_desktop(c, num - 1, FALSE);
489     }
490  
491     /* change our struts/area to match (after moving windows) */
492     screen_update_areas();
493
494     /* may be some unnamed desktops that we need to fill in with names
495      (after updating the areas so the popup can resize) */
496     screen_update_desktop_names();
497
498     /* change our desktop if we're on one that no longer exists! */
499     if (screen_desktop >= screen_num_desktops)
500         screen_set_desktop(num - 1, TRUE);
501 }
502
503 void screen_set_desktop(guint num, gboolean dofocus)
504 {
505     ObClient *c;
506     GList *it;
507     guint old;
508      
509     g_assert(num < screen_num_desktops);
510
511     old = screen_desktop;
512     screen_desktop = num;
513
514     if (old == num) return;
515
516     PROP_SET32(RootWindow(ob_display, ob_screen),
517                net_current_desktop, cardinal, num);
518
519     screen_last_desktop = old;
520
521     ob_debug("Moving to desktop %d\n", num+1);
522
523     if (moveresize_client)
524         client_set_desktop(moveresize_client, num, TRUE);
525
526     /* show windows before hiding the rest to lessen the enter/leave events */
527
528     /* show windows from top to bottom */
529     for (it = stacking_list; it; it = g_list_next(it)) {
530         if (WINDOW_IS_CLIENT(it->data)) {
531             ObClient *c = it->data;
532             client_show(c);
533         }
534     }
535
536     if (focus_client && ((client_normal(focus_client) &&
537                           focus_client->desktop == DESKTOP_ALL) ||
538                          focus_client->desktop == screen_desktop))
539         dofocus = FALSE;
540
541     /* have to try focus here because when you leave an empty desktop
542        there is no focus out to watch for
543
544        do this before hiding the windows so if helper windows are coming
545        with us, they don't get hidden
546     */
547     if (dofocus && (c = focus_fallback(TRUE)))
548     {
549         /* only do the flicker reducing stuff ahead of time if we are going
550            to call xsetinputfocus on the window ourselves. otherwise there is
551            no guarantee the window will actually take focus.. */
552         if (c->can_focus) {
553             /* reduce flicker by hiliting now rather than waiting for the
554                server FocusIn event */
555             frame_adjust_focus(c->frame, TRUE);
556             /* do this here so that if you switch desktops to a window with
557                helper windows then the helper windows won't flash */
558             client_bring_helper_windows(c);
559         }
560     }
561
562     /* hide windows from bottom to top */
563     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
564         if (WINDOW_IS_CLIENT(it->data)) {
565             ObClient *c = it->data;
566             client_hide(c);
567         }
568     }
569
570     event_ignore_all_queued_enters();
571
572     if (event_curtime != CurrentTime)
573         screen_desktop_user_time = event_curtime;
574 }
575
576 static void get_row_col(guint d, guint *r, guint *c)
577 {
578     switch (screen_desktop_layout.orientation) {
579     case OB_ORIENTATION_HORZ:
580         switch (screen_desktop_layout.start_corner) {
581         case OB_CORNER_TOPLEFT:
582             *r = d / screen_desktop_layout.columns;
583             *c = d % screen_desktop_layout.columns;
584             break;
585         case OB_CORNER_BOTTOMLEFT:
586             *r = screen_desktop_layout.rows - 1 -
587                 d / screen_desktop_layout.columns;
588             *c = d % screen_desktop_layout.columns;
589             break;
590         case OB_CORNER_TOPRIGHT:
591             *r = d / screen_desktop_layout.columns;
592             *c = screen_desktop_layout.columns - 1 -
593                 d % screen_desktop_layout.columns;
594             break;
595         case OB_CORNER_BOTTOMRIGHT:
596             *r = screen_desktop_layout.rows - 1 -
597                 d / screen_desktop_layout.columns;
598             *c = screen_desktop_layout.columns - 1 -
599                 d % screen_desktop_layout.columns;
600             break;
601         }
602         break;
603     case OB_ORIENTATION_VERT:
604         switch (screen_desktop_layout.start_corner) {
605         case OB_CORNER_TOPLEFT:
606             *r = d % screen_desktop_layout.rows;
607             *c = d / screen_desktop_layout.rows;
608             break;
609         case OB_CORNER_BOTTOMLEFT:
610             *r = screen_desktop_layout.rows - 1 -
611                 d % screen_desktop_layout.rows;
612             *c = d / screen_desktop_layout.rows;
613             break;
614         case OB_CORNER_TOPRIGHT:
615             *r = d % screen_desktop_layout.rows;
616             *c = screen_desktop_layout.columns - 1 -
617                 d / screen_desktop_layout.rows;
618             break;
619         case OB_CORNER_BOTTOMRIGHT:
620             *r = screen_desktop_layout.rows - 1 -
621                 d % screen_desktop_layout.rows;
622             *c = screen_desktop_layout.columns - 1 -
623                 d / screen_desktop_layout.rows;
624             break;
625         }
626         break;
627     }
628 }
629
630 static guint translate_row_col(guint r, guint c)
631 {
632     switch (screen_desktop_layout.orientation) {
633     case OB_ORIENTATION_HORZ:
634         switch (screen_desktop_layout.start_corner) {
635         case OB_CORNER_TOPLEFT:
636             return r % screen_desktop_layout.rows *
637                 screen_desktop_layout.columns +
638                 c % screen_desktop_layout.columns;
639         case OB_CORNER_BOTTOMLEFT:
640             return (screen_desktop_layout.rows - 1 -
641                     r % screen_desktop_layout.rows) *
642                 screen_desktop_layout.columns +
643                 c % screen_desktop_layout.columns;
644         case OB_CORNER_TOPRIGHT:
645             return r % screen_desktop_layout.rows *
646                 screen_desktop_layout.columns +
647                 (screen_desktop_layout.columns - 1 -
648                  c % screen_desktop_layout.columns);
649         case OB_CORNER_BOTTOMRIGHT:
650             return (screen_desktop_layout.rows - 1 -
651                     r % screen_desktop_layout.rows) *
652                 screen_desktop_layout.columns +
653                 (screen_desktop_layout.columns - 1 -
654                  c % screen_desktop_layout.columns);
655         }
656     case OB_ORIENTATION_VERT:
657         switch (screen_desktop_layout.start_corner) {
658         case OB_CORNER_TOPLEFT:
659             return c % screen_desktop_layout.columns *
660                 screen_desktop_layout.rows +
661                 r % screen_desktop_layout.rows;
662         case OB_CORNER_BOTTOMLEFT:
663             return c % screen_desktop_layout.columns *
664                 screen_desktop_layout.rows +
665                 (screen_desktop_layout.rows - 1 -
666                  r % screen_desktop_layout.rows);
667         case OB_CORNER_TOPRIGHT:
668             return (screen_desktop_layout.columns - 1 -
669                     c % screen_desktop_layout.columns) *
670                 screen_desktop_layout.rows +
671                 r % screen_desktop_layout.rows;
672         case OB_CORNER_BOTTOMRIGHT:
673             return (screen_desktop_layout.columns - 1 -
674                     c % screen_desktop_layout.columns) *
675                 screen_desktop_layout.rows +
676                 (screen_desktop_layout.rows - 1 -
677                  r % screen_desktop_layout.rows);
678         }
679     }
680     g_assert_not_reached();
681     return 0;
682 }
683
684 void screen_desktop_popup(guint d, gboolean show)
685 {
686     Rect *a;
687
688     if (!show) {
689         pager_popup_hide(desktop_cycle_popup);
690     } else {
691         a = screen_physical_area_monitor(0);
692         pager_popup_position(desktop_cycle_popup, CenterGravity,
693                              a->x + a->width / 2, a->y + a->height / 2);
694         pager_popup_icon_size_multiplier(desktop_cycle_popup,
695                                          (screen_desktop_layout.columns /
696                                           screen_desktop_layout.rows) / 2,
697                                          (screen_desktop_layout.rows/
698                                           screen_desktop_layout.columns) / 2);
699         pager_popup_max_width(desktop_cycle_popup,
700                               MAX(a->width/3, POPUP_WIDTH));
701         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
702     }
703 }
704
705 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
706                            gboolean dialog, gboolean done, gboolean cancel)
707 {
708     guint r, c;
709     static guint d = (guint)-1;
710     guint ret, oldd;
711
712     if (d == (guint)-1)
713         d = screen_desktop;
714
715     if ((cancel || done) && dialog)
716         goto show_cycle_dialog;
717
718     oldd = d;
719     get_row_col(d, &r, &c);
720
721     if (linear) {
722         switch (dir) {
723         case OB_DIRECTION_EAST:
724             if (d < screen_num_desktops - 1)
725                 ++d;
726             else if (wrap)
727                 d = 0;
728             break;
729         case OB_DIRECTION_WEST:
730             if (d > 0)
731                 --d;
732             else if (wrap)
733                 d = screen_num_desktops - 1;
734             break;
735         default:
736             assert(0);
737             return screen_desktop;
738         }
739     } else {
740         switch (dir) {
741         case OB_DIRECTION_EAST:
742             ++c;
743             if (c >= screen_desktop_layout.columns) {
744                 if (wrap)
745                     c = 0;
746                 else
747                     goto show_cycle_dialog;
748             }
749             d = translate_row_col(r, c);
750             if (d >= screen_num_desktops) {
751                 if (wrap) {
752                     ++c;
753                 } else {
754                     d = oldd;
755                     goto show_cycle_dialog;
756                 }
757             }
758             break;
759         case OB_DIRECTION_WEST:
760             --c;
761             if (c >= screen_desktop_layout.columns) {
762                 if (wrap)
763                     c = screen_desktop_layout.columns - 1;
764                 else
765                     goto show_cycle_dialog;
766             }
767             d = translate_row_col(r, c);
768             if (d >= screen_num_desktops) {
769                 if (wrap) {
770                     --c;
771                 } else {
772                     d = oldd;
773                     goto show_cycle_dialog;
774                 }
775             }
776             break;
777         case OB_DIRECTION_SOUTH:
778             ++r;
779             if (r >= screen_desktop_layout.rows) {
780                 if (wrap)
781                     r = 0;
782                 else
783                     goto show_cycle_dialog;
784             }
785             d = translate_row_col(r, c);
786             if (d >= screen_num_desktops) {
787                 if (wrap) {
788                     ++r;
789                 } else {
790                     d = oldd;
791                     goto show_cycle_dialog;
792                 }
793             }
794             break;
795         case OB_DIRECTION_NORTH:
796             --r;
797             if (r >= screen_desktop_layout.rows) {
798                 if (wrap)
799                     r = screen_desktop_layout.rows - 1;
800                 else
801                     goto show_cycle_dialog;
802             }
803             d = translate_row_col(r, c);
804             if (d >= screen_num_desktops) {
805                 if (wrap) {
806                     --r;
807                 } else {
808                     d = oldd;
809                     goto show_cycle_dialog;
810                 }
811             }
812             break;
813         default:
814             assert(0);
815             return d = screen_desktop;
816         }
817
818         d = translate_row_col(r, c);
819     }
820
821 show_cycle_dialog:
822     if (dialog && !cancel && !done) {
823         screen_desktop_popup(d, TRUE);
824     } else
825         screen_desktop_popup(0, FALSE);
826     ret = d;
827
828     if (!dialog || cancel || done)
829         d = (guint)-1;
830
831     return ret;
832 }
833
834 static gboolean screen_validate_layout(ObDesktopLayout *l)
835 {
836     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
837         return FALSE;
838
839     /* fill in a zero rows/columns */
840     if (l->columns == 0) {
841         l->columns = screen_num_desktops / l->rows;
842         if (l->rows * l->columns < screen_num_desktops)
843             l->columns++;
844         if (l->rows * l->columns >= screen_num_desktops + l->columns)
845             l->rows--;
846     } else if (l->rows == 0) {
847         l->rows = screen_num_desktops / l->columns;
848         if (l->columns * l->rows < screen_num_desktops)
849             l->rows++;
850         if (l->columns * l->rows >= screen_num_desktops + l->rows)
851             l->columns--;
852     }
853
854     /* bounds checking */
855     if (l->orientation == OB_ORIENTATION_HORZ) {
856         l->columns = MIN(screen_num_desktops, l->columns);
857         l->rows = MIN(l->rows,
858                       (screen_num_desktops + l->columns - 1) / l->columns);
859         l->columns = screen_num_desktops / l->rows +
860             !!(screen_num_desktops % l->rows);
861     } else {
862         l->rows = MIN(screen_num_desktops, l->rows);
863         l->columns = MIN(l->columns,
864                          (screen_num_desktops + l->rows - 1) / l->rows);
865         l->rows = screen_num_desktops / l->columns +
866             !!(screen_num_desktops % l->columns);
867     }
868     return TRUE;
869 }
870
871 void screen_update_layout()
872
873 {
874     ObDesktopLayout l;
875     guint32 *data;
876     guint num;
877
878     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
879     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
880     screen_desktop_layout.rows = 1;
881     screen_desktop_layout.columns = screen_num_desktops;
882
883     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
884                     net_desktop_layout, cardinal, &data, &num)) {
885         if (num == 3 || num == 4) {
886             
887             if (data[0] == prop_atoms.net_wm_orientation_vert)
888                 l.orientation = OB_ORIENTATION_VERT;
889             else if (data[0] == prop_atoms.net_wm_orientation_horz)
890                 l.orientation = OB_ORIENTATION_HORZ;
891             else
892                 return;
893
894             if (num < 4)
895                 l.start_corner = OB_CORNER_TOPLEFT;
896             else {
897                 if (data[3] == prop_atoms.net_wm_topleft)
898                     l.start_corner = OB_CORNER_TOPLEFT;
899                 else if (data[3] == prop_atoms.net_wm_topright)
900                     l.start_corner = OB_CORNER_TOPRIGHT;
901                 else if (data[3] == prop_atoms.net_wm_bottomright)
902                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
903                 else if (data[3] == prop_atoms.net_wm_bottomleft)
904                     l.start_corner = OB_CORNER_BOTTOMLEFT;
905                 else
906                     return;
907             }
908
909             l.columns = data[1];
910             l.rows = data[2];
911
912             if (screen_validate_layout(&l))
913                 screen_desktop_layout = l;
914
915             g_free(data);
916         }
917     }
918 }
919
920 void screen_update_desktop_names()
921 {
922     guint i;
923
924     /* empty the array */
925     g_strfreev(screen_desktop_names);
926     screen_desktop_names = NULL;
927
928     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
929                    net_desktop_names, utf8, &screen_desktop_names))
930         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
931     else
932         i = 0;
933     if (i < screen_num_desktops) {
934         GSList *it;
935
936         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
937                                        screen_num_desktops + 1);
938         screen_desktop_names[screen_num_desktops] = NULL;
939
940         it = g_slist_nth(config_desktops_names, i);
941
942         for (; i < screen_num_desktops; ++i) {
943             if (it && ((char*)it->data)[0]) /* not empty */
944                 /* use the names from the config file when possible */
945                 screen_desktop_names[i] = g_strdup(it->data);
946             else
947                 /* make up a nice name if it's not though */
948                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
949                                                           i + 1);
950             if (it) it = g_slist_next(it);
951         }
952
953         /* if we changed any names, then set the root property so we can
954            all agree on the names */
955         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
956                    screen_desktop_names);
957     }
958
959     /* resize the pager for these names */
960     pager_popup_text_width_to_strings(desktop_cycle_popup,
961                                       screen_desktop_names,
962                                       screen_num_desktops);
963 }
964
965 void screen_show_desktop(gboolean show, ObClient *show_only)
966 {
967     GList *it;
968      
969     if (show == screen_showing_desktop) return; /* no change */
970
971     screen_showing_desktop = show;
972
973     if (show) {
974         /* hide windows bottom to top */
975         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
976             if (WINDOW_IS_CLIENT(it->data)) {
977                 ObClient *client = it->data;
978                 client_showhide(client);
979             }
980         }
981     }
982     else {
983         /* restore windows top to bottom */
984         for (it = stacking_list; it; it = g_list_next(it)) {
985             if (WINDOW_IS_CLIENT(it->data)) {
986                 ObClient *client = it->data;
987                 if (client_should_show(client)) {
988                     if (!show_only || client == show_only)
989                         client_show(client);
990                     else
991                         client_iconify(client, TRUE, FALSE, TRUE);
992                 }
993             }
994         }
995     }
996
997     if (show) {
998         /* focus the desktop */
999         for (it = focus_order; it; it = g_list_next(it)) {
1000             ObClient *c = it->data;
1001             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1002                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1003                 client_focus(it->data))
1004                 break;
1005         }
1006     }
1007     else if (!show_only) {
1008         ObClient *c;
1009
1010         if ((c = focus_fallback(TRUE))) {
1011             /* only do the flicker reducing stuff ahead of time if we are going
1012                to call xsetinputfocus on the window ourselves. otherwise there
1013                is no guarantee the window will actually take focus.. */
1014             if (c->can_focus) {
1015                 /* reduce flicker by hiliting now rather than waiting for the
1016                    server FocusIn event */
1017                 frame_adjust_focus(c->frame, TRUE);
1018             }
1019         }
1020     }
1021
1022     show = !!show; /* make it boolean */
1023     PROP_SET32(RootWindow(ob_display, ob_screen),
1024                net_showing_desktop, cardinal, show);
1025 }
1026
1027 void screen_install_colormap(ObClient *client, gboolean install)
1028 {
1029     if (client == NULL) {
1030         if (install)
1031             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1032         else
1033             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1034     } else {
1035         xerror_set_ignore(TRUE);
1036         if (install) {
1037             if (client->colormap != None)
1038                 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1039         } else
1040             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1041         xerror_set_ignore(FALSE);
1042     }
1043 }
1044
1045 static inline void
1046 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1047                            gint edge, Strut *ret)
1048 {
1049     if (s->left &&
1050         ((s->left_end <= s->left_start) ||
1051          (RECT_TOP(*monitor_area) < s->left_end &&
1052           RECT_BOTTOM(*monitor_area) > s->left_start)))
1053         ret->left = MAX(ret->left, edge);
1054 }
1055
1056 static inline void
1057 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1058                           gint edge, Strut *ret)
1059 {
1060     if (s->top &&
1061         ((s->top_end <= s->top_start) ||
1062          (RECT_LEFT(*monitor_area) < s->top_end &&
1063           RECT_RIGHT(*monitor_area) > s->top_start)))
1064         ret->top = MAX(ret->top, edge);
1065 }
1066
1067 static inline void
1068 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1069                             gint edge, Strut *ret)
1070 {
1071     if (s->right &&
1072         ((s->right_end <= s->right_start) ||
1073          (RECT_TOP(*monitor_area) < s->right_end &&
1074           RECT_BOTTOM(*monitor_area) > s->right_start)))
1075         ret->right = MAX(ret->right, edge);
1076 }
1077
1078 static inline void
1079 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1080                              gint edge, Strut *ret)
1081 {
1082     if (s->bottom &&
1083         ((s->bottom_end <= s->bottom_start) ||
1084          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1085           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1086         ret->bottom = MAX(ret->bottom, edge);
1087 }
1088
1089 void screen_update_areas()
1090 {
1091     guint i, x;
1092     gulong *dims;
1093     GList *it;
1094     gint o;
1095
1096     g_free(monitor_area);
1097     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1098
1099     if (area) {
1100         for (i = 0; area[i]; ++i)
1101             g_free(area[i]);
1102         g_free(area);
1103     }
1104
1105     area = g_new(Rect*, screen_num_desktops + 2);
1106     for (i = 0; i < screen_num_desktops + 1; ++i)
1107         area[i] = g_new0(Rect, screen_num_monitors + 1);
1108     area[i] = NULL;
1109      
1110     dims = g_new(gulong, 4 * screen_num_desktops);
1111
1112     for (i = 0; i < screen_num_desktops + 1; ++i) {
1113         Strut *struts;
1114         gint l, r, t, b;
1115
1116         struts = g_new0(Strut, screen_num_monitors);
1117
1118         /* calc the xinerama areas */
1119         for (x = 0; x < screen_num_monitors; ++x) {
1120             area[i][x] = monitor_area[x];
1121             if (x == 0) {
1122                 l = monitor_area[x].x;
1123                 t = monitor_area[x].y;
1124                 r = monitor_area[x].x + monitor_area[x].width - 1;
1125                 b = monitor_area[x].y + monitor_area[x].height - 1;
1126             } else {
1127                 l = MIN(l, monitor_area[x].x);
1128                 t = MIN(t, monitor_area[x].y);
1129                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1130                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1131             }
1132         }
1133         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1134
1135         /* apply the struts */
1136
1137         /* find the left-most xin heads, i do this in 2 loops :| */
1138         o = area[i][0].x;
1139         for (x = 1; x < screen_num_monitors; ++x)
1140             o = MIN(o, area[i][x].x);
1141
1142         for (x = 0; x < screen_num_monitors; ++x) {
1143             for (it = client_list; it; it = g_list_next(it)) {
1144                 ObClient *c = it->data;
1145                 screen_area_add_strut_left(&c->strut,
1146                                            &monitor_area[x],
1147                                            o + c->strut.left - area[i][x].x,
1148                                            &struts[x]);
1149             }
1150             screen_area_add_strut_left(&dock_strut,
1151                                        &monitor_area[x],
1152                                        o + dock_strut.left - area[i][x].x,
1153                                        &struts[x]);
1154
1155             area[i][x].x += struts[x].left;
1156             area[i][x].width -= struts[x].left;
1157         }
1158
1159         /* find the top-most xin heads, i do this in 2 loops :| */
1160         o = area[i][0].y;
1161         for (x = 1; x < screen_num_monitors; ++x)
1162             o = MIN(o, area[i][x].y);
1163
1164         for (x = 0; x < screen_num_monitors; ++x) {
1165             for (it = client_list; it; it = g_list_next(it)) {
1166                 ObClient *c = it->data;
1167                 screen_area_add_strut_top(&c->strut,
1168                                            &monitor_area[x],
1169                                            o + c->strut.top - area[i][x].y,
1170                                            &struts[x]);
1171             }
1172             screen_area_add_strut_top(&dock_strut,
1173                                       &monitor_area[x],
1174                                       o + dock_strut.top - area[i][x].y,
1175                                       &struts[x]);
1176
1177             area[i][x].y += struts[x].top;
1178             area[i][x].height -= struts[x].top;
1179         }
1180
1181         /* find the right-most xin heads, i do this in 2 loops :| */
1182         o = area[i][0].x + area[i][0].width - 1;
1183         for (x = 1; x < screen_num_monitors; ++x)
1184             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1185
1186         for (x = 0; x < screen_num_monitors; ++x) {
1187             for (it = client_list; it; it = g_list_next(it)) {
1188                 ObClient *c = it->data;
1189                 screen_area_add_strut_right(&c->strut,
1190                                            &monitor_area[x],
1191                                            (area[i][x].x +
1192                                             area[i][x].width - 1) -
1193                                             (o - c->strut.right),
1194                                             &struts[x]);
1195             }
1196             screen_area_add_strut_right(&dock_strut,
1197                                         &monitor_area[x],
1198                                         (area[i][x].x +
1199                                          area[i][x].width - 1) -
1200                                         (o - dock_strut.right),
1201                                         &struts[x]);
1202
1203             area[i][x].width -= struts[x].right;
1204         }
1205
1206         /* find the bottom-most xin heads, i do this in 2 loops :| */
1207         o = area[i][0].y + area[i][0].height - 1;
1208         for (x = 1; x < screen_num_monitors; ++x)
1209             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1210
1211         for (x = 0; x < screen_num_monitors; ++x) {
1212             for (it = client_list; it; it = g_list_next(it)) {
1213                 ObClient *c = it->data;
1214                 screen_area_add_strut_bottom(&c->strut,
1215                                              &monitor_area[x],
1216                                              (area[i][x].y +
1217                                               area[i][x].height - 1) - \
1218                                              (o - c->strut.bottom),
1219                                              &struts[x]);
1220             }
1221             screen_area_add_strut_bottom(&dock_strut,
1222                                          &monitor_area[x],
1223                                          (area[i][x].y +
1224                                           area[i][x].height - 1) - \
1225                                          (o - dock_strut.bottom),
1226                                          &struts[x]);
1227
1228             area[i][x].height -= struts[x].bottom;
1229         }
1230
1231         l = RECT_LEFT(area[i][0]);
1232         t = RECT_TOP(area[i][0]);
1233         r = RECT_RIGHT(area[i][0]);
1234         b = RECT_BOTTOM(area[i][0]);
1235         for (x = 1; x < screen_num_monitors; ++x) {
1236             l = MIN(l, RECT_LEFT(area[i][x]));
1237             t = MIN(l, RECT_TOP(area[i][x]));
1238             r = MAX(r, RECT_RIGHT(area[i][x]));
1239             b = MAX(b, RECT_BOTTOM(area[i][x]));
1240         }
1241         RECT_SET(area[i][screen_num_monitors], l, t,
1242                  r - l + 1, b - t + 1);
1243
1244         /* XXX optimize when this is run? */
1245
1246         /* the area has changed, adjust all the maximized 
1247            windows */
1248         for (it = client_list; it; it = g_list_next(it)) {
1249             ObClient *c = it->data; 
1250             if (i < screen_num_desktops) {
1251                 if (c->desktop == i)
1252                     client_reconfigure(c);
1253             } else if (c->desktop == DESKTOP_ALL)
1254                 client_reconfigure(c);
1255         }
1256         if (i < screen_num_desktops) {
1257             /* don't set these for the 'all desktops' area */
1258             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1259             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1260             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1261             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1262         }
1263
1264         g_free(struts);
1265     }
1266
1267     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1268                 dims, 4 * screen_num_desktops);
1269
1270     g_free(dims);
1271 }
1272
1273 Rect *screen_area(guint desktop)
1274 {
1275     return screen_area_monitor(desktop, screen_num_monitors);
1276 }
1277
1278 Rect *screen_area_monitor(guint desktop, guint head)
1279 {
1280     if (head > screen_num_monitors)
1281         return NULL;
1282     if (desktop >= screen_num_desktops) {
1283         if (desktop == DESKTOP_ALL)
1284             return &area[screen_num_desktops][head];
1285         return NULL;
1286     }
1287     return &area[desktop][head];
1288 }
1289
1290 guint screen_find_monitor(Rect *search)
1291 {
1292     guint i;
1293     guint most = 0;
1294     guint mostv = 0;
1295
1296     for (i = 0; i < screen_num_monitors; ++i) {
1297         Rect *area = screen_physical_area_monitor(i);
1298         if (RECT_INTERSECTS_RECT(*area, *search)) {
1299             Rect r;
1300             guint v;
1301
1302             RECT_SET_INTERSECTION(r, *area, *search);
1303             v = r.width * r.height;
1304
1305             if (v > mostv) {
1306                 mostv = v;
1307                 most = i;
1308             }
1309         }
1310     }
1311     return most;
1312 }
1313
1314 Rect *screen_physical_area()
1315 {
1316     return screen_physical_area_monitor(screen_num_monitors);
1317 }
1318
1319 Rect *screen_physical_area_monitor(guint head)
1320 {
1321     if (head > screen_num_monitors)
1322         return NULL;
1323     return &monitor_area[head];
1324 }
1325
1326 void screen_set_root_cursor()
1327 {
1328     if (sn_app_starting())
1329         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1330                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1331     else
1332         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1333                       ob_cursor(OB_CURSOR_POINTER));
1334 }
1335
1336 gboolean screen_pointer_pos(gint *x, gint *y)
1337 {
1338     Window w;
1339     gint i;
1340     guint u;
1341     gboolean ret;
1342
1343     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1344                           &w, &w, x, y, &i, &i, &u);
1345     if (!ret) {
1346         for (i = 0; i < ScreenCount(ob_display); ++i)
1347             if (i != ob_screen)
1348                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1349                                   &w, &w, x, y, &i, &i, &u))
1350                     break;
1351     }
1352     return ret;
1353 }