comment out the applications example
[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()
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_wm_full_placement;
219     supported[i++] = prop_atoms.net_current_desktop;
220     supported[i++] = prop_atoms.net_number_of_desktops;
221     supported[i++] = prop_atoms.net_desktop_geometry;
222     supported[i++] = prop_atoms.net_desktop_viewport;
223     supported[i++] = prop_atoms.net_active_window;
224     supported[i++] = prop_atoms.net_workarea;
225     supported[i++] = prop_atoms.net_client_list;
226     supported[i++] = prop_atoms.net_client_list_stacking;
227     supported[i++] = prop_atoms.net_desktop_names;
228     supported[i++] = prop_atoms.net_close_window;
229     supported[i++] = prop_atoms.net_desktop_layout;
230     supported[i++] = prop_atoms.net_showing_desktop;
231     supported[i++] = prop_atoms.net_wm_name;
232     supported[i++] = prop_atoms.net_wm_visible_name;
233     supported[i++] = prop_atoms.net_wm_icon_name;
234     supported[i++] = prop_atoms.net_wm_visible_icon_name;
235     supported[i++] = prop_atoms.net_wm_desktop;
236     supported[i++] = prop_atoms.net_wm_strut;
237     supported[i++] = prop_atoms.net_wm_strut_partial;
238     supported[i++] = prop_atoms.net_wm_icon;
239     supported[i++] = prop_atoms.net_wm_icon_geometry;
240     supported[i++] = prop_atoms.net_wm_window_type;
241     supported[i++] = prop_atoms.net_wm_window_type_desktop;
242     supported[i++] = prop_atoms.net_wm_window_type_dock;
243     supported[i++] = prop_atoms.net_wm_window_type_toolbar;
244     supported[i++] = prop_atoms.net_wm_window_type_menu;
245     supported[i++] = prop_atoms.net_wm_window_type_utility;
246     supported[i++] = prop_atoms.net_wm_window_type_splash;
247     supported[i++] = prop_atoms.net_wm_window_type_dialog;
248     supported[i++] = prop_atoms.net_wm_window_type_normal;
249     supported[i++] = prop_atoms.net_wm_allowed_actions;
250     supported[i++] = prop_atoms.net_wm_action_move;
251     supported[i++] = prop_atoms.net_wm_action_resize;
252     supported[i++] = prop_atoms.net_wm_action_minimize;
253     supported[i++] = prop_atoms.net_wm_action_shade;
254     supported[i++] = prop_atoms.net_wm_action_maximize_horz;
255     supported[i++] = prop_atoms.net_wm_action_maximize_vert;
256     supported[i++] = prop_atoms.net_wm_action_fullscreen;
257     supported[i++] = prop_atoms.net_wm_action_change_desktop;
258     supported[i++] = prop_atoms.net_wm_action_close;
259     supported[i++] = prop_atoms.net_wm_state;
260     supported[i++] = prop_atoms.net_wm_state_modal;
261     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
262     supported[i++] = prop_atoms.net_wm_state_maximized_horz;
263     supported[i++] = prop_atoms.net_wm_state_shaded;
264     supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
265     supported[i++] = prop_atoms.net_wm_state_skip_pager;
266     supported[i++] = prop_atoms.net_wm_state_hidden;
267     supported[i++] = prop_atoms.net_wm_state_fullscreen;
268     supported[i++] = prop_atoms.net_wm_state_above;
269     supported[i++] = prop_atoms.net_wm_state_below;
270     supported[i++] = prop_atoms.net_wm_state_demands_attention;
271     supported[i++] = prop_atoms.net_moveresize_window;
272     supported[i++] = prop_atoms.net_wm_moveresize;
273     supported[i++] = prop_atoms.net_wm_user_time;
274     supported[i++] = prop_atoms.net_wm_user_time_window;
275     supported[i++] = prop_atoms.net_frame_extents;
276     supported[i++] = prop_atoms.net_request_frame_extents;
277     supported[i++] = prop_atoms.net_restack_window;
278     supported[i++] = prop_atoms.net_startup_id;
279 #ifdef SYNC
280     supported[i++] = prop_atoms.net_wm_sync_request;
281     supported[i++] = prop_atoms.net_wm_sync_request_counter;
282 #endif
283
284     supported[i++] = prop_atoms.net_supporting_wm_check;
285     supported[i++] = prop_atoms.net_wm_action_above;
286     supported[i++] = prop_atoms.net_wm_action_below;
287
288     supported[i++] = prop_atoms.kde_wm_change_state;
289     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
290     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
291
292     supported[i++] = prop_atoms.ob_wm_action_undecorate;
293     supported[i++] = prop_atoms.ob_wm_state_undecorated;
294     supported[i++] = prop_atoms.openbox_pid;
295     supported[i++] = prop_atoms.ob_config;
296     supported[i++] = prop_atoms.ob_control;
297     g_assert(i == num_support);
298
299     PROP_SETA32(RootWindow(ob_display, ob_screen),
300                 net_supported, atom, supported, num_support);
301     g_free(supported);
302
303     return TRUE;
304 }
305
306 void screen_startup(gboolean reconfig)
307 {
308     guint i, numnames;
309     gchar **names;
310     GSList *it;
311     guint32 d;
312
313     desktop_cycle_popup = pager_popup_new(FALSE);
314     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
315
316     if (reconfig) {
317         /* update the pager popup's width */
318         pager_popup_text_width_to_strings(desktop_cycle_popup,
319                                           screen_desktop_names,
320                                           screen_num_desktops);
321         return;
322     }
323
324     /* get the initial size */
325     screen_resize();
326
327     /* get the desktop names */
328     numnames = g_slist_length(config_desktops_names);
329     names = g_new(gchar*, numnames + 1);
330     names[numnames] = NULL;
331     for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
332         names[i] = g_strdup(it->data);
333
334     /* set the root window property */
335     PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
336
337     g_strfreev(names);
338
339     /* set the number of desktops */
340     screen_num_desktops = 0;
341     screen_set_num_desktops(config_desktops_num);
342
343     /* start on the current desktop when a wm was already running */
344     if (PROP_GET32(RootWindow(ob_display, ob_screen),
345                    net_current_desktop, cardinal, &d) &&
346         d < screen_num_desktops)
347     {
348         screen_set_desktop(d, FALSE);
349     } else if (session_desktop >= 0)
350         screen_set_desktop(MIN((guint)session_desktop,
351                                screen_num_desktops), FALSE);
352     else
353         screen_set_desktop(MIN(config_screen_firstdesk,
354                                screen_num_desktops) - 1, FALSE);
355
356     /* don't start in showing-desktop mode */
357     screen_showing_desktop = FALSE;
358     PROP_SET32(RootWindow(ob_display, ob_screen),
359                net_showing_desktop, cardinal, screen_showing_desktop);
360
361     screen_update_layout();
362 }
363
364 void screen_shutdown(gboolean reconfig)
365 {
366     Rect **r;
367
368     pager_popup_free(desktop_cycle_popup);
369
370     if (reconfig)
371         return;
372
373     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
374                  NoEventMask);
375
376     /* we're not running here no more! */
377     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
378     /* not without us */
379     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
380     /* don't keep this mode */
381     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
382
383     XDestroyWindow(ob_display, screen_support_win);
384
385     g_strfreev(screen_desktop_names);
386     screen_desktop_names = NULL;
387
388     for (r = area; *r; ++r)
389         g_free(*r);
390     g_free(area);
391     area = NULL;
392 }
393
394 void screen_resize()
395 {
396     static gint oldw = 0, oldh = 0;
397     gint w, h;
398     GList *it;
399     gulong geometry[2];
400
401     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
402     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
403
404     if (w == oldw && h == oldh) return;
405
406     oldw = w; oldh = h;
407
408     /* Set the _NET_DESKTOP_GEOMETRY hint */
409     screen_physical_size.width = geometry[0] = w;
410     screen_physical_size.height = geometry[1] = h;
411     PROP_SETA32(RootWindow(ob_display, ob_screen),
412                 net_desktop_geometry, cardinal, geometry, 2);
413
414     if (ob_state() == OB_STATE_STARTING)
415         return;
416
417     screen_update_areas();
418     dock_configure();
419
420     for (it = client_list; it; it = g_list_next(it))
421         client_move_onscreen(it->data, FALSE);
422 }
423
424 void screen_set_num_desktops(guint num)
425 {
426     guint old;
427     gulong *viewport;
428     GList *it;
429
430     g_assert(num > 0);
431
432     if (screen_num_desktops == num) return;
433
434     old = screen_num_desktops;
435     screen_num_desktops = num;
436     PROP_SET32(RootWindow(ob_display, ob_screen),
437                net_number_of_desktops, cardinal, num);
438
439     /* set the viewport hint */
440     viewport = g_new0(gulong, num * 2);
441     PROP_SETA32(RootWindow(ob_display, ob_screen),
442                 net_desktop_viewport, cardinal, viewport, num * 2);
443     g_free(viewport);
444
445     /* the number of rows/columns will differ */
446     screen_update_layout();
447
448     /* move windows on desktops that will no longer exist! */
449     for (it = client_list; it; it = g_list_next(it)) {
450         ObClient *c = it->data;
451         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
452             client_set_desktop(c, num - 1, FALSE);
453     }
454  
455     /* change our struts/area to match (after moving windows) */
456     screen_update_areas();
457
458     /* may be some unnamed desktops that we need to fill in with names
459      (after updating the areas so the popup can resize) */
460     screen_update_desktop_names();
461
462     /* change our desktop if we're on one that no longer exists! */
463     if (screen_desktop >= screen_num_desktops)
464         screen_set_desktop(num - 1, TRUE);
465 }
466
467 void screen_set_desktop(guint num, gboolean dofocus)
468 {
469     ObClient *c;
470     GList *it;
471     guint old;
472      
473     g_assert(num < screen_num_desktops);
474
475     old = screen_desktop;
476     screen_desktop = num;
477     PROP_SET32(RootWindow(ob_display, ob_screen),
478                net_current_desktop, cardinal, num);
479
480     if (old == num) return;
481
482     screen_last_desktop = old;
483
484     ob_debug("Moving to desktop %d\n", num+1);
485
486     if (moveresize_client)
487         client_set_desktop(moveresize_client, num, TRUE);
488
489     /* show windows before hiding the rest to lessen the enter/leave events */
490
491     /* show windows from top to bottom */
492     for (it = stacking_list; it; it = g_list_next(it)) {
493         if (WINDOW_IS_CLIENT(it->data)) {
494             ObClient *c = it->data;
495             client_show(c);
496         }
497     }
498
499     /* have to try focus here because when you leave an empty desktop
500        there is no focus out to watch for
501
502        do this before hiding the windows so if helper windows are coming
503        with us, they don't get hidden
504     */
505     if (dofocus && (c = focus_fallback(TRUE))) {
506         /* only do the flicker reducing stuff ahead of time if we are going
507            to call xsetinputfocus on the window ourselves. otherwise there is
508            no guarantee the window will actually take focus.. */
509         if (c->can_focus) {
510             /* reduce flicker by hiliting now rather than waiting for the
511                server FocusIn event */
512             frame_adjust_focus(c->frame, TRUE);
513             /* do this here so that if you switch desktops to a window with
514                helper windows then the helper windows won't flash */
515             client_bring_helper_windows(c);
516         }
517     }
518
519     /* hide windows from bottom to top */
520     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
521         if (WINDOW_IS_CLIENT(it->data)) {
522             ObClient *c = it->data;
523             client_hide(c);
524         }
525     }
526
527     event_ignore_all_queued_enters();
528
529     if (event_curtime != CurrentTime)
530         screen_desktop_user_time = event_curtime;
531 }
532
533 static void get_row_col(guint d, guint *r, guint *c)
534 {
535     switch (screen_desktop_layout.orientation) {
536     case OB_ORIENTATION_HORZ:
537         switch (screen_desktop_layout.start_corner) {
538         case OB_CORNER_TOPLEFT:
539             *r = d / screen_desktop_layout.columns;
540             *c = d % screen_desktop_layout.columns;
541             break;
542         case OB_CORNER_BOTTOMLEFT:
543             *r = screen_desktop_layout.rows - 1 -
544                 d / screen_desktop_layout.columns;
545             *c = d % screen_desktop_layout.columns;
546             break;
547         case OB_CORNER_TOPRIGHT:
548             *r = d / screen_desktop_layout.columns;
549             *c = screen_desktop_layout.columns - 1 -
550                 d % screen_desktop_layout.columns;
551             break;
552         case OB_CORNER_BOTTOMRIGHT:
553             *r = screen_desktop_layout.rows - 1 -
554                 d / screen_desktop_layout.columns;
555             *c = screen_desktop_layout.columns - 1 -
556                 d % screen_desktop_layout.columns;
557             break;
558         }
559         break;
560     case OB_ORIENTATION_VERT:
561         switch (screen_desktop_layout.start_corner) {
562         case OB_CORNER_TOPLEFT:
563             *r = d % screen_desktop_layout.rows;
564             *c = d / screen_desktop_layout.rows;
565             break;
566         case OB_CORNER_BOTTOMLEFT:
567             *r = screen_desktop_layout.rows - 1 -
568                 d % screen_desktop_layout.rows;
569             *c = d / screen_desktop_layout.rows;
570             break;
571         case OB_CORNER_TOPRIGHT:
572             *r = d % screen_desktop_layout.rows;
573             *c = screen_desktop_layout.columns - 1 -
574                 d / screen_desktop_layout.rows;
575             break;
576         case OB_CORNER_BOTTOMRIGHT:
577             *r = screen_desktop_layout.rows - 1 -
578                 d % screen_desktop_layout.rows;
579             *c = screen_desktop_layout.columns - 1 -
580                 d / screen_desktop_layout.rows;
581             break;
582         }
583         break;
584     }
585 }
586
587 static guint translate_row_col(guint r, guint c)
588 {
589     switch (screen_desktop_layout.orientation) {
590     case OB_ORIENTATION_HORZ:
591         switch (screen_desktop_layout.start_corner) {
592         case OB_CORNER_TOPLEFT:
593             return r % screen_desktop_layout.rows *
594                 screen_desktop_layout.columns +
595                 c % screen_desktop_layout.columns;
596         case OB_CORNER_BOTTOMLEFT:
597             return (screen_desktop_layout.rows - 1 -
598                     r % screen_desktop_layout.rows) *
599                 screen_desktop_layout.columns +
600                 c % screen_desktop_layout.columns;
601         case OB_CORNER_TOPRIGHT:
602             return r % screen_desktop_layout.rows *
603                 screen_desktop_layout.columns +
604                 (screen_desktop_layout.columns - 1 -
605                  c % screen_desktop_layout.columns);
606         case OB_CORNER_BOTTOMRIGHT:
607             return (screen_desktop_layout.rows - 1 -
608                     r % screen_desktop_layout.rows) *
609                 screen_desktop_layout.columns +
610                 (screen_desktop_layout.columns - 1 -
611                  c % screen_desktop_layout.columns);
612         }
613     case OB_ORIENTATION_VERT:
614         switch (screen_desktop_layout.start_corner) {
615         case OB_CORNER_TOPLEFT:
616             return c % screen_desktop_layout.columns *
617                 screen_desktop_layout.rows +
618                 r % screen_desktop_layout.rows;
619         case OB_CORNER_BOTTOMLEFT:
620             return c % screen_desktop_layout.columns *
621                 screen_desktop_layout.rows +
622                 (screen_desktop_layout.rows - 1 -
623                  r % screen_desktop_layout.rows);
624         case OB_CORNER_TOPRIGHT:
625             return (screen_desktop_layout.columns - 1 -
626                     c % screen_desktop_layout.columns) *
627                 screen_desktop_layout.rows +
628                 r % screen_desktop_layout.rows;
629         case OB_CORNER_BOTTOMRIGHT:
630             return (screen_desktop_layout.columns - 1 -
631                     c % screen_desktop_layout.columns) *
632                 screen_desktop_layout.rows +
633                 (screen_desktop_layout.rows - 1 -
634                  r % screen_desktop_layout.rows);
635         }
636     }
637     g_assert_not_reached();
638     return 0;
639 }
640
641 void screen_desktop_popup(guint d, gboolean show)
642 {
643     Rect *a;
644
645     if (!show) {
646         pager_popup_hide(desktop_cycle_popup);
647     } else {
648         a = screen_physical_area_monitor(0);
649         pager_popup_position(desktop_cycle_popup, CenterGravity,
650                              a->x + a->width / 2, a->y + a->height / 2);
651         pager_popup_icon_size_multiplier(desktop_cycle_popup,
652                                          (screen_desktop_layout.columns /
653                                           screen_desktop_layout.rows) / 2,
654                                          (screen_desktop_layout.rows/
655                                           screen_desktop_layout.columns) / 2);
656         pager_popup_max_width(desktop_cycle_popup,
657                               MAX(a->width/3, POPUP_WIDTH));
658         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
659     }
660 }
661
662 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
663                            gboolean dialog, gboolean done, gboolean cancel)
664 {
665     guint r, c;
666     static guint d = (guint)-1;
667     guint ret, oldd;
668
669     if (d == (guint)-1)
670         d = screen_desktop;
671
672     if ((cancel || done) && dialog)
673         goto show_cycle_dialog;
674
675     oldd = d;
676     get_row_col(d, &r, &c);
677
678     if (linear) {
679         switch (dir) {
680         case OB_DIRECTION_EAST:
681             if (d < screen_num_desktops - 1)
682                 ++d;
683             else if (wrap)
684                 d = 0;
685             break;
686         case OB_DIRECTION_WEST:
687             if (d > 0)
688                 --d;
689             else if (wrap)
690                 d = screen_num_desktops - 1;
691             break;
692         default:
693             assert(0);
694             return screen_desktop;
695         }
696     } else {
697         switch (dir) {
698         case OB_DIRECTION_EAST:
699             ++c;
700             if (c >= screen_desktop_layout.columns) {
701                 if (wrap)
702                     c = 0;
703                 else
704                     goto show_cycle_dialog;
705             }
706             d = translate_row_col(r, c);
707             if (d >= screen_num_desktops) {
708                 if (wrap) {
709                     ++c;
710                 } else {
711                     d = oldd;
712                     goto show_cycle_dialog;
713                 }
714             }
715             break;
716         case OB_DIRECTION_WEST:
717             --c;
718             if (c >= screen_desktop_layout.columns) {
719                 if (wrap)
720                     c = screen_desktop_layout.columns - 1;
721                 else
722                     goto show_cycle_dialog;
723             }
724             d = translate_row_col(r, c);
725             if (d >= screen_num_desktops) {
726                 if (wrap) {
727                     --c;
728                 } else {
729                     d = oldd;
730                     goto show_cycle_dialog;
731                 }
732             }
733             break;
734         case OB_DIRECTION_SOUTH:
735             ++r;
736             if (r >= screen_desktop_layout.rows) {
737                 if (wrap)
738                     r = 0;
739                 else
740                     goto show_cycle_dialog;
741             }
742             d = translate_row_col(r, c);
743             if (d >= screen_num_desktops) {
744                 if (wrap) {
745                     ++r;
746                 } else {
747                     d = oldd;
748                     goto show_cycle_dialog;
749                 }
750             }
751             break;
752         case OB_DIRECTION_NORTH:
753             --r;
754             if (r >= screen_desktop_layout.rows) {
755                 if (wrap)
756                     r = screen_desktop_layout.rows - 1;
757                 else
758                     goto show_cycle_dialog;
759             }
760             d = translate_row_col(r, c);
761             if (d >= screen_num_desktops) {
762                 if (wrap) {
763                     --r;
764                 } else {
765                     d = oldd;
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 the 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         if ((c = focus_fallback(TRUE))) {
955             /* only do the flicker reducing stuff ahead of time if we are going
956                to call xsetinputfocus on the window ourselves. otherwise there
957                is no guarantee the window will actually take focus.. */
958             if (c->can_focus) {
959                 /* reduce flicker by hiliting now rather than waiting for the
960                    server FocusIn event */
961                 frame_adjust_focus(c->frame, TRUE);
962             }
963         }
964     }
965
966     show = !!show; /* make it boolean */
967     PROP_SET32(RootWindow(ob_display, ob_screen),
968                net_showing_desktop, cardinal, show);
969 }
970
971 void screen_install_colormap(ObClient *client, gboolean install)
972 {
973     if (client == NULL) {
974         if (install)
975             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
976         else
977             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
978     } else {
979         xerror_set_ignore(TRUE);
980         if (install) {
981             if (client->colormap != None)
982                 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
983         } else
984             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
985         xerror_set_ignore(FALSE);
986     }
987 }
988
989 static inline void
990 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
991                            gint edge, Strut *ret)
992 {
993     if (s->left &&
994         ((s->left_end <= s->left_start) ||
995          (RECT_TOP(*monitor_area) < s->left_end &&
996           RECT_BOTTOM(*monitor_area) > s->left_start)))
997         ret->left = MAX(ret->left, edge);
998 }
999
1000 static inline void
1001 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1002                           gint edge, Strut *ret)
1003 {
1004     if (s->top &&
1005         ((s->top_end <= s->top_start) ||
1006          (RECT_LEFT(*monitor_area) < s->top_end &&
1007           RECT_RIGHT(*monitor_area) > s->top_start)))
1008         ret->top = MAX(ret->top, edge);
1009 }
1010
1011 static inline void
1012 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1013                             gint edge, Strut *ret)
1014 {
1015     if (s->right &&
1016         ((s->right_end <= s->right_start) ||
1017          (RECT_TOP(*monitor_area) < s->right_end &&
1018           RECT_BOTTOM(*monitor_area) > s->right_start)))
1019         ret->right = MAX(ret->right, edge);
1020 }
1021
1022 static inline void
1023 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1024                              gint edge, Strut *ret)
1025 {
1026     if (s->bottom &&
1027         ((s->bottom_end <= s->bottom_start) ||
1028          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1029           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1030         ret->bottom = MAX(ret->bottom, edge);
1031 }
1032
1033 void screen_update_areas()
1034 {
1035     guint i, x;
1036     gulong *dims;
1037     GList *it;
1038     gint o;
1039
1040     g_free(monitor_area);
1041     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1042
1043     if (area) {
1044         for (i = 0; area[i]; ++i)
1045             g_free(area[i]);
1046         g_free(area);
1047     }
1048
1049     area = g_new(Rect*, screen_num_desktops + 2);
1050     for (i = 0; i < screen_num_desktops + 1; ++i)
1051         area[i] = g_new0(Rect, screen_num_monitors + 1);
1052     area[i] = NULL;
1053      
1054     dims = g_new(gulong, 4 * screen_num_desktops);
1055
1056     for (i = 0; i < screen_num_desktops + 1; ++i) {
1057         Strut *struts;
1058         gint l, r, t, b;
1059
1060         struts = g_new0(Strut, screen_num_monitors);
1061
1062         /* calc the xinerama areas */
1063         for (x = 0; x < screen_num_monitors; ++x) {
1064             area[i][x] = monitor_area[x];
1065             if (x == 0) {
1066                 l = monitor_area[x].x;
1067                 t = monitor_area[x].y;
1068                 r = monitor_area[x].x + monitor_area[x].width - 1;
1069                 b = monitor_area[x].y + monitor_area[x].height - 1;
1070             } else {
1071                 l = MIN(l, monitor_area[x].x);
1072                 t = MIN(t, monitor_area[x].y);
1073                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1074                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1075             }
1076         }
1077         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1078
1079         /* apply the struts */
1080
1081         /* find the left-most xin heads, i do this in 2 loops :| */
1082         o = area[i][0].x;
1083         for (x = 1; x < screen_num_monitors; ++x)
1084             o = MIN(o, area[i][x].x);
1085
1086         for (x = 0; x < screen_num_monitors; ++x) {
1087             for (it = client_list; it; it = g_list_next(it)) {
1088                 ObClient *c = it->data;
1089                 screen_area_add_strut_left(&c->strut,
1090                                            &monitor_area[x],
1091                                            o + c->strut.left - area[i][x].x,
1092                                            &struts[x]);
1093             }
1094             screen_area_add_strut_left(&dock_strut,
1095                                        &monitor_area[x],
1096                                        o + dock_strut.left - area[i][x].x,
1097                                        &struts[x]);
1098
1099             area[i][x].x += struts[x].left;
1100             area[i][x].width -= struts[x].left;
1101         }
1102
1103         /* find the top-most xin heads, i do this in 2 loops :| */
1104         o = area[i][0].y;
1105         for (x = 1; x < screen_num_monitors; ++x)
1106             o = MIN(o, area[i][x].y);
1107
1108         for (x = 0; x < screen_num_monitors; ++x) {
1109             for (it = client_list; it; it = g_list_next(it)) {
1110                 ObClient *c = it->data;
1111                 screen_area_add_strut_top(&c->strut,
1112                                            &monitor_area[x],
1113                                            o + c->strut.top - area[i][x].y,
1114                                            &struts[x]);
1115             }
1116             screen_area_add_strut_top(&dock_strut,
1117                                       &monitor_area[x],
1118                                       o + dock_strut.top - area[i][x].y,
1119                                       &struts[x]);
1120
1121             area[i][x].y += struts[x].top;
1122             area[i][x].height -= struts[x].top;
1123         }
1124
1125         /* find the right-most xin heads, i do this in 2 loops :| */
1126         o = area[i][0].x + area[i][0].width - 1;
1127         for (x = 1; x < screen_num_monitors; ++x)
1128             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1129
1130         for (x = 0; x < screen_num_monitors; ++x) {
1131             for (it = client_list; it; it = g_list_next(it)) {
1132                 ObClient *c = it->data;
1133                 screen_area_add_strut_right(&c->strut,
1134                                            &monitor_area[x],
1135                                            (area[i][x].x +
1136                                             area[i][x].width - 1) -
1137                                             (o - c->strut.right),
1138                                             &struts[x]);
1139             }
1140             screen_area_add_strut_right(&dock_strut,
1141                                         &monitor_area[x],
1142                                         (area[i][x].x +
1143                                          area[i][x].width - 1) -
1144                                         (o - dock_strut.right),
1145                                         &struts[x]);
1146
1147             area[i][x].width -= struts[x].right;
1148         }
1149
1150         /* find the bottom-most xin heads, i do this in 2 loops :| */
1151         o = area[i][0].y + area[i][0].height - 1;
1152         for (x = 1; x < screen_num_monitors; ++x)
1153             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1154
1155         for (x = 0; x < screen_num_monitors; ++x) {
1156             for (it = client_list; it; it = g_list_next(it)) {
1157                 ObClient *c = it->data;
1158                 screen_area_add_strut_bottom(&c->strut,
1159                                              &monitor_area[x],
1160                                              (area[i][x].y +
1161                                               area[i][x].height - 1) - \
1162                                              (o - c->strut.bottom),
1163                                              &struts[x]);
1164             }
1165             screen_area_add_strut_bottom(&dock_strut,
1166                                          &monitor_area[x],
1167                                          (area[i][x].y +
1168                                           area[i][x].height - 1) - \
1169                                          (o - dock_strut.bottom),
1170                                          &struts[x]);
1171
1172             area[i][x].height -= struts[x].bottom;
1173         }
1174
1175         l = RECT_LEFT(area[i][0]);
1176         t = RECT_TOP(area[i][0]);
1177         r = RECT_RIGHT(area[i][0]);
1178         b = RECT_BOTTOM(area[i][0]);
1179         for (x = 1; x < screen_num_monitors; ++x) {
1180             l = MIN(l, RECT_LEFT(area[i][x]));
1181             t = MIN(l, RECT_TOP(area[i][x]));
1182             r = MAX(r, RECT_RIGHT(area[i][x]));
1183             b = MAX(b, RECT_BOTTOM(area[i][x]));
1184         }
1185         RECT_SET(area[i][screen_num_monitors], l, t,
1186                  r - l + 1, b - t + 1);
1187
1188         /* XXX optimize when this is run? */
1189
1190         /* the area has changed, adjust all the maximized 
1191            windows */
1192         for (it = client_list; it; it = g_list_next(it)) {
1193             ObClient *c = it->data; 
1194             if (i < screen_num_desktops) {
1195                 if (c->desktop == i)
1196                     client_reconfigure(c);
1197             } else if (c->desktop == DESKTOP_ALL)
1198                 client_reconfigure(c);
1199         }
1200         if (i < screen_num_desktops) {
1201             /* don't set these for the 'all desktops' area */
1202             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1203             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1204             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1205             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1206         }
1207
1208         g_free(struts);
1209     }
1210
1211     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1212                 dims, 4 * screen_num_desktops);
1213
1214     g_free(dims);
1215 }
1216
1217 Rect *screen_area(guint desktop)
1218 {
1219     return screen_area_monitor(desktop, screen_num_monitors);
1220 }
1221
1222 Rect *screen_area_monitor(guint desktop, guint head)
1223 {
1224     if (head > screen_num_monitors)
1225         return NULL;
1226     if (desktop >= screen_num_desktops) {
1227         if (desktop == DESKTOP_ALL)
1228             return &area[screen_num_desktops][head];
1229         return NULL;
1230     }
1231     return &area[desktop][head];
1232 }
1233
1234 guint screen_find_monitor(Rect *search)
1235 {
1236     guint i;
1237     guint most = 0;
1238     guint mostv = 0;
1239
1240     for (i = 0; i < screen_num_monitors; ++i) {
1241         Rect *area = screen_physical_area_monitor(i);
1242         if (RECT_INTERSECTS_RECT(*area, *search)) {
1243             Rect r;
1244             guint v;
1245
1246             RECT_SET_INTERSECTION(r, *area, *search);
1247             v = r.width * r.height;
1248
1249             if (v > mostv) {
1250                 mostv = v;
1251                 most = i;
1252             }
1253         }
1254     }
1255     return most;
1256 }
1257
1258 Rect *screen_physical_area()
1259 {
1260     return screen_physical_area_monitor(screen_num_monitors);
1261 }
1262
1263 Rect *screen_physical_area_monitor(guint head)
1264 {
1265     if (head > screen_num_monitors)
1266         return NULL;
1267     return &monitor_area[head];
1268 }
1269
1270 void screen_set_root_cursor()
1271 {
1272     if (sn_app_starting())
1273         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1274                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1275     else
1276         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1277                       ob_cursor(OB_CURSOR_POINTER));
1278 }
1279
1280 gboolean screen_pointer_pos(gint *x, gint *y)
1281 {
1282     Window w;
1283     gint i;
1284     guint u;
1285     gboolean ret;
1286
1287     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1288                           &w, &w, x, y, &i, &i, &u);
1289     if (!ret) {
1290         for (i = 0; i < ScreenCount(ob_display); ++i)
1291             if (i != ob_screen)
1292                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1293                                   &w, &w, x, y, &i, &i, &u))
1294                     break;
1295     }
1296     return ret;
1297 }