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