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