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