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