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