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_tell_ksplash(void);
58 static void screen_fallback_focus(void);
60 guint screen_num_desktops;
61 guint screen_num_monitors;
63 guint screen_last_desktop;
64 ObScreenShowDestopMode screen_show_desktop_mode;
65 ObDesktopLayout screen_desktop_layout;
66 gchar **screen_desktop_names;
67 Window screen_support_win;
68 Time screen_desktop_user_time = CurrentTime;
70 static Size screen_physical_size;
71 static guint screen_old_desktop;
72 static gboolean screen_desktop_timeout = TRUE;
73 static guint screen_desktop_timer = 0;
74 /*! An array of desktops, holding an array of areas per monitor */
75 static Rect *monitor_area = NULL;
76 /*! An array of desktops, holding an array of struts */
77 static GSList *struts_top = NULL;
78 static GSList *struts_left = NULL;
79 static GSList *struts_right = NULL;
80 static GSList *struts_bottom = NULL;
82 static ObPagerPopup *desktop_popup;
83 static guint desktop_popup_timer = 0;
84 static gboolean desktop_popup_perm;
86 /*! The number of microseconds that you need to be on a desktop before it will
87 replace the remembered "last desktop" */
88 #define REMEMBER_LAST_DESKTOP_TIME 750
90 static gboolean replace_wm(void)
94 Window current_wm_sn_owner;
97 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
98 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
101 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
102 if (current_wm_sn_owner == screen_support_win)
103 current_wm_sn_owner = None;
104 if (current_wm_sn_owner) {
105 if (!ob_replace_wm) {
106 g_message(_("A window manager is already running on screen %d"),
110 obt_display_ignore_errors(TRUE);
112 /* We want to find out when the current selection owner dies */
113 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
114 XSync(obt_display, FALSE);
116 obt_display_ignore_errors(FALSE);
117 if (obt_display_error_occured)
118 current_wm_sn_owner = None;
121 timestamp = event_time();
123 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
126 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
127 g_message(_("Could not acquire window manager selection on screen %d"),
132 /* Wait for old window manager to go away */
133 if (current_wm_sn_owner) {
135 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
136 ObtXQueueWindowType wt;
138 wt.window = current_wm_sn_owner;
139 wt.type = DestroyNotify;
141 while (wait < timeout) {
142 /* Checks the local queue and incoming events for this event */
143 if (xqueue_exists_local(xqueue_match_window_type, &wt))
145 g_usleep(G_USEC_PER_SEC / 10);
146 wait += G_USEC_PER_SEC / 10;
149 if (wait >= timeout) {
150 g_message(_("The WM on screen %d is not exiting"), ob_screen);
155 /* Send client message indicating that we are now the WM */
156 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
157 timestamp, wm_sn_atom, screen_support_win, 0, 0,
158 SubstructureNotifyMask);
163 gboolean screen_annex(void)
165 XSetWindowAttributes attrib;
170 /* create the netwm support window */
171 attrib.override_redirect = TRUE;
172 attrib.event_mask = PropertyChangeMask;
173 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
175 CopyFromParent, InputOutput,
177 CWEventMask | CWOverrideRedirect,
179 XMapWindow(obt_display, screen_support_win);
180 XLowerWindow(obt_display, screen_support_win);
183 XDestroyWindow(obt_display, screen_support_win);
187 obt_display_ignore_errors(TRUE);
188 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
189 obt_display_ignore_errors(FALSE);
190 if (obt_display_error_occured) {
191 g_message(_("A window manager is already running on screen %d"),
194 XDestroyWindow(obt_display, screen_support_win);
198 screen_set_root_cursor();
200 /* set the OPENBOX_PID hint */
202 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
204 /* set supporting window */
205 OBT_PROP_SET32(obt_root(ob_screen),
206 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
208 /* set properties on the supporting window */
209 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
210 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
211 WINDOW, screen_support_win);
213 /* set the _NET_SUPPORTED_ATOMS hint */
215 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
216 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
218 supported = g_new(gulong, num_support);
219 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
220 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
221 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
222 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
223 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
224 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
225 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
226 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
227 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
228 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
229 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
230 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
231 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
232 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
233 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
268 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
271 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
273 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
274 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
275 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
276 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
277 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
278 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
280 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
282 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
283 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
284 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
285 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
287 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
288 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
290 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
291 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
293 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
294 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
295 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
297 supported[i++] = OBT_PROP_ATOM(OB_FOCUS);
298 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
299 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
300 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
301 supported[i++] = OBT_PROP_ATOM(OB_THEME);
302 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
303 supported[i++] = OBT_PROP_ATOM(OB_LAST_DESKTOP);
304 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
305 supported[i++] = OBT_PROP_ATOM(OB_VERSION);
306 supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
307 supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
308 supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
309 supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
310 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
311 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
312 supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
313 g_assert(i == num_support);
315 OBT_PROP_SETA32(obt_root(ob_screen),
316 NET_SUPPORTED, ATOM, supported, num_support);
319 OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
322 //screen_tell_ksplash();
327 static void screen_tell_ksplash(void)
332 argv = g_new(gchar*, 6);
333 argv[0] = g_strdup("dcop");
334 argv[1] = g_strdup("ksplash");
335 argv[2] = g_strdup("ksplash");
336 argv[3] = g_strdup("upAndRunning(QString)");
337 argv[4] = g_strdup("wm started");
340 /* tell ksplash through the dcop server command line interface */
341 g_spawn_async(NULL, argv, NULL,
342 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
343 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
344 NULL, NULL, NULL, NULL);
347 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
348 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
349 something. oh well. */
350 e.xclient.type = ClientMessage;
351 e.xclient.display = obt_display;
352 e.xclient.window = obt_root(ob_screen);
353 e.xclient.message_type =
354 XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
355 e.xclient.format = 8;
356 strcpy(e.xclient.data.b, "wm started");
357 XSendEvent(obt_display, obt_root(ob_screen),
358 False, SubstructureNotifyMask, &e);
361 void screen_startup(gboolean reconfig)
363 gchar **names = NULL;
365 gboolean namesexist = FALSE;
367 desktop_popup = pager_popup_new();
368 desktop_popup_perm = FALSE;
369 pager_popup_height(desktop_popup, POPUP_HEIGHT);
372 /* update the pager popup's width */
373 pager_popup_text_width_to_strings(desktop_popup,
374 screen_desktop_names,
375 screen_num_desktops);
379 /* get the initial size */
382 /* have names already been set for the desktops? */
383 if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
388 /* if names don't exist and we have session names, set those.
389 do this stuff BEFORE setting the number of desktops, because that
390 will create default names for them
392 if (!namesexist && session_desktop_names != NULL) {
396 /* get the desktop names */
397 numnames = g_slist_length(session_desktop_names);
398 names = g_new(gchar*, numnames + 1);
399 names[numnames] = NULL;
400 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
401 names[i] = g_strdup(it->data);
403 /* set the root window property */
404 OBT_PROP_SETSS(obt_root(ob_screen),
405 NET_DESKTOP_NAMES, (const gchar*const*)names);
410 /* set the number of desktops, if it's not already set.
412 this will also set the default names from the config file up for
413 desktops that don't have names yet */
414 screen_num_desktops = 0;
415 if (OBT_PROP_GET32(obt_root(ob_screen),
416 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
418 if (d != config_desktops_num) {
419 /* TRANSLATORS: If you need to specify a different order of the
420 arguments, you can use %1$d for the first one and %2$d for the
421 second one. For example,
422 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
423 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),
424 config_desktops_num, d);
426 screen_set_num_desktops(d);
428 /* restore from session if possible */
429 else if (session_num_desktops)
430 screen_set_num_desktops(session_num_desktops);
432 screen_set_num_desktops(config_desktops_num);
434 screen_desktop = screen_num_desktops; /* something invalid */
435 /* start on the current desktop when a wm was already running */
436 if (OBT_PROP_GET32(obt_root(ob_screen),
437 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
438 d < screen_num_desktops)
440 screen_set_desktop(d, FALSE);
441 } else if (session_desktop >= 0)
442 screen_set_desktop(MIN((guint)session_desktop,
443 screen_num_desktops), FALSE);
445 screen_set_desktop(MIN(config_screen_firstdesk,
446 screen_num_desktops) - 1, FALSE);
447 OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
448 if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
449 screen_last_desktop = screen_desktop;
450 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
453 /* don't start in showing-desktop mode */
454 screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
455 OBT_PROP_SET32(obt_root(ob_screen),
456 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
458 if (session_desktop_layout_present &&
459 screen_validate_layout(&session_desktop_layout))
461 screen_desktop_layout = session_desktop_layout;
464 screen_update_layout();
467 void screen_shutdown(gboolean reconfig)
469 pager_popup_free(desktop_popup);
474 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
476 /* we're not running here no more! */
477 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
479 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
480 /* don't keep this mode */
481 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
483 XDestroyWindow(obt_display, screen_support_win);
485 g_strfreev(screen_desktop_names);
486 screen_desktop_names = NULL;
489 void screen_resize(void)
495 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
496 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
498 /* Set the _NET_DESKTOP_GEOMETRY hint */
499 screen_physical_size.width = geometry[0] = w;
500 screen_physical_size.height = geometry[1] = h;
501 OBT_PROP_SETA32(obt_root(ob_screen),
502 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
504 if (ob_state() != OB_STATE_RUNNING)
507 /* this calls screen_update_areas(), which we need ! */
510 for (it = client_list; it; it = g_list_next(it)) {
511 client_move_onscreen(it->data, FALSE);
512 client_reconfigure(it->data, FALSE);
516 void screen_set_num_desktops(guint num)
519 GList *it, *stacking_copy;
523 if (screen_num_desktops == num) return;
525 screen_num_desktops = num;
526 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
528 /* set the viewport hint */
529 viewport = g_new0(gulong, num * 2);
530 OBT_PROP_SETA32(obt_root(ob_screen),
531 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
534 /* the number of rows/columns will differ */
535 screen_update_layout();
537 /* move windows on desktops that will no longer exist!
538 make a copy of the list cuz we're changing it */
539 stacking_copy = g_list_copy(stacking_list);
540 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
541 if (WINDOW_IS_CLIENT(it->data)) {
542 ObClient *c = it->data;
543 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
544 client_set_desktop(c, num - 1, FALSE, TRUE);
545 /* raise all the windows that are on the current desktop which
547 else if (screen_desktop == num - 1 &&
548 (c->desktop == DESKTOP_ALL ||
549 c->desktop == screen_desktop))
550 stacking_raise(CLIENT_AS_WINDOW(c));
553 g_list_free(stacking_copy);
555 /* change our struts/area to match (after moving windows) */
556 screen_update_areas();
558 /* may be some unnamed desktops that we need to fill in with names
559 (after updating the areas so the popup can resize) */
560 screen_update_desktop_names();
562 /* change our desktop if we're on one that no longer exists! */
563 if (screen_desktop >= screen_num_desktops)
564 screen_set_desktop(num - 1, TRUE);
567 static void screen_fallback_focus(void)
572 /* only allow omnipresent windows to get focus on desktop change if
573 an omnipresent window is already focused (it'll keep focus probably, but
574 maybe not depending on mouse-focus options) */
575 allow_omni = focus_client && (client_normal(focus_client) &&
576 focus_client->desktop == DESKTOP_ALL);
578 /* the client moved there already so don't move focus. prevent flicker
579 on sendtodesktop + follow */
580 if (focus_client && focus_client->desktop == screen_desktop)
583 /* have to try focus here because when you leave an empty desktop
584 there is no focus out to watch for. also, we have different rules
585 here. we always allow it to look under the mouse pointer if
586 config_focus_last is FALSE
588 do this before hiding the windows so if helper windows are coming
589 with us, they don't get hidden
591 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
594 /* only do the flicker reducing stuff ahead of time if we are going
595 to call xsetinputfocus on the window ourselves. otherwise there is
596 no guarantee the window will actually take focus.. */
598 /* reduce flicker by hiliting now rather than waiting for the
599 server FocusIn event */
600 frame_adjust_focus(c->frame, TRUE);
601 /* do this here so that if you switch desktops to a window with
602 helper windows then the helper windows won't flash */
603 client_bring_helper_windows(c);
608 static gboolean last_desktop_func(gpointer data)
610 screen_desktop_timeout = TRUE;
611 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
612 screen_desktop_timer = 0;
613 return FALSE; /* don't repeat */
616 void screen_set_desktop(guint num, gboolean dofocus)
622 g_assert(num < screen_num_desktops);
624 previous = screen_desktop;
625 screen_desktop = num;
627 if (previous == num) return;
629 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
631 /* This whole thing decides when/how to save the screen_last_desktop so
632 that it can be restored later if you want */
633 if (screen_desktop_timeout) {
634 /* If screen_desktop_timeout is true, then we've been on this desktop
635 long enough and we can save it as the last desktop. */
637 if (screen_last_desktop == previous)
638 /* this is the startup state only */
639 screen_old_desktop = screen_desktop;
641 /* save the "last desktop" as the "old desktop" */
642 screen_old_desktop = screen_last_desktop;
643 /* save the desktop we're coming from as the "last desktop" */
644 screen_last_desktop = previous;
648 /* If screen_desktop_timeout is false, then we just got to this desktop
649 and we are moving away again. */
651 if (screen_desktop == screen_last_desktop) {
652 /* If we are moving to the "last desktop" .. */
653 if (previous == screen_old_desktop) {
654 /* .. from the "old desktop", change the last desktop to
655 be where we are coming from */
656 screen_last_desktop = screen_old_desktop;
658 else if (screen_last_desktop == screen_old_desktop) {
659 /* .. and also to the "old desktop", change the "last
660 desktop" to be where we are coming from */
661 screen_last_desktop = previous;
664 /* .. from some other desktop, then set the "last desktop" to
665 be the saved "old desktop", i.e. where we were before the
667 screen_last_desktop = screen_old_desktop;
671 /* If we are moving to any desktop besides the "last desktop"..
672 (this is the normal case) */
673 if (screen_desktop == screen_old_desktop) {
674 /* If moving to the "old desktop", which is not the
675 "last desktop", don't save anything */
677 else if (previous == screen_old_desktop) {
678 /* If moving from the "old desktop", and not to the
679 "last desktop", don't save anything */
681 else if (screen_last_desktop == screen_old_desktop) {
682 /* If the "last desktop" is the same as "old desktop" and
683 you're not moving to the "last desktop" then save where
684 we're coming from as the "last desktop" */
685 screen_last_desktop = previous;
688 /* If the "last desktop" is different from the "old desktop"
689 and you're not moving to the "last desktop", then don't save
694 screen_desktop_timeout = FALSE;
695 if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
696 screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
697 last_desktop_func, NULL);
699 ob_debug("Moving to desktop %d", num+1);
701 if (ob_state() == OB_STATE_RUNNING)
702 screen_show_desktop_popup(screen_desktop, FALSE);
704 /* ignore enter events caused by the move */
705 ignore_start = event_start_ignore_all_enters();
707 if (moveresize_client)
708 client_set_desktop(moveresize_client, num, TRUE, FALSE);
710 /* show windows before hiding the rest to lessen the enter/leave events */
712 /* show windows from top to bottom */
713 for (it = stacking_list; it; it = g_list_next(it)) {
714 if (WINDOW_IS_CLIENT(it->data)) {
715 ObClient *c = it->data;
720 if (dofocus) screen_fallback_focus();
722 /* hide windows from bottom to top */
723 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
724 if (WINDOW_IS_CLIENT(it->data)) {
725 ObClient *c = it->data;
726 if (client_hide(c)) {
727 if (c == focus_client) {
728 /* c was focused and we didn't do fallback clearly so make
729 sure openbox doesnt still consider the window focused.
730 this happens when using NextWindow with allDesktops,
731 since it doesnt want to move focus on desktop change,
732 but the focus is not going to stay with the current
733 window, which has now disappeared.
734 only do this if the client was actually hidden,
735 otherwise it can keep focus. */
736 focus_set_client(NULL);
742 focus_cycle_addremove(NULL, TRUE);
744 event_end_ignore_all_enters(ignore_start);
746 if (event_source_time() != CurrentTime)
747 screen_desktop_user_time = event_source_time();
750 void screen_add_desktop(gboolean current)
754 /* ignore enter events caused by this */
755 ignore_start = event_start_ignore_all_enters();
757 screen_set_num_desktops(screen_num_desktops+1);
759 /* move all the clients over */
763 for (it = client_list; it; it = g_list_next(it)) {
764 ObClient *c = it->data;
765 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
766 /* don't move direct children, they'll be moved with their
767 parent - which will have to be on the same desktop */
768 !client_direct_parent(c))
770 ob_debug("moving window %s", c->title);
771 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
776 event_end_ignore_all_enters(ignore_start);
779 void screen_remove_desktop(gboolean current)
781 guint rmdesktop, movedesktop;
782 GList *it, *stacking_copy;
785 if (screen_num_desktops <= 1) return;
787 /* ignore enter events caused by this */
788 ignore_start = event_start_ignore_all_enters();
790 /* what desktop are we removing and moving to? */
792 rmdesktop = screen_desktop;
794 rmdesktop = screen_num_desktops - 1;
795 if (rmdesktop < screen_num_desktops - 1)
796 movedesktop = rmdesktop + 1;
798 movedesktop = rmdesktop;
800 /* make a copy of the list cuz we're changing it */
801 stacking_copy = g_list_copy(stacking_list);
802 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
803 if (WINDOW_IS_CLIENT(it->data)) {
804 ObClient *c = it->data;
805 guint d = c->desktop;
806 if (d != DESKTOP_ALL && d >= movedesktop &&
807 /* don't move direct children, they'll be moved with their
808 parent - which will have to be on the same desktop */
809 !client_direct_parent(c))
811 ob_debug("moving window %s", c->title);
812 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
814 /* raise all the windows that are on the current desktop which
816 if ((screen_desktop == rmdesktop - 1 ||
817 screen_desktop == rmdesktop) &&
818 (d == DESKTOP_ALL || d == screen_desktop))
820 stacking_raise(CLIENT_AS_WINDOW(c));
821 ob_debug("raising window %s", c->title);
825 g_list_free(stacking_copy);
827 /* fallback focus like we're changing desktops */
828 if (screen_desktop < screen_num_desktops - 1) {
829 screen_fallback_focus();
830 ob_debug("fake desktop change");
833 screen_set_num_desktops(screen_num_desktops-1);
835 event_end_ignore_all_enters(ignore_start);
838 static void get_row_col(guint d, guint *r, guint *c)
840 switch (screen_desktop_layout.orientation) {
841 case OB_ORIENTATION_HORZ:
842 switch (screen_desktop_layout.start_corner) {
843 case OB_CORNER_TOPLEFT:
844 *r = d / screen_desktop_layout.columns;
845 *c = d % screen_desktop_layout.columns;
847 case OB_CORNER_BOTTOMLEFT:
848 *r = screen_desktop_layout.rows - 1 -
849 d / screen_desktop_layout.columns;
850 *c = d % screen_desktop_layout.columns;
852 case OB_CORNER_TOPRIGHT:
853 *r = d / screen_desktop_layout.columns;
854 *c = screen_desktop_layout.columns - 1 -
855 d % screen_desktop_layout.columns;
857 case OB_CORNER_BOTTOMRIGHT:
858 *r = screen_desktop_layout.rows - 1 -
859 d / screen_desktop_layout.columns;
860 *c = screen_desktop_layout.columns - 1 -
861 d % screen_desktop_layout.columns;
865 case OB_ORIENTATION_VERT:
866 switch (screen_desktop_layout.start_corner) {
867 case OB_CORNER_TOPLEFT:
868 *r = d % screen_desktop_layout.rows;
869 *c = d / screen_desktop_layout.rows;
871 case OB_CORNER_BOTTOMLEFT:
872 *r = screen_desktop_layout.rows - 1 -
873 d % screen_desktop_layout.rows;
874 *c = d / screen_desktop_layout.rows;
876 case OB_CORNER_TOPRIGHT:
877 *r = d % screen_desktop_layout.rows;
878 *c = screen_desktop_layout.columns - 1 -
879 d / screen_desktop_layout.rows;
881 case OB_CORNER_BOTTOMRIGHT:
882 *r = screen_desktop_layout.rows - 1 -
883 d % screen_desktop_layout.rows;
884 *c = screen_desktop_layout.columns - 1 -
885 d / screen_desktop_layout.rows;
892 static guint translate_row_col(guint r, guint c)
894 switch (screen_desktop_layout.orientation) {
895 case OB_ORIENTATION_HORZ:
896 switch (screen_desktop_layout.start_corner) {
897 case OB_CORNER_TOPLEFT:
898 return r % screen_desktop_layout.rows *
899 screen_desktop_layout.columns +
900 c % screen_desktop_layout.columns;
901 case OB_CORNER_BOTTOMLEFT:
902 return (screen_desktop_layout.rows - 1 -
903 r % screen_desktop_layout.rows) *
904 screen_desktop_layout.columns +
905 c % screen_desktop_layout.columns;
906 case OB_CORNER_TOPRIGHT:
907 return r % screen_desktop_layout.rows *
908 screen_desktop_layout.columns +
909 (screen_desktop_layout.columns - 1 -
910 c % screen_desktop_layout.columns);
911 case OB_CORNER_BOTTOMRIGHT:
912 return (screen_desktop_layout.rows - 1 -
913 r % screen_desktop_layout.rows) *
914 screen_desktop_layout.columns +
915 (screen_desktop_layout.columns - 1 -
916 c % screen_desktop_layout.columns);
918 case OB_ORIENTATION_VERT:
919 switch (screen_desktop_layout.start_corner) {
920 case OB_CORNER_TOPLEFT:
921 return c % screen_desktop_layout.columns *
922 screen_desktop_layout.rows +
923 r % screen_desktop_layout.rows;
924 case OB_CORNER_BOTTOMLEFT:
925 return c % screen_desktop_layout.columns *
926 screen_desktop_layout.rows +
927 (screen_desktop_layout.rows - 1 -
928 r % screen_desktop_layout.rows);
929 case OB_CORNER_TOPRIGHT:
930 return (screen_desktop_layout.columns - 1 -
931 c % screen_desktop_layout.columns) *
932 screen_desktop_layout.rows +
933 r % screen_desktop_layout.rows;
934 case OB_CORNER_BOTTOMRIGHT:
935 return (screen_desktop_layout.columns - 1 -
936 c % screen_desktop_layout.columns) *
937 screen_desktop_layout.rows +
938 (screen_desktop_layout.rows - 1 -
939 r % screen_desktop_layout.rows);
942 g_assert_not_reached();
946 static gboolean hide_desktop_popup_func(gpointer data)
948 pager_popup_hide(desktop_popup);
949 desktop_popup_timer = 0;
950 return FALSE; /* don't repeat */
953 void screen_show_desktop_popup(guint d, gboolean perm)
957 /* 0 means don't show the popup */
958 if (!config_desktop_popup_time) return;
960 a = screen_physical_area_primary(FALSE);
961 pager_popup_position(desktop_popup, CenterGravity,
962 a->x + a->width / 2, a->y + a->height / 2);
963 pager_popup_icon_size_multiplier(desktop_popup,
964 (screen_desktop_layout.columns /
965 screen_desktop_layout.rows) / 2,
966 (screen_desktop_layout.rows/
967 screen_desktop_layout.columns) / 2);
968 pager_popup_max_width(desktop_popup,
969 MAX(a->width/3, POPUP_WIDTH));
970 pager_popup_show(desktop_popup, screen_desktop_names[d], d);
972 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
973 desktop_popup_timer = 0;
974 if (!perm && !desktop_popup_perm)
975 /* only hide if its not already being show permanently */
976 desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
977 hide_desktop_popup_func,
980 desktop_popup_perm = TRUE;
983 void screen_hide_desktop_popup(void)
985 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
986 desktop_popup_timer = 0;
987 pager_popup_hide(desktop_popup);
988 desktop_popup_perm = FALSE;
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 pager_popup_text_width_to_strings(desktop_popup,
1224 screen_desktop_names,
1225 screen_num_desktops);
1228 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1232 ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1234 gboolean showing_before = screen_showing_desktop();
1235 screen_show_desktop_mode = show_mode;
1236 gboolean showing_after = screen_showing_desktop();
1238 if (showing_before == showing_after) {
1240 screen_show_desktop_mode = before_mode;
1244 if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1247 /* If we're showing the desktop until the show-mode is toggled, we
1248 don't allow breaking out of showing-desktop mode unless we're
1249 showing all the windows again. */
1250 screen_show_desktop_mode = before_mode;
1254 if (showing_after) {
1255 /* hide windows bottom to top */
1256 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1257 if (WINDOW_IS_CLIENT(it->data)) {
1258 ObClient *client = it->data;
1259 client_showhide(client);
1264 /* restore windows top to bottom */
1265 for (it = stacking_list; it; it = g_list_next(it)) {
1266 if (WINDOW_IS_CLIENT(it->data)) {
1267 ObClient *client = it->data;
1268 if (client_should_show(client)) {
1269 if (!show_only || client == show_only)
1270 client_show(client);
1272 client_iconify(client, TRUE, FALSE, TRUE);
1278 if (showing_after) {
1279 /* focus the desktop */
1280 for (it = focus_order; it; it = g_list_next(it)) {
1281 ObClient *c = it->data;
1282 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1283 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1284 client_focus(it->data))
1288 else if (!show_only) {
1291 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1292 /* only do the flicker reducing stuff ahead of time if we are going
1293 to call xsetinputfocus on the window ourselves. otherwise there
1294 is no guarantee the window will actually take focus.. */
1296 /* reduce flicker by hiliting now rather than waiting for the
1297 server FocusIn event */
1298 frame_adjust_focus(c->frame, TRUE);
1303 OBT_PROP_SET32(obt_root(ob_screen),
1304 NET_SHOWING_DESKTOP,
1309 gboolean screen_showing_desktop()
1311 switch (screen_show_desktop_mode) {
1312 case SCREEN_SHOW_DESKTOP_NO:
1314 case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1315 case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1318 g_assert_not_reached();
1322 void screen_install_colormap(ObClient *client, gboolean install)
1324 if (client == NULL || client->colormap == None) {
1326 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1328 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1330 obt_display_ignore_errors(TRUE);
1332 XInstallColormap(obt_display, client->colormap);
1334 XUninstallColormap(obt_display, client->colormap);
1335 obt_display_ignore_errors(FALSE);
1341 StrutPartial *strut;
1344 #define RESET_STRUT_LIST(sl) \
1346 g_slice_free(ObScreenStrut, (sl)->data); \
1347 sl = g_slist_delete_link(sl, sl); \
1350 #define ADD_STRUT_TO_LIST(sl, d, s) \
1352 ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1355 sl = g_slist_prepend(sl, ss); \
1358 #define VALIDATE_STRUTS(sl, side, max) \
1361 for (it = sl; it; it = g_slist_next(it)) { \
1362 ObScreenStrut *ss = it->data; \
1363 ss->strut->side = MIN(max, ss->strut->side); \
1367 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1373 XineramaScreenInfo *info;
1376 if (ob_debug_xinerama) {
1377 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1378 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1380 *xin_areas = g_new(Rect, *nxin + 1);
1381 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1382 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1385 else if (obt_display_extension_xinerama &&
1386 (info = XineramaQueryScreens(obt_display, &n))) {
1388 *xin_areas = g_new(Rect, *nxin + 1);
1389 for (i = 0; i < *nxin; ++i)
1390 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1391 info[i].width, info[i].height);
1397 *xin_areas = g_new(Rect, *nxin + 1);
1398 RECT_SET((*xin_areas)[0], 0, 0,
1399 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1400 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1403 /* returns one extra with the total area in it */
1404 l = (*xin_areas)[0].x;
1405 t = (*xin_areas)[0].y;
1406 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1407 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1408 for (i = 1; i < *nxin; ++i) {
1409 l = MIN(l, (*xin_areas)[i].x);
1410 t = MIN(l, (*xin_areas)[i].y);
1411 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1412 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1414 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1416 for (i = 0; i < *nxin; ++i)
1417 ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1418 (*xin_areas)[i].x, (*xin_areas)[i].y,
1419 (*xin_areas)[i].width, (*xin_areas)[i].height);
1420 ob_debug("Full desktop @ %d,%d %dx%d\n",
1421 (*xin_areas)[i].x, (*xin_areas)[i].y,
1422 (*xin_areas)[i].width, (*xin_areas)[i].height);
1425 void screen_update_areas(void)
1429 GList *it, *onscreen;
1431 /* collect the clients that are on screen */
1433 for (it = client_list; it; it = g_list_next(it)) {
1434 if (client_monitor(it->data) != screen_num_monitors)
1435 onscreen = g_list_prepend(onscreen, it->data);
1438 g_free(monitor_area);
1439 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1441 /* set up the user-specified margins */
1442 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1443 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1444 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1445 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1446 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1447 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1448 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1449 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1451 RESET_STRUT_LIST(struts_left);
1452 RESET_STRUT_LIST(struts_top);
1453 RESET_STRUT_LIST(struts_right);
1454 RESET_STRUT_LIST(struts_bottom);
1456 /* collect the struts */
1457 for (it = client_list; it; it = g_list_next(it)) {
1458 ObClient *c = it->data;
1460 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1462 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1464 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1465 if (c->strut.bottom)
1466 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1468 if (dock_strut.left)
1469 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1471 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1472 if (dock_strut.right)
1473 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1474 if (dock_strut.bottom)
1475 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1477 if (config_margins.left)
1478 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1479 if (config_margins.top)
1480 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1481 if (config_margins.right)
1482 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1483 if (config_margins.bottom)
1484 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1486 VALIDATE_STRUTS(struts_left, left,
1487 monitor_area[screen_num_monitors].width / 2);
1488 VALIDATE_STRUTS(struts_right, right,
1489 monitor_area[screen_num_monitors].width / 2);
1490 VALIDATE_STRUTS(struts_top, top,
1491 monitor_area[screen_num_monitors].height / 2);
1492 VALIDATE_STRUTS(struts_bottom, bottom,
1493 monitor_area[screen_num_monitors].height / 2);
1495 dims = g_new(gulong, 4 * screen_num_desktops);
1496 for (i = 0; i < screen_num_desktops; ++i) {
1497 Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1498 dims[i*4+0] = area->x;
1499 dims[i*4+1] = area->y;
1500 dims[i*4+2] = area->width;
1501 dims[i*4+3] = area->height;
1502 g_slice_free(Rect, area);
1505 /* set the legacy workarea hint to the union of all the monitors */
1506 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1507 dims, 4 * screen_num_desktops);
1509 /* the area has changed, adjust all the windows if they need it */
1510 for (it = onscreen; it; it = g_list_next(it))
1511 client_reconfigure(it->data, FALSE);
1517 Rect* screen_area_all_monitors(guint desktop)
1522 a = screen_area_monitor(desktop, 0);
1524 /* combine all the monitors together */
1525 for (i = 1; i < screen_num_monitors; ++i) {
1526 Rect *m = screen_area_monitor(desktop, i);
1529 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1530 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1531 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1532 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1534 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1543 #define STRUT_LEFT_IN_SEARCH(s, search) \
1544 (RANGES_INTERSECT(search->y, search->height, \
1545 s->left_start, s->left_end - s->left_start + 1))
1546 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1547 (RANGES_INTERSECT(search->y, search->height, \
1548 s->right_start, s->right_end - s->right_start + 1))
1549 #define STRUT_TOP_IN_SEARCH(s, search) \
1550 (RANGES_INTERSECT(search->x, search->width, \
1551 s->top_start, s->top_end - s->top_start + 1))
1552 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1553 (RANGES_INTERSECT(search->x, search->width, \
1554 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1556 #define STRUT_LEFT_IGNORE(s, us, search) \
1557 (head == SCREEN_AREA_ALL_MONITORS && us && \
1558 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1559 #define STRUT_RIGHT_IGNORE(s, us, search) \
1560 (head == SCREEN_AREA_ALL_MONITORS && us && \
1561 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1562 #define STRUT_TOP_IGNORE(s, us, search) \
1563 (head == SCREEN_AREA_ALL_MONITORS && us && \
1564 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1565 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1566 (head == SCREEN_AREA_ALL_MONITORS && us && \
1567 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1569 Rect* screen_area(guint desktop, guint head, Rect *search)
1575 gboolean us = search != NULL; /* user provided search */
1577 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1578 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1579 head == SCREEN_AREA_ALL_MONITORS);
1580 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1582 /* find any struts for this monitor
1583 which will be affecting the search area.
1586 /* search everything if search is null */
1588 if (head < screen_num_monitors) search = &monitor_area[head];
1589 else search = &monitor_area[screen_num_monitors];
1591 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1593 /* al is "all left" meaning the furthest left you can get, l is our
1594 "working left" meaning our current strut edge which we're calculating
1597 /* only include monitors which the search area lines up with */
1598 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1599 l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1600 t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1601 r = RECT_LEFT(monitor_area[screen_num_monitors]);
1602 b = RECT_TOP(monitor_area[screen_num_monitors]);
1603 for (i = 0; i < screen_num_monitors; ++i) {
1604 /* add the monitor if applicable */
1605 if (RANGES_INTERSECT(search->x, search->width,
1606 monitor_area[i].x, monitor_area[i].width))
1608 t = MIN(t, RECT_TOP(monitor_area[i]));
1609 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1611 if (RANGES_INTERSECT(search->y, search->height,
1612 monitor_area[i].y, monitor_area[i].height))
1614 l = MIN(l, RECT_LEFT(monitor_area[i]));
1615 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1619 l = RECT_LEFT(monitor_area[screen_num_monitors]);
1620 t = RECT_TOP(monitor_area[screen_num_monitors]);
1621 r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1622 b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1625 for (d = 0; d < screen_num_desktops; ++d) {
1626 if (d != desktop && desktop != DESKTOP_ALL) continue;
1628 for (i = 0; i < screen_num_monitors; ++i) {
1629 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1631 for (it = struts_left; it; it = g_slist_next(it)) {
1632 ObScreenStrut *s = it->data;
1633 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1634 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1635 !STRUT_LEFT_IGNORE(s->strut, us, search))
1636 l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1639 for (it = struts_top; it; it = g_slist_next(it)) {
1640 ObScreenStrut *s = it->data;
1641 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1642 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1643 !STRUT_TOP_IGNORE(s->strut, us, search))
1644 t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1647 for (it = struts_right; it; it = g_slist_next(it)) {
1648 ObScreenStrut *s = it->data;
1649 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1650 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1651 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1652 r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1655 for (it = struts_bottom; it; it = g_slist_next(it)) {
1656 ObScreenStrut *s = it->data;
1657 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1658 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1659 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1660 b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1661 - s->strut->bottom);
1664 /* limit to this monitor */
1666 l = MAX(l, RECT_LEFT(monitor_area[i]));
1667 t = MAX(t, RECT_TOP(monitor_area[i]));
1668 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1669 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1674 a = g_slice_new(Rect);
1677 a->width = r - l + 1;
1678 a->height = b - t + 1;
1687 guint screen_find_monitor(const Rect *search)
1690 guint mostpx_index = screen_num_monitors;
1692 guint closest_distance_index = screen_num_monitors;
1693 guint closest_distance = G_MAXUINT;
1694 GSList *counted = NULL;
1696 /* we want to count the number of pixels search has on each monitor, but not
1697 double count. so if a pixel is counted on monitor A then we should not
1698 count it again on monitor B. in the end we want to return the monitor
1699 that had the most pixels counted under this scheme.
1701 this assumes that monitors earlier in the list are more desirable to be
1702 considered the search area's monitor. we try the configured primary
1703 monitor first, so it gets the highest preference.
1705 if we have counted an area A, then we want to subtract the intersection
1706 of A with the area on future monitors.
1707 but now consider if we count an area B that intersects A. we want to
1708 subtract the area B from that counted on future monitors, but not
1709 subtract the intersection of A and B twice! so we would add the
1710 intersection of A and B back, to account for it being subtracted both
1713 this is the idea behind the algorithm. we always subtract the full area
1714 for monitor M intersected with the search area. we'll call that AREA.
1715 but then we go through the list |counted| and for each rectangle in
1716 the list that is being subtracted from future monitors, we insert a
1717 request to add back the intersection of the subtracted rect with AREA.
1718 vice versa for a rect in |counted| that is getting added back.
1721 if (config_primary_monitor_index < screen_num_monitors) {
1722 const Rect *monitor;
1723 Rect on_current_monitor;
1726 monitor = screen_physical_area_monitor(config_primary_monitor_index);
1728 if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1729 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1730 area = RECT_AREA(on_current_monitor);
1732 if (area > mostpx) {
1734 mostpx_index = config_primary_monitor_index;
1737 /* add the intersection rect on the current monitor to the
1738 counted list. that's easy for the first one, we just mark it for
1741 RectArithmetic *ra = g_slice_new(RectArithmetic);
1742 ra->r = on_current_monitor;
1743 ra->subtract = TRUE;
1744 counted = g_slist_prepend(counted, ra);
1749 for (i = 0; i < screen_num_monitors; ++i) {
1750 const Rect *monitor;
1751 Rect on_current_monitor;
1755 monitor = screen_physical_area_monitor(i);
1757 if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1758 /* If we don't intersect then find the distance between the search
1759 rect and the monitor. We'll use the closest monitor from this
1760 metric if none of the monitors intersect. */
1761 guint distance = rect_manhatten_distance(*monitor, *search);
1763 if (distance < closest_distance) {
1764 closest_distance = distance;
1765 closest_distance_index = i;
1770 if (i == config_primary_monitor_index)
1771 continue; /* already did this one */
1773 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1774 area = RECT_AREA(on_current_monitor);
1776 /* remove pixels we already counted on any previous monitors. */
1777 for (it = counted; it; it = g_slist_next(it)) {
1778 RectArithmetic *ra = it->data;
1781 RECT_SET_INTERSECTION(intersection, ra->r, *search);
1782 if (ra->subtract) area -= RECT_AREA(intersection);
1783 else area += RECT_AREA(intersection);
1786 if (area > mostpx) {
1791 /* add the intersection rect on the current monitor I to the counted
1793 but now we need to compensate for every rectangle R already in the
1794 counted list, and add a new rect R' that is the intersection of
1795 R and I, but with the reverse subtraction/addition operation.
1797 for (it = counted; it; it = g_slist_next(it)) {
1798 RectArithmetic *saved = it->data;
1800 if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1802 /* we are going to subtract our rect from future monitors, but
1803 part of it may already be being subtracted/added, so compensate
1804 to not double add/subtract. */
1805 RectArithmetic *reverse = g_slice_new(RectArithmetic);
1806 RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1807 reverse->subtract = !saved->subtract;
1808 /* prepend so we can continue thru the list uninterupted */
1809 counted = g_slist_prepend(counted, reverse);
1812 RectArithmetic *ra = g_slice_new(RectArithmetic);
1813 ra->r = on_current_monitor;
1814 ra->subtract = TRUE;
1815 counted = g_slist_prepend(counted, ra);
1820 g_slice_free(RectArithmetic, counted->data);
1821 counted = g_slist_delete_link(counted, counted);
1824 if (mostpx_index < screen_num_monitors)
1825 return mostpx_index;
1827 g_assert(closest_distance_index < screen_num_monitors);
1828 return closest_distance_index;
1831 const Rect* screen_physical_area_all_monitors(void)
1833 return screen_physical_area_monitor(screen_num_monitors);
1836 const Rect* screen_physical_area_monitor(guint head)
1838 g_assert(head <= screen_num_monitors);
1840 return &monitor_area[head];
1843 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1845 g_assert(head <= screen_num_monitors);
1847 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1850 guint screen_monitor_active(void)
1852 if (moveresize_client)
1853 return client_monitor(moveresize_client);
1854 else if (focus_client)
1855 return client_monitor(focus_client);
1857 return screen_monitor_pointer();
1860 const Rect* screen_physical_area_active(void)
1862 return screen_physical_area_monitor(screen_monitor_active());
1865 guint screen_monitor_primary(gboolean fixed)
1867 if (config_primary_monitor_index > 0) {
1868 if (config_primary_monitor_index-1 < screen_num_monitors)
1869 return config_primary_monitor_index - 1;
1875 else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1876 return screen_monitor_active();
1877 else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1878 return screen_monitor_pointer();
1881 const Rect* screen_physical_area_primary(gboolean fixed)
1883 return screen_physical_area_monitor(screen_monitor_primary(fixed));
1886 void screen_set_root_cursor(void)
1888 if (sn_app_starting())
1889 XDefineCursor(obt_display, obt_root(ob_screen),
1890 ob_cursor(OB_CURSOR_BUSY));
1892 XDefineCursor(obt_display, obt_root(ob_screen),
1893 ob_cursor(OB_CURSOR_POINTER));
1896 guint screen_find_monitor_point(guint x, guint y)
1899 RECT_SET(mon, x, y, 1, 1);
1900 return screen_find_monitor(&mon);
1903 guint screen_monitor_pointer()
1906 if (!screen_pointer_pos(&x, &y))
1908 return screen_find_monitor_point(x, y);
1911 gboolean screen_pointer_pos(gint *x, gint *y)
1918 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1919 &w, &w, x, y, &i, &i, &u);
1921 for (i = 0; i < ScreenCount(obt_display); ++i)
1923 if (XQueryPointer(obt_display, obt_root(i),
1924 &w, &w, x, y, &i, &i, &u))
1930 gboolean screen_compare_desktops(guint a, guint b)
1932 if (a == DESKTOP_ALL)
1934 if (b == DESKTOP_ALL)
1939 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
1940 const GravityPoint *position, const Rect *area)
1942 if (position->x.center)
1943 *x = area->width / 2 - width / 2;
1945 *x = position->x.pos;
1946 if (position->x.denom)
1947 *x = (*x * area->width) / position->x.denom;
1948 if (position->x.opposite)
1949 *x = area->width - width - *x;
1952 if (position->y.center)
1953 *y = area->height / 2 - height / 2;
1955 *y = position->y.pos;
1956 if (position->y.denom)
1957 *y = (*y * area->height) / position->y.denom;
1958 if (position->y.opposite)
1959 *y = area->height - height - *y;