Merge branch 'backport' into work
[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     guint old;
509     gulong *viewport;
510     GList *it, *stacking_copy;
511
512     g_assert(num > 0);
513
514     if (screen_num_desktops == num) return;
515
516     old = screen_num_desktops;
517     screen_num_desktops = num;
518     OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
519
520     /* set the viewport hint */
521     viewport = g_new0(gulong, num * 2);
522     OBT_PROP_SETA32(obt_root(ob_screen),
523                     NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
524     g_free(viewport);
525
526     /* the number of rows/columns will differ */
527     screen_update_layout();
528
529     /* move windows on desktops that will no longer exist!
530        make a copy of the list cuz we're changing it */
531     stacking_copy = g_list_copy(stacking_list);
532     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
533         if (WINDOW_IS_CLIENT(it->data)) {
534             ObClient *c = it->data;
535             if (c->desktop != DESKTOP_ALL && c->desktop >= num)
536                 client_set_desktop(c, num - 1, FALSE, TRUE);
537             /* raise all the windows that are on the current desktop which
538                is being merged */
539             else if (screen_desktop == num - 1 &&
540                      (c->desktop == DESKTOP_ALL ||
541                       c->desktop == screen_desktop))
542                 stacking_raise(CLIENT_AS_WINDOW(c));
543         }
544     }
545     g_list_free(stacking_copy);
546
547     /* change our struts/area to match (after moving windows) */
548     screen_update_areas();
549
550     /* may be some unnamed desktops that we need to fill in with names
551        (after updating the areas so the popup can resize) */
552     screen_update_desktop_names();
553
554     /* change our desktop if we're on one that no longer exists! */
555     if (screen_desktop >= screen_num_desktops)
556         screen_set_desktop(num - 1, TRUE);
557 }
558
559 static void screen_fallback_focus(void)
560 {
561     ObClient *c;
562     gboolean allow_omni;
563
564     /* only allow omnipresent windows to get focus on desktop change if
565        an omnipresent window is already focused (it'll keep focus probably, but
566        maybe not depending on mouse-focus options) */
567     allow_omni = focus_client && (client_normal(focus_client) &&
568                                   focus_client->desktop == DESKTOP_ALL);
569
570     /* the client moved there already so don't move focus. prevent flicker
571        on sendtodesktop + follow */
572     if (focus_client && focus_client->desktop == screen_desktop)
573         return;
574
575     /* have to try focus here because when you leave an empty desktop
576        there is no focus out to watch for. also, we have different rules
577        here. we always allow it to look under the mouse pointer if
578        config_focus_last is FALSE
579
580        do this before hiding the windows so if helper windows are coming
581        with us, they don't get hidden
582     */
583     if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
584                             !allow_omni)))
585     {
586         /* only do the flicker reducing stuff ahead of time if we are going
587            to call xsetinputfocus on the window ourselves. otherwise there is
588            no guarantee the window will actually take focus.. */
589         if (c->can_focus) {
590             /* reduce flicker by hiliting now rather than waiting for the
591                server FocusIn event */
592             frame_adjust_focus(c->frame, TRUE);
593             /* do this here so that if you switch desktops to a window with
594                helper windows then the helper windows won't flash */
595             client_bring_helper_windows(c);
596         }
597     }
598 }
599
600 static gboolean last_desktop_func(gpointer data)
601 {
602     screen_desktop_timeout = TRUE;
603     return FALSE;
604 }
605
606 void screen_set_desktop(guint num, gboolean dofocus)
607 {
608     GList *it;
609     guint previous;
610     gulong ignore_start;
611
612     g_assert(num < screen_num_desktops);
613
614     previous = screen_desktop;
615     screen_desktop = num;
616
617     if (previous == num) return;
618
619     OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
620
621     /* This whole thing decides when/how to save the screen_last_desktop so
622        that it can be restored later if you want */
623     if (screen_desktop_timeout) {
624         /* If screen_desktop_timeout is true, then we've been on this desktop
625            long enough and we can save it as the last desktop. */
626
627         if (screen_last_desktop == previous)
628             /* this is the startup state only */
629             screen_old_desktop = screen_desktop;
630         else {
631             /* save the "last desktop" as the "old desktop" */
632             screen_old_desktop = screen_last_desktop;
633             /* save the desktop we're coming from as the "last desktop" */
634             screen_last_desktop = previous;
635         }
636     }
637     else {
638         /* If screen_desktop_timeout is false, then we just got to this desktop
639            and we are moving away again. */
640
641         if (screen_desktop == screen_last_desktop) {
642             /* If we are moving to the "last desktop" .. */
643             if (previous == screen_old_desktop) {
644                 /* .. from the "old desktop", change the last desktop to
645                    be where we are coming from */
646                 screen_last_desktop = screen_old_desktop;
647             }
648             else if (screen_last_desktop == screen_old_desktop) {
649                 /* .. and also to the "old desktop", change the "last
650                    desktop" to be where we are coming from */
651                 screen_last_desktop = previous;
652             }
653             else {
654                 /* .. from some other desktop, then set the "last desktop" to
655                    be the saved "old desktop", i.e. where we were before the
656                    "last desktop" */
657                 screen_last_desktop = screen_old_desktop;
658             }
659         }
660         else {
661             /* If we are moving to any desktop besides the "last desktop"..
662                (this is the normal case) */
663             if (screen_desktop == screen_old_desktop) {
664                 /* If moving to the "old desktop", which is not the
665                    "last desktop", don't save anything */
666             }
667             else if (previous == screen_old_desktop) {
668                 /* If moving from the "old desktop", and not to the
669                    "last desktop", don't save anything */
670             }
671             else if (screen_last_desktop == screen_old_desktop) {
672                 /* If the "last desktop" is the same as "old desktop" and
673                    you're not moving to the "last desktop" then save where
674                    we're coming from as the "last desktop" */
675                 screen_last_desktop = previous;
676             }
677             else {
678                 /* If the "last desktop" is different from the "old desktop"
679                    and you're not moving to the "last desktop", then don't save
680                    anything */
681             }
682         }
683     }
684     screen_desktop_timeout = FALSE;
685     obt_main_loop_timeout_remove(ob_main_loop, last_desktop_func);
686     obt_main_loop_timeout_add(ob_main_loop, REMEMBER_LAST_DESKTOP_TIME,
687                               last_desktop_func, NULL, NULL, NULL);
688
689     ob_debug("Moving to desktop %d", num+1);
690
691     if (ob_state() == OB_STATE_RUNNING)
692         screen_show_desktop_popup(screen_desktop);
693
694     /* ignore enter events caused by the move */
695     ignore_start = event_start_ignore_all_enters();
696
697     if (moveresize_client)
698         client_set_desktop(moveresize_client, num, TRUE, FALSE);
699
700     /* show windows before hiding the rest to lessen the enter/leave events */
701
702     /* show windows from top to bottom */
703     for (it = stacking_list; it; it = g_list_next(it)) {
704         if (WINDOW_IS_CLIENT(it->data)) {
705             ObClient *c = it->data;
706             client_show(c);
707         }
708     }
709
710     if (dofocus) screen_fallback_focus();
711
712     /* hide windows from bottom to top */
713     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
714         if (WINDOW_IS_CLIENT(it->data)) {
715             ObClient *c = it->data;
716             client_hide(c);
717         }
718     }
719
720     event_end_ignore_all_enters(ignore_start);
721
722     if (event_curtime != CurrentTime)
723         screen_desktop_user_time = event_curtime;
724
725     hooks_queue(OB_HOOK_SCREEN_DESK_CHANGE, NULL);
726 }
727
728 void screen_add_desktop(gboolean current)
729 {
730     gulong ignore_start;
731
732     /* ignore enter events caused by this */
733     ignore_start = event_start_ignore_all_enters();
734
735     screen_set_num_desktops(screen_num_desktops+1);
736
737     /* move all the clients over */
738     if (current) {
739         GList *it;
740
741         for (it = client_list; it; it = g_list_next(it)) {
742             ObClient *c = it->data;
743             if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
744                 /* don't move direct children, they'll be moved with their
745                    parent - which will have to be on the same desktop */
746                 !client_direct_parent(c))
747             {
748                 ob_debug("moving window %s", c->title);
749                 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
750             }
751         }
752     }
753
754     event_end_ignore_all_enters(ignore_start);
755 }
756
757 void screen_remove_desktop(gboolean current)
758 {
759     guint rmdesktop, movedesktop;
760     GList *it, *stacking_copy;
761     gulong ignore_start;
762
763     if (screen_num_desktops <= 1) return;
764
765     /* ignore enter events caused by this */
766     ignore_start = event_start_ignore_all_enters();
767
768     /* what desktop are we removing and moving to? */
769     if (current)
770         rmdesktop = screen_desktop;
771     else
772         rmdesktop = screen_num_desktops - 1;
773     if (rmdesktop < screen_num_desktops - 1)
774         movedesktop = rmdesktop + 1;
775     else
776         movedesktop = rmdesktop;
777
778     /* make a copy of the list cuz we're changing it */
779     stacking_copy = g_list_copy(stacking_list);
780     for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
781         if (WINDOW_IS_CLIENT(it->data)) {
782             ObClient *c = it->data;
783             guint d = c->desktop;
784             if (d != DESKTOP_ALL && d >= movedesktop &&
785                 /* don't move direct children, they'll be moved with their
786                    parent - which will have to be on the same desktop */
787                 !client_direct_parent(c))
788             {
789                 ob_debug("moving window %s", c->title);
790                 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
791             }
792             /* raise all the windows that are on the current desktop which
793                is being merged */
794             if ((screen_desktop == rmdesktop - 1 ||
795                  screen_desktop == rmdesktop) &&
796                 (d == DESKTOP_ALL || d == screen_desktop))
797             {
798                 stacking_raise(CLIENT_AS_WINDOW(c));
799                 ob_debug("raising window %s", c->title);
800             }
801         }
802     }
803     g_list_free(stacking_copy);
804
805     /* fallback focus like we're changing desktops */
806     if (screen_desktop < screen_num_desktops - 1) {
807         screen_fallback_focus();
808         ob_debug("fake desktop change");
809     }
810
811     screen_set_num_desktops(screen_num_desktops-1);
812
813     event_end_ignore_all_enters(ignore_start);
814 }
815
816 static void get_row_col(guint d, guint *r, guint *c)
817 {
818     switch (screen_desktop_layout.orientation) {
819     case OB_ORIENTATION_HORZ:
820         switch (screen_desktop_layout.start_corner) {
821         case OB_CORNER_TOPLEFT:
822             *r = d / screen_desktop_layout.columns;
823             *c = d % screen_desktop_layout.columns;
824             break;
825         case OB_CORNER_BOTTOMLEFT:
826             *r = screen_desktop_layout.rows - 1 -
827                 d / screen_desktop_layout.columns;
828             *c = d % screen_desktop_layout.columns;
829             break;
830         case OB_CORNER_TOPRIGHT:
831             *r = d / screen_desktop_layout.columns;
832             *c = screen_desktop_layout.columns - 1 -
833                 d % screen_desktop_layout.columns;
834             break;
835         case OB_CORNER_BOTTOMRIGHT:
836             *r = screen_desktop_layout.rows - 1 -
837                 d / screen_desktop_layout.columns;
838             *c = screen_desktop_layout.columns - 1 -
839                 d % screen_desktop_layout.columns;
840             break;
841         }
842         break;
843     case OB_ORIENTATION_VERT:
844         switch (screen_desktop_layout.start_corner) {
845         case OB_CORNER_TOPLEFT:
846             *r = d % screen_desktop_layout.rows;
847             *c = d / screen_desktop_layout.rows;
848             break;
849         case OB_CORNER_BOTTOMLEFT:
850             *r = screen_desktop_layout.rows - 1 -
851                 d % screen_desktop_layout.rows;
852             *c = d / screen_desktop_layout.rows;
853             break;
854         case OB_CORNER_TOPRIGHT:
855             *r = d % screen_desktop_layout.rows;
856             *c = screen_desktop_layout.columns - 1 -
857                 d / screen_desktop_layout.rows;
858             break;
859         case OB_CORNER_BOTTOMRIGHT:
860             *r = screen_desktop_layout.rows - 1 -
861                 d % screen_desktop_layout.rows;
862             *c = screen_desktop_layout.columns - 1 -
863                 d / screen_desktop_layout.rows;
864             break;
865         }
866         break;
867     }
868 }
869
870 static guint translate_row_col(guint r, guint c)
871 {
872     switch (screen_desktop_layout.orientation) {
873     case OB_ORIENTATION_HORZ:
874         switch (screen_desktop_layout.start_corner) {
875         case OB_CORNER_TOPLEFT:
876             return r % screen_desktop_layout.rows *
877                 screen_desktop_layout.columns +
878                 c % screen_desktop_layout.columns;
879         case OB_CORNER_BOTTOMLEFT:
880             return (screen_desktop_layout.rows - 1 -
881                     r % screen_desktop_layout.rows) *
882                 screen_desktop_layout.columns +
883                 c % screen_desktop_layout.columns;
884         case OB_CORNER_TOPRIGHT:
885             return r % screen_desktop_layout.rows *
886                 screen_desktop_layout.columns +
887                 (screen_desktop_layout.columns - 1 -
888                  c % screen_desktop_layout.columns);
889         case OB_CORNER_BOTTOMRIGHT:
890             return (screen_desktop_layout.rows - 1 -
891                     r % screen_desktop_layout.rows) *
892                 screen_desktop_layout.columns +
893                 (screen_desktop_layout.columns - 1 -
894                  c % screen_desktop_layout.columns);
895         }
896     case OB_ORIENTATION_VERT:
897         switch (screen_desktop_layout.start_corner) {
898         case OB_CORNER_TOPLEFT:
899             return c % screen_desktop_layout.columns *
900                 screen_desktop_layout.rows +
901                 r % screen_desktop_layout.rows;
902         case OB_CORNER_BOTTOMLEFT:
903             return c % screen_desktop_layout.columns *
904                 screen_desktop_layout.rows +
905                 (screen_desktop_layout.rows - 1 -
906                  r % screen_desktop_layout.rows);
907         case OB_CORNER_TOPRIGHT:
908             return (screen_desktop_layout.columns - 1 -
909                     c % screen_desktop_layout.columns) *
910                 screen_desktop_layout.rows +
911                 r % screen_desktop_layout.rows;
912         case OB_CORNER_BOTTOMRIGHT:
913             return (screen_desktop_layout.columns - 1 -
914                     c % screen_desktop_layout.columns) *
915                 screen_desktop_layout.rows +
916                 (screen_desktop_layout.rows - 1 -
917                  r % screen_desktop_layout.rows);
918         }
919     }
920     g_assert_not_reached();
921     return 0;
922 }
923
924 static gboolean hide_desktop_popup_func(gpointer data)
925 {
926     guint i;
927
928     for (i = 0; i < screen_num_monitors; i++) {
929         pager_popup_hide(desktop_popup[i]);
930     }
931     return FALSE; /* don't repeat */
932 }
933
934 void screen_show_desktop_popup(guint d)
935 {
936     Rect *a;
937     guint i;
938
939     /* 0 means don't show the popup */
940     if (!config_desktop_popup_time) return;
941
942     for (i = 0; i < screen_num_monitors; i++) {
943         a = screen_physical_area_monitor(i);
944         pager_popup_position(desktop_popup[i], CenterGravity,
945                              a->x + a->width / 2, a->y + a->height / 2);
946         pager_popup_icon_size_multiplier(desktop_popup[i],
947                                          (screen_desktop_layout.columns /
948                                           screen_desktop_layout.rows) / 2,
949                                          (screen_desktop_layout.rows/
950                                           screen_desktop_layout.columns) / 2);
951         pager_popup_max_width(desktop_popup[i],
952                               MAX(a->width/3, POPUP_WIDTH));
953         pager_popup_show(desktop_popup[i], screen_desktop_names[d], d);
954
955         obt_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
956         obt_main_loop_timeout_add(ob_main_loop, config_desktop_popup_time * 1000,
957                                   hide_desktop_popup_func, desktop_popup[i],
958                                   g_direct_equal, NULL);
959         g_free(a);
960     }
961 }
962
963 void screen_hide_desktop_popup(void)
964 {
965     guint i;
966
967     for (i = 0; i < screen_num_monitors; i++) {
968         obt_main_loop_timeout_remove_data(ob_main_loop, hide_desktop_popup_func,
969                                           desktop_popup[i], FALSE);
970         pager_popup_hide(desktop_popup[i]);
971     }
972 }
973
974 guint screen_find_desktop(guint from, ObDirection dir,
975                           gboolean wrap, gboolean linear)
976 {
977     guint r, c;
978     guint d;
979
980     d = from;
981     get_row_col(d, &r, &c);
982     if (linear) {
983         switch (dir) {
984         case OB_DIRECTION_EAST:
985             if (d < screen_num_desktops - 1)
986                 ++d;
987             else if (wrap)
988                 d = 0;
989             else
990                 return from;
991             break;
992         case OB_DIRECTION_WEST:
993             if (d > 0)
994                 --d;
995             else if (wrap)
996                 d = screen_num_desktops - 1;
997             else
998                 return from;
999             break;
1000         default:
1001             g_assert_not_reached();
1002             return from;
1003         }
1004     } else {
1005         switch (dir) {
1006         case OB_DIRECTION_EAST:
1007             ++c;
1008             if (c >= screen_desktop_layout.columns) {
1009                 if (wrap)
1010                     c = 0;
1011                 else
1012                     return from;
1013             }
1014             d = translate_row_col(r, c);
1015             if (d >= screen_num_desktops) {
1016                 if (wrap)
1017                     ++c;
1018                 else
1019                     return from;
1020             }
1021             break;
1022         case OB_DIRECTION_WEST:
1023             --c;
1024             if (c >= screen_desktop_layout.columns) {
1025                 if (wrap)
1026                     c = screen_desktop_layout.columns - 1;
1027                 else
1028                     return from;
1029             }
1030             d = translate_row_col(r, c);
1031             if (d >= screen_num_desktops) {
1032                 if (wrap)
1033                     --c;
1034                 else
1035                     return from;
1036             }
1037             break;
1038         case OB_DIRECTION_SOUTH:
1039             ++r;
1040             if (r >= screen_desktop_layout.rows) {
1041                 if (wrap)
1042                     r = 0;
1043                 else
1044                     return from;
1045             }
1046             d = translate_row_col(r, c);
1047             if (d >= screen_num_desktops) {
1048                 if (wrap)
1049                     ++r;
1050                 else
1051                     return from;
1052             }
1053             break;
1054         case OB_DIRECTION_NORTH:
1055             --r;
1056             if (r >= screen_desktop_layout.rows) {
1057                 if (wrap)
1058                     r = screen_desktop_layout.rows - 1;
1059                 else
1060                     return from;
1061             }
1062             d = translate_row_col(r, c);
1063             if (d >= screen_num_desktops) {
1064                 if (wrap)
1065                     --r;
1066                 else
1067                     return from;
1068             }
1069             break;
1070         default:
1071             g_assert_not_reached();
1072             return from;
1073         }
1074
1075         d = translate_row_col(r, c);
1076     }
1077     return d;
1078 }
1079
1080 static gboolean screen_validate_layout(ObDesktopLayout *l)
1081 {
1082     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1083         return FALSE;
1084
1085     /* fill in a zero rows/columns */
1086     if (l->columns == 0) {
1087         l->columns = screen_num_desktops / l->rows;
1088         if (l->rows * l->columns < screen_num_desktops)
1089             l->columns++;
1090         if (l->rows * l->columns >= screen_num_desktops + l->columns)
1091             l->rows--;
1092     } else if (l->rows == 0) {
1093         l->rows = screen_num_desktops / l->columns;
1094         if (l->columns * l->rows < screen_num_desktops)
1095             l->rows++;
1096         if (l->columns * l->rows >= screen_num_desktops + l->rows)
1097             l->columns--;
1098     }
1099
1100     /* bounds checking */
1101     if (l->orientation == OB_ORIENTATION_HORZ) {
1102         l->columns = MIN(screen_num_desktops, l->columns);
1103         l->rows = MIN(l->rows,
1104                       (screen_num_desktops + l->columns - 1) / l->columns);
1105         l->columns = screen_num_desktops / l->rows +
1106             !!(screen_num_desktops % l->rows);
1107     } else {
1108         l->rows = MIN(screen_num_desktops, l->rows);
1109         l->columns = MIN(l->columns,
1110                          (screen_num_desktops + l->rows - 1) / l->rows);
1111         l->rows = screen_num_desktops / l->columns +
1112             !!(screen_num_desktops % l->columns);
1113     }
1114     return TRUE;
1115 }
1116
1117 void screen_update_layout(void)
1118
1119 {
1120     ObDesktopLayout l;
1121     guint32 *data;
1122     guint num;
1123
1124     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1125     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1126     screen_desktop_layout.rows = 1;
1127     screen_desktop_layout.columns = screen_num_desktops;
1128
1129     if (OBT_PROP_GETA32(obt_root(ob_screen),
1130                         NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1131         if (num == 3 || num == 4) {
1132
1133             if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1134                 l.orientation = OB_ORIENTATION_VERT;
1135             else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1136                 l.orientation = OB_ORIENTATION_HORZ;
1137             else
1138                 return;
1139
1140             if (num < 4)
1141                 l.start_corner = OB_CORNER_TOPLEFT;
1142             else {
1143                 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1144                     l.start_corner = OB_CORNER_TOPLEFT;
1145                 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1146                     l.start_corner = OB_CORNER_TOPRIGHT;
1147                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1148                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
1149                 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1150                     l.start_corner = OB_CORNER_BOTTOMLEFT;
1151                 else
1152                     return;
1153             }
1154
1155             l.columns = data[1];
1156             l.rows = data[2];
1157
1158             if (screen_validate_layout(&l))
1159                 screen_desktop_layout = l;
1160
1161             g_free(data);
1162         }
1163     }
1164 }
1165
1166 void screen_update_desktop_names(void)
1167 {
1168     guint i;
1169
1170     /* empty the array */
1171     g_strfreev(screen_desktop_names);
1172     screen_desktop_names = NULL;
1173
1174     if (OBT_PROP_GETSS(obt_root(ob_screen),
1175                        NET_DESKTOP_NAMES, utf8, &screen_desktop_names))
1176         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1177     else
1178         i = 0;
1179     if (i < screen_num_desktops) {
1180         GSList *it;
1181
1182         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1183                                        screen_num_desktops + 1);
1184         screen_desktop_names[screen_num_desktops] = NULL;
1185
1186         it = g_slist_nth(config_desktops_names, i);
1187
1188         for (; i < screen_num_desktops; ++i) {
1189             if (it && ((char*)it->data)[0]) /* not empty */
1190                 /* use the names from the config file when possible */
1191                 screen_desktop_names[i] = g_strdup(it->data);
1192             else
1193                 /* make up a nice name if it's not though */
1194                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1195                                                           i + 1);
1196             if (it) it = g_slist_next(it);
1197         }
1198
1199         /* if we changed any names, then set the root property so we can
1200            all agree on the names */
1201         OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1202                        utf8, (const gchar**)screen_desktop_names);
1203     }
1204
1205     /* resize the pager for these names */
1206     for (i = 0; i < screen_num_monitors; i++) {
1207         pager_popup_text_width_to_strings(desktop_popup[i],
1208                                           screen_desktop_names,
1209                                           screen_num_desktops);
1210     }
1211 }
1212
1213 void screen_show_desktop(gboolean show, ObClient *show_only)
1214 {
1215     GList *it;
1216
1217     if (show == screen_showing_desktop) return; /* no change */
1218
1219     screen_showing_desktop = show;
1220
1221     if (show) {
1222         /* hide windows bottom to top */
1223         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1224             if (WINDOW_IS_CLIENT(it->data)) {
1225                 ObClient *client = it->data;
1226                 client_showhide(client);
1227             }
1228         }
1229     }
1230     else {
1231         /* restore windows top to bottom */
1232         for (it = stacking_list; it; it = g_list_next(it)) {
1233             if (WINDOW_IS_CLIENT(it->data)) {
1234                 ObClient *client = it->data;
1235                 if (client_should_show(client)) {
1236                     if (!show_only || client == show_only)
1237                         client_show(client);
1238                     else
1239                         client_iconify(client, TRUE, FALSE, TRUE);
1240                 }
1241             }
1242         }
1243     }
1244
1245     if (show) {
1246         /* focus the desktop */
1247         for (it = focus_order; it; it = g_list_next(it)) {
1248             ObClient *c = it->data;
1249             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1250                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1251                 client_focus(it->data))
1252                 break;
1253         }
1254     }
1255     else if (!show_only) {
1256         ObClient *c;
1257
1258         if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1259             /* only do the flicker reducing stuff ahead of time if we are going
1260                to call xsetinputfocus on the window ourselves. otherwise there
1261                is no guarantee the window will actually take focus.. */
1262             if (c->can_focus) {
1263                 /* reduce flicker by hiliting now rather than waiting for the
1264                    server FocusIn event */
1265                 frame_adjust_focus(c->frame, TRUE);
1266             }
1267         }
1268     }
1269
1270     show = !!show; /* make it boolean */
1271     OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
1272 }
1273
1274 void screen_install_colormap(ObClient *client, gboolean install)
1275 {
1276     if (client == NULL || client->colormap == None) {
1277         if (install)
1278             XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1279         else
1280             XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1281     } else {
1282         obt_display_ignore_errors(TRUE);
1283         if (install)
1284             XInstallColormap(obt_display, client->colormap);
1285         else
1286             XUninstallColormap(obt_display, client->colormap);
1287         obt_display_ignore_errors(FALSE);
1288     }
1289 }
1290
1291 #define STRUT_LEFT_ON_MONITOR(s, i) \
1292     (RANGES_INTERSECT(s->left_start, s->left_end - s->left_start + 1, \
1293                       monitor_area[i].y, monitor_area[i].height))
1294 #define STRUT_RIGHT_ON_MONITOR(s, i) \
1295     (RANGES_INTERSECT(s->right_start, s->right_end - s->right_start + 1, \
1296                       monitor_area[i].y, monitor_area[i].height))
1297 #define STRUT_TOP_ON_MONITOR(s, i) \
1298     (RANGES_INTERSECT(s->top_start, s->top_end - s->top_start + 1, \
1299                       monitor_area[i].x, monitor_area[i].width))
1300 #define STRUT_BOTTOM_ON_MONITOR(s, i) \
1301     (RANGES_INTERSECT(s->bottom_start, s->bottom_end - s->bottom_start + 1, \
1302                       monitor_area[i].x, monitor_area[i].width))
1303
1304 typedef struct {
1305     guint desktop;
1306     StrutPartial *strut;
1307 } ObScreenStrut;
1308
1309 #define RESET_STRUT_LIST(sl) \
1310     (g_slist_free(sl), sl = NULL)
1311
1312 #define ADD_STRUT_TO_LIST(sl, d, s) \
1313 { \
1314     ObScreenStrut *ss = g_new(ObScreenStrut, 1); \
1315     ss->desktop = d; \
1316     ss->strut = s;  \
1317     sl = g_slist_prepend(sl, ss); \
1318 }
1319
1320 #define VALIDATE_STRUTS(sl, side, max) \
1321 { \
1322     GSList *it; \
1323     for (it = sl; it; it = g_slist_next(it)) { \
1324       ObScreenStrut *ss = it->data; \
1325       ss->strut->side = MIN(max, ss->strut->side); \
1326     } \
1327 }
1328
1329 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1330 {
1331     guint i;
1332     gint n, l, r, t, b;
1333 #ifdef XINERAMA
1334     XineramaScreenInfo *info;
1335 #endif
1336
1337     if (ob_debug_xinerama) {
1338         gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1339         gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1340         *nxin = 2;
1341         *xin_areas = g_new(Rect, *nxin + 1);
1342         RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1343         RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1344     }
1345 #ifdef XINERAMA
1346     else if (obt_display_extension_xinerama &&
1347              (info = XineramaQueryScreens(obt_display, &n))) {
1348         *nxin = n;
1349         *xin_areas = g_new(Rect, *nxin + 1);
1350         for (i = 0; i < *nxin; ++i)
1351             RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1352                      info[i].width, info[i].height);
1353         XFree(info);
1354     }
1355 #endif
1356     else {
1357         *nxin = 1;
1358         *xin_areas = g_new(Rect, *nxin + 1);
1359         RECT_SET((*xin_areas)[0], 0, 0,
1360                  WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1361                  HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1362     }
1363
1364     /* returns one extra with the total area in it */
1365     l = (*xin_areas)[0].x;
1366     t = (*xin_areas)[0].y;
1367     r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1368     b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1369     for (i = 1; i < *nxin; ++i) {
1370         l = MIN(l, (*xin_areas)[i].x);
1371         t = MIN(l, (*xin_areas)[i].y);
1372         r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1373         b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1374     }
1375     RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1376 }
1377
1378 void screen_update_areas(void)
1379 {
1380     guint i, j;
1381     gulong *dims;
1382     GList *it;
1383     GSList *sit;
1384
1385     g_free(monitor_area);
1386     get_xinerama_screens(&monitor_area, &screen_num_monitors);
1387
1388     if (!desktop_popup) {
1389         desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
1390         for (i = 0; i < screen_num_monitors; i++) {
1391             desktop_popup[i] = pager_popup_new();
1392             pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
1393
1394             if (screen_desktop_names)
1395                 /* update the pager popup's width */
1396                 pager_popup_text_width_to_strings(desktop_popup[i],
1397                                                   screen_desktop_names,
1398                                                   screen_num_desktops);
1399         }
1400     }
1401
1402     /* set up the user-specified margins */
1403     config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1404     config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1405     config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1406     config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1407     config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1408     config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1409     config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1410     config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1411
1412     dims = g_new(gulong, 4 * screen_num_desktops * screen_num_monitors);
1413
1414     RESET_STRUT_LIST(struts_left);
1415     RESET_STRUT_LIST(struts_top);
1416     RESET_STRUT_LIST(struts_right);
1417     RESET_STRUT_LIST(struts_bottom);
1418
1419     /* collect the struts */
1420     for (it = client_list; it; it = g_list_next(it)) {
1421         ObClient *c = it->data;
1422         if (c->strut.left)
1423             ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1424         if (c->strut.top)
1425             ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1426         if (c->strut.right)
1427             ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1428         if (c->strut.bottom)
1429             ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1430     }
1431     if (dock_strut.left)
1432         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1433     if (dock_strut.top)
1434         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1435     if (dock_strut.right)
1436         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1437     if (dock_strut.bottom)
1438         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1439
1440     if (config_margins.left)
1441         ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1442     if (config_margins.top)
1443         ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1444     if (config_margins.right)
1445         ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1446     if (config_margins.bottom)
1447         ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1448
1449     VALIDATE_STRUTS(struts_left, left,
1450                     monitor_area[screen_num_monitors].width / 2);
1451     VALIDATE_STRUTS(struts_right, right,
1452                     monitor_area[screen_num_monitors].width / 2);
1453     VALIDATE_STRUTS(struts_top, top,
1454                     monitor_area[screen_num_monitors].height / 2);
1455     VALIDATE_STRUTS(struts_bottom, bottom,
1456                     monitor_area[screen_num_monitors].height / 2);
1457
1458     /* set up the work areas to be full screen */
1459     for (i = 0; i < screen_num_monitors; ++i)
1460         for (j = 0; j < screen_num_desktops; ++j) {
1461             dims[(i * screen_num_desktops + j) * 4+0] = monitor_area[i].x;
1462             dims[(i * screen_num_desktops + j) * 4+1] = monitor_area[i].y;
1463             dims[(i * screen_num_desktops + j) * 4+2] = monitor_area[i].width;
1464             dims[(i * screen_num_desktops + j) * 4+3] = monitor_area[i].height;
1465         }
1466
1467     /* calculate the work areas from the struts */
1468     for (i = 0; i < screen_num_monitors; ++i)
1469         for (j = 0; j < screen_num_desktops; ++j) {
1470             gint l = 0, r = 0, t = 0, b = 0;
1471
1472             /* only add the strut to the area if it touches the monitor */
1473
1474             for (sit = struts_left; sit; sit = g_slist_next(sit)) {
1475                 ObScreenStrut *s = sit->data;
1476                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1477                     STRUT_LEFT_ON_MONITOR(s->strut, i))
1478                     l = MAX(l, s->strut->left);
1479             }
1480             for (sit = struts_top; sit; sit = g_slist_next(sit)) {
1481                 ObScreenStrut *s = sit->data;
1482                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1483                     STRUT_TOP_ON_MONITOR(s->strut, i))
1484                     t = MAX(t, s->strut->top);
1485             }
1486             for (sit = struts_right; sit; sit = g_slist_next(sit)) {
1487                 ObScreenStrut *s = sit->data;
1488                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1489                     STRUT_RIGHT_ON_MONITOR(s->strut, i))
1490                     r = MAX(r, s->strut->right);
1491             }
1492             for (sit = struts_bottom; sit; sit = g_slist_next(sit)) {
1493                 ObScreenStrut *s = sit->data;
1494                 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1495                     STRUT_BOTTOM_ON_MONITOR(s->strut, i))
1496                     b = MAX(b, s->strut->bottom);
1497             }
1498
1499             if (l) l += RECT_LEFT  (monitor_area[screen_num_monitors])
1500                         - RECT_LEFT  (monitor_area[i]);
1501             if (t) t += RECT_TOP   (monitor_area[screen_num_monitors])
1502                         - RECT_TOP   (monitor_area[i]);
1503             if (r) r -= RECT_RIGHT (monitor_area[screen_num_monitors])
1504                         - RECT_RIGHT (monitor_area[i]);
1505             if (b) b -= RECT_BOTTOM(monitor_area[screen_num_monitors])
1506                         - RECT_BOTTOM(monitor_area[i]);
1507
1508             /* based on these margins, set the work area for the
1509                monitor/desktop */
1510             dims[(i * screen_num_desktops + j) * 4 + 0] += l;
1511             dims[(i * screen_num_desktops + j) * 4 + 1] += t;
1512             dims[(i * screen_num_desktops + j) * 4 + 2] -= l + r;
1513             dims[(i * screen_num_desktops + j) * 4 + 3] -= t + b;
1514         }
1515
1516     /* all the work areas are not used here, only the ones for the first
1517        monitor are */
1518     OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1519                     dims, 4 * screen_num_desktops);
1520
1521     /* the area has changed, adjust all the windows if they need it */
1522     for (it = client_list; it; it = g_list_next(it))
1523         client_reconfigure(it->data, FALSE);
1524
1525     g_free(dims);
1526 }
1527
1528 #if 0
1529 Rect* screen_area_all_monitors(guint desktop)
1530 {
1531     guint i;
1532     Rect *a;
1533
1534     a = screen_area_monitor(desktop, 0);
1535
1536     /* combine all the monitors together */
1537     for (i = 1; i < screen_num_monitors; ++i) {
1538         Rect *m = screen_area_monitor(desktop, i);
1539         gint l, r, t, b;
1540
1541         l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1542         t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1543         r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1544         b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1545
1546         RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1547
1548         g_free(m);
1549     }
1550
1551     return a;
1552 }
1553 #endif
1554
1555 #define STRUT_LEFT_IN_SEARCH(s, search) \
1556     (RANGES_INTERSECT(search->y, search->height, \
1557                       s->left_start, s->left_end - s->left_start + 1))
1558 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1559     (RANGES_INTERSECT(search->y, search->height, \
1560                       s->right_start, s->right_end - s->right_start + 1))
1561 #define STRUT_TOP_IN_SEARCH(s, search) \
1562     (RANGES_INTERSECT(search->x, search->width, \
1563                       s->top_start, s->top_end - s->top_start + 1))
1564 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1565     (RANGES_INTERSECT(search->x, search->width, \
1566                       s->bottom_start, s->bottom_end - s->bottom_start + 1))
1567
1568 #define STRUT_LEFT_IGNORE(s, us, search) \
1569     (head == SCREEN_AREA_ALL_MONITORS && us && \
1570      RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1571 #define STRUT_RIGHT_IGNORE(s, us, search) \
1572     (head == SCREEN_AREA_ALL_MONITORS && us && \
1573      RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1574 #define STRUT_TOP_IGNORE(s, us, search) \
1575     (head == SCREEN_AREA_ALL_MONITORS && us && \
1576      RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1577 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1578     (head == SCREEN_AREA_ALL_MONITORS && us && \
1579      RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1580
1581 Rect* screen_area(guint desktop, guint head, Rect *search)
1582 {
1583     Rect *a;
1584     GSList *it;
1585     gint l, r, t, b, al, ar, at, ab;
1586     guint i, d;
1587     gboolean us = search != NULL; /* user provided search */
1588
1589     g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1590     g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1591              head == SCREEN_AREA_ALL_MONITORS);
1592     g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1593
1594     /* find any struts for this monitor
1595        which will be affecting the search area.
1596     */
1597
1598     /* search everything if search is null */
1599     if (!search) {
1600         if (head < screen_num_monitors) search = &monitor_area[head];
1601         else search = &monitor_area[screen_num_monitors];
1602     }
1603     if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1604
1605     /* al is "all left" meaning the furthest left you can get, l is our
1606        "working left" meaning our current strut edge which we're calculating
1607     */
1608
1609     /* only include monitors which the search area lines up with */
1610     if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1611         al = l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1612         at = t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1613         ar = r = RECT_LEFT(monitor_area[screen_num_monitors]);
1614         ab = b = RECT_TOP(monitor_area[screen_num_monitors]);
1615         for (i = 0; i < screen_num_monitors; ++i) {
1616             /* add the monitor if applicable */
1617             if (RANGES_INTERSECT(search->x, search->width,
1618                                  monitor_area[i].x, monitor_area[i].width))
1619             {
1620                 at = t = MIN(t, RECT_TOP(monitor_area[i]));
1621                 ab = b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1622             }
1623             if (RANGES_INTERSECT(search->y, search->height,
1624                                  monitor_area[i].y, monitor_area[i].height))
1625             {
1626                 al = l = MIN(l, RECT_LEFT(monitor_area[i]));
1627                 ar = r = MAX(r, RECT_RIGHT(monitor_area[i]));
1628             }
1629         }
1630     } else {
1631         al = l = RECT_LEFT(monitor_area[screen_num_monitors]);
1632         at = t = RECT_TOP(monitor_area[screen_num_monitors]);
1633         ar = r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1634         ab = b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1635     }
1636
1637     for (d = 0; d < screen_num_desktops; ++d) {
1638         if (d != desktop && desktop != DESKTOP_ALL) continue;
1639
1640         for (i = 0; i < screen_num_monitors; ++i) {
1641             if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1642
1643             for (it = struts_left; it; it = g_slist_next(it)) {
1644                 ObScreenStrut *s = it->data;
1645                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1646                     STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1647                     !STRUT_LEFT_IGNORE(s->strut, us, search))
1648                     l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1649                                + s->strut->left);
1650             }
1651             for (it = struts_top; it; it = g_slist_next(it)) {
1652                 ObScreenStrut *s = it->data;
1653                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1654                     STRUT_TOP_IN_SEARCH(s->strut, search) &&
1655                     !STRUT_TOP_IGNORE(s->strut, us, search))
1656                     t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1657                                + s->strut->top);
1658             }
1659             for (it = struts_right; it; it = g_slist_next(it)) {
1660                 ObScreenStrut *s = it->data;
1661                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1662                     STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1663                     !STRUT_RIGHT_IGNORE(s->strut, us, search))
1664                     r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1665                                - s->strut->right);
1666             }
1667             for (it = struts_bottom; it; it = g_slist_next(it)) {
1668                 ObScreenStrut *s = it->data;
1669                 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1670                     STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1671                     !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1672                     b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1673                                - s->strut->bottom);
1674             }
1675
1676             /* limit to this monitor */
1677             if (head == i) {
1678                 l = MAX(l, RECT_LEFT(monitor_area[i]));
1679                 t = MAX(t, RECT_TOP(monitor_area[i]));
1680                 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1681                 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1682             }
1683         }
1684     }
1685
1686     a = g_new(Rect, 1);
1687     a->x = l;
1688     a->y = t;
1689     a->width = r - l + 1;
1690     a->height = b - t + 1;
1691     return a;
1692 }
1693
1694 guint screen_find_monitor(Rect *search)
1695 {
1696     guint i;
1697     guint most = screen_num_monitors;
1698     guint mostv = 0;
1699
1700     for (i = 0; i < screen_num_monitors; ++i) {
1701         Rect *area = screen_physical_area_monitor(i);
1702         if (RECT_INTERSECTS_RECT(*area, *search)) {
1703             Rect r;
1704             guint v;
1705
1706             RECT_SET_INTERSECTION(r, *area, *search);
1707             v = r.width * r.height;
1708
1709             if (v > mostv) {
1710                 mostv = v;
1711                 most = i;
1712             }
1713         }
1714         g_free(area);
1715     }
1716     return most;
1717 }
1718
1719 Rect* screen_physical_area_all_monitors(void)
1720 {
1721     return screen_physical_area_monitor(screen_num_monitors);
1722 }
1723
1724 Rect* screen_physical_area_monitor(guint head)
1725 {
1726     Rect *a;
1727     g_assert(head <= screen_num_monitors);
1728
1729     a = g_new(Rect, 1);
1730     *a = monitor_area[head];
1731     return a;
1732 }
1733
1734 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1735 {
1736     g_assert(head <= screen_num_monitors);
1737     g_assert(search);
1738     return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1739 }
1740
1741 Rect* screen_physical_area_active(void)
1742 {
1743     Rect *a;
1744     gint x, y;
1745
1746     if (moveresize_client)
1747         a = screen_physical_area_monitor(client_monitor(focus_client));
1748     else if (focus_client)
1749         a = screen_physical_area_monitor(client_monitor(focus_client));
1750     else {
1751         Rect mon;
1752         if (screen_pointer_pos(&x, &y))
1753             RECT_SET(mon, x, y, 1, 1);
1754         else
1755             RECT_SET(mon, 0, 0, 1, 1);
1756         a = screen_physical_area_monitor(screen_find_monitor(&mon));
1757     }
1758     return a;
1759 }
1760
1761 void screen_set_root_cursor(void)
1762 {
1763     if (sn_app_starting())
1764         XDefineCursor(obt_display, obt_root(ob_screen),
1765                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1766     else
1767         XDefineCursor(obt_display, obt_root(ob_screen),
1768                       ob_cursor(OB_CURSOR_POINTER));
1769 }
1770
1771 gboolean screen_pointer_pos(gint *x, gint *y)
1772 {
1773     Window w;
1774     gint i;
1775     guint u;
1776     gboolean ret;
1777
1778     ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1779                           &w, &w, x, y, &i, &i, &u);
1780     if (!ret) {
1781         for (i = 0; i < ScreenCount(obt_display); ++i)
1782             if (i != ob_screen)
1783                 if (XQueryPointer(obt_display, obt_root(i),
1784                                   &w, &w, x, y, &i, &i, &u))
1785                     break;
1786     }
1787     return ret;
1788 }