1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 screen.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
24 #include "startupnotify.h"
25 #include "moveresize.h"
33 #include "focus_cycle.h"
36 #include "obrender/render.h"
38 #include "obt/display.h"
39 #include "obt/xqueue.h"
44 # include <sys/types.h>
49 /*! The event mask to grab on the root window */
50 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
51 EnterWindowMask | LeaveWindowMask | \
52 SubstructureRedirectMask | FocusChangeMask | \
53 ButtonPressMask | ButtonReleaseMask)
55 static gboolean screen_validate_layout(ObDesktopLayout *l);
56 static gboolean replace_wm(void);
57 static void screen_fallback_focus(void);
59 guint screen_num_desktops;
60 guint screen_num_monitors;
62 guint screen_last_desktop;
63 ObScreenShowDestopMode screen_show_desktop_mode;
64 ObDesktopLayout screen_desktop_layout;
65 gchar **screen_desktop_names;
66 Window screen_support_win;
67 Time screen_desktop_user_time = CurrentTime;
69 static Size screen_physical_size;
70 static guint screen_old_desktop;
71 static gboolean screen_desktop_timeout = TRUE;
72 static guint screen_desktop_timer = 0;
73 /*! An array of desktops, holding an array of areas per monitor */
74 static Rect *monitor_area = NULL;
75 /*! An array of desktops, holding an array of struts */
76 static GSList *struts_top = NULL;
77 static GSList *struts_left = NULL;
78 static GSList *struts_right = NULL;
79 static GSList *struts_bottom = NULL;
81 static ObPagerPopup **desktop_popup;
82 static guint desktop_popup_timer = 0;
83 static gboolean desktop_popup_perm;
85 /*! The number of microseconds that you need to be on a desktop before it will
86 replace the remembered "last desktop" */
87 #define REMEMBER_LAST_DESKTOP_TIME 750
89 static gboolean replace_wm(void)
93 Window current_wm_sn_owner;
96 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
97 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
100 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
101 if (current_wm_sn_owner == screen_support_win)
102 current_wm_sn_owner = None;
103 if (current_wm_sn_owner) {
104 if (!ob_replace_wm) {
105 g_message(_("A window manager is already running on screen %d"),
109 obt_display_ignore_errors(TRUE);
111 /* We want to find out when the current selection owner dies */
112 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
113 XSync(obt_display, FALSE);
115 obt_display_ignore_errors(FALSE);
116 if (obt_display_error_occured)
117 current_wm_sn_owner = None;
120 timestamp = event_time();
122 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
125 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
126 g_message(_("Could not acquire window manager selection on screen %d"),
131 /* Wait for old window manager to go away */
132 if (current_wm_sn_owner) {
134 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
135 ObtXQueueWindowType wt;
137 wt.window = current_wm_sn_owner;
138 wt.type = DestroyNotify;
140 while (wait < timeout) {
141 /* Checks the local queue and incoming events for this event */
142 if (xqueue_exists_local(xqueue_match_window_type, &wt))
144 g_usleep(G_USEC_PER_SEC / 10);
145 wait += G_USEC_PER_SEC / 10;
148 if (wait >= timeout) {
149 g_message(_("The WM on screen %d is not exiting"), ob_screen);
154 /* Send client message indicating that we are now the WM */
155 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
156 timestamp, wm_sn_atom, screen_support_win, 0, 0,
157 SubstructureNotifyMask);
162 gboolean screen_annex(void)
164 XSetWindowAttributes attrib;
169 /* create the netwm support window */
170 attrib.override_redirect = TRUE;
171 attrib.event_mask = PropertyChangeMask;
172 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
174 CopyFromParent, InputOutput,
176 CWEventMask | CWOverrideRedirect,
178 XMapWindow(obt_display, screen_support_win);
179 XLowerWindow(obt_display, screen_support_win);
182 XDestroyWindow(obt_display, screen_support_win);
186 obt_display_ignore_errors(TRUE);
187 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
188 obt_display_ignore_errors(FALSE);
189 if (obt_display_error_occured) {
190 g_message(_("A window manager is already running on screen %d"),
193 XDestroyWindow(obt_display, screen_support_win);
197 screen_set_root_cursor();
199 /* set the OPENBOX_PID hint */
201 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
203 /* set supporting window */
204 OBT_PROP_SET32(obt_root(ob_screen),
205 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
207 /* set properties on the supporting window */
208 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
209 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
210 WINDOW, screen_support_win);
212 /* set the _NET_SUPPORTED_ATOMS hint */
214 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
215 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
217 supported = g_new(gulong, num_support);
218 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
219 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
220 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
221 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
222 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
223 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
224 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
225 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
226 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
227 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
228 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
229 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
230 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
231 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
232 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
233 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
268 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
271 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
273 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
274 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
275 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
276 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
277 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
279 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
281 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
282 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
283 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
284 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
286 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
287 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
289 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
290 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
292 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
293 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
294 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
296 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
297 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
298 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
299 supported[i++] = OBT_PROP_ATOM(OB_THEME);
300 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
301 supported[i++] = OBT_PROP_ATOM(OB_LAST_DESKTOP);
302 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
303 supported[i++] = OBT_PROP_ATOM(OB_VERSION);
304 supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
305 supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
306 supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
307 supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
308 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
309 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
310 supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
311 g_assert(i == num_support);
313 OBT_PROP_SETA32(obt_root(ob_screen),
314 NET_SUPPORTED, ATOM, supported, num_support);
317 OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
323 static void desktop_popup_new()
326 desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
327 for (i = 0; i < screen_num_monitors; i++) {
328 desktop_popup[i] = pager_popup_new();
329 desktop_popup[i]->popup->a_text->texture[0].data.text.font = ob_rr_theme->menu_title_font;
330 pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
332 /* update the pager popup's width */
333 if (screen_desktop_names)
334 pager_popup_text_width_to_strings(desktop_popup[i],
335 screen_desktop_names,
336 screen_num_desktops);
341 void screen_startup(gboolean reconfig)
343 gchar **names = NULL;
345 gboolean namesexist = FALSE;
347 desktop_popup_perm = FALSE;
354 ob_debug("desktop_popup wasn't NULL when expected %x", desktop_popup);
355 desktop_popup = NULL;
358 /* get the initial size */
361 /* have names already been set for the desktops? */
362 if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
367 /* if names don't exist and we have session names, set those.
368 do this stuff BEFORE setting the number of desktops, because that
369 will create default names for them
371 if (!namesexist && session_desktop_names != NULL) {
375 /* get the desktop names */
376 numnames = g_slist_length(session_desktop_names);
377 names = g_new(gchar*, numnames + 1);
378 names[numnames] = NULL;
379 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
380 names[i] = g_strdup(it->data);
382 /* set the root window property */
383 OBT_PROP_SETSS(obt_root(ob_screen),
384 NET_DESKTOP_NAMES, (const gchar*const*)names);
389 /* set the number of desktops, if it's not already set.
391 this will also set the default names from the config file up for
392 desktops that don't have names yet */
393 screen_num_desktops = 0;
394 if (OBT_PROP_GET32(obt_root(ob_screen),
395 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
397 if (d != config_desktops_num) {
398 /* TRANSLATORS: If you need to specify a different order of the
399 arguments, you can use %1$d for the first one and %2$d for the
400 second one. For example,
401 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
402 g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d. Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration.", config_desktops_num),
403 config_desktops_num, d);
405 screen_set_num_desktops(d);
407 /* restore from session if possible */
408 else if (session_num_desktops)
409 screen_set_num_desktops(session_num_desktops);
411 screen_set_num_desktops(config_desktops_num);
413 screen_desktop = screen_num_desktops; /* something invalid */
414 /* start on the current desktop when a wm was already running */
415 if (OBT_PROP_GET32(obt_root(ob_screen),
416 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
417 d < screen_num_desktops)
419 screen_set_desktop(d, FALSE);
420 } else if (session_desktop >= 0)
421 screen_set_desktop(MIN((guint)session_desktop,
422 screen_num_desktops), FALSE);
424 screen_set_desktop(MIN(config_screen_firstdesk,
425 screen_num_desktops) - 1, FALSE);
426 OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
427 if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
428 screen_last_desktop = screen_desktop;
429 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
432 /* don't start in showing-desktop mode */
433 screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
434 OBT_PROP_SET32(obt_root(ob_screen),
435 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
437 if (session_desktop_layout_present &&
438 screen_validate_layout(&session_desktop_layout))
440 screen_desktop_layout = session_desktop_layout;
443 screen_update_layout();
446 static void desktop_popup_free(guint n)
449 for (i = 0; i < n; i++) {
450 pager_popup_free(desktop_popup[i]);
452 g_free(desktop_popup);
453 desktop_popup = NULL;
456 void screen_shutdown(gboolean reconfig)
458 desktop_popup_free(screen_num_monitors);
463 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
465 /* we're not running here no more! */
466 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
468 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
469 /* don't keep this mode */
470 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
472 XDestroyWindow(obt_display, screen_support_win);
474 g_strfreev(screen_desktop_names);
475 screen_desktop_names = NULL;
478 void screen_resize(void)
484 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
485 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
487 /* Set the _NET_DESKTOP_GEOMETRY hint */
488 screen_physical_size.width = geometry[0] = w;
489 screen_physical_size.height = geometry[1] = h;
490 OBT_PROP_SETA32(obt_root(ob_screen),
491 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
493 if (ob_state() != OB_STATE_RUNNING)
496 /* this calls screen_update_areas(), which we need ! */
499 for (it = client_list; it; it = g_list_next(it)) {
500 client_move_onscreen(it->data, FALSE);
501 client_reconfigure(it->data, FALSE);
505 void screen_set_num_desktops(guint num)
508 GList *it, *stacking_copy;
512 if (screen_num_desktops == num) return;
514 screen_num_desktops = num;
515 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
517 /* set the viewport hint */
518 viewport = g_new0(gulong, num * 2);
519 OBT_PROP_SETA32(obt_root(ob_screen),
520 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
523 /* the number of rows/columns will differ */
524 screen_update_layout();
526 /* move windows on desktops that will no longer exist!
527 make a copy of the list cuz we're changing it */
528 stacking_copy = g_list_copy(stacking_list);
529 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
530 if (WINDOW_IS_CLIENT(it->data)) {
531 ObClient *c = it->data;
532 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
533 client_set_desktop(c, num - 1, FALSE, TRUE);
534 /* raise all the windows that are on the current desktop which
536 else if (screen_desktop == num - 1 &&
537 (c->desktop == DESKTOP_ALL ||
538 c->desktop == screen_desktop))
539 stacking_raise(CLIENT_AS_WINDOW(c));
542 g_list_free(stacking_copy);
544 /* change our struts/area to match (after moving windows) */
545 screen_update_areas();
547 /* may be some unnamed desktops that we need to fill in with names
548 (after updating the areas so the popup can resize) */
549 screen_update_desktop_names();
551 /* change our desktop if we're on one that no longer exists! */
552 if (screen_desktop >= screen_num_desktops)
553 screen_set_desktop(num - 1, TRUE);
556 static void screen_fallback_focus(void)
561 /* only allow omnipresent windows to get focus on desktop change if
562 an omnipresent window is already focused (it'll keep focus probably, but
563 maybe not depending on mouse-focus options) */
564 allow_omni = focus_client && (client_normal(focus_client) &&
565 focus_client->desktop == DESKTOP_ALL);
567 /* the client moved there already so don't move focus. prevent flicker
568 on sendtodesktop + follow */
569 if (focus_client && focus_client->desktop == screen_desktop)
572 /* have to try focus here because when you leave an empty desktop
573 there is no focus out to watch for. also, we have different rules
574 here. we always allow it to look under the mouse pointer if
575 config_focus_last is FALSE
577 do this before hiding the windows so if helper windows are coming
578 with us, they don't get hidden
580 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
583 /* only do the flicker reducing stuff ahead of time if we are going
584 to call xsetinputfocus on the window ourselves. otherwise there is
585 no guarantee the window will actually take focus.. */
587 /* reduce flicker by hiliting now rather than waiting for the
588 server FocusIn event */
589 frame_adjust_focus(c->frame, TRUE);
590 /* do this here so that if you switch desktops to a window with
591 helper windows then the helper windows won't flash */
592 client_bring_helper_windows(c);
597 static gboolean last_desktop_func(gpointer data)
599 screen_desktop_timeout = TRUE;
600 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
601 screen_desktop_timer = 0;
602 return FALSE; /* don't repeat */
605 void screen_set_desktop(guint num, gboolean dofocus)
611 g_assert(num < screen_num_desktops);
613 previous = screen_desktop;
614 screen_desktop = num;
616 if (ob_state() == OB_STATE_RUNNING)
617 screen_show_desktop_popup(screen_desktop, FALSE);
619 if (previous == num) return;
621 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
623 /* This whole thing decides when/how to save the screen_last_desktop so
624 that it can be restored later if you want */
625 if (screen_desktop_timeout) {
626 /* If screen_desktop_timeout is true, then we've been on this desktop
627 long enough and we can save it as the last desktop. */
629 if (screen_last_desktop == previous)
630 /* this is the startup state only */
631 screen_old_desktop = screen_desktop;
633 /* save the "last desktop" as the "old desktop" */
634 screen_old_desktop = screen_last_desktop;
635 /* save the desktop we're coming from as the "last desktop" */
636 screen_last_desktop = previous;
640 /* If screen_desktop_timeout is false, then we just got to this desktop
641 and we are moving away again. */
643 if (screen_desktop == screen_last_desktop) {
644 /* If we are moving to the "last desktop" .. */
645 if (previous == screen_old_desktop) {
646 /* .. from the "old desktop", change the last desktop to
647 be where we are coming from */
648 screen_last_desktop = screen_old_desktop;
650 else if (screen_last_desktop == screen_old_desktop) {
651 /* .. and also to the "old desktop", change the "last
652 desktop" to be where we are coming from */
653 screen_last_desktop = previous;
656 /* .. from some other desktop, then set the "last desktop" to
657 be the saved "old desktop", i.e. where we were before the
659 screen_last_desktop = screen_old_desktop;
663 /* If we are moving to any desktop besides the "last desktop"..
664 (this is the normal case) */
665 if (screen_desktop == screen_old_desktop) {
666 /* If moving to the "old desktop", which is not the
667 "last desktop", don't save anything */
669 else if (previous == screen_old_desktop) {
670 /* If moving from the "old desktop", and not to the
671 "last desktop", don't save anything */
673 else if (screen_last_desktop == screen_old_desktop) {
674 /* If the "last desktop" is the same as "old desktop" and
675 you're not moving to the "last desktop" then save where
676 we're coming from as the "last desktop" */
677 screen_last_desktop = previous;
680 /* If the "last desktop" is different from the "old desktop"
681 and you're not moving to the "last desktop", then don't save
686 screen_desktop_timeout = FALSE;
687 if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
688 screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
689 last_desktop_func, NULL);
691 ob_debug("Moving to desktop %d", num+1);
693 /* ignore enter events caused by the move */
694 ignore_start = event_start_ignore_all_enters();
696 if (moveresize_client)
697 client_set_desktop(moveresize_client, num, TRUE, FALSE);
699 /* show windows before hiding the rest to lessen the enter/leave events */
701 /* show windows from top to bottom */
702 for (it = stacking_list; it; it = g_list_next(it)) {
703 if (WINDOW_IS_CLIENT(it->data)) {
704 ObClient *c = it->data;
709 if (dofocus) screen_fallback_focus();
711 /* hide windows from bottom to top */
712 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
713 if (WINDOW_IS_CLIENT(it->data)) {
714 ObClient *c = it->data;
715 if (client_hide(c)) {
716 if (c == focus_client) {
717 /* c was focused and we didn't do fallback clearly so make
718 sure openbox doesnt still consider the window focused.
719 this happens when using NextWindow with allDesktops,
720 since it doesnt want to move focus on desktop change,
721 but the focus is not going to stay with the current
722 window, which has now disappeared.
723 only do this if the client was actually hidden,
724 otherwise it can keep focus. */
725 focus_set_client(NULL);
731 focus_cycle_addremove(NULL, TRUE);
733 event_end_ignore_all_enters(ignore_start);
735 if (event_source_time() != CurrentTime)
736 screen_desktop_user_time = event_source_time();
739 void screen_add_desktop(gboolean current)
743 /* ignore enter events caused by this */
744 ignore_start = event_start_ignore_all_enters();
746 screen_set_num_desktops(screen_num_desktops+1);
748 /* move all the clients over */
752 for (it = client_list; it; it = g_list_next(it)) {
753 ObClient *c = it->data;
754 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
755 /* don't move direct children, they'll be moved with their
756 parent - which will have to be on the same desktop */
757 !client_direct_parent(c))
759 ob_debug("moving window %s", c->title);
760 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
765 event_end_ignore_all_enters(ignore_start);
768 void screen_remove_desktop(gboolean current)
770 guint rmdesktop, movedesktop;
771 GList *it, *stacking_copy;
774 if (screen_num_desktops <= 1) return;
776 /* ignore enter events caused by this */
777 ignore_start = event_start_ignore_all_enters();
779 /* what desktop are we removing and moving to? */
781 rmdesktop = screen_desktop;
783 rmdesktop = screen_num_desktops - 1;
784 if (rmdesktop < screen_num_desktops - 1)
785 movedesktop = rmdesktop + 1;
787 movedesktop = rmdesktop;
789 /* make a copy of the list cuz we're changing it */
790 stacking_copy = g_list_copy(stacking_list);
791 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
792 if (WINDOW_IS_CLIENT(it->data)) {
793 ObClient *c = it->data;
794 guint d = c->desktop;
795 if (d != DESKTOP_ALL && d >= movedesktop &&
796 /* don't move direct children, they'll be moved with their
797 parent - which will have to be on the same desktop */
798 !client_direct_parent(c))
800 ob_debug("moving window %s", c->title);
801 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
803 /* raise all the windows that are on the current desktop which
805 if ((screen_desktop == rmdesktop - 1 ||
806 screen_desktop == rmdesktop) &&
807 (d == DESKTOP_ALL || d == screen_desktop))
809 stacking_raise(CLIENT_AS_WINDOW(c));
810 ob_debug("raising window %s", c->title);
814 g_list_free(stacking_copy);
816 /* fallback focus like we're changing desktops */
817 if (screen_desktop < screen_num_desktops - 1) {
818 screen_fallback_focus();
819 ob_debug("fake desktop change");
822 screen_set_num_desktops(screen_num_desktops-1);
824 event_end_ignore_all_enters(ignore_start);
827 static void get_row_col(guint d, guint *r, guint *c)
829 switch (screen_desktop_layout.orientation) {
830 case OB_ORIENTATION_HORZ:
831 switch (screen_desktop_layout.start_corner) {
832 case OB_CORNER_TOPLEFT:
833 *r = d / screen_desktop_layout.columns;
834 *c = d % screen_desktop_layout.columns;
836 case OB_CORNER_BOTTOMLEFT:
837 *r = screen_desktop_layout.rows - 1 -
838 d / screen_desktop_layout.columns;
839 *c = d % screen_desktop_layout.columns;
841 case OB_CORNER_TOPRIGHT:
842 *r = d / screen_desktop_layout.columns;
843 *c = screen_desktop_layout.columns - 1 -
844 d % screen_desktop_layout.columns;
846 case OB_CORNER_BOTTOMRIGHT:
847 *r = screen_desktop_layout.rows - 1 -
848 d / screen_desktop_layout.columns;
849 *c = screen_desktop_layout.columns - 1 -
850 d % screen_desktop_layout.columns;
854 case OB_ORIENTATION_VERT:
855 switch (screen_desktop_layout.start_corner) {
856 case OB_CORNER_TOPLEFT:
857 *r = d % screen_desktop_layout.rows;
858 *c = d / screen_desktop_layout.rows;
860 case OB_CORNER_BOTTOMLEFT:
861 *r = screen_desktop_layout.rows - 1 -
862 d % screen_desktop_layout.rows;
863 *c = d / screen_desktop_layout.rows;
865 case OB_CORNER_TOPRIGHT:
866 *r = d % screen_desktop_layout.rows;
867 *c = screen_desktop_layout.columns - 1 -
868 d / screen_desktop_layout.rows;
870 case OB_CORNER_BOTTOMRIGHT:
871 *r = screen_desktop_layout.rows - 1 -
872 d % screen_desktop_layout.rows;
873 *c = screen_desktop_layout.columns - 1 -
874 d / screen_desktop_layout.rows;
881 static guint translate_row_col(guint r, guint c)
883 switch (screen_desktop_layout.orientation) {
884 case OB_ORIENTATION_HORZ:
885 switch (screen_desktop_layout.start_corner) {
886 case OB_CORNER_TOPLEFT:
887 return r % screen_desktop_layout.rows *
888 screen_desktop_layout.columns +
889 c % screen_desktop_layout.columns;
890 case OB_CORNER_BOTTOMLEFT:
891 return (screen_desktop_layout.rows - 1 -
892 r % screen_desktop_layout.rows) *
893 screen_desktop_layout.columns +
894 c % screen_desktop_layout.columns;
895 case OB_CORNER_TOPRIGHT:
896 return r % screen_desktop_layout.rows *
897 screen_desktop_layout.columns +
898 (screen_desktop_layout.columns - 1 -
899 c % screen_desktop_layout.columns);
900 case OB_CORNER_BOTTOMRIGHT:
901 return (screen_desktop_layout.rows - 1 -
902 r % screen_desktop_layout.rows) *
903 screen_desktop_layout.columns +
904 (screen_desktop_layout.columns - 1 -
905 c % screen_desktop_layout.columns);
907 case OB_ORIENTATION_VERT:
908 switch (screen_desktop_layout.start_corner) {
909 case OB_CORNER_TOPLEFT:
910 return c % screen_desktop_layout.columns *
911 screen_desktop_layout.rows +
912 r % screen_desktop_layout.rows;
913 case OB_CORNER_BOTTOMLEFT:
914 return c % screen_desktop_layout.columns *
915 screen_desktop_layout.rows +
916 (screen_desktop_layout.rows - 1 -
917 r % screen_desktop_layout.rows);
918 case OB_CORNER_TOPRIGHT:
919 return (screen_desktop_layout.columns - 1 -
920 c % screen_desktop_layout.columns) *
921 screen_desktop_layout.rows +
922 r % screen_desktop_layout.rows;
923 case OB_CORNER_BOTTOMRIGHT:
924 return (screen_desktop_layout.columns - 1 -
925 c % screen_desktop_layout.columns) *
926 screen_desktop_layout.rows +
927 (screen_desktop_layout.rows - 1 -
928 r % screen_desktop_layout.rows);
931 g_assert_not_reached();
935 static gboolean hide_desktop_popup_func(gpointer data)
939 desktop_popup_timer = 0;
941 for (i = 0; i < screen_num_monitors; i++) {
942 pager_popup_hide(desktop_popup[i]);
944 return FALSE; /* don't repeat */
947 void screen_show_desktop_popup(guint d, gboolean perm)
952 /* 0 means don't show the popup */
953 if (!config_desktop_popup_time) return;
955 for (i = 0; i < screen_num_monitors; i++) {
956 a = screen_physical_area_monitor(i);
957 pager_popup_position(desktop_popup[i], CenterGravity,
958 a->x + a->width / 2, a->y + a->height / 2);
959 pager_popup_icon_size_multiplier(desktop_popup[i],
960 (screen_desktop_layout.columns /
961 screen_desktop_layout.rows) / 2,
962 (screen_desktop_layout.rows/
963 screen_desktop_layout.columns) / 2);
964 pager_popup_max_width(desktop_popup[i],
965 MAX(a->width/3, POPUP_WIDTH));
966 pager_popup_show(desktop_popup[i], screen_desktop_names[d], d);
968 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
969 desktop_popup_timer = 0;
970 if (!perm && !desktop_popup_perm)
971 /* only hide if its not already being show permanently */
972 desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
973 hide_desktop_popup_func,
976 desktop_popup_perm = TRUE;
979 void screen_hide_desktop_popup(void)
981 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
982 desktop_popup_timer = 0;
983 desktop_popup_perm = FALSE;
986 for (i = 0; i < screen_num_monitors; i++) {
987 pager_popup_hide(desktop_popup[i]);
991 guint screen_find_desktop(guint from, ObDirection dir,
992 gboolean wrap, gboolean linear)
998 get_row_col(d, &r, &c);
1001 case OB_DIRECTION_EAST:
1002 if (d < screen_num_desktops - 1)
1009 case OB_DIRECTION_WEST:
1013 d = screen_num_desktops - 1;
1018 g_assert_not_reached();
1023 case OB_DIRECTION_EAST:
1025 if (c >= screen_desktop_layout.columns) {
1031 d = translate_row_col(r, c);
1032 if (d >= screen_num_desktops) {
1039 case OB_DIRECTION_WEST:
1041 if (c >= screen_desktop_layout.columns) {
1043 c = screen_desktop_layout.columns - 1;
1047 d = translate_row_col(r, c);
1048 if (d >= screen_num_desktops) {
1055 case OB_DIRECTION_SOUTH:
1057 if (r >= screen_desktop_layout.rows) {
1063 d = translate_row_col(r, c);
1064 if (d >= screen_num_desktops) {
1071 case OB_DIRECTION_NORTH:
1073 if (r >= screen_desktop_layout.rows) {
1075 r = screen_desktop_layout.rows - 1;
1079 d = translate_row_col(r, c);
1080 if (d >= screen_num_desktops) {
1088 g_assert_not_reached();
1092 d = translate_row_col(r, c);
1097 static gboolean screen_validate_layout(ObDesktopLayout *l)
1099 if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1102 /* fill in a zero rows/columns */
1103 if (l->columns == 0) {
1104 l->columns = screen_num_desktops / l->rows;
1105 if (l->rows * l->columns < screen_num_desktops)
1107 if (l->rows * l->columns >= screen_num_desktops + l->columns)
1109 } else if (l->rows == 0) {
1110 l->rows = screen_num_desktops / l->columns;
1111 if (l->columns * l->rows < screen_num_desktops)
1113 if (l->columns * l->rows >= screen_num_desktops + l->rows)
1117 /* bounds checking */
1118 if (l->orientation == OB_ORIENTATION_HORZ) {
1119 l->columns = MIN(screen_num_desktops, l->columns);
1120 l->rows = MIN(l->rows,
1121 (screen_num_desktops + l->columns - 1) / l->columns);
1122 l->columns = screen_num_desktops / l->rows +
1123 !!(screen_num_desktops % l->rows);
1125 l->rows = MIN(screen_num_desktops, l->rows);
1126 l->columns = MIN(l->columns,
1127 (screen_num_desktops + l->rows - 1) / l->rows);
1128 l->rows = screen_num_desktops / l->columns +
1129 !!(screen_num_desktops % l->columns);
1134 void screen_update_layout(void)
1141 screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1142 screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1143 screen_desktop_layout.rows = 1;
1144 screen_desktop_layout.columns = screen_num_desktops;
1146 if (OBT_PROP_GETA32(obt_root(ob_screen),
1147 NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1148 if (num == 3 || num == 4) {
1150 if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1151 l.orientation = OB_ORIENTATION_VERT;
1152 else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1153 l.orientation = OB_ORIENTATION_HORZ;
1158 l.start_corner = OB_CORNER_TOPLEFT;
1160 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1161 l.start_corner = OB_CORNER_TOPLEFT;
1162 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1163 l.start_corner = OB_CORNER_TOPRIGHT;
1164 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1165 l.start_corner = OB_CORNER_BOTTOMRIGHT;
1166 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1167 l.start_corner = OB_CORNER_BOTTOMLEFT;
1172 l.columns = data[1];
1175 if (screen_validate_layout(&l))
1176 screen_desktop_layout = l;
1183 void screen_update_desktop_names(void)
1187 /* empty the array */
1188 g_strfreev(screen_desktop_names);
1189 screen_desktop_names = NULL;
1191 if (OBT_PROP_GETSS(obt_root(ob_screen),
1192 NET_DESKTOP_NAMES, &screen_desktop_names))
1193 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1196 if (i < screen_num_desktops) {
1199 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1200 screen_num_desktops + 1);
1201 screen_desktop_names[screen_num_desktops] = NULL;
1203 it = g_slist_nth(config_desktops_names, i);
1205 for (; i < screen_num_desktops; ++i) {
1206 if (it && ((char*)it->data)[0]) /* not empty */
1207 /* use the names from the config file when possible */
1208 screen_desktop_names[i] = g_strdup(it->data);
1210 /* make up a nice name if it's not though */
1211 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1213 if (it) it = g_slist_next(it);
1216 /* if we changed any names, then set the root property so we can
1217 all agree on the names */
1218 OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1219 (const gchar*const*)screen_desktop_names);
1222 /* resize the pager for these names */
1223 for (i = 0; i < screen_num_monitors; i++) {
1224 pager_popup_text_width_to_strings(desktop_popup[i],
1225 screen_desktop_names,
1226 screen_num_desktops);
1230 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1234 ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1236 gboolean showing_before = screen_showing_desktop();
1237 screen_show_desktop_mode = show_mode;
1238 gboolean showing_after = screen_showing_desktop();
1240 if (showing_before == showing_after) {
1242 screen_show_desktop_mode = before_mode;
1246 if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1249 /* If we're showing the desktop until the show-mode is toggled, we
1250 don't allow breaking out of showing-desktop mode unless we're
1251 showing all the windows again. */
1252 screen_show_desktop_mode = before_mode;
1256 if (showing_after) {
1257 /* hide windows bottom to top */
1258 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1259 if (WINDOW_IS_CLIENT(it->data)) {
1260 ObClient *client = it->data;
1261 client_showhide(client);
1266 /* restore windows top to bottom */
1267 for (it = stacking_list; it; it = g_list_next(it)) {
1268 if (WINDOW_IS_CLIENT(it->data)) {
1269 ObClient *client = it->data;
1270 if (client_should_show(client)) {
1271 if (!show_only || client == show_only)
1272 client_show(client);
1274 client_iconify(client, TRUE, FALSE, TRUE);
1280 if (showing_after) {
1281 /* focus the desktop */
1282 for (it = focus_order; it; it = g_list_next(it)) {
1283 ObClient *c = it->data;
1284 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1285 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1286 client_focus(it->data))
1290 else if (!show_only) {
1293 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1294 /* only do the flicker reducing stuff ahead of time if we are going
1295 to call xsetinputfocus on the window ourselves. otherwise there
1296 is no guarantee the window will actually take focus.. */
1298 /* reduce flicker by hiliting now rather than waiting for the
1299 server FocusIn event */
1300 frame_adjust_focus(c->frame, TRUE);
1305 OBT_PROP_SET32(obt_root(ob_screen),
1306 NET_SHOWING_DESKTOP,
1311 gboolean screen_showing_desktop()
1313 switch (screen_show_desktop_mode) {
1314 case SCREEN_SHOW_DESKTOP_NO:
1316 case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1317 case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1320 g_assert_not_reached();
1324 void screen_install_colormap(ObClient *client, gboolean install)
1326 if (client == NULL || client->colormap == None) {
1328 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1330 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1332 obt_display_ignore_errors(TRUE);
1334 XInstallColormap(obt_display, client->colormap);
1336 XUninstallColormap(obt_display, client->colormap);
1337 obt_display_ignore_errors(FALSE);
1343 StrutPartial *strut;
1346 #define RESET_STRUT_LIST(sl) \
1348 g_slice_free(ObScreenStrut, (sl)->data); \
1349 sl = g_slist_delete_link(sl, sl); \
1352 #define ADD_STRUT_TO_LIST(sl, d, s) \
1354 ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1357 sl = g_slist_prepend(sl, ss); \
1360 #define VALIDATE_STRUTS(sl, side, max) \
1363 for (it = sl; it; it = g_slist_next(it)) { \
1364 ObScreenStrut *ss = it->data; \
1365 ss->strut->side = MIN(max, ss->strut->side); \
1369 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1375 XineramaScreenInfo *info;
1378 if (ob_debug_xinerama) {
1379 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1380 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1382 *xin_areas = g_new(Rect, *nxin + 1);
1383 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1384 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1387 else if (obt_display_extension_xinerama &&
1388 (info = XineramaQueryScreens(obt_display, &n))) {
1390 *xin_areas = g_new(Rect, *nxin + 1);
1391 for (i = 0; i < *nxin; ++i)
1392 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1393 info[i].width, info[i].height);
1399 *xin_areas = g_new(Rect, *nxin + 1);
1400 RECT_SET((*xin_areas)[0], 0, 0,
1401 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1402 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1405 /* returns one extra with the total area in it */
1406 l = (*xin_areas)[0].x;
1407 t = (*xin_areas)[0].y;
1408 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1409 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1410 for (i = 1; i < *nxin; ++i) {
1411 l = MIN(l, (*xin_areas)[i].x);
1412 t = MIN(l, (*xin_areas)[i].y);
1413 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1414 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1416 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1418 for (i = 0; i < *nxin; ++i)
1419 ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1420 (*xin_areas)[i].x, (*xin_areas)[i].y,
1421 (*xin_areas)[i].width, (*xin_areas)[i].height);
1422 ob_debug("Full desktop @ %d,%d %dx%d\n",
1423 (*xin_areas)[i].x, (*xin_areas)[i].y,
1424 (*xin_areas)[i].width, (*xin_areas)[i].height);
1427 void screen_update_areas(void)
1429 guint i, old_num_monitors = screen_num_monitors;
1431 GList *it, *onscreen;
1433 /* collect the clients that are on screen */
1435 for (it = client_list; it; it = g_list_next(it)) {
1436 if (client_monitor(it->data) != screen_num_monitors)
1437 onscreen = g_list_prepend(onscreen, it->data);
1440 g_free(monitor_area);
1441 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1442 if (screen_num_monitors != old_num_monitors) {
1444 desktop_popup_free(old_num_monitors);
1445 desktop_popup_new();
1448 /* set up the user-specified margins */
1449 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1450 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1451 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1452 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1453 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1454 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1455 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1456 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1458 RESET_STRUT_LIST(struts_left);
1459 RESET_STRUT_LIST(struts_top);
1460 RESET_STRUT_LIST(struts_right);
1461 RESET_STRUT_LIST(struts_bottom);
1463 /* collect the struts */
1464 for (it = client_list; it; it = g_list_next(it)) {
1465 ObClient *c = it->data;
1467 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1469 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1471 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1472 if (c->strut.bottom)
1473 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1475 if (dock_strut.left)
1476 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1478 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1479 if (dock_strut.right)
1480 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1481 if (dock_strut.bottom)
1482 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1484 if (config_margins.left)
1485 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1486 if (config_margins.top)
1487 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1488 if (config_margins.right)
1489 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1490 if (config_margins.bottom)
1491 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1493 VALIDATE_STRUTS(struts_left, left,
1494 monitor_area[screen_num_monitors].width / 2);
1495 VALIDATE_STRUTS(struts_right, right,
1496 monitor_area[screen_num_monitors].width / 2);
1497 VALIDATE_STRUTS(struts_top, top,
1498 monitor_area[screen_num_monitors].height / 2);
1499 VALIDATE_STRUTS(struts_bottom, bottom,
1500 monitor_area[screen_num_monitors].height / 2);
1502 dims = g_new(gulong, 4 * screen_num_desktops);
1503 for (i = 0; i < screen_num_desktops; ++i) {
1504 Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1505 dims[i*4+0] = area->x;
1506 dims[i*4+1] = area->y;
1507 dims[i*4+2] = area->width;
1508 dims[i*4+3] = area->height;
1509 g_slice_free(Rect, area);
1512 /* set the legacy workarea hint to the union of all the monitors */
1513 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1514 dims, 4 * screen_num_desktops);
1516 /* the area has changed, adjust all the windows if they need it */
1517 for (it = onscreen; it; it = g_list_next(it))
1518 client_reconfigure(it->data, FALSE);
1524 Rect* screen_area_all_monitors(guint desktop)
1529 a = screen_area_monitor(desktop, 0);
1531 /* combine all the monitors together */
1532 for (i = 1; i < screen_num_monitors; ++i) {
1533 Rect *m = screen_area_monitor(desktop, i);
1536 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1537 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1538 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1539 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1541 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1550 #define STRUT_LEFT_IN_SEARCH(s, search) \
1551 (RANGES_INTERSECT(search->y, search->height, \
1552 s->left_start, s->left_end - s->left_start + 1))
1553 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1554 (RANGES_INTERSECT(search->y, search->height, \
1555 s->right_start, s->right_end - s->right_start + 1))
1556 #define STRUT_TOP_IN_SEARCH(s, search) \
1557 (RANGES_INTERSECT(search->x, search->width, \
1558 s->top_start, s->top_end - s->top_start + 1))
1559 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1560 (RANGES_INTERSECT(search->x, search->width, \
1561 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1563 #define STRUT_LEFT_IGNORE(s, us, search) \
1564 (head == SCREEN_AREA_ALL_MONITORS && us && \
1565 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1566 #define STRUT_RIGHT_IGNORE(s, us, search) \
1567 (head == SCREEN_AREA_ALL_MONITORS && us && \
1568 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1569 #define STRUT_TOP_IGNORE(s, us, search) \
1570 (head == SCREEN_AREA_ALL_MONITORS && us && \
1571 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1572 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1573 (head == SCREEN_AREA_ALL_MONITORS && us && \
1574 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1576 Rect* screen_area(guint desktop, guint head, Rect *search)
1582 gboolean us = search != NULL; /* user provided search */
1584 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1585 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1586 head == SCREEN_AREA_ALL_MONITORS);
1587 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1589 /* find any struts for this monitor
1590 which will be affecting the search area.
1593 /* search everything if search is null */
1595 if (head < screen_num_monitors) search = &monitor_area[head];
1596 else search = &monitor_area[screen_num_monitors];
1598 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1600 /* al is "all left" meaning the furthest left you can get, l is our
1601 "working left" meaning our current strut edge which we're calculating
1604 /* only include monitors which the search area lines up with */
1605 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1606 l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1607 t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1608 r = RECT_LEFT(monitor_area[screen_num_monitors]);
1609 b = RECT_TOP(monitor_area[screen_num_monitors]);
1610 for (i = 0; i < screen_num_monitors; ++i) {
1611 /* add the monitor if applicable */
1612 if (RANGES_INTERSECT(search->x, search->width,
1613 monitor_area[i].x, monitor_area[i].width))
1615 t = MIN(t, RECT_TOP(monitor_area[i]));
1616 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1618 if (RANGES_INTERSECT(search->y, search->height,
1619 monitor_area[i].y, monitor_area[i].height))
1621 l = MIN(l, RECT_LEFT(monitor_area[i]));
1622 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1626 l = RECT_LEFT(monitor_area[screen_num_monitors]);
1627 t = RECT_TOP(monitor_area[screen_num_monitors]);
1628 r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1629 b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1632 for (d = 0; d < screen_num_desktops; ++d) {
1633 if (d != desktop && desktop != DESKTOP_ALL) continue;
1635 for (i = 0; i < screen_num_monitors; ++i) {
1636 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1638 for (it = struts_left; it; it = g_slist_next(it)) {
1639 ObScreenStrut *s = it->data;
1640 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1641 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1642 !STRUT_LEFT_IGNORE(s->strut, us, search))
1643 l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1646 for (it = struts_top; it; it = g_slist_next(it)) {
1647 ObScreenStrut *s = it->data;
1648 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1649 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1650 !STRUT_TOP_IGNORE(s->strut, us, search))
1651 t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1654 for (it = struts_right; it; it = g_slist_next(it)) {
1655 ObScreenStrut *s = it->data;
1656 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1657 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1658 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1659 r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1662 for (it = struts_bottom; it; it = g_slist_next(it)) {
1663 ObScreenStrut *s = it->data;
1664 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1665 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1666 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1667 b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1668 - s->strut->bottom);
1671 /* limit to this monitor */
1673 l = MAX(l, RECT_LEFT(monitor_area[i]));
1674 t = MAX(t, RECT_TOP(monitor_area[i]));
1675 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1676 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1681 a = g_slice_new(Rect);
1684 a->width = r - l + 1;
1685 a->height = b - t + 1;
1694 guint screen_find_monitor(const Rect *search)
1697 guint mostpx_index = screen_num_monitors;
1699 guint closest_distance_index = screen_num_monitors;
1700 guint closest_distance = G_MAXUINT;
1701 GSList *counted = NULL;
1703 /* we want to count the number of pixels search has on each monitor, but not
1704 double count. so if a pixel is counted on monitor A then we should not
1705 count it again on monitor B. in the end we want to return the monitor
1706 that had the most pixels counted under this scheme.
1708 this assumes that monitors earlier in the list are more desirable to be
1709 considered the search area's monitor. we try the configured primary
1710 monitor first, so it gets the highest preference.
1712 if we have counted an area A, then we want to subtract the intersection
1713 of A with the area on future monitors.
1714 but now consider if we count an area B that intersects A. we want to
1715 subtract the area B from that counted on future monitors, but not
1716 subtract the intersection of A and B twice! so we would add the
1717 intersection of A and B back, to account for it being subtracted both
1720 this is the idea behind the algorithm. we always subtract the full area
1721 for monitor M intersected with the search area. we'll call that AREA.
1722 but then we go through the list |counted| and for each rectangle in
1723 the list that is being subtracted from future monitors, we insert a
1724 request to add back the intersection of the subtracted rect with AREA.
1725 vice versa for a rect in |counted| that is getting added back.
1728 if (config_primary_monitor_index < screen_num_monitors) {
1729 const Rect *monitor;
1730 Rect on_current_monitor;
1733 monitor = screen_physical_area_monitor(config_primary_monitor_index);
1735 if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1736 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1737 area = RECT_AREA(on_current_monitor);
1739 if (area > mostpx) {
1741 mostpx_index = config_primary_monitor_index;
1744 /* add the intersection rect on the current monitor to the
1745 counted list. that's easy for the first one, we just mark it for
1748 RectArithmetic *ra = g_slice_new(RectArithmetic);
1749 ra->r = on_current_monitor;
1750 ra->subtract = TRUE;
1751 counted = g_slist_prepend(counted, ra);
1756 for (i = 0; i < screen_num_monitors; ++i) {
1757 const Rect *monitor;
1758 Rect on_current_monitor;
1762 monitor = screen_physical_area_monitor(i);
1764 if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1765 /* If we don't intersect then find the distance between the search
1766 rect and the monitor. We'll use the closest monitor from this
1767 metric if none of the monitors intersect. */
1768 guint distance = rect_manhatten_distance(*monitor, *search);
1770 if (distance < closest_distance) {
1771 closest_distance = distance;
1772 closest_distance_index = i;
1777 if (i == config_primary_monitor_index)
1778 continue; /* already did this one */
1780 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1781 area = RECT_AREA(on_current_monitor);
1783 /* remove pixels we already counted on any previous monitors. */
1784 for (it = counted; it; it = g_slist_next(it)) {
1785 RectArithmetic *ra = it->data;
1788 RECT_SET_INTERSECTION(intersection, ra->r, *search);
1789 if (ra->subtract) area -= RECT_AREA(intersection);
1790 else area += RECT_AREA(intersection);
1793 if (area > mostpx) {
1798 /* add the intersection rect on the current monitor I to the counted
1800 but now we need to compensate for every rectangle R already in the
1801 counted list, and add a new rect R' that is the intersection of
1802 R and I, but with the reverse subtraction/addition operation.
1804 for (it = counted; it; it = g_slist_next(it)) {
1805 RectArithmetic *saved = it->data;
1807 if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1809 /* we are going to subtract our rect from future monitors, but
1810 part of it may already be being subtracted/added, so compensate
1811 to not double add/subtract. */
1812 RectArithmetic *reverse = g_slice_new(RectArithmetic);
1813 RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1814 reverse->subtract = !saved->subtract;
1815 /* prepend so we can continue thru the list uninterupted */
1816 counted = g_slist_prepend(counted, reverse);
1819 RectArithmetic *ra = g_slice_new(RectArithmetic);
1820 ra->r = on_current_monitor;
1821 ra->subtract = TRUE;
1822 counted = g_slist_prepend(counted, ra);
1827 g_slice_free(RectArithmetic, counted->data);
1828 counted = g_slist_delete_link(counted, counted);
1831 if (mostpx_index < screen_num_monitors)
1832 return mostpx_index;
1834 g_assert(closest_distance_index < screen_num_monitors);
1835 return closest_distance_index;
1838 const Rect* screen_physical_area_all_monitors(void)
1840 return screen_physical_area_monitor(screen_num_monitors);
1843 const Rect* screen_physical_area_monitor(guint head)
1845 g_assert(head <= screen_num_monitors);
1847 return &monitor_area[head];
1850 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1852 g_assert(head <= screen_num_monitors);
1854 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1857 guint screen_monitor_active(void)
1859 if (moveresize_client)
1860 return client_monitor(moveresize_client);
1861 else if (focus_client)
1862 return client_monitor(focus_client);
1864 return screen_monitor_pointer();
1867 const Rect* screen_physical_area_active(void)
1869 return screen_physical_area_monitor(screen_monitor_active());
1872 guint screen_monitor_primary(gboolean fixed)
1874 if (config_primary_monitor_index > 0) {
1875 if (config_primary_monitor_index-1 < screen_num_monitors)
1876 return config_primary_monitor_index - 1;
1882 else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1883 return screen_monitor_active();
1884 else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1885 return screen_monitor_pointer();
1888 const Rect* screen_physical_area_primary(gboolean fixed)
1890 return screen_physical_area_monitor(screen_monitor_primary(fixed));
1893 void screen_set_root_cursor(void)
1895 if (sn_app_starting())
1896 XDefineCursor(obt_display, obt_root(ob_screen),
1897 ob_cursor(OB_CURSOR_BUSYPOINTER));
1899 XDefineCursor(obt_display, obt_root(ob_screen),
1900 ob_cursor(OB_CURSOR_POINTER));
1903 guint screen_find_monitor_point(guint x, guint y)
1906 RECT_SET(mon, x, y, 1, 1);
1907 return screen_find_monitor(&mon);
1910 guint screen_monitor_pointer()
1913 if (!screen_pointer_pos(&x, &y))
1915 return screen_find_monitor_point(x, y);
1918 gboolean screen_pointer_pos(gint *x, gint *y)
1923 /* we don't care about any of these return values, but we can't pass NULL */
1928 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1929 &w, &w, x, y, &j, &j, &u);
1931 for (i = 0; i < ScreenCount(obt_display); ++i)
1933 if ((ret=XQueryPointer(obt_display, obt_root(i),
1934 &w, &w, x, y, &j, &j, &u)))
1940 gboolean screen_compare_desktops(guint a, guint b)
1942 if (a == DESKTOP_ALL)
1944 if (b == DESKTOP_ALL)
1949 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
1950 const GravityPoint *position, const Rect *area)
1952 if (position->x.center)
1953 *x = area->width / 2 - width / 2;
1955 *x = position->x.pos;
1956 if (position->x.denom)
1957 *x = (*x * area->width) / position->x.denom;
1958 if (position->x.opposite)
1959 *x = area->width - width - *x;
1962 if (position->y.center)
1963 *y = area->height / 2 - height / 2;
1965 *y = position->y.pos;
1966 if (position->y.denom)
1967 *y = (*y * area->height) / position->y.denom;
1968 if (position->y.opposite)
1969 *y = area->height - height - *y;