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