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