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