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