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