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