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