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