]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/screen.c
put the shade icon in the right place
[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_restack_window;
280     supported[i++] = prop_atoms.net_startup_id;
281 #ifdef SYNC
282     supported[i++] = prop_atoms.net_wm_sync_request;
283     supported[i++] = prop_atoms.net_wm_sync_request_counter;
284 #endif
285
286     supported[i++] = prop_atoms.kde_wm_change_state;
287     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
288     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
289
290     supported[i++] = prop_atoms.openbox_wm_state_undecorated;
291     supported[i++] = prop_atoms.openbox_pid;
292     supported[i++] = prop_atoms.openbox_config;
293     supported[i++] = prop_atoms.openbox_control;
294     g_assert(i == num_support);
295
296     PROP_SETA32(RootWindow(ob_display, ob_screen),
297                 net_supported, atom, supported, num_support);
298     g_free(supported);
299
300     return TRUE;
301 }
302
303 void screen_startup(gboolean reconfig)
304 {
305     guint i, numnames;
306     gchar **names;
307     GSList *it;
308     guint32 d;
309
310     desktop_cycle_popup = pager_popup_new(FALSE);
311     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
312
313     if (reconfig)
314         return;
315
316     /* get the initial size */
317     screen_resize();
318
319     /* get the desktop names */
320     numnames = g_slist_length(config_desktops_names);
321     names = g_new(gchar*, numnames + 1);
322     names[numnames] = NULL;
323     for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
324         names[i] = g_strdup(it->data);
325
326     /* set the root window property */
327     PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
328
329     g_strfreev(names);
330
331     /* set the number of desktops */
332     screen_num_desktops = 0;
333     screen_set_num_desktops(config_desktops_num);
334
335     /* start on the current desktop when a wm was already running */
336     if (PROP_GET32(RootWindow(ob_display, ob_screen),
337                    net_current_desktop, cardinal, &d) &&
338         d < screen_num_desktops)
339     {
340         screen_set_desktop(d, FALSE);
341     } else if (session_desktop >= 0)
342         screen_set_desktop(MIN((guint)session_desktop,
343                                screen_num_desktops), FALSE);
344     else
345         screen_set_desktop(MIN(config_screen_firstdesk,
346                                screen_num_desktops) - 1, FALSE);
347
348     /* don't start in showing-desktop mode */
349     screen_showing_desktop = FALSE;
350     PROP_SET32(RootWindow(ob_display, ob_screen),
351                net_showing_desktop, cardinal, screen_showing_desktop);
352
353     screen_update_layout();
354 }
355
356 void screen_shutdown(gboolean reconfig)
357 {
358     Rect **r;
359
360     pager_popup_free(desktop_cycle_popup);
361
362     if (reconfig)
363         return;
364
365     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
366                  NoEventMask);
367
368     /* we're not running here no more! */
369     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
370     /* not without us */
371     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
372     /* don't keep this mode */
373     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
374
375     XDestroyWindow(ob_display, screen_support_win);
376
377     g_strfreev(screen_desktop_names);
378     screen_desktop_names = NULL;
379
380     for (r = area; *r; ++r)
381         g_free(*r);
382     g_free(area);
383     area = NULL;
384 }
385
386 void screen_resize()
387 {
388     static gint oldw = 0, oldh = 0;
389     gint w, h;
390     GList *it;
391     gulong geometry[2];
392
393     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
394     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
395
396     if (w == oldw && h == oldh) return;
397
398     oldw = w; oldh = h;
399
400     /* Set the _NET_DESKTOP_GEOMETRY hint */
401     screen_physical_size.width = geometry[0] = w;
402     screen_physical_size.height = geometry[1] = h;
403     PROP_SETA32(RootWindow(ob_display, ob_screen),
404                 net_desktop_geometry, cardinal, geometry, 2);
405
406     if (ob_state() == OB_STATE_STARTING)
407         return;
408
409     screen_update_areas();
410     dock_configure();
411
412     for (it = client_list; it; it = g_list_next(it))
413         client_move_onscreen(it->data, FALSE);
414 }
415
416 void screen_set_num_desktops(guint num)
417 {
418     guint old;
419     gulong *viewport;
420     GList *it;
421
422     g_assert(num > 0);
423
424     if (screen_num_desktops == num) return;
425
426     old = screen_num_desktops;
427     screen_num_desktops = num;
428     PROP_SET32(RootWindow(ob_display, ob_screen),
429                net_number_of_desktops, cardinal, num);
430
431     /* set the viewport hint */
432     viewport = g_new0(gulong, num * 2);
433     PROP_SETA32(RootWindow(ob_display, ob_screen),
434                 net_desktop_viewport, cardinal, viewport, num * 2);
435     g_free(viewport);
436
437     /* the number of rows/columns will differ */
438     screen_update_layout();
439
440     /* move windows on desktops that will no longer exist! */
441     for (it = client_list; it; it = g_list_next(it)) {
442         ObClient *c = it->data;
443         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
444             client_set_desktop(c, num - 1, FALSE);
445     }
446  
447     /* change our struts/area to match (after moving windows) */
448     screen_update_areas();
449
450     /* may be some unnamed desktops that we need to fill in with names
451      (after updating the areas so the popup can resize) */
452     screen_update_desktop_names();
453
454     /* change our desktop if we're on one that no longer exists! */
455     if (screen_desktop >= screen_num_desktops)
456         screen_set_desktop(num - 1, TRUE);
457 }
458
459 void screen_set_desktop(guint num, gboolean dofocus)
460 {
461     ObClient *c;
462     GList *it;
463     guint old;
464      
465     g_assert(num < screen_num_desktops);
466
467     old = screen_desktop;
468     screen_desktop = num;
469     PROP_SET32(RootWindow(ob_display, ob_screen),
470                net_current_desktop, cardinal, num);
471
472     if (old == num) return;
473
474     screen_last_desktop = old;
475
476     ob_debug("Moving to desktop %d\n", num+1);
477
478     if (moveresize_client)
479         client_set_desktop(moveresize_client, num, TRUE);
480
481     /* show windows before hiding the rest to lessen the enter/leave events */
482
483     /* show windows from top to bottom */
484     for (it = stacking_list; it; it = g_list_next(it)) {
485         if (WINDOW_IS_CLIENT(it->data)) {
486             ObClient *c = it->data;
487             client_show(c);
488         }
489     }
490
491     /* have to try focus here because when you leave an empty desktop
492        there is no focus out to watch for
493
494        do this before hiding the windows so if helper windows are coming
495        with us, they don't get hidden
496     */
497     if (dofocus && (c = focus_fallback_target(TRUE, focus_client))) {
498         /* only do the flicker reducing stuff ahead of time if we are going
499            to call xsetinputfocus on the window ourselves. otherwise there is
500            no guarantee the window will actually take focus.. */
501         if (c->can_focus) {
502             /* do this here so that if you switch desktops to a window with
503                helper windows then the helper windows won't flash */
504             client_bring_helper_windows(c);
505             /* reduce flicker by hiliting now rather than waiting for the
506                server FocusIn event */
507             frame_adjust_focus(c->frame, TRUE);
508         }
509         client_focus(c);
510     }
511
512     /* hide windows from bottom to top */
513     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
514         if (WINDOW_IS_CLIENT(it->data)) {
515             ObClient *c = it->data;
516             client_hide(c);
517         }
518     }
519
520     event_ignore_queued_enters();
521
522     if (event_curtime != CurrentTime)
523         screen_desktop_user_time = event_curtime;
524 }
525
526 static void get_row_col(guint d, guint *r, guint *c)
527 {
528     switch (screen_desktop_layout.orientation) {
529     case OB_ORIENTATION_HORZ:
530         switch (screen_desktop_layout.start_corner) {
531         case OB_CORNER_TOPLEFT:
532             *r = d / screen_desktop_layout.columns;
533             *c = d % screen_desktop_layout.columns;
534             break;
535         case OB_CORNER_BOTTOMLEFT:
536             *r = screen_desktop_layout.rows - 1 -
537                 d / screen_desktop_layout.columns;
538             *c = d % screen_desktop_layout.columns;
539             break;
540         case OB_CORNER_TOPRIGHT:
541             *r = d / screen_desktop_layout.columns;
542             *c = screen_desktop_layout.columns - 1 -
543                 d % screen_desktop_layout.columns;
544             break;
545         case OB_CORNER_BOTTOMRIGHT:
546             *r = screen_desktop_layout.rows - 1 -
547                 d / screen_desktop_layout.columns;
548             *c = screen_desktop_layout.columns - 1 -
549                 d % screen_desktop_layout.columns;
550             break;
551         }
552         break;
553     case OB_ORIENTATION_VERT:
554         switch (screen_desktop_layout.start_corner) {
555         case OB_CORNER_TOPLEFT:
556             *r = d % screen_desktop_layout.rows;
557             *c = d / screen_desktop_layout.rows;
558             break;
559         case OB_CORNER_BOTTOMLEFT:
560             *r = screen_desktop_layout.rows - 1 -
561                 d % screen_desktop_layout.rows;
562             *c = d / screen_desktop_layout.rows;
563             break;
564         case OB_CORNER_TOPRIGHT:
565             *r = d % screen_desktop_layout.rows;
566             *c = screen_desktop_layout.columns - 1 -
567                 d / screen_desktop_layout.rows;
568             break;
569         case OB_CORNER_BOTTOMRIGHT:
570             *r = screen_desktop_layout.rows - 1 -
571                 d % screen_desktop_layout.rows;
572             *c = screen_desktop_layout.columns - 1 -
573                 d / screen_desktop_layout.rows;
574             break;
575         }
576         break;
577     }
578 }
579
580 static guint translate_row_col(guint r, guint c)
581 {
582     switch (screen_desktop_layout.orientation) {
583     case OB_ORIENTATION_HORZ:
584         switch (screen_desktop_layout.start_corner) {
585         case OB_CORNER_TOPLEFT:
586             return r % screen_desktop_layout.rows *
587                 screen_desktop_layout.columns +
588                 c % screen_desktop_layout.columns;
589         case OB_CORNER_BOTTOMLEFT:
590             return (screen_desktop_layout.rows - 1 -
591                     r % screen_desktop_layout.rows) *
592                 screen_desktop_layout.columns +
593                 c % screen_desktop_layout.columns;
594         case OB_CORNER_TOPRIGHT:
595             return r % screen_desktop_layout.rows *
596                 screen_desktop_layout.columns +
597                 (screen_desktop_layout.columns - 1 -
598                  c % screen_desktop_layout.columns);
599         case OB_CORNER_BOTTOMRIGHT:
600             return (screen_desktop_layout.rows - 1 -
601                     r % screen_desktop_layout.rows) *
602                 screen_desktop_layout.columns +
603                 (screen_desktop_layout.columns - 1 -
604                  c % screen_desktop_layout.columns);
605         }
606     case OB_ORIENTATION_VERT:
607         switch (screen_desktop_layout.start_corner) {
608         case OB_CORNER_TOPLEFT:
609             return c % screen_desktop_layout.columns *
610                 screen_desktop_layout.rows +
611                 r % screen_desktop_layout.rows;
612         case OB_CORNER_BOTTOMLEFT:
613             return c % screen_desktop_layout.columns *
614                 screen_desktop_layout.rows +
615                 (screen_desktop_layout.rows - 1 -
616                  r % screen_desktop_layout.rows);
617         case OB_CORNER_TOPRIGHT:
618             return (screen_desktop_layout.columns - 1 -
619                     c % screen_desktop_layout.columns) *
620                 screen_desktop_layout.rows +
621                 r % screen_desktop_layout.rows;
622         case OB_CORNER_BOTTOMRIGHT:
623             return (screen_desktop_layout.columns - 1 -
624                     c % screen_desktop_layout.columns) *
625                 screen_desktop_layout.rows +
626                 (screen_desktop_layout.rows - 1 -
627                  r % screen_desktop_layout.rows);
628         }
629     }
630     g_assert_not_reached();
631     return 0;
632 }
633
634 void screen_desktop_popup(guint d, gboolean show)
635 {
636     Rect *a;
637
638     if (!show) {
639         pager_popup_hide(desktop_cycle_popup);
640     } else {
641         a = screen_physical_area_monitor(0);
642         pager_popup_position(desktop_cycle_popup, CenterGravity,
643                              a->x + a->width / 2, a->y + a->height / 2);
644         pager_popup_icon_size_multiplier(desktop_cycle_popup,
645                                          (screen_desktop_layout.columns /
646                                           screen_desktop_layout.rows) / 2,
647                                          (screen_desktop_layout.rows/
648                                           screen_desktop_layout.columns) / 2);
649         pager_popup_max_width(desktop_cycle_popup,
650                               MAX(a->width/3, POPUP_WIDTH));
651         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
652     }
653 }
654
655 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
656                            gboolean dialog, gboolean done, gboolean cancel)
657 {
658     guint r, c;
659     static guint d = (guint)-1;
660     guint ret;
661
662     if (d == (guint)-1)
663         d = screen_desktop;
664
665     if ((cancel || done) && dialog)
666         goto show_cycle_dialog;
667
668     get_row_col(d, &r, &c);
669
670     if (linear) {
671         switch (dir) {
672         case OB_DIRECTION_EAST:
673             if (d < screen_num_desktops - 1)
674                 ++d;
675             else if (wrap)
676                 d = 0;
677             break;
678         case OB_DIRECTION_WEST:
679             if (d > 0)
680                 --d;
681             else if (wrap)
682                 d = screen_num_desktops - 1;
683             break;
684         default:
685             assert(0);
686             return screen_desktop;
687         }
688     } else {
689         switch (dir) {
690         case OB_DIRECTION_EAST:
691             ++c;
692             if (c >= screen_desktop_layout.columns) {
693                 if (wrap) {
694                     c = 0;
695                 } else {
696                     d = screen_desktop;
697                     goto show_cycle_dialog;
698                 }
699             }
700             d = translate_row_col(r, c);
701             if (d >= screen_num_desktops) {
702                 if (wrap) {
703                     ++c;
704                 } else {
705                     d = screen_desktop;
706                     goto show_cycle_dialog;
707                 }
708             }
709             break;
710         case OB_DIRECTION_WEST:
711             --c;
712             if (c >= screen_desktop_layout.columns) {
713                 if (wrap) {
714                     c = screen_desktop_layout.columns - 1;
715                 } else {
716                     d = screen_desktop;
717                     goto show_cycle_dialog;
718                 }
719             }
720             d = translate_row_col(r, c);
721             if (d >= screen_num_desktops) {
722                 if (wrap) {
723                     --c;
724                 } else {
725                     d = screen_desktop;
726                     goto show_cycle_dialog;
727                 }
728             }
729             break;
730         case OB_DIRECTION_SOUTH:
731             ++r;
732             if (r >= screen_desktop_layout.rows) {
733                 if (wrap) {
734                     r = 0;
735                 } else {
736                     d = screen_desktop;
737                     goto show_cycle_dialog;
738                 }
739             }
740             d = translate_row_col(r, c);
741             if (d >= screen_num_desktops) {
742                 if (wrap) {
743                     ++r;
744                 } else {
745                     d = screen_desktop;
746                     goto show_cycle_dialog;
747                 }
748             }
749             break;
750         case OB_DIRECTION_NORTH:
751             --r;
752             if (r >= screen_desktop_layout.rows) {
753                 if (wrap) {
754                     r = screen_desktop_layout.rows - 1;
755                 } else {
756                     d = screen_desktop;
757                     goto show_cycle_dialog;
758                 }
759             }
760             d = translate_row_col(r, c);
761             if (d >= screen_num_desktops) {
762                 if (wrap) {
763                     --r;
764                 } else {
765                     d = screen_desktop;
766                     goto show_cycle_dialog;
767                 }
768             }
769             break;
770         default:
771             assert(0);
772             return d = screen_desktop;
773         }
774
775         d = translate_row_col(r, c);
776     }
777
778 show_cycle_dialog:
779     if (dialog && !cancel && !done) {
780         screen_desktop_popup(d, TRUE);
781     } else
782         screen_desktop_popup(0, FALSE);
783     ret = d;
784
785     if (!dialog || cancel || done)
786         d = (guint)-1;
787
788     return ret;
789 }
790
791 void screen_update_layout()
792 {
793     ObOrientation orient;
794     ObCorner corner;
795     guint rows;
796     guint cols;
797     guint32 *data;
798     guint num;
799     gboolean valid = FALSE;
800
801     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
802                     net_desktop_layout, cardinal, &data, &num)) {
803         if (num == 3 || num == 4) {
804
805             if (data[0] == prop_atoms.net_wm_orientation_vert)
806                 orient = OB_ORIENTATION_VERT;
807             else if (data[0] == prop_atoms.net_wm_orientation_horz)
808                 orient = OB_ORIENTATION_HORZ;
809             else
810                 goto screen_update_layout_bail;
811
812             if (num < 4)
813                 corner = OB_CORNER_TOPLEFT;
814             else {
815                 if (data[3] == prop_atoms.net_wm_topleft)
816                     corner = OB_CORNER_TOPLEFT;
817                 else if (data[3] == prop_atoms.net_wm_topright)
818                     corner = OB_CORNER_TOPRIGHT;
819                 else if (data[3] == prop_atoms.net_wm_bottomright)
820                     corner = OB_CORNER_BOTTOMRIGHT;
821                 else if (data[3] == prop_atoms.net_wm_bottomleft)
822                     corner = OB_CORNER_BOTTOMLEFT;
823                 else
824                     goto screen_update_layout_bail;
825             }
826
827             cols = data[1];
828             rows = data[2];
829
830             /* fill in a zero rows/columns */
831             if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */
832                 goto screen_update_layout_bail;
833             } else {
834                 if (cols == 0) {
835                     cols = screen_num_desktops / rows;
836                     if (rows * cols < screen_num_desktops)
837                         cols++;
838                     if (rows * cols >= screen_num_desktops + cols)
839                         rows--;
840                 } else if (rows == 0) {
841                     rows = screen_num_desktops / cols;
842                     if (cols * rows < screen_num_desktops)
843                         rows++;
844                     if (cols * rows >= screen_num_desktops + rows)
845                         cols--;
846                 }
847             }
848
849             /* bounds checking */
850             if (orient == OB_ORIENTATION_HORZ) {
851                 cols = MIN(screen_num_desktops, cols);
852                 rows = MIN(rows, (screen_num_desktops + cols - 1) / cols);
853                 cols = screen_num_desktops / rows +
854                     !!(screen_num_desktops % rows);
855             } else {
856                 rows = MIN(screen_num_desktops, rows);
857                 cols = MIN(cols, (screen_num_desktops + rows - 1) / rows);
858                 rows = screen_num_desktops / cols +
859                     !!(screen_num_desktops % cols);
860             }
861
862             valid = TRUE;
863         }
864     screen_update_layout_bail:
865         g_free(data);
866     }
867
868     if (!valid) {
869         /* defaults */
870         orient = OB_ORIENTATION_HORZ;
871         corner = OB_CORNER_TOPLEFT;
872         rows = 1;
873         cols = screen_num_desktops;
874     }
875
876     screen_desktop_layout.orientation = orient;
877     screen_desktop_layout.start_corner = corner;
878     screen_desktop_layout.rows = rows;
879     screen_desktop_layout.columns = cols;
880 }
881
882 void screen_update_desktop_names()
883 {
884     guint i;
885
886     /* empty the array */
887     g_strfreev(screen_desktop_names);
888     screen_desktop_names = NULL;
889
890     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
891                    net_desktop_names, utf8, &screen_desktop_names))
892         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
893     else
894         i = 0;
895     if (i < screen_num_desktops) {
896         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
897                                        screen_num_desktops + 1);
898         screen_desktop_names[screen_num_desktops] = NULL;
899         for (; i < screen_num_desktops; ++i)
900             screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
901     }
902
903     /* resize the pager for these names */
904     pager_popup_text_width_to_strings(desktop_cycle_popup,
905                                       screen_desktop_names,
906                                       screen_num_desktops);
907 }
908
909 void screen_show_desktop(gboolean show, ObClient *show_only)
910 {
911     GList *it;
912      
913     if (show == screen_showing_desktop) return; /* no change */
914
915     screen_showing_desktop = show;
916
917     if (show) {
918         /* hide windows bottom to top */
919         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
920             if (WINDOW_IS_CLIENT(it->data)) {
921                 ObClient *client = it->data;
922                 client_showhide(client);
923             }
924         }
925     }
926     else {
927         /* restore windows top to bottom */
928         for (it = stacking_list; it; it = g_list_next(it)) {
929             if (WINDOW_IS_CLIENT(it->data)) {
930                 ObClient *client = it->data;
931                 if (client_should_show(client)) {
932                     if (!show_only || client == show_only)
933                         client_show(client);
934                     else
935                         client_iconify(client, TRUE, FALSE, TRUE);
936                 }
937             }
938         }
939     }
940
941     if (show) {
942         /* focus desktop */
943         for (it = focus_order; it; it = g_list_next(it)) {
944             ObClient *c = it->data;
945             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
946                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
947                 client_focus(it->data))
948                 break;
949         }
950     }
951     else if (!show_only) {
952         ObClient *c;
953
954         /* use NULL for the "old" argument because the desktop was focused
955            and we don't want to fallback to the desktop by default */
956         if ((c = focus_fallback_target(TRUE, NULL)))
957             client_focus(c);
958     }
959
960     show = !!show; /* make it boolean */
961     PROP_SET32(RootWindow(ob_display, ob_screen),
962                net_showing_desktop, cardinal, show);
963 }
964
965 void screen_install_colormap(ObClient *client, gboolean install)
966 {
967     if (client == NULL) {
968         if (install)
969             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
970         else
971             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
972     } else {
973         xerror_set_ignore(TRUE);
974         if (install) {
975             if (client->colormap != None)
976                 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
977         } else
978             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
979         xerror_set_ignore(FALSE);
980     }
981 }
982
983 static inline void
984 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
985                            gint edge, Strut *ret)
986 {
987     if (s->left &&
988         ((s->left_end <= s->left_start) ||
989          (RECT_TOP(*monitor_area) < s->left_end &&
990           RECT_BOTTOM(*monitor_area) > s->left_start)))
991         ret->left = MAX(ret->left, edge);
992 }
993
994 static inline void
995 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
996                           gint edge, Strut *ret)
997 {
998     if (s->top &&
999         ((s->top_end <= s->top_start) ||
1000          (RECT_LEFT(*monitor_area) < s->top_end &&
1001           RECT_RIGHT(*monitor_area) > s->top_start)))
1002         ret->top = MAX(ret->top, edge);
1003 }
1004
1005 static inline void
1006 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1007                             gint edge, Strut *ret)
1008 {
1009     if (s->right &&
1010         ((s->right_end <= s->right_start) ||
1011          (RECT_TOP(*monitor_area) < s->right_end &&
1012           RECT_BOTTOM(*monitor_area) > s->right_start)))
1013         ret->right = MAX(ret->right, edge);
1014 }
1015
1016 static inline void
1017 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1018                              gint edge, Strut *ret)
1019 {
1020     if (s->bottom &&
1021         ((s->bottom_end <= s->bottom_start) ||
1022          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1023           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1024         ret->bottom = MAX(ret->bottom, edge);
1025 }
1026
1027 void screen_update_areas()
1028 {
1029     guint i, x;
1030     gulong *dims;
1031     GList *it;
1032     gint o;
1033
1034     g_free(monitor_area);
1035     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1036
1037     if (area) {
1038         for (i = 0; area[i]; ++i)
1039             g_free(area[i]);
1040         g_free(area);
1041     }
1042
1043     area = g_new(Rect*, screen_num_desktops + 2);
1044     for (i = 0; i < screen_num_desktops + 1; ++i)
1045         area[i] = g_new0(Rect, screen_num_monitors + 1);
1046     area[i] = NULL;
1047      
1048     dims = g_new(gulong, 4 * screen_num_desktops);
1049
1050     for (i = 0; i < screen_num_desktops + 1; ++i) {
1051         Strut *struts;
1052         gint l, r, t, b;
1053
1054         struts = g_new0(Strut, screen_num_monitors);
1055
1056         /* calc the xinerama areas */
1057         for (x = 0; x < screen_num_monitors; ++x) {
1058             area[i][x] = monitor_area[x];
1059             if (x == 0) {
1060                 l = monitor_area[x].x;
1061                 t = monitor_area[x].y;
1062                 r = monitor_area[x].x + monitor_area[x].width - 1;
1063                 b = monitor_area[x].y + monitor_area[x].height - 1;
1064             } else {
1065                 l = MIN(l, monitor_area[x].x);
1066                 t = MIN(t, monitor_area[x].y);
1067                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1068                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1069             }
1070         }
1071         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1072
1073         /* apply the struts */
1074
1075         /* find the left-most xin heads, i do this in 2 loops :| */
1076         o = area[i][0].x;
1077         for (x = 1; x < screen_num_monitors; ++x)
1078             o = MIN(o, area[i][x].x);
1079
1080         for (x = 0; x < screen_num_monitors; ++x) {
1081             for (it = client_list; it; it = g_list_next(it)) {
1082                 ObClient *c = it->data;
1083                 screen_area_add_strut_left(&c->strut,
1084                                            &monitor_area[x],
1085                                            o + c->strut.left - area[i][x].x,
1086                                            &struts[x]);
1087             }
1088             screen_area_add_strut_left(&dock_strut,
1089                                        &monitor_area[x],
1090                                        o + dock_strut.left - area[i][x].x,
1091                                        &struts[x]);
1092
1093             area[i][x].x += struts[x].left;
1094             area[i][x].width -= struts[x].left;
1095         }
1096
1097         /* find the top-most xin heads, i do this in 2 loops :| */
1098         o = area[i][0].y;
1099         for (x = 1; x < screen_num_monitors; ++x)
1100             o = MIN(o, area[i][x].y);
1101
1102         for (x = 0; x < screen_num_monitors; ++x) {
1103             for (it = client_list; it; it = g_list_next(it)) {
1104                 ObClient *c = it->data;
1105                 screen_area_add_strut_top(&c->strut,
1106                                            &monitor_area[x],
1107                                            o + c->strut.top - area[i][x].y,
1108                                            &struts[x]);
1109             }
1110             screen_area_add_strut_top(&dock_strut,
1111                                       &monitor_area[x],
1112                                       o + dock_strut.top - area[i][x].y,
1113                                       &struts[x]);
1114
1115             area[i][x].y += struts[x].top;
1116             area[i][x].height -= struts[x].top;
1117         }
1118
1119         /* find the right-most xin heads, i do this in 2 loops :| */
1120         o = area[i][0].x + area[i][0].width - 1;
1121         for (x = 1; x < screen_num_monitors; ++x)
1122             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1123
1124         for (x = 0; x < screen_num_monitors; ++x) {
1125             for (it = client_list; it; it = g_list_next(it)) {
1126                 ObClient *c = it->data;
1127                 screen_area_add_strut_right(&c->strut,
1128                                            &monitor_area[x],
1129                                            (area[i][x].x +
1130                                             area[i][x].width - 1) -
1131                                             (o - c->strut.right),
1132                                             &struts[x]);
1133             }
1134             screen_area_add_strut_right(&dock_strut,
1135                                         &monitor_area[x],
1136                                         (area[i][x].x +
1137                                          area[i][x].width - 1) -
1138                                         (o - dock_strut.right),
1139                                         &struts[x]);
1140
1141             area[i][x].width -= struts[x].right;
1142         }
1143
1144         /* find the bottom-most xin heads, i do this in 2 loops :| */
1145         o = area[i][0].y + area[i][0].height - 1;
1146         for (x = 1; x < screen_num_monitors; ++x)
1147             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1148
1149         for (x = 0; x < screen_num_monitors; ++x) {
1150             for (it = client_list; it; it = g_list_next(it)) {
1151                 ObClient *c = it->data;
1152                 screen_area_add_strut_bottom(&c->strut,
1153                                              &monitor_area[x],
1154                                              (area[i][x].y +
1155                                               area[i][x].height - 1) - \
1156                                              (o - c->strut.bottom),
1157                                              &struts[x]);
1158             }
1159             screen_area_add_strut_bottom(&dock_strut,
1160                                          &monitor_area[x],
1161                                          (area[i][x].y +
1162                                           area[i][x].height - 1) - \
1163                                          (o - dock_strut.bottom),
1164                                          &struts[x]);
1165
1166             area[i][x].height -= struts[x].bottom;
1167         }
1168
1169         l = RECT_LEFT(area[i][0]);
1170         t = RECT_TOP(area[i][0]);
1171         r = RECT_RIGHT(area[i][0]);
1172         b = RECT_BOTTOM(area[i][0]);
1173         for (x = 1; x < screen_num_monitors; ++x) {
1174             l = MIN(l, RECT_LEFT(area[i][x]));
1175             t = MIN(l, RECT_TOP(area[i][x]));
1176             r = MAX(r, RECT_RIGHT(area[i][x]));
1177             b = MAX(b, RECT_BOTTOM(area[i][x]));
1178         }
1179         RECT_SET(area[i][screen_num_monitors], l, t,
1180                  r - l + 1, b - t + 1);
1181
1182         /* XXX optimize when this is run? */
1183
1184         /* the area has changed, adjust all the maximized 
1185            windows */
1186         for (it = client_list; it; it = g_list_next(it)) {
1187             ObClient *c = it->data; 
1188             if (i < screen_num_desktops) {
1189                 if (c->desktop == i)
1190                     client_reconfigure(c);
1191             } else if (c->desktop == DESKTOP_ALL)
1192                 client_reconfigure(c);
1193         }
1194         if (i < screen_num_desktops) {
1195             /* don't set these for the 'all desktops' area */
1196             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1197             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1198             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1199             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1200         }
1201
1202         g_free(struts);
1203     }
1204
1205     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1206                 dims, 4 * screen_num_desktops);
1207
1208     g_free(dims);
1209 }
1210
1211 Rect *screen_area(guint desktop)
1212 {
1213     return screen_area_monitor(desktop, screen_num_monitors);
1214 }
1215
1216 Rect *screen_area_monitor(guint desktop, guint head)
1217 {
1218     if (head > screen_num_monitors)
1219         return NULL;
1220     if (desktop >= screen_num_desktops) {
1221         if (desktop == DESKTOP_ALL)
1222             return &area[screen_num_desktops][head];
1223         return NULL;
1224     }
1225     return &area[desktop][head];
1226 }
1227
1228 guint screen_find_monitor(Rect *search)
1229 {
1230     guint i;
1231     guint most = 0;
1232     guint mostv = 0;
1233
1234     for (i = 0; i < screen_num_monitors; ++i) {
1235         Rect *area = screen_physical_area_monitor(i);
1236         if (RECT_INTERSECTS_RECT(*area, *search)) {
1237             Rect r;
1238             guint v;
1239
1240             RECT_SET_INTERSECTION(r, *area, *search);
1241             v = r.width * r.height;
1242
1243             if (v > mostv) {
1244                 mostv = v;
1245                 most = i;
1246             }
1247         }
1248     }
1249     return most;
1250 }
1251
1252 Rect *screen_physical_area()
1253 {
1254     return screen_physical_area_monitor(screen_num_monitors);
1255 }
1256
1257 Rect *screen_physical_area_monitor(guint head)
1258 {
1259     if (head > screen_num_monitors)
1260         return NULL;
1261     return &monitor_area[head];
1262 }
1263
1264 void screen_set_root_cursor()
1265 {
1266     if (sn_app_starting())
1267         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1268                       ob_cursor(OB_CURSOR_BUSY));
1269     else
1270         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1271                       ob_cursor(OB_CURSOR_POINTER));
1272 }
1273
1274 gboolean screen_pointer_pos(gint *x, gint *y)
1275 {
1276     Window w;
1277     gint i;
1278     guint u;
1279     gboolean ret;
1280
1281     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1282                           &w, &w, x, y, &i, &i, &u);
1283     if (!ret) {
1284         for (i = 0; i < ScreenCount(ob_display); ++i)
1285             if (i != ob_screen)
1286                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1287                                   &w, &w, x, y, &i, &i, &u))
1288                     break;
1289     }
1290     return ret;
1291 }