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