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