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