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