]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/screen.c
less negatives means less confusing.. but yeah. get rid of omnipresent check in focus...
[mikachu/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     /* start on the current desktop when a wm was already running */
372     if (PROP_GET32(RootWindow(ob_display, ob_screen),
373                    net_current_desktop, cardinal, &d) &&
374         d < screen_num_desktops)
375     {
376         screen_set_desktop(d, FALSE);
377     } else if (session_desktop >= 0)
378         screen_set_desktop(MIN((guint)session_desktop,
379                                screen_num_desktops), FALSE);
380     else
381         screen_set_desktop(MIN(config_screen_firstdesk,
382                                screen_num_desktops) - 1, FALSE);
383
384     /* don't start in showing-desktop mode */
385     screen_showing_desktop = FALSE;
386     PROP_SET32(RootWindow(ob_display, ob_screen),
387                net_showing_desktop, cardinal, screen_showing_desktop);
388
389     if (session_desktop_layout_present &&
390         screen_validate_layout(&session_desktop_layout))
391     {
392         screen_desktop_layout = session_desktop_layout;
393     }
394     else
395         screen_update_layout();
396 }
397
398 void screen_shutdown(gboolean reconfig)
399 {
400     Rect **r;
401
402     pager_popup_free(desktop_cycle_popup);
403
404     if (reconfig)
405         return;
406
407     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
408                  NoEventMask);
409
410     /* we're not running here no more! */
411     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
412     /* not without us */
413     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
414     /* don't keep this mode */
415     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
416
417     XDestroyWindow(ob_display, screen_support_win);
418
419     g_strfreev(screen_desktop_names);
420     screen_desktop_names = NULL;
421
422     for (r = area; *r; ++r)
423         g_free(*r);
424     g_free(area);
425     area = NULL;
426 }
427
428 void screen_resize()
429 {
430     static gint oldw = 0, oldh = 0;
431     gint w, h;
432     GList *it;
433     gulong geometry[2];
434
435     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
436     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
437
438     if (w == oldw && h == oldh) return;
439
440     oldw = w; oldh = h;
441
442     /* Set the _NET_DESKTOP_GEOMETRY hint */
443     screen_physical_size.width = geometry[0] = w;
444     screen_physical_size.height = geometry[1] = h;
445     PROP_SETA32(RootWindow(ob_display, ob_screen),
446                 net_desktop_geometry, cardinal, geometry, 2);
447
448     if (ob_state() == OB_STATE_STARTING)
449         return;
450
451     screen_update_areas();
452     dock_configure();
453
454     for (it = client_list; it; it = g_list_next(it))
455         client_move_onscreen(it->data, FALSE);
456 }
457
458 void screen_set_num_desktops(guint num)
459 {
460     guint old;
461     gulong *viewport;
462     GList *it;
463
464     g_assert(num > 0);
465
466     if (screen_num_desktops == num) return;
467
468     old = screen_num_desktops;
469     screen_num_desktops = num;
470     PROP_SET32(RootWindow(ob_display, ob_screen),
471                net_number_of_desktops, cardinal, num);
472
473     /* set the viewport hint */
474     viewport = g_new0(gulong, num * 2);
475     PROP_SETA32(RootWindow(ob_display, ob_screen),
476                 net_desktop_viewport, cardinal, viewport, num * 2);
477     g_free(viewport);
478
479     /* the number of rows/columns will differ */
480     screen_update_layout();
481
482     /* move windows on desktops that will no longer exist! */
483     for (it = client_list; it; it = g_list_next(it)) {
484         ObClient *c = it->data;
485         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
486             client_set_desktop(c, num - 1, FALSE);
487     }
488  
489     /* change our struts/area to match (after moving windows) */
490     screen_update_areas();
491
492     /* may be some unnamed desktops that we need to fill in with names
493      (after updating the areas so the popup can resize) */
494     screen_update_desktop_names();
495
496     /* change our desktop if we're on one that no longer exists! */
497     if (screen_desktop >= screen_num_desktops)
498         screen_set_desktop(num - 1, TRUE);
499 }
500
501 void screen_set_desktop(guint num, gboolean dofocus)
502 {
503     ObClient *c;
504     GList *it;
505     guint old;
506      
507     g_assert(num < screen_num_desktops);
508
509     old = screen_desktop;
510     screen_desktop = num;
511     PROP_SET32(RootWindow(ob_display, ob_screen),
512                net_current_desktop, cardinal, num);
513
514     if (old == num) return;
515
516     screen_last_desktop = old;
517
518     ob_debug("Moving to desktop %d\n", num+1);
519
520     if (moveresize_client)
521         client_set_desktop(moveresize_client, num, TRUE);
522
523     /* show windows before hiding the rest to lessen the enter/leave events */
524
525     /* show windows from top to bottom */
526     for (it = stacking_list; it; it = g_list_next(it)) {
527         if (WINDOW_IS_CLIENT(it->data)) {
528             ObClient *c = it->data;
529             client_show(c);
530         }
531     }
532
533     if (focus_client && (focus_client->desktop == DESKTOP_ALL ||
534                          focus_client->desktop == screen_desktop))
535         dofocus = FALSE;
536
537     /* have to try focus here because when you leave an empty desktop
538        there is no focus out to watch for
539
540        do this before hiding the windows so if helper windows are coming
541        with us, they don't get hidden
542     */
543     if (dofocus && (c = focus_fallback(TRUE)))
544     {
545         /* only do the flicker reducing stuff ahead of time if we are going
546            to call xsetinputfocus on the window ourselves. otherwise there is
547            no guarantee the window will actually take focus.. */
548         if (c->can_focus) {
549             /* reduce flicker by hiliting now rather than waiting for the
550                server FocusIn event */
551             frame_adjust_focus(c->frame, TRUE);
552             /* do this here so that if you switch desktops to a window with
553                helper windows then the helper windows won't flash */
554             client_bring_helper_windows(c);
555         }
556     }
557
558     /* hide windows from bottom to top */
559     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
560         if (WINDOW_IS_CLIENT(it->data)) {
561             ObClient *c = it->data;
562             client_hide(c);
563         }
564     }
565
566     event_ignore_all_queued_enters();
567
568     if (event_curtime != CurrentTime)
569         screen_desktop_user_time = event_curtime;
570 }
571
572 static void get_row_col(guint d, guint *r, guint *c)
573 {
574     switch (screen_desktop_layout.orientation) {
575     case OB_ORIENTATION_HORZ:
576         switch (screen_desktop_layout.start_corner) {
577         case OB_CORNER_TOPLEFT:
578             *r = d / screen_desktop_layout.columns;
579             *c = d % screen_desktop_layout.columns;
580             break;
581         case OB_CORNER_BOTTOMLEFT:
582             *r = screen_desktop_layout.rows - 1 -
583                 d / screen_desktop_layout.columns;
584             *c = d % screen_desktop_layout.columns;
585             break;
586         case OB_CORNER_TOPRIGHT:
587             *r = d / screen_desktop_layout.columns;
588             *c = screen_desktop_layout.columns - 1 -
589                 d % screen_desktop_layout.columns;
590             break;
591         case OB_CORNER_BOTTOMRIGHT:
592             *r = screen_desktop_layout.rows - 1 -
593                 d / screen_desktop_layout.columns;
594             *c = screen_desktop_layout.columns - 1 -
595                 d % screen_desktop_layout.columns;
596             break;
597         }
598         break;
599     case OB_ORIENTATION_VERT:
600         switch (screen_desktop_layout.start_corner) {
601         case OB_CORNER_TOPLEFT:
602             *r = d % screen_desktop_layout.rows;
603             *c = d / screen_desktop_layout.rows;
604             break;
605         case OB_CORNER_BOTTOMLEFT:
606             *r = screen_desktop_layout.rows - 1 -
607                 d % screen_desktop_layout.rows;
608             *c = d / screen_desktop_layout.rows;
609             break;
610         case OB_CORNER_TOPRIGHT:
611             *r = d % screen_desktop_layout.rows;
612             *c = screen_desktop_layout.columns - 1 -
613                 d / screen_desktop_layout.rows;
614             break;
615         case OB_CORNER_BOTTOMRIGHT:
616             *r = screen_desktop_layout.rows - 1 -
617                 d % screen_desktop_layout.rows;
618             *c = screen_desktop_layout.columns - 1 -
619                 d / screen_desktop_layout.rows;
620             break;
621         }
622         break;
623     }
624 }
625
626 static guint translate_row_col(guint r, guint c)
627 {
628     switch (screen_desktop_layout.orientation) {
629     case OB_ORIENTATION_HORZ:
630         switch (screen_desktop_layout.start_corner) {
631         case OB_CORNER_TOPLEFT:
632             return r % screen_desktop_layout.rows *
633                 screen_desktop_layout.columns +
634                 c % screen_desktop_layout.columns;
635         case OB_CORNER_BOTTOMLEFT:
636             return (screen_desktop_layout.rows - 1 -
637                     r % screen_desktop_layout.rows) *
638                 screen_desktop_layout.columns +
639                 c % screen_desktop_layout.columns;
640         case OB_CORNER_TOPRIGHT:
641             return r % screen_desktop_layout.rows *
642                 screen_desktop_layout.columns +
643                 (screen_desktop_layout.columns - 1 -
644                  c % screen_desktop_layout.columns);
645         case OB_CORNER_BOTTOMRIGHT:
646             return (screen_desktop_layout.rows - 1 -
647                     r % screen_desktop_layout.rows) *
648                 screen_desktop_layout.columns +
649                 (screen_desktop_layout.columns - 1 -
650                  c % screen_desktop_layout.columns);
651         }
652     case OB_ORIENTATION_VERT:
653         switch (screen_desktop_layout.start_corner) {
654         case OB_CORNER_TOPLEFT:
655             return c % screen_desktop_layout.columns *
656                 screen_desktop_layout.rows +
657                 r % screen_desktop_layout.rows;
658         case OB_CORNER_BOTTOMLEFT:
659             return c % screen_desktop_layout.columns *
660                 screen_desktop_layout.rows +
661                 (screen_desktop_layout.rows - 1 -
662                  r % screen_desktop_layout.rows);
663         case OB_CORNER_TOPRIGHT:
664             return (screen_desktop_layout.columns - 1 -
665                     c % screen_desktop_layout.columns) *
666                 screen_desktop_layout.rows +
667                 r % screen_desktop_layout.rows;
668         case OB_CORNER_BOTTOMRIGHT:
669             return (screen_desktop_layout.columns - 1 -
670                     c % screen_desktop_layout.columns) *
671                 screen_desktop_layout.rows +
672                 (screen_desktop_layout.rows - 1 -
673                  r % screen_desktop_layout.rows);
674         }
675     }
676     g_assert_not_reached();
677     return 0;
678 }
679
680 void screen_desktop_popup(guint d, gboolean show)
681 {
682     Rect *a;
683
684     if (!show) {
685         pager_popup_hide(desktop_cycle_popup);
686     } else {
687         a = screen_physical_area_monitor(0);
688         pager_popup_position(desktop_cycle_popup, CenterGravity,
689                              a->x + a->width / 2, a->y + a->height / 2);
690         pager_popup_icon_size_multiplier(desktop_cycle_popup,
691                                          (screen_desktop_layout.columns /
692                                           screen_desktop_layout.rows) / 2,
693                                          (screen_desktop_layout.rows/
694                                           screen_desktop_layout.columns) / 2);
695         pager_popup_max_width(desktop_cycle_popup,
696                               MAX(a->width/3, POPUP_WIDTH));
697         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
698     }
699 }
700
701 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
702                            gboolean dialog, gboolean done, gboolean cancel)
703 {
704     guint r, c;
705     static guint d = (guint)-1;
706     guint ret, oldd;
707
708     if (d == (guint)-1)
709         d = screen_desktop;
710
711     if ((cancel || done) && dialog)
712         goto show_cycle_dialog;
713
714     oldd = d;
715     get_row_col(d, &r, &c);
716
717     if (linear) {
718         switch (dir) {
719         case OB_DIRECTION_EAST:
720             if (d < screen_num_desktops - 1)
721                 ++d;
722             else if (wrap)
723                 d = 0;
724             break;
725         case OB_DIRECTION_WEST:
726             if (d > 0)
727                 --d;
728             else if (wrap)
729                 d = screen_num_desktops - 1;
730             break;
731         default:
732             assert(0);
733             return screen_desktop;
734         }
735     } else {
736         switch (dir) {
737         case OB_DIRECTION_EAST:
738             ++c;
739             if (c >= screen_desktop_layout.columns) {
740                 if (wrap)
741                     c = 0;
742                 else
743                     goto show_cycle_dialog;
744             }
745             d = translate_row_col(r, c);
746             if (d >= screen_num_desktops) {
747                 if (wrap) {
748                     ++c;
749                 } else {
750                     d = oldd;
751                     goto show_cycle_dialog;
752                 }
753             }
754             break;
755         case OB_DIRECTION_WEST:
756             --c;
757             if (c >= screen_desktop_layout.columns) {
758                 if (wrap)
759                     c = screen_desktop_layout.columns - 1;
760                 else
761                     goto show_cycle_dialog;
762             }
763             d = translate_row_col(r, c);
764             if (d >= screen_num_desktops) {
765                 if (wrap) {
766                     --c;
767                 } else {
768                     d = oldd;
769                     goto show_cycle_dialog;
770                 }
771             }
772             break;
773         case OB_DIRECTION_SOUTH:
774             ++r;
775             if (r >= screen_desktop_layout.rows) {
776                 if (wrap)
777                     r = 0;
778                 else
779                     goto show_cycle_dialog;
780             }
781             d = translate_row_col(r, c);
782             if (d >= screen_num_desktops) {
783                 if (wrap) {
784                     ++r;
785                 } else {
786                     d = oldd;
787                     goto show_cycle_dialog;
788                 }
789             }
790             break;
791         case OB_DIRECTION_NORTH:
792             --r;
793             if (r >= screen_desktop_layout.rows) {
794                 if (wrap)
795                     r = screen_desktop_layout.rows - 1;
796                 else
797                     goto show_cycle_dialog;
798             }
799             d = translate_row_col(r, c);
800             if (d >= screen_num_desktops) {
801                 if (wrap) {
802                     --r;
803                 } else {
804                     d = oldd;
805                     goto show_cycle_dialog;
806                 }
807             }
808             break;
809         default:
810             assert(0);
811             return d = screen_desktop;
812         }
813
814         d = translate_row_col(r, c);
815     }
816
817 show_cycle_dialog:
818     if (dialog && !cancel && !done) {
819         screen_desktop_popup(d, TRUE);
820     } else
821         screen_desktop_popup(0, FALSE);
822     ret = d;
823
824     if (!dialog || cancel || done)
825         d = (guint)-1;
826
827     return ret;
828 }
829
830 static gboolean screen_validate_layout(ObDesktopLayout *l)
831 {
832     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
833         return FALSE;
834
835     /* fill in a zero rows/columns */
836     if (l->columns == 0) {
837         l->columns = screen_num_desktops / l->rows;
838         if (l->rows * l->columns < screen_num_desktops)
839             l->columns++;
840         if (l->rows * l->columns >= screen_num_desktops + l->columns)
841             l->rows--;
842     } else if (l->rows == 0) {
843         l->rows = screen_num_desktops / l->columns;
844         if (l->columns * l->rows < screen_num_desktops)
845             l->rows++;
846         if (l->columns * l->rows >= screen_num_desktops + l->rows)
847             l->columns--;
848     }
849
850     /* bounds checking */
851     if (l->orientation == OB_ORIENTATION_HORZ) {
852         l->columns = MIN(screen_num_desktops, l->columns);
853         l->rows = MIN(l->rows,
854                       (screen_num_desktops + l->columns - 1) / l->columns);
855         l->columns = screen_num_desktops / l->rows +
856             !!(screen_num_desktops % l->rows);
857     } else {
858         l->rows = MIN(screen_num_desktops, l->rows);
859         l->columns = MIN(l->columns,
860                          (screen_num_desktops + l->rows - 1) / l->rows);
861         l->rows = screen_num_desktops / l->columns +
862             !!(screen_num_desktops % l->columns);
863     }
864     return TRUE;
865 }
866
867 void screen_update_layout()
868
869 {
870     ObDesktopLayout l;
871     guint32 *data;
872     guint num;
873
874     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
875     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
876     screen_desktop_layout.rows = 1;
877     screen_desktop_layout.columns = screen_num_desktops;
878
879     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
880                     net_desktop_layout, cardinal, &data, &num)) {
881         if (num == 3 || num == 4) {
882             
883             if (data[0] == prop_atoms.net_wm_orientation_vert)
884                 l.orientation = OB_ORIENTATION_VERT;
885             else if (data[0] == prop_atoms.net_wm_orientation_horz)
886                 l.orientation = OB_ORIENTATION_HORZ;
887             else
888                 return;
889
890             if (num < 4)
891                 l.start_corner = OB_CORNER_TOPLEFT;
892             else {
893                 if (data[3] == prop_atoms.net_wm_topleft)
894                     l.start_corner = OB_CORNER_TOPLEFT;
895                 else if (data[3] == prop_atoms.net_wm_topright)
896                     l.start_corner = OB_CORNER_TOPRIGHT;
897                 else if (data[3] == prop_atoms.net_wm_bottomright)
898                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
899                 else if (data[3] == prop_atoms.net_wm_bottomleft)
900                     l.start_corner = OB_CORNER_BOTTOMLEFT;
901                 else
902                     return;
903             }
904
905             l.columns = data[1];
906             l.rows = data[2];
907
908             if (screen_validate_layout(&l))
909                 screen_desktop_layout = l;
910
911             g_free(data);
912         }
913     }
914 }
915
916 void screen_update_desktop_names()
917 {
918     guint i;
919
920     /* empty the array */
921     g_strfreev(screen_desktop_names);
922     screen_desktop_names = NULL;
923
924     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
925                    net_desktop_names, utf8, &screen_desktop_names))
926         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
927     else
928         i = 0;
929     if (i < screen_num_desktops) {
930         GSList *it;
931
932         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
933                                        screen_num_desktops + 1);
934         screen_desktop_names[screen_num_desktops] = NULL;
935
936         it = g_slist_nth(config_desktops_names, i);
937
938         for (; i < screen_num_desktops; ++i) {
939             if (it)
940                 /* use the names from the config file when possible */
941                 screen_desktop_names[i] = g_strdup(it->data);
942             else
943                 /* make up a nice name if it's not though */
944                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
945                                                           i + 1);
946             if (it) it = g_slist_next(it);
947         }
948
949         /* if we changed any names, then set the root property so we can
950            all agree on the names */
951         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
952                    screen_desktop_names);
953     }
954
955     /* resize the pager for these names */
956     pager_popup_text_width_to_strings(desktop_cycle_popup,
957                                       screen_desktop_names,
958                                       screen_num_desktops);
959 }
960
961 void screen_show_desktop(gboolean show, ObClient *show_only)
962 {
963     GList *it;
964      
965     if (show == screen_showing_desktop) return; /* no change */
966
967     screen_showing_desktop = show;
968
969     if (show) {
970         /* hide windows bottom to top */
971         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
972             if (WINDOW_IS_CLIENT(it->data)) {
973                 ObClient *client = it->data;
974                 client_showhide(client);
975             }
976         }
977     }
978     else {
979         /* restore windows top to bottom */
980         for (it = stacking_list; it; it = g_list_next(it)) {
981             if (WINDOW_IS_CLIENT(it->data)) {
982                 ObClient *client = it->data;
983                 if (client_should_show(client)) {
984                     if (!show_only || client == show_only)
985                         client_show(client);
986                     else
987                         client_iconify(client, TRUE, FALSE, TRUE);
988                 }
989             }
990         }
991     }
992
993     if (show) {
994         /* focus the desktop */
995         for (it = focus_order; it; it = g_list_next(it)) {
996             ObClient *c = it->data;
997             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
998                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
999                 client_focus(it->data))
1000                 break;
1001         }
1002     }
1003     else if (!show_only) {
1004         ObClient *c;
1005
1006         if ((c = focus_fallback(TRUE))) {
1007             /* only do the flicker reducing stuff ahead of time if we are going
1008                to call xsetinputfocus on the window ourselves. otherwise there
1009                is no guarantee the window will actually take focus.. */
1010             if (c->can_focus) {
1011                 /* reduce flicker by hiliting now rather than waiting for the
1012                    server FocusIn event */
1013                 frame_adjust_focus(c->frame, TRUE);
1014             }
1015         }
1016     }
1017
1018     show = !!show; /* make it boolean */
1019     PROP_SET32(RootWindow(ob_display, ob_screen),
1020                net_showing_desktop, cardinal, show);
1021 }
1022
1023 void screen_install_colormap(ObClient *client, gboolean install)
1024 {
1025     if (client == NULL) {
1026         if (install)
1027             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1028         else
1029             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1030     } else {
1031         xerror_set_ignore(TRUE);
1032         if (install) {
1033             if (client->colormap != None)
1034                 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1035         } else
1036             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1037         xerror_set_ignore(FALSE);
1038     }
1039 }
1040
1041 static inline void
1042 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1043                            gint edge, Strut *ret)
1044 {
1045     if (s->left &&
1046         ((s->left_end <= s->left_start) ||
1047          (RECT_TOP(*monitor_area) < s->left_end &&
1048           RECT_BOTTOM(*monitor_area) > s->left_start)))
1049         ret->left = MAX(ret->left, edge);
1050 }
1051
1052 static inline void
1053 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1054                           gint edge, Strut *ret)
1055 {
1056     if (s->top &&
1057         ((s->top_end <= s->top_start) ||
1058          (RECT_LEFT(*monitor_area) < s->top_end &&
1059           RECT_RIGHT(*monitor_area) > s->top_start)))
1060         ret->top = MAX(ret->top, edge);
1061 }
1062
1063 static inline void
1064 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1065                             gint edge, Strut *ret)
1066 {
1067     if (s->right &&
1068         ((s->right_end <= s->right_start) ||
1069          (RECT_TOP(*monitor_area) < s->right_end &&
1070           RECT_BOTTOM(*monitor_area) > s->right_start)))
1071         ret->right = MAX(ret->right, edge);
1072 }
1073
1074 static inline void
1075 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1076                              gint edge, Strut *ret)
1077 {
1078     if (s->bottom &&
1079         ((s->bottom_end <= s->bottom_start) ||
1080          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1081           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1082         ret->bottom = MAX(ret->bottom, edge);
1083 }
1084
1085 void screen_update_areas()
1086 {
1087     guint i, x;
1088     gulong *dims;
1089     GList *it;
1090     gint o;
1091
1092     g_free(monitor_area);
1093     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1094
1095     if (area) {
1096         for (i = 0; area[i]; ++i)
1097             g_free(area[i]);
1098         g_free(area);
1099     }
1100
1101     area = g_new(Rect*, screen_num_desktops + 2);
1102     for (i = 0; i < screen_num_desktops + 1; ++i)
1103         area[i] = g_new0(Rect, screen_num_monitors + 1);
1104     area[i] = NULL;
1105      
1106     dims = g_new(gulong, 4 * screen_num_desktops);
1107
1108     for (i = 0; i < screen_num_desktops + 1; ++i) {
1109         Strut *struts;
1110         gint l, r, t, b;
1111
1112         struts = g_new0(Strut, screen_num_monitors);
1113
1114         /* calc the xinerama areas */
1115         for (x = 0; x < screen_num_monitors; ++x) {
1116             area[i][x] = monitor_area[x];
1117             if (x == 0) {
1118                 l = monitor_area[x].x;
1119                 t = monitor_area[x].y;
1120                 r = monitor_area[x].x + monitor_area[x].width - 1;
1121                 b = monitor_area[x].y + monitor_area[x].height - 1;
1122             } else {
1123                 l = MIN(l, monitor_area[x].x);
1124                 t = MIN(t, monitor_area[x].y);
1125                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1126                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1127             }
1128         }
1129         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1130
1131         /* apply the struts */
1132
1133         /* find the left-most xin heads, i do this in 2 loops :| */
1134         o = area[i][0].x;
1135         for (x = 1; x < screen_num_monitors; ++x)
1136             o = MIN(o, area[i][x].x);
1137
1138         for (x = 0; x < screen_num_monitors; ++x) {
1139             for (it = client_list; it; it = g_list_next(it)) {
1140                 ObClient *c = it->data;
1141                 screen_area_add_strut_left(&c->strut,
1142                                            &monitor_area[x],
1143                                            o + c->strut.left - area[i][x].x,
1144                                            &struts[x]);
1145             }
1146             screen_area_add_strut_left(&dock_strut,
1147                                        &monitor_area[x],
1148                                        o + dock_strut.left - area[i][x].x,
1149                                        &struts[x]);
1150
1151             area[i][x].x += struts[x].left;
1152             area[i][x].width -= struts[x].left;
1153         }
1154
1155         /* find the top-most xin heads, i do this in 2 loops :| */
1156         o = area[i][0].y;
1157         for (x = 1; x < screen_num_monitors; ++x)
1158             o = MIN(o, area[i][x].y);
1159
1160         for (x = 0; x < screen_num_monitors; ++x) {
1161             for (it = client_list; it; it = g_list_next(it)) {
1162                 ObClient *c = it->data;
1163                 screen_area_add_strut_top(&c->strut,
1164                                            &monitor_area[x],
1165                                            o + c->strut.top - area[i][x].y,
1166                                            &struts[x]);
1167             }
1168             screen_area_add_strut_top(&dock_strut,
1169                                       &monitor_area[x],
1170                                       o + dock_strut.top - area[i][x].y,
1171                                       &struts[x]);
1172
1173             area[i][x].y += struts[x].top;
1174             area[i][x].height -= struts[x].top;
1175         }
1176
1177         /* find the right-most xin heads, i do this in 2 loops :| */
1178         o = area[i][0].x + area[i][0].width - 1;
1179         for (x = 1; x < screen_num_monitors; ++x)
1180             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1181
1182         for (x = 0; x < screen_num_monitors; ++x) {
1183             for (it = client_list; it; it = g_list_next(it)) {
1184                 ObClient *c = it->data;
1185                 screen_area_add_strut_right(&c->strut,
1186                                            &monitor_area[x],
1187                                            (area[i][x].x +
1188                                             area[i][x].width - 1) -
1189                                             (o - c->strut.right),
1190                                             &struts[x]);
1191             }
1192             screen_area_add_strut_right(&dock_strut,
1193                                         &monitor_area[x],
1194                                         (area[i][x].x +
1195                                          area[i][x].width - 1) -
1196                                         (o - dock_strut.right),
1197                                         &struts[x]);
1198
1199             area[i][x].width -= struts[x].right;
1200         }
1201
1202         /* find the bottom-most xin heads, i do this in 2 loops :| */
1203         o = area[i][0].y + area[i][0].height - 1;
1204         for (x = 1; x < screen_num_monitors; ++x)
1205             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1206
1207         for (x = 0; x < screen_num_monitors; ++x) {
1208             for (it = client_list; it; it = g_list_next(it)) {
1209                 ObClient *c = it->data;
1210                 screen_area_add_strut_bottom(&c->strut,
1211                                              &monitor_area[x],
1212                                              (area[i][x].y +
1213                                               area[i][x].height - 1) - \
1214                                              (o - c->strut.bottom),
1215                                              &struts[x]);
1216             }
1217             screen_area_add_strut_bottom(&dock_strut,
1218                                          &monitor_area[x],
1219                                          (area[i][x].y +
1220                                           area[i][x].height - 1) - \
1221                                          (o - dock_strut.bottom),
1222                                          &struts[x]);
1223
1224             area[i][x].height -= struts[x].bottom;
1225         }
1226
1227         l = RECT_LEFT(area[i][0]);
1228         t = RECT_TOP(area[i][0]);
1229         r = RECT_RIGHT(area[i][0]);
1230         b = RECT_BOTTOM(area[i][0]);
1231         for (x = 1; x < screen_num_monitors; ++x) {
1232             l = MIN(l, RECT_LEFT(area[i][x]));
1233             t = MIN(l, RECT_TOP(area[i][x]));
1234             r = MAX(r, RECT_RIGHT(area[i][x]));
1235             b = MAX(b, RECT_BOTTOM(area[i][x]));
1236         }
1237         RECT_SET(area[i][screen_num_monitors], l, t,
1238                  r - l + 1, b - t + 1);
1239
1240         /* XXX optimize when this is run? */
1241
1242         /* the area has changed, adjust all the maximized 
1243            windows */
1244         for (it = client_list; it; it = g_list_next(it)) {
1245             ObClient *c = it->data; 
1246             if (i < screen_num_desktops) {
1247                 if (c->desktop == i)
1248                     client_reconfigure(c);
1249             } else if (c->desktop == DESKTOP_ALL)
1250                 client_reconfigure(c);
1251         }
1252         if (i < screen_num_desktops) {
1253             /* don't set these for the 'all desktops' area */
1254             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1255             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1256             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1257             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1258         }
1259
1260         g_free(struts);
1261     }
1262
1263     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1264                 dims, 4 * screen_num_desktops);
1265
1266     g_free(dims);
1267 }
1268
1269 Rect *screen_area(guint desktop)
1270 {
1271     return screen_area_monitor(desktop, screen_num_monitors);
1272 }
1273
1274 Rect *screen_area_monitor(guint desktop, guint head)
1275 {
1276     if (head > screen_num_monitors)
1277         return NULL;
1278     if (desktop >= screen_num_desktops) {
1279         if (desktop == DESKTOP_ALL)
1280             return &area[screen_num_desktops][head];
1281         return NULL;
1282     }
1283     return &area[desktop][head];
1284 }
1285
1286 guint screen_find_monitor(Rect *search)
1287 {
1288     guint i;
1289     guint most = 0;
1290     guint mostv = 0;
1291
1292     for (i = 0; i < screen_num_monitors; ++i) {
1293         Rect *area = screen_physical_area_monitor(i);
1294         if (RECT_INTERSECTS_RECT(*area, *search)) {
1295             Rect r;
1296             guint v;
1297
1298             RECT_SET_INTERSECTION(r, *area, *search);
1299             v = r.width * r.height;
1300
1301             if (v > mostv) {
1302                 mostv = v;
1303                 most = i;
1304             }
1305         }
1306     }
1307     return most;
1308 }
1309
1310 Rect *screen_physical_area()
1311 {
1312     return screen_physical_area_monitor(screen_num_monitors);
1313 }
1314
1315 Rect *screen_physical_area_monitor(guint head)
1316 {
1317     if (head > screen_num_monitors)
1318         return NULL;
1319     return &monitor_area[head];
1320 }
1321
1322 void screen_set_root_cursor()
1323 {
1324     if (sn_app_starting())
1325         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1326                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1327     else
1328         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1329                       ob_cursor(OB_CURSOR_POINTER));
1330 }
1331
1332 gboolean screen_pointer_pos(gint *x, gint *y)
1333 {
1334     Window w;
1335     gint i;
1336     guint u;
1337     gboolean ret;
1338
1339     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1340                           &w, &w, x, y, &i, &i, &u);
1341     if (!ret) {
1342         for (i = 0; i < ScreenCount(ob_display); ++i)
1343             if (i != ob_screen)
1344                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1345                                   &w, &w, x, y, &i, &i, &u))
1346                     break;
1347     }
1348     return ret;
1349 }