Make clang happier
[dana/openbox.git] / openbox / screen.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    screen.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "grab.h"
24 #include "startupnotify.h"
25 #include "moveresize.h"
26 #include "config.h"
27 #include "screen.h"
28 #include "client.h"
29 #include "session.h"
30 #include "frame.h"
31 #include "event.h"
32 #include "focus.h"
33 #include "popup.h"
34 #include "hooks.h"
35 #include "render/render.h"
36 #include "gettext.h"
37 #include "obt/display.h"
38 #include "obt/prop.h"
39 #include "obt/mainloop.h"
40
41 #include <X11/Xlib.h>
42 #ifdef HAVE_UNISTD_H
43 #  include <sys/types.h>
44 #  include <unistd.h>
45 #endif
46 #include <assert.h>
47
48 /*! The event mask to grab on the root window */
49 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
50                         EnterWindowMask | LeaveWindowMask | \
51                         SubstructureRedirectMask | FocusChangeMask | \
52                         ButtonPressMask | ButtonReleaseMask)
53
54 static gboolean screen_validate_layout(ObDesktopLayout *l);
55 static gboolean replace_wm(void);
56 static void     screen_tell_ksplash(void);
57 static void     screen_fallback_focus(void);
58
59 guint           screen_num_desktops;
60 guint           screen_num_monitors;
61 guint           screen_desktop;
62 guint           screen_last_desktop;
63 gboolean        screen_showing_desktop;
64 ObDesktopLayout screen_desktop_layout;
65 gchar         **screen_desktop_names;
66 Window          screen_support_win;
67 Time            screen_desktop_user_time = CurrentTime;
68
69 static Size     screen_physical_size;
70 static guint    screen_old_desktop;
71 static gboolean screen_desktop_timeout = TRUE;
72 /*! An array of desktops, holding an array of areas per monitor */
73 static Rect  *monitor_area = NULL;
74 /*! An array of desktops, holding an array of struts */
75 static GSList *struts_top = NULL;
76 static GSList *struts_left = NULL;
77 static GSList *struts_right = NULL;
78 static GSList *struts_bottom = NULL;
79
80 static ObPagerPopup **desktop_popup;
81
82 /*! The number of microseconds that you need to be on a desktop before it will
83   replace the remembered "last desktop" */
84 #define REMEMBER_LAST_DESKTOP_TIME 750000
85
86 static gboolean replace_wm(void)
87 {
88     gchar *wm_sn;
89     Atom wm_sn_atom;
90     Window current_wm_sn_owner;
91     Time timestamp;
92
93     wm_sn = g_strdup_printf("WM_S%d", ob_screen);
94     wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
95     g_free(wm_sn);
96
97     current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
98     if (current_wm_sn_owner == screen_support_win)
99         current_wm_sn_owner = None;
100     if (current_wm_sn_owner) {
101         if (!ob_replace_wm) {
102             g_message(_("A window manager is already running on screen %d"),
103                       ob_screen);
104             return FALSE;
105         }
106         obt_display_ignore_errors(TRUE);
107
108         /* We want to find out when the current selection owner dies */
109         XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
110         XSync(obt_display, FALSE);
111
112         obt_display_ignore_errors(FALSE);
113         if (obt_display_error_occured)
114             current_wm_sn_owner = None;
115     }
116
117     timestamp = event_get_server_time();
118
119     XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
120                        timestamp);
121
122     if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
123         g_message(_("Could not acquire window manager selection on screen %d"),
124                   ob_screen);
125         return FALSE;
126     }
127
128     /* Wait for old window manager to go away */
129     if (current_wm_sn_owner) {
130       XEvent event;
131       gulong wait = 0;
132       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
133
134       while (wait < timeout) {
135           if (XCheckWindowEvent(obt_display, current_wm_sn_owner,
136                                 StructureNotifyMask, &event) &&
137               event.type == DestroyNotify)
138               break;
139           g_usleep(G_USEC_PER_SEC / 10);
140           wait += G_USEC_PER_SEC / 10;
141       }
142
143       if (wait >= timeout) {
144           g_message(_("The WM on screen %d is not exiting"), ob_screen);
145           return FALSE;
146       }
147     }
148
149     /* Send client message indicating that we are now the WM */
150     obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
151                      timestamp, wm_sn_atom, screen_support_win, 0, 0,
152                      SubstructureNotifyMask);
153
154     return TRUE;
155 }
156
157 gboolean screen_annex(void)
158 {
159     XSetWindowAttributes attrib;
160     pid_t pid;
161     gint i, num_support;
162     gulong *supported;
163
164     /* create the netwm support window */
165     attrib.override_redirect = TRUE;
166     attrib.event_mask = PropertyChangeMask;
167     screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
168                                        -100, -100, 1, 1, 0,
169                                        CopyFromParent, InputOutput,
170                                        CopyFromParent,
171                                        CWEventMask | CWOverrideRedirect,
172                                        &attrib);
173     XMapWindow(obt_display, screen_support_win);
174     XLowerWindow(obt_display, screen_support_win);
175
176     if (!replace_wm()) {
177         XDestroyWindow(obt_display, screen_support_win);
178         return FALSE;
179     }
180
181     obt_display_ignore_errors(TRUE);
182     XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
183     obt_display_ignore_errors(FALSE);
184     if (obt_display_error_occured) {
185         g_message(_("A window manager is already running on screen %d"),
186                   ob_screen);
187
188         XDestroyWindow(obt_display, screen_support_win);
189         return FALSE;
190     }
191
192     screen_set_root_cursor();
193
194     /* set the OPENBOX_PID hint */
195     pid = getpid();
196     OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
197
198     /* set supporting window */
199     OBT_PROP_SET32(obt_root(ob_screen),
200                    NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
201
202     /* set properties on the supporting window */
203     OBT_PROP_SETS(screen_support_win, NET_WM_NAME, utf8, "Openbox");
204     OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
205                    WINDOW, screen_support_win);
206
207     /* set the _NET_SUPPORTED_ATOMS hint */
208
209     /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
210     num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
211     i = 0;
212     supported = g_new(gulong, num_support);
213     supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
214     supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
215     supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
216     supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
217     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
218     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
219     supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
220     supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
221     supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
222     supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
223     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
224     supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
225     supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
226     supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
227     supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
228     supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
229     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
230     supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
231     supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
232     supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
233     supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
234     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
235     supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
236     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
237     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
238     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
239     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
240     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
241     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
242     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
243     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
244     supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
245     supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
246     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
247     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
248     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
249     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
250     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
251     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
252     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
253     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
254     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
255     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
256     supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
257     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
258     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
259     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
260     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
261     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
262     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
263     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
264     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
265     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
266     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
267     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
268     supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
269     supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
270     supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
271     supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
272 /*
273     supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
274 */
275     supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
276     supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
277     supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
278     supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
279 #ifdef SYNC
280     supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
281     supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
282 #endif
283     supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
284     supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
285
286     supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
287     supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
288     supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
289
290     supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
291     supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
292     supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
293     supported[i++] = OBT_PROP_ATOM(OB_THEME);
294     supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
295     supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
296     g_assert(i == num_support);
297
298     OBT_PROP_SETA32(obt_root(ob_screen),
299                     NET_SUPPORTED, ATOM, supported, num_support);
300     g_free(supported);
301
302     screen_tell_ksplash();
303
304     return TRUE;
305 }
306
307 static void screen_tell_ksplash(void)
308 {
309     XEvent e;
310     char **argv;
311
312     argv = g_new(gchar*, 6);
313     argv[0] = g_strdup("dcop");
314     argv[1] = g_strdup("ksplash");
315     argv[2] = g_strdup("ksplash");
316     argv[3] = g_strdup("upAndRunning(QString)");
317     argv[4] = g_strdup("wm started");
318     argv[5] = NULL;
319
320     /* tell ksplash through the dcop server command line interface */
321     g_spawn_async(NULL, argv, NULL,
322                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
323                   G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
324                   NULL, NULL, NULL, NULL);
325     g_strfreev(argv);
326
327     /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
328        hear it anyways. perhaps it is for old ksplash. or new ksplash. or
329        something. oh well. */
330     e.xclient.type = ClientMessage;
331     e.xclient.display = obt_display;
332     e.xclient.window = obt_root(ob_screen);
333     e.xclient.message_type =
334         XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
335     e.xclient.format = 8;
336     strcpy(e.xclient.data.b, "wm started");
337     XSendEvent(obt_display, obt_root(ob_screen),
338                False, SubstructureNotifyMask, &e);
339 }
340
341 void screen_startup(gboolean reconfig)
342 {
343     gchar **names = NULL;
344     guint32 d;
345     gboolean namesexist = FALSE;
346
347     if (reconfig) {
348         guint i;
349         desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
350         for (i = 0; i < screen_num_monitors; i++) {
351             desktop_popup[i] = pager_popup_new();
352             pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
353
354             /* update the pager popup's width */
355             pager_popup_text_width_to_strings(desktop_popup[i],
356                                               screen_desktop_names,
357                                               screen_num_desktops);
358         }
359
360         return;
361     } else {
362         desktop_popup = NULL;
363     }
364
365     /* get the initial size */
366     screen_resize();
367
368     /* have names already been set for the desktops? */
369     if (OBT_PROP_GETSS(obt_root(ob_screen), NET_DESKTOP_NAMES, utf8, &names)) {
370         g_strfreev(names);
371         namesexist = TRUE;
372     }
373
374     /* if names don't exist and we have session names, set those.
375        do this stuff BEFORE setting the number of desktops, because that
376        will create default names for them
377     */
378     if (!namesexist && session_desktop_names != NULL) {
379         guint i, numnames;
380         GSList *it;
381
382         /* get the desktop names */
383         numnames = g_slist_length(session_desktop_names);
384         names = g_new(gchar*, numnames + 1);
385         names[numnames] = NULL;
386         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
387             names[i] = g_strdup(it->data);
388
389         /* set the root window property */
390         OBT_PROP_SETSS(obt_root(ob_screen),
391                        NET_DESKTOP_NAMES, utf8, (const gchar**)names);
392
393         g_strfreev(names);
394     }
395
396     /* set the number of desktops, if it's not already set.
397
398        this will also set the default names from the config file up for
399        desktops that don't have names yet */
400     screen_num_desktops = 0;
401     if (OBT_PROP_GET32(obt_root(ob_screen),
402                        NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
403     {
404         if (d != config_desktops_num) {
405             /* TRANSLATORS: If you need to specify a different order of the
406                arguments, you can use %1$d for the first one and %2$d for the
407                second one. For example,
408                "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
409             g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d.  Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d.  Overriding the Openbox configuration.", config_desktops_num),
410                       config_desktops_num, d);
411         }
412         screen_set_num_desktops(d);
413     }
414     /* restore from session if possible */
415     else if (session_num_desktops)
416         screen_set_num_desktops(session_num_desktops);
417     else
418         screen_set_num_desktops(config_desktops_num);
419
420     screen_desktop = screen_num_desktops;  /* something invalid */
421     /* start on the current desktop when a wm was already running */
422     if (OBT_PROP_GET32(obt_root(ob_screen),
423                        NET_CURRENT_DESKTOP, CARDINAL, &d) &&
424         d < screen_num_desktops)
425     {
426         screen_set_desktop(d, FALSE);
427     } else if (session_desktop >= 0)
428         screen_set_desktop(MIN((guint)session_desktop,
429                                screen_num_desktops), FALSE);
430     else
431         screen_set_desktop(MIN(config_screen_firstdesk,
432                                screen_num_desktops) - 1, FALSE);
433     screen_last_desktop = screen_desktop;
434
435     /* don't start in showing-desktop mode */
436     screen_showing_desktop = FALSE;
437     OBT_PROP_SET32(obt_root(ob_screen),
438                    NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
439
440     if (session_desktop_layout_present &&
441         screen_validate_layout(&session_desktop_layout))
442     {
443         screen_desktop_layout = session_desktop_layout;
444     }
445     else
446         screen_update_layout();
447 }
448
449 void screen_shutdown(gboolean reconfig)
450 {
451     guint i;
452
453     for (i = 0; i < screen_num_monitors; i++) {
454         pager_popup_free(desktop_popup[i]);
455     }
456     g_free(desktop_popup);
457
458     if (reconfig)
459         return;
460
461     XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
462
463     /* we're not running here no more! */
464     OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
465     /* not without us */
466     OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
467     /* don't keep this mode */
468     OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
469
470     XDestroyWindow(obt_display, screen_support_win);
471
472     g_strfreev(screen_desktop_names);
473     screen_desktop_names = NULL;
474 }
475
476 void screen_resize(void)
477 {
478     static gint oldw = 0, oldh = 0;
479     gint w, h;
480     GList *it;
481     gulong geometry[2];
482
483     w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
484     h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
485
486     if (w == oldw && h == oldh) return;
487
488     oldw = w; oldh = h;
489
490     /* Set the _NET_DESKTOP_GEOMETRY hint */
491     screen_physical_size.width = geometry[0] = w;
492     screen_physical_size.height = geometry[1] = h;
493     OBT_PROP_SETA32(obt_root(ob_screen),
494                     NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
495
496     if (ob_state() != OB_STATE_RUNNING)
497         return;
498
499     screen_update_areas();
500     dock_configure();
501
502     for (it = client_list; it; it = g_list_next(it))
503         client_move_onscreen(it->data, FALSE);
504 }
505
506 void screen_set_num_desktops(guint num)
507 {
508     gulong *viewport;
509     GList *it, *stacking_copy;
510
511     g_assert(num > 0);
512
513     if (screen_num_desktops == num) return;
514
515     screen_num_desktops = num;
516     OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
517
518     /* set the viewport hint */
519     viewport = g_new0(gulong, num * 2);
520     OBT_PROP_SETA32(obt_root(ob_screen),
521                     NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
522     g_free(viewport);
523
524     /* the number of rows/columns will differ */
525     screen_update_layout();
526
527     /* move windows on desktops that will no longer exist!
528        make a copy of the list cuz we're changing it */
529     stacking_copy = g_list_copy(stacking_list);
530     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
531         if (WINDOW_IS_CLIENT(it->data)) {
532             ObClient *c = it->data;
533             if (c->desktop != DESKTOP_ALL && c->desktop >= num)
534                 client_set_desktop(c, num - 1, FALSE, TRUE);
535             /* raise all the windows that are on the current desktop which
536                is being merged */
537             else if (screen_desktop == num - 1 &&
538                      (c->desktop == DESKTOP_ALL ||
539                       c->desktop == screen_desktop))
540                 stacking_raise(CLIENT_AS_WINDOW(c));
541         }
542     }
543     g_list_free(stacking_copy);
544
545     /* change our struts/area to match (after moving windows) */
546     screen_update_areas();
547
548     /* may be some unnamed desktops that we need to fill in with names
549        (after updating the areas so the popup can resize) */
550     screen_update_desktop_names();
551
552     /* change our desktop if we're on one that no longer exists! */
553     if (screen_desktop >= screen_num_desktops)
554         screen_set_desktop(num - 1, TRUE);
555 }
556
557 static void screen_fallback_focus(void)
558 {
559     ObClient *c;
560     gboolean allow_omni;
561
562     /* only allow omnipresent windows to get focus on desktop change if
563        an omnipresent window is already focused (it'll keep focus probably, but
564        maybe not depending on mouse-focus options) */
565     allow_omni = focus_client && (client_normal(focus_client) &&
566                                   focus_client->desktop == DESKTOP_ALL);
567
568     /* the client moved there already so don't move focus. prevent flicker
569        on sendtodesktop + follow */
570     if (focus_client && focus_client->desktop == screen_desktop)
571         return;
572
573     /* have to try focus here because when you leave an empty desktop
574        there is no focus out to watch for. also, we have different rules
575        here. we always allow it to look under the mouse pointer if
576        config_focus_last is FALSE
577
578        do this before hiding the windows so if helper windows are coming
579        with us, they don't get hidden
580     */
581     if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
582                             !allow_omni)))
583     {
584         /* only do the flicker reducing stuff ahead of time if we are going
585            to call xsetinputfocus on the window ourselves. otherwise there is
586            no guarantee the window will actually take focus.. */
587         if (c->can_focus) {
588             /* reduce flicker by hiliting now rather than waiting for the
589                server FocusIn event */
590             frame_adjust_focus(c->frame, TRUE);
591             /* do this here so that if you switch desktops to a window with
592                helper windows then the helper windows won't flash */
593             client_bring_helper_windows(c);
594         }
595     }
596 }
597
598 static gboolean last_desktop_func(gpointer data)
599 {
600     screen_desktop_timeout = TRUE;
601     return FALSE;
602 }
603
604 void screen_set_desktop(guint num, gboolean dofocus)
605 {
606     GList *it;
607     guint previous;
608     gulong ignore_start;
609
610     g_assert(num < screen_num_desktops);
611
612     previous = screen_desktop;
613     screen_desktop = num;
614
615     if (previous == num) return;
616
617     OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
618
619     /* This whole thing decides when/how to save the screen_last_desktop so
620        that it can be restored later if you want */
621     if (screen_desktop_timeout) {
622         /* If screen_desktop_timeout is true, then we've been on this desktop
623            long enough and we can save it as the last desktop. */
624
625         if (screen_last_desktop == previous)
626             /* this is the startup state only */
627             screen_old_desktop = screen_desktop;
628         else {
629             /* save the "last desktop" as the "old desktop" */
630             screen_old_desktop = screen_last_desktop;
631             /* save the desktop we're coming from as the "last desktop" */
632             screen_last_desktop = previous;
633         }
634     }
635     else {
636         /* If screen_desktop_timeout is false, then we just got to this desktop
637            and we are moving away again. */
638
639         if (screen_desktop == screen_last_desktop) {
640             /* If we are moving to the "last desktop" .. */
641             if (previous == screen_old_desktop) {
642                 /* .. from the "old desktop", change the last desktop to
643                    be where we are coming from */
644                 screen_last_desktop = screen_old_desktop;
645             }
646             else if (screen_last_desktop == screen_old_desktop) {
647                 /* .. and also to the "old desktop", change the "last
648                    desktop" to be where we are coming from */
649                 screen_last_desktop = previous;
650             }
651             else {
652                 /* .. from some other desktop, then set the "last desktop" to
653                    be the saved "old desktop", i.e. where we were before the
654                    "last desktop" */
655                 screen_last_desktop = screen_old_desktop;
656             }
657         }
658         else {
659             /* If we are moving to any desktop besides the "last desktop"..
660                (this is the normal case) */
661             if (screen_desktop == screen_old_desktop) {
662                 /* If moving to the "old desktop", which is not the
663                    "last desktop", don't save anything */
664             }
665             else if (previous == screen_old_desktop) {
666                 /* If moving from the "old desktop", and not to the
667                    "last desktop", don't save anything */
668             }
669             else if (screen_last_desktop == screen_old_desktop) {
670                 /* If the "last desktop" is the same as "old desktop" and
671                    you're not moving to the "last desktop" then save where
672                    we're coming from as the "last desktop" */
673                 screen_last_desktop = previous;
674             }
675             else {
676                 /* If the "last desktop" is different from the "old desktop"
677                    and you're not moving to the "last desktop", then don't save
678                    anything */
679             }
680         }
681     }
682     screen_desktop_timeout = FALSE;
683     obt_main_loop_timeout_remove(ob_main_loop, last_desktop_func);
684     obt_main_loop_timeout_add(ob_main_loop, REMEMBER_LAST_DESKTOP_TIME,
685                               last_desktop_func, NULL, NULL, NULL);
686
687     ob_debug("Moving to desktop %d", num+1);
688
689     if (ob_state() == OB_STATE_RUNNING)
690         screen_show_desktop_popup(screen_desktop);
691
692     /* ignore enter events caused by the move */
693     ignore_start = event_start_ignore_all_enters();
694
695     if (moveresize_client)
696         client_set_desktop(moveresize_client, num, TRUE, FALSE);
697
698     /* show windows before hiding the rest to lessen the enter/leave events */
699
700     /* show windows from top to bottom */
701     for (it = stacking_list; it; it = g_list_next(it)) {
702         if (WINDOW_IS_CLIENT(it->data)) {
703             ObClient *c = it->data;
704             client_show(c);
705         }
706     }
707
708     if (dofocus) screen_fallback_focus();
709
710     /* hide windows from bottom to top */
711     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
712         if (WINDOW_IS_CLIENT(it->data)) {
713             ObClient *c = it->data;
714             client_hide(c);
715         }
716     }
717
718     event_end_ignore_all_enters(ignore_start);
719
720     if (event_curtime != CurrentTime)
721         screen_desktop_user_time = event_curtime;
722
723     hooks_queue(OB_HOOK_SCREEN_DESK_CHANGE, NULL);
724 }
725
726 void screen_add_desktop(gboolean current)
727 {
728     gulong ignore_start;
729
730     /* ignore enter events caused by this */
731     ignore_start = event_start_ignore_all_enters();
732
733     screen_set_num_desktops(screen_num_desktops+1);
734
735     /* move all the clients over */
736     if (current) {
737         GList *it;
738
739         for (it = client_list; it; it = g_list_next(it)) {
740             ObClient *c = it->data;
741             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
742                 /* don't move direct children, they'll be moved with their
743                    parent - which will have to be on the same desktop */
744                 !client_direct_parent(c))
745             {
746                 ob_debug("moving window %s", c->title);
747                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
748             }
749         }
750     }
751
752     event_end_ignore_all_enters(ignore_start);
753 }
754
755 void screen_remove_desktop(gboolean current)
756 {
757     guint rmdesktop, movedesktop;
758     GList *it, *stacking_copy;
759     gulong ignore_start;
760
761     if (screen_num_desktops <= 1) return;
762
763     /* ignore enter events caused by this */
764     ignore_start = event_start_ignore_all_enters();
765
766     /* what desktop are we removing and moving to? */
767     if (current)
768         rmdesktop = screen_desktop;
769     else
770         rmdesktop = screen_num_desktops - 1;
771     if (rmdesktop < screen_num_desktops - 1)
772         movedesktop = rmdesktop + 1;
773     else
774         movedesktop = rmdesktop;
775
776     /* make a copy of the list cuz we're changing it */
777     stacking_copy = g_list_copy(stacking_list);
778     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
779         if (WINDOW_IS_CLIENT(it->data)) {
780             ObClient *c = it->data;
781             guint d = c->desktop;
782             if (d != DESKTOP_ALL && d >= movedesktop &&
783                 /* don't move direct children, they'll be moved with their
784                    parent - which will have to be on the same desktop */
785                 !client_direct_parent(c))
786             {
787                 ob_debug("moving window %s", c->title);
788                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
789             }
790             /* raise all the windows that are on the current desktop which
791                is being merged */
792             if ((screen_desktop == rmdesktop - 1 ||
793                  screen_desktop == rmdesktop) &&
794                 (d == DESKTOP_ALL || d == screen_desktop))
795             {
796                 stacking_raise(CLIENT_AS_WINDOW(c));
797                 ob_debug("raising window %s", c->title);
798             }
799         }
800     }
801     g_list_free(stacking_copy);
802
803     /* fallback focus like we're changing desktops */
804     if (screen_desktop < screen_num_desktops - 1) {
805         screen_fallback_focus();
806         ob_debug("fake desktop change");
807     }
808
809     screen_set_num_desktops(screen_num_desktops-1);
810
811     event_end_ignore_all_enters(ignore_start);
812 }
813
814 static void get_row_col(guint d, guint *r, guint *c)
815 {
816     switch (screen_desktop_layout.orientation) {
817     case OB_ORIENTATION_HORZ:
818         switch (screen_desktop_layout.start_corner) {
819         case OB_CORNER_TOPLEFT:
820             *r = d / screen_desktop_layout.columns;
821             *c = d % screen_desktop_layout.columns;
822             break;
823         case OB_CORNER_BOTTOMLEFT:
824             *r = screen_desktop_layout.rows - 1 -
825                 d / screen_desktop_layout.columns;
826             *c = d % screen_desktop_layout.columns;
827             break;
828         case OB_CORNER_TOPRIGHT:
829             *r = d / screen_desktop_layout.columns;
830             *c = screen_desktop_layout.columns - 1 -
831                 d % screen_desktop_layout.columns;
832             break;
833         case OB_CORNER_BOTTOMRIGHT:
834             *r = screen_desktop_layout.rows - 1 -
835                 d / screen_desktop_layout.columns;
836             *c = screen_desktop_layout.columns - 1 -
837                 d % screen_desktop_layout.columns;
838             break;
839         }
840         break;
841     case OB_ORIENTATION_VERT:
842         switch (screen_desktop_layout.start_corner) {
843         case OB_CORNER_TOPLEFT:
844             *r = d % screen_desktop_layout.rows;
845             *c = d / screen_desktop_layout.rows;
846             break;
847         case OB_CORNER_BOTTOMLEFT:
848             *r = screen_desktop_layout.rows - 1 -
849                 d % screen_desktop_layout.rows;
850             *c = d / screen_desktop_layout.rows;
851             break;
852         case OB_CORNER_TOPRIGHT:
853             *r = d % screen_desktop_layout.rows;
854             *c = screen_desktop_layout.columns - 1 -
855                 d / screen_desktop_layout.rows;
856             break;
857         case OB_CORNER_BOTTOMRIGHT:
858             *r = screen_desktop_layout.rows - 1 -
859                 d % screen_desktop_layout.rows;
860             *c = screen_desktop_layout.columns - 1 -
861                 d / screen_desktop_layout.rows;
862             break;
863         }
864         break;
865     }
866 }
867
868 static guint translate_row_col(guint r, guint c)
869 {
870     switch (screen_desktop_layout.orientation) {
871     case OB_ORIENTATION_HORZ:
872         switch (screen_desktop_layout.start_corner) {
873         case OB_CORNER_TOPLEFT:
874             return r % screen_desktop_layout.rows *
875                 screen_desktop_layout.columns +
876                 c % screen_desktop_layout.columns;
877         case OB_CORNER_BOTTOMLEFT:
878             return (screen_desktop_layout.rows - 1 -
879                     r % screen_desktop_layout.rows) *
880                 screen_desktop_layout.columns +
881                 c % screen_desktop_layout.columns;
882         case OB_CORNER_TOPRIGHT:
883             return r % screen_desktop_layout.rows *
884                 screen_desktop_layout.columns +
885                 (screen_desktop_layout.columns - 1 -
886                  c % screen_desktop_layout.columns);
887         case OB_CORNER_BOTTOMRIGHT:
888             return (screen_desktop_layout.rows - 1 -
889                     r % screen_desktop_layout.rows) *
890                 screen_desktop_layout.columns +
891                 (screen_desktop_layout.columns - 1 -
892                  c % screen_desktop_layout.columns);
893         }
894     case OB_ORIENTATION_VERT:
895         switch (screen_desktop_layout.start_corner) {
896         case OB_CORNER_TOPLEFT:
897             return c % screen_desktop_layout.columns *
898                 screen_desktop_layout.rows +
899                 r % screen_desktop_layout.rows;
900         case OB_CORNER_BOTTOMLEFT:
901             return c % screen_desktop_layout.columns *
902                 screen_desktop_layout.rows +
903                 (screen_desktop_layout.rows - 1 -
904                  r % screen_desktop_layout.rows);
905         case OB_CORNER_TOPRIGHT:
906             return (screen_desktop_layout.columns - 1 -
907                     c % screen_desktop_layout.columns) *
908                 screen_desktop_layout.rows +
909                 r % screen_desktop_layout.rows;
910         case OB_CORNER_BOTTOMRIGHT:
911             return (screen_desktop_layout.columns - 1 -
912                     c % screen_desktop_layout.columns) *
913                 screen_desktop_layout.rows +
914                 (screen_desktop_layout.rows - 1 -
915                  r % screen_desktop_layout.rows);
916         }
917     }
918     g_assert_not_reached();
919     return 0;
920 }
921
922 static gboolean hide_desktop_popup_func(gpointer data)
923 {
924     guint i;
925
926     for (i = 0; i < screen_num_monitors; i++) {
927         pager_popup_hide(desktop_popup[i]);
928     }
929     return FALSE; /* don't repeat */
930 }
931
932 void screen_show_desktop_popup(guint d)
933 {
934     Rect *a;
935     guint i;
936
937     /* 0 means don't show the popup */
938     if (!config_desktop_popup_time) return;
939
940     for (i = 0; i < screen_num_monitors; i++) {
941         a = screen_physical_area_monitor(i);
942         pager_popup_position(desktop_popup[i], CenterGravity,
943                              a->x + a->width / 2, a->y + a->height / 2);
944         pager_popup_icon_size_multiplier(desktop_popup[i],
945                                          (screen_desktop_layout.columns /
946                                           screen_desktop_layout.rows) / 2,
947                                          (screen_desktop_layout.rows/
948                                           screen_desktop_layout.columns) / 2);
949         pager_popup_max_width(desktop_popup[i],
950                               MAX(a->width/3, POPUP_WIDTH));
951         pager_popup_show(desktop_popup[i], screen_desktop_names[d], d);
952
953         obt_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
954         obt_main_loop_timeout_add(ob_main_loop, config_desktop_popup_time * 1000,
955                                   hide_desktop_popup_func, desktop_popup[i],
956                                   g_direct_equal, NULL);
957         g_free(a);
958     }
959 }
960
961 void screen_hide_desktop_popup(void)
962 {
963     guint i;
964
965     for (i = 0; i < screen_num_monitors; i++) {
966         obt_main_loop_timeout_remove_data(ob_main_loop, hide_desktop_popup_func,
967                                           desktop_popup[i], FALSE);
968         pager_popup_hide(desktop_popup[i]);
969     }
970 }
971
972 guint screen_find_desktop(guint from, ObDirection dir,
973                           gboolean wrap, gboolean linear)
974 {
975     guint r, c;
976     guint d;
977
978     d = from;
979     get_row_col(d, &r, &c);
980     if (linear) {
981         switch (dir) {
982         case OB_DIRECTION_EAST:
983             if (d < screen_num_desktops - 1)
984                 ++d;
985             else if (wrap)
986                 d = 0;
987             else
988                 return from;
989             break;
990         case OB_DIRECTION_WEST:
991             if (d > 0)
992                 --d;
993             else if (wrap)
994                 d = screen_num_desktops - 1;
995             else
996                 return from;
997             break;
998         default:
999             g_assert_not_reached();
1000             return from;
1001         }
1002     } else {
1003         switch (dir) {
1004         case OB_DIRECTION_EAST:
1005             ++c;
1006             if (c >= screen_desktop_layout.columns) {
1007                 if (wrap)
1008                     c = 0;
1009                 else
1010                     return from;
1011             }
1012             d = translate_row_col(r, c);
1013             if (d >= screen_num_desktops) {
1014                 if (wrap)
1015                     ++c;
1016                 else
1017                     return from;
1018             }
1019             break;
1020         case OB_DIRECTION_WEST:
1021             --c;
1022             if (c >= screen_desktop_layout.columns) {
1023                 if (wrap)
1024                     c = screen_desktop_layout.columns - 1;
1025                 else
1026                     return from;
1027             }
1028             d = translate_row_col(r, c);
1029             if (d >= screen_num_desktops) {
1030                 if (wrap)
1031                     --c;
1032                 else
1033                     return from;
1034             }
1035             break;
1036         case OB_DIRECTION_SOUTH:
1037             ++r;
1038             if (r >= screen_desktop_layout.rows) {
1039                 if (wrap)
1040                     r = 0;
1041                 else
1042                     return from;
1043             }
1044             d = translate_row_col(r, c);
1045             if (d >= screen_num_desktops) {
1046                 if (wrap)
1047                     ++r;
1048                 else
1049                     return from;
1050             }
1051             break;
1052         case OB_DIRECTION_NORTH:
1053             --r;
1054             if (r >= screen_desktop_layout.rows) {
1055                 if (wrap)
1056                     r = screen_desktop_layout.rows - 1;
1057                 else
1058                     return from;
1059             }
1060             d = translate_row_col(r, c);
1061             if (d >= screen_num_desktops) {
1062                 if (wrap)
1063                     --r;
1064                 else
1065                     return from;
1066             }
1067             break;
1068         default:
1069             g_assert_not_reached();
1070             return from;
1071         }
1072
1073         d = translate_row_col(r, c);
1074     }
1075     return d;
1076 }
1077
1078 static gboolean screen_validate_layout(ObDesktopLayout *l)
1079 {
1080     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1081         return FALSE;
1082
1083     /* fill in a zero rows/columns */
1084     if (l->columns == 0) {
1085         l->columns = screen_num_desktops / l->rows;
1086         if (l->rows * l->columns < screen_num_desktops)
1087             l->columns++;
1088         if (l->rows * l->columns >= screen_num_desktops + l->columns)
1089             l->rows--;
1090     } else if (l->rows == 0) {
1091         l->rows = screen_num_desktops / l->columns;
1092         if (l->columns * l->rows < screen_num_desktops)
1093             l->rows++;
1094         if (l->columns * l->rows >= screen_num_desktops + l->rows)
1095             l->columns--;
1096     }
1097
1098     /* bounds checking */
1099     if (l->orientation == OB_ORIENTATION_HORZ) {
1100         l->columns = MIN(screen_num_desktops, l->columns);
1101         l->rows = MIN(l->rows,
1102                       (screen_num_desktops + l->columns - 1) / l->columns);
1103         l->columns = screen_num_desktops / l->rows +
1104             !!(screen_num_desktops % l->rows);
1105     } else {
1106         l->rows = MIN(screen_num_desktops, l->rows);
1107         l->columns = MIN(l->columns,
1108                          (screen_num_desktops + l->rows - 1) / l->rows);
1109         l->rows = screen_num_desktops / l->columns +
1110             !!(screen_num_desktops % l->columns);
1111     }
1112     return TRUE;
1113 }
1114
1115 void screen_update_layout(void)
1116
1117 {
1118     ObDesktopLayout l;
1119     guint32 *data;
1120     guint num;
1121
1122     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1123     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1124     screen_desktop_layout.rows = 1;
1125     screen_desktop_layout.columns = screen_num_desktops;
1126
1127     if (OBT_PROP_GETA32(obt_root(ob_screen),
1128                         NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1129         if (num == 3 || num == 4) {
1130
1131             if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1132                 l.orientation = OB_ORIENTATION_VERT;
1133             else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1134                 l.orientation = OB_ORIENTATION_HORZ;
1135             else
1136                 return;
1137
1138             if (num < 4)
1139                 l.start_corner = OB_CORNER_TOPLEFT;
1140             else {
1141                 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1142                     l.start_corner = OB_CORNER_TOPLEFT;
1143                 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1144                     l.start_corner = OB_CORNER_TOPRIGHT;
1145                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1146                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
1147                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1148                     l.start_corner = OB_CORNER_BOTTOMLEFT;
1149                 else
1150                     return;
1151             }
1152
1153             l.columns = data[1];
1154             l.rows = data[2];
1155
1156             if (screen_validate_layout(&l))
1157                 screen_desktop_layout = l;
1158
1159             g_free(data);
1160         }
1161     }
1162 }
1163
1164 void screen_update_desktop_names(void)
1165 {
1166     guint i;
1167
1168     /* empty the array */
1169     g_strfreev(screen_desktop_names);
1170     screen_desktop_names = NULL;
1171
1172     if (OBT_PROP_GETSS(obt_root(ob_screen),
1173                        NET_DESKTOP_NAMES, utf8, &screen_desktop_names))
1174         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1175     else
1176         i = 0;
1177     if (i < screen_num_desktops) {
1178         GSList *it;
1179
1180         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1181                                        screen_num_desktops + 1);
1182         screen_desktop_names[screen_num_desktops] = NULL;
1183
1184         it = g_slist_nth(config_desktops_names, i);
1185
1186         for (; i < screen_num_desktops; ++i) {
1187             if (it && ((char*)it->data)[0]) /* not empty */
1188                 /* use the names from the config file when possible */
1189                 screen_desktop_names[i] = g_strdup(it->data);
1190             else
1191                 /* make up a nice name if it's not though */
1192                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1193                                                           i + 1);
1194             if (it) it = g_slist_next(it);
1195         }
1196
1197         /* if we changed any names, then set the root property so we can
1198            all agree on the names */
1199         OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1200                        utf8, (const gchar**)screen_desktop_names);
1201     }
1202
1203     /* resize the pager for these names */
1204     for (i = 0; i < screen_num_monitors; i++) {
1205         pager_popup_text_width_to_strings(desktop_popup[i],
1206                                           screen_desktop_names,
1207                                           screen_num_desktops);
1208     }
1209 }
1210
1211 void screen_show_desktop(gboolean show, ObClient *show_only)
1212 {
1213     GList *it;
1214
1215     if (show == screen_showing_desktop) return; /* no change */
1216
1217     screen_showing_desktop = show;
1218
1219     if (show) {
1220         /* hide windows bottom to top */
1221         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1222             if (WINDOW_IS_CLIENT(it->data)) {
1223                 ObClient *client = it->data;
1224                 client_showhide(client);
1225             }
1226         }
1227     }
1228     else {
1229         /* restore windows top to bottom */
1230         for (it = stacking_list; it; it = g_list_next(it)) {
1231             if (WINDOW_IS_CLIENT(it->data)) {
1232                 ObClient *client = it->data;
1233                 if (client_should_show(client)) {
1234                     if (!show_only || client == show_only)
1235                         client_show(client);
1236                     else
1237                         client_iconify(client, TRUE, FALSE, TRUE);
1238                 }
1239             }
1240         }
1241     }
1242
1243     if (show) {
1244         /* focus the desktop */
1245         for (it = focus_order; it; it = g_list_next(it)) {
1246             ObClient *c = it->data;
1247             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1248                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1249                 client_focus(it->data))
1250                 break;
1251         }
1252     }
1253     else if (!show_only) {
1254         ObClient *c;
1255
1256         if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1257             /* only do the flicker reducing stuff ahead of time if we are going
1258                to call xsetinputfocus on the window ourselves. otherwise there
1259                is no guarantee the window will actually take focus.. */
1260             if (c->can_focus) {
1261                 /* reduce flicker by hiliting now rather than waiting for the
1262                    server FocusIn event */
1263                 frame_adjust_focus(c->frame, TRUE);
1264             }
1265         }
1266     }
1267
1268     show = !!show; /* make it boolean */
1269     OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
1270 }
1271
1272 void screen_install_colormap(ObClient *client, gboolean install)
1273 {
1274     if (client == NULL || client->colormap == None) {
1275         if (install)
1276             XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1277         else
1278             XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1279     } else {
1280         obt_display_ignore_errors(TRUE);
1281         if (install)
1282             XInstallColormap(obt_display, client->colormap);
1283         else
1284             XUninstallColormap(obt_display, client->colormap);
1285         obt_display_ignore_errors(FALSE);
1286     }
1287 }
1288
1289 #define STRUT_LEFT_ON_MONITOR(s, i) \
1290     (RANGES_INTERSECT(s->left_start, s->left_end - s->left_start + 1, \
1291                       monitor_area[i].y, monitor_area[i].height))
1292 #define STRUT_RIGHT_ON_MONITOR(s, i) \
1293     (RANGES_INTERSECT(s->right_start, s->right_end - s->right_start + 1, \
1294                       monitor_area[i].y, monitor_area[i].height))
1295 #define STRUT_TOP_ON_MONITOR(s, i) \
1296     (RANGES_INTERSECT(s->top_start, s->top_end - s->top_start + 1, \
1297                       monitor_area[i].x, monitor_area[i].width))
1298 #define STRUT_BOTTOM_ON_MONITOR(s, i) \
1299     (RANGES_INTERSECT(s->bottom_start, s->bottom_end - s->bottom_start + 1, \
1300                       monitor_area[i].x, monitor_area[i].width))
1301
1302 typedef struct {
1303     guint desktop;
1304     StrutPartial *strut;
1305 } ObScreenStrut;
1306
1307 #define RESET_STRUT_LIST(sl) \
1308     (g_slist_free(sl), sl = NULL)
1309
1310 #define ADD_STRUT_TO_LIST(sl, d, s) \
1311 { \
1312     ObScreenStrut *ss = g_new(ObScreenStrut, 1); \
1313     ss->desktop = d; \
1314     ss->strut = s;  \
1315     sl = g_slist_prepend(sl, ss); \
1316 }
1317
1318 #define VALIDATE_STRUTS(sl, side, max) \
1319 { \
1320     GSList *it; \
1321     for (it = sl; it; it = g_slist_next(it)) { \
1322       ObScreenStrut *ss = it->data; \
1323       ss->strut->side = MIN(max, ss->strut->side); \
1324     } \
1325 }
1326
1327 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1328 {
1329     guint i;
1330     gint n, l, r, t, b;
1331 #ifdef XINERAMA
1332     XineramaScreenInfo *info;
1333 #endif
1334
1335     if (ob_debug_xinerama) {
1336         gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1337         gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1338         *nxin = 2;
1339         *xin_areas = g_new(Rect, *nxin + 1);
1340         RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1341         RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1342     }
1343 #ifdef XINERAMA
1344     else if (obt_display_extension_xinerama &&
1345              (info = XineramaQueryScreens(obt_display, &n))) {
1346         *nxin = n;
1347         *xin_areas = g_new(Rect, *nxin + 1);
1348         for (i = 0; i < *nxin; ++i)
1349             RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1350                      info[i].width, info[i].height);
1351         XFree(info);
1352     }
1353 #endif
1354     else {
1355         *nxin = 1;
1356         *xin_areas = g_new(Rect, *nxin + 1);
1357         RECT_SET((*xin_areas)[0], 0, 0,
1358                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1359                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1360     }
1361
1362     /* returns one extra with the total area in it */
1363     l = (*xin_areas)[0].x;
1364     t = (*xin_areas)[0].y;
1365     r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1366     b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1367     for (i = 1; i < *nxin; ++i) {
1368         l = MIN(l, (*xin_areas)[i].x);
1369         t = MIN(l, (*xin_areas)[i].y);
1370         r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1371         b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1372     }
1373     RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1374 }
1375
1376 void screen_update_areas(void)
1377 {
1378     guint i, j;
1379     gulong *dims;
1380     GList *it;
1381     GSList *sit;
1382
1383     g_free(monitor_area);
1384     get_xinerama_screens(&monitor_area, &screen_num_monitors);
1385
1386     if (!desktop_popup) {
1387         desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
1388         for (i = 0; i < screen_num_monitors; i++) {
1389             desktop_popup[i] = pager_popup_new();
1390             pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
1391
1392             if (screen_desktop_names)
1393                 /* update the pager popup's width */
1394                 pager_popup_text_width_to_strings(desktop_popup[i],
1395                                                   screen_desktop_names,
1396                                                   screen_num_desktops);
1397         }
1398     }
1399
1400     /* set up the user-specified margins */
1401     config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1402     config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1403     config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1404     config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1405     config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1406     config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1407     config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1408     config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1409
1410     dims = g_new(gulong, 4 * screen_num_desktops * screen_num_monitors);
1411
1412     RESET_STRUT_LIST(struts_left);
1413     RESET_STRUT_LIST(struts_top);
1414     RESET_STRUT_LIST(struts_right);
1415     RESET_STRUT_LIST(struts_bottom);
1416
1417     /* collect the struts */
1418     for (it = client_list; it; it = g_list_next(it)) {
1419         ObClient *c = it->data;
1420         if (c->strut.left)
1421             ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1422         if (c->strut.top)
1423             ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1424         if (c->strut.right)
1425             ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1426         if (c->strut.bottom)
1427             ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1428     }
1429     if (dock_strut.left)
1430         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1431     if (dock_strut.top)
1432         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1433     if (dock_strut.right)
1434         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1435     if (dock_strut.bottom)
1436         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1437
1438     if (config_margins.left)
1439         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1440     if (config_margins.top)
1441         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1442     if (config_margins.right)
1443         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1444     if (config_margins.bottom)
1445         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1446
1447     VALIDATE_STRUTS(struts_left, left,
1448                     monitor_area[screen_num_monitors].width / 2);
1449     VALIDATE_STRUTS(struts_right, right,
1450                     monitor_area[screen_num_monitors].width / 2);
1451     VALIDATE_STRUTS(struts_top, top,
1452                     monitor_area[screen_num_monitors].height / 2);
1453     VALIDATE_STRUTS(struts_bottom, bottom,
1454                     monitor_area[screen_num_monitors].height / 2);
1455
1456     /* set up the work areas to be full screen */
1457     for (i = 0; i < screen_num_monitors; ++i)
1458         for (j = 0; j < screen_num_desktops; ++j) {
1459             dims[(i * screen_num_desktops + j) * 4+0] = monitor_area[i].x;
1460             dims[(i * screen_num_desktops + j) * 4+1] = monitor_area[i].y;
1461             dims[(i * screen_num_desktops + j) * 4+2] = monitor_area[i].width;
1462             dims[(i * screen_num_desktops + j) * 4+3] = monitor_area[i].height;
1463         }
1464
1465     /* calculate the work areas from the struts */
1466     for (i = 0; i < screen_num_monitors; ++i)
1467         for (j = 0; j < screen_num_desktops; ++j) {
1468             gint l = 0, r = 0, t = 0, b = 0;
1469
1470             /* only add the strut to the area if it touches the monitor */
1471
1472             for (sit = struts_left; sit; sit = g_slist_next(sit)) {
1473                 ObScreenStrut *s = sit->data;
1474                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1475                     STRUT_LEFT_ON_MONITOR(s->strut, i))
1476                     l = MAX(l, s->strut->left);
1477             }
1478             for (sit = struts_top; sit; sit = g_slist_next(sit)) {
1479                 ObScreenStrut *s = sit->data;
1480                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1481                     STRUT_TOP_ON_MONITOR(s->strut, i))
1482                     t = MAX(t, s->strut->top);
1483             }
1484             for (sit = struts_right; sit; sit = g_slist_next(sit)) {
1485                 ObScreenStrut *s = sit->data;
1486                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1487                     STRUT_RIGHT_ON_MONITOR(s->strut, i))
1488                     r = MAX(r, s->strut->right);
1489             }
1490             for (sit = struts_bottom; sit; sit = g_slist_next(sit)) {
1491                 ObScreenStrut *s = sit->data;
1492                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1493                     STRUT_BOTTOM_ON_MONITOR(s->strut, i))
1494                     b = MAX(b, s->strut->bottom);
1495             }
1496
1497             if (l) l += RECT_LEFT  (monitor_area[screen_num_monitors])
1498                         - RECT_LEFT  (monitor_area[i]);
1499             if (t) t += RECT_TOP   (monitor_area[screen_num_monitors])
1500                         - RECT_TOP   (monitor_area[i]);
1501             if (r) r -= RECT_RIGHT (monitor_area[screen_num_monitors])
1502                         - RECT_RIGHT (monitor_area[i]);
1503             if (b) b -= RECT_BOTTOM(monitor_area[screen_num_monitors])
1504                         - RECT_BOTTOM(monitor_area[i]);
1505
1506             /* based on these margins, set the work area for the
1507                monitor/desktop */
1508             dims[(i * screen_num_desktops + j) * 4 + 0] += l;
1509             dims[(i * screen_num_desktops + j) * 4 + 1] += t;
1510             dims[(i * screen_num_desktops + j) * 4 + 2] -= l + r;
1511             dims[(i * screen_num_desktops + j) * 4 + 3] -= t + b;
1512         }
1513
1514     /* all the work areas are not used here, only the ones for the first
1515        monitor are */
1516     OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1517                     dims, 4 * screen_num_desktops);
1518
1519     /* the area has changed, adjust all the windows if they need it */
1520     for (it = client_list; it; it = g_list_next(it))
1521         client_reconfigure(it->data, FALSE);
1522
1523     g_free(dims);
1524 }
1525
1526 #if 0
1527 Rect* screen_area_all_monitors(guint desktop)
1528 {
1529     guint i;
1530     Rect *a;
1531
1532     a = screen_area_monitor(desktop, 0);
1533
1534     /* combine all the monitors together */
1535     for (i = 1; i < screen_num_monitors; ++i) {
1536         Rect *m = screen_area_monitor(desktop, i);
1537         gint l, r, t, b;
1538
1539         l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1540         t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1541         r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1542         b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1543
1544         RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1545
1546         g_free(m);
1547     }
1548
1549     return a;
1550 }
1551 #endif
1552
1553 #define STRUT_LEFT_IN_SEARCH(s, search) \
1554     (RANGES_INTERSECT(search->y, search->height, \
1555                       s->left_start, s->left_end - s->left_start + 1))
1556 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1557     (RANGES_INTERSECT(search->y, search->height, \
1558                       s->right_start, s->right_end - s->right_start + 1))
1559 #define STRUT_TOP_IN_SEARCH(s, search) \
1560     (RANGES_INTERSECT(search->x, search->width, \
1561                       s->top_start, s->top_end - s->top_start + 1))
1562 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1563     (RANGES_INTERSECT(search->x, search->width, \
1564                       s->bottom_start, s->bottom_end - s->bottom_start + 1))
1565
1566 #define STRUT_LEFT_IGNORE(s, us, search) \
1567     (head == SCREEN_AREA_ALL_MONITORS && us && \
1568      RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1569 #define STRUT_RIGHT_IGNORE(s, us, search) \
1570     (head == SCREEN_AREA_ALL_MONITORS && us && \
1571      RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1572 #define STRUT_TOP_IGNORE(s, us, search) \
1573     (head == SCREEN_AREA_ALL_MONITORS && us && \
1574      RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1575 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1576     (head == SCREEN_AREA_ALL_MONITORS && us && \
1577      RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1578
1579 Rect* screen_area(guint desktop, guint head, Rect *search)
1580 {
1581     Rect *a;
1582     GSList *it;
1583     gint l, r, t, b;
1584     guint i, d;
1585     gboolean us = search != NULL; /* user provided search */
1586
1587     g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1588     g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1589              head == SCREEN_AREA_ALL_MONITORS);
1590     g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1591
1592     /* find any struts for this monitor
1593        which will be affecting the search area.
1594     */
1595
1596     /* search everything if search is null */
1597     if (!search) {
1598         if (head < screen_num_monitors) search = &monitor_area[head];
1599         else search = &monitor_area[screen_num_monitors];
1600     }
1601     if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1602
1603     /* al is "all left" meaning the furthest left you can get, l is our
1604        "working left" meaning our current strut edge which we're calculating
1605     */
1606
1607     /* only include monitors which the search area lines up with */
1608     if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1609         l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1610         t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1611         r = RECT_LEFT(monitor_area[screen_num_monitors]);
1612         b = RECT_TOP(monitor_area[screen_num_monitors]);
1613         for (i = 0; i < screen_num_monitors; ++i) {
1614             /* add the monitor if applicable */
1615             if (RANGES_INTERSECT(search->x, search->width,
1616                                  monitor_area[i].x, monitor_area[i].width))
1617             {
1618                 t = MIN(t, RECT_TOP(monitor_area[i]));
1619                 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1620             }
1621             if (RANGES_INTERSECT(search->y, search->height,
1622                                  monitor_area[i].y, monitor_area[i].height))
1623             {
1624                 l = MIN(l, RECT_LEFT(monitor_area[i]));
1625                 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1626             }
1627         }
1628     } else {
1629         l = RECT_LEFT(monitor_area[screen_num_monitors]);
1630         t = RECT_TOP(monitor_area[screen_num_monitors]);
1631         r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1632         b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1633     }
1634
1635     for (d = 0; d < screen_num_desktops; ++d) {
1636         if (d != desktop && desktop != DESKTOP_ALL) continue;
1637
1638         for (i = 0; i < screen_num_monitors; ++i) {
1639             if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1640
1641             for (it = struts_left; it; it = g_slist_next(it)) {
1642                 ObScreenStrut *s = it->data;
1643                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1644                     STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1645                     !STRUT_LEFT_IGNORE(s->strut, us, search))
1646                     l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1647                                + s->strut->left);
1648             }
1649             for (it = struts_top; it; it = g_slist_next(it)) {
1650                 ObScreenStrut *s = it->data;
1651                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1652                     STRUT_TOP_IN_SEARCH(s->strut, search) &&
1653                     !STRUT_TOP_IGNORE(s->strut, us, search))
1654                     t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1655                                + s->strut->top);
1656             }
1657             for (it = struts_right; it; it = g_slist_next(it)) {
1658                 ObScreenStrut *s = it->data;
1659                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1660                     STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1661                     !STRUT_RIGHT_IGNORE(s->strut, us, search))
1662                     r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1663                                - s->strut->right);
1664             }
1665             for (it = struts_bottom; it; it = g_slist_next(it)) {
1666                 ObScreenStrut *s = it->data;
1667                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1668                     STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1669                     !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1670                     b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1671                                - s->strut->bottom);
1672             }
1673
1674             /* limit to this monitor */
1675             if (head == i) {
1676                 l = MAX(l, RECT_LEFT(monitor_area[i]));
1677                 t = MAX(t, RECT_TOP(monitor_area[i]));
1678                 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1679                 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1680             }
1681         }
1682     }
1683
1684     a = g_new(Rect, 1);
1685     a->x = l;
1686     a->y = t;
1687     a->width = r - l + 1;
1688     a->height = b - t + 1;
1689     return a;
1690 }
1691
1692 guint screen_find_monitor(Rect *search)
1693 {
1694     guint i;
1695     guint most = screen_num_monitors;
1696     guint mostv = 0;
1697
1698     for (i = 0; i < screen_num_monitors; ++i) {
1699         Rect *area = screen_physical_area_monitor(i);
1700         if (RECT_INTERSECTS_RECT(*area, *search)) {
1701             Rect r;
1702             guint v;
1703
1704             RECT_SET_INTERSECTION(r, *area, *search);
1705             v = r.width * r.height;
1706
1707             if (v > mostv) {
1708                 mostv = v;
1709                 most = i;
1710             }
1711         }
1712         g_free(area);
1713     }
1714     return most;
1715 }
1716
1717 Rect* screen_physical_area_all_monitors(void)
1718 {
1719     return screen_physical_area_monitor(screen_num_monitors);
1720 }
1721
1722 Rect* screen_physical_area_monitor(guint head)
1723 {
1724     Rect *a;
1725     g_assert(head <= screen_num_monitors);
1726
1727     a = g_new(Rect, 1);
1728     *a = monitor_area[head];
1729     return a;
1730 }
1731
1732 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1733 {
1734     g_assert(head <= screen_num_monitors);
1735     g_assert(search);
1736     return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1737 }
1738
1739 Rect* screen_physical_area_active(void)
1740 {
1741     Rect *a;
1742     gint x, y;
1743
1744     if (moveresize_client)
1745         a = screen_physical_area_monitor(client_monitor(focus_client));
1746     else if (focus_client)
1747         a = screen_physical_area_monitor(client_monitor(focus_client));
1748     else {
1749         Rect mon;
1750         if (screen_pointer_pos(&x, &y))
1751             RECT_SET(mon, x, y, 1, 1);
1752         else
1753             RECT_SET(mon, 0, 0, 1, 1);
1754         a = screen_physical_area_monitor(screen_find_monitor(&mon));
1755     }
1756     return a;
1757 }
1758
1759 void screen_set_root_cursor(void)
1760 {
1761     if (sn_app_starting())
1762         XDefineCursor(obt_display, obt_root(ob_screen),
1763                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1764     else
1765         XDefineCursor(obt_display, obt_root(ob_screen),
1766                       ob_cursor(OB_CURSOR_POINTER));
1767 }
1768
1769 gboolean screen_pointer_pos(gint *x, gint *y)
1770 {
1771     Window w;
1772     gint i;
1773     guint u;
1774     gboolean ret;
1775
1776     ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1777                           &w, &w, x, y, &i, &i, &u);
1778     if (!ret) {
1779         for (i = 0; i < ScreenCount(obt_display); ++i)
1780             if (i != ob_screen)
1781                 if (XQueryPointer(obt_display, obt_root(i),
1782                                   &w, &w, x, y, &i, &i, &u))
1783                     break;
1784     }
1785     return ret;
1786 }