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