]> Dogcows Code - chaz/openbox/blob - openbox/screen.c
make the client list menu update properly and not crash
[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 GSList *it;
904
905 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
906 screen_num_desktops + 1);
907 screen_desktop_names[screen_num_desktops] = NULL;
908
909 it = g_slist_nth(config_desktops_names, i);
910
911 for (; i < screen_num_desktops; ++i) {
912 if (it)
913 /* use the names from the config file when possible */
914 screen_desktop_names[i] = g_strdup(it->data);
915 else
916 /* make up a nice name if it's not though */
917 screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
918 if (it) it = g_slist_next(it);
919 }
920
921 /* if we changed any names, then set the root property so we can
922 all agree on the names */
923 PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
924 screen_desktop_names);
925 }
926
927 /* resize the pager for these names */
928 pager_popup_text_width_to_strings(desktop_cycle_popup,
929 screen_desktop_names,
930 screen_num_desktops);
931 }
932
933 void screen_show_desktop(gboolean show, ObClient *show_only)
934 {
935 GList *it;
936
937 if (show == screen_showing_desktop) return; /* no change */
938
939 screen_showing_desktop = show;
940
941 if (show) {
942 /* hide windows bottom to top */
943 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
944 if (WINDOW_IS_CLIENT(it->data)) {
945 ObClient *client = it->data;
946 client_showhide(client);
947 }
948 }
949 }
950 else {
951 /* restore windows top to bottom */
952 for (it = stacking_list; it; it = g_list_next(it)) {
953 if (WINDOW_IS_CLIENT(it->data)) {
954 ObClient *client = it->data;
955 if (client_should_show(client)) {
956 if (!show_only || client == show_only)
957 client_show(client);
958 else
959 client_iconify(client, TRUE, FALSE, TRUE);
960 }
961 }
962 }
963 }
964
965 if (show) {
966 /* focus the desktop */
967 for (it = focus_order; it; it = g_list_next(it)) {
968 ObClient *c = it->data;
969 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
970 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
971 client_focus(it->data))
972 break;
973 }
974 }
975 else if (!show_only) {
976 ObClient *c;
977
978 if ((c = focus_fallback(TRUE))) {
979 /* only do the flicker reducing stuff ahead of time if we are going
980 to call xsetinputfocus on the window ourselves. otherwise there
981 is no guarantee the window will actually take focus.. */
982 if (c->can_focus) {
983 /* reduce flicker by hiliting now rather than waiting for the
984 server FocusIn event */
985 frame_adjust_focus(c->frame, TRUE);
986 }
987 }
988 }
989
990 show = !!show; /* make it boolean */
991 PROP_SET32(RootWindow(ob_display, ob_screen),
992 net_showing_desktop, cardinal, show);
993 }
994
995 void screen_install_colormap(ObClient *client, gboolean install)
996 {
997 if (client == NULL) {
998 if (install)
999 XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1000 else
1001 XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1002 } else {
1003 xerror_set_ignore(TRUE);
1004 if (install) {
1005 if (client->colormap != None)
1006 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1007 } else
1008 XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1009 xerror_set_ignore(FALSE);
1010 }
1011 }
1012
1013 static inline void
1014 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1015 gint edge, Strut *ret)
1016 {
1017 if (s->left &&
1018 ((s->left_end <= s->left_start) ||
1019 (RECT_TOP(*monitor_area) < s->left_end &&
1020 RECT_BOTTOM(*monitor_area) > s->left_start)))
1021 ret->left = MAX(ret->left, edge);
1022 }
1023
1024 static inline void
1025 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1026 gint edge, Strut *ret)
1027 {
1028 if (s->top &&
1029 ((s->top_end <= s->top_start) ||
1030 (RECT_LEFT(*monitor_area) < s->top_end &&
1031 RECT_RIGHT(*monitor_area) > s->top_start)))
1032 ret->top = MAX(ret->top, edge);
1033 }
1034
1035 static inline void
1036 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1037 gint edge, Strut *ret)
1038 {
1039 if (s->right &&
1040 ((s->right_end <= s->right_start) ||
1041 (RECT_TOP(*monitor_area) < s->right_end &&
1042 RECT_BOTTOM(*monitor_area) > s->right_start)))
1043 ret->right = MAX(ret->right, edge);
1044 }
1045
1046 static inline void
1047 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1048 gint edge, Strut *ret)
1049 {
1050 if (s->bottom &&
1051 ((s->bottom_end <= s->bottom_start) ||
1052 (RECT_LEFT(*monitor_area) < s->bottom_end &&
1053 RECT_RIGHT(*monitor_area) > s->bottom_start)))
1054 ret->bottom = MAX(ret->bottom, edge);
1055 }
1056
1057 void screen_update_areas()
1058 {
1059 guint i, x;
1060 gulong *dims;
1061 GList *it;
1062 gint o;
1063
1064 g_free(monitor_area);
1065 extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1066
1067 if (area) {
1068 for (i = 0; area[i]; ++i)
1069 g_free(area[i]);
1070 g_free(area);
1071 }
1072
1073 area = g_new(Rect*, screen_num_desktops + 2);
1074 for (i = 0; i < screen_num_desktops + 1; ++i)
1075 area[i] = g_new0(Rect, screen_num_monitors + 1);
1076 area[i] = NULL;
1077
1078 dims = g_new(gulong, 4 * screen_num_desktops);
1079
1080 for (i = 0; i < screen_num_desktops + 1; ++i) {
1081 Strut *struts;
1082 gint l, r, t, b;
1083
1084 struts = g_new0(Strut, screen_num_monitors);
1085
1086 /* calc the xinerama areas */
1087 for (x = 0; x < screen_num_monitors; ++x) {
1088 area[i][x] = monitor_area[x];
1089 if (x == 0) {
1090 l = monitor_area[x].x;
1091 t = monitor_area[x].y;
1092 r = monitor_area[x].x + monitor_area[x].width - 1;
1093 b = monitor_area[x].y + monitor_area[x].height - 1;
1094 } else {
1095 l = MIN(l, monitor_area[x].x);
1096 t = MIN(t, monitor_area[x].y);
1097 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1098 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1099 }
1100 }
1101 RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1102
1103 /* apply the struts */
1104
1105 /* find the left-most xin heads, i do this in 2 loops :| */
1106 o = area[i][0].x;
1107 for (x = 1; x < screen_num_monitors; ++x)
1108 o = MIN(o, area[i][x].x);
1109
1110 for (x = 0; x < screen_num_monitors; ++x) {
1111 for (it = client_list; it; it = g_list_next(it)) {
1112 ObClient *c = it->data;
1113 screen_area_add_strut_left(&c->strut,
1114 &monitor_area[x],
1115 o + c->strut.left - area[i][x].x,
1116 &struts[x]);
1117 }
1118 screen_area_add_strut_left(&dock_strut,
1119 &monitor_area[x],
1120 o + dock_strut.left - area[i][x].x,
1121 &struts[x]);
1122
1123 area[i][x].x += struts[x].left;
1124 area[i][x].width -= struts[x].left;
1125 }
1126
1127 /* find the top-most xin heads, i do this in 2 loops :| */
1128 o = area[i][0].y;
1129 for (x = 1; x < screen_num_monitors; ++x)
1130 o = MIN(o, area[i][x].y);
1131
1132 for (x = 0; x < screen_num_monitors; ++x) {
1133 for (it = client_list; it; it = g_list_next(it)) {
1134 ObClient *c = it->data;
1135 screen_area_add_strut_top(&c->strut,
1136 &monitor_area[x],
1137 o + c->strut.top - area[i][x].y,
1138 &struts[x]);
1139 }
1140 screen_area_add_strut_top(&dock_strut,
1141 &monitor_area[x],
1142 o + dock_strut.top - area[i][x].y,
1143 &struts[x]);
1144
1145 area[i][x].y += struts[x].top;
1146 area[i][x].height -= struts[x].top;
1147 }
1148
1149 /* find the right-most xin heads, i do this in 2 loops :| */
1150 o = area[i][0].x + area[i][0].width - 1;
1151 for (x = 1; x < screen_num_monitors; ++x)
1152 o = MAX(o, area[i][x].x + area[i][x].width - 1);
1153
1154 for (x = 0; x < screen_num_monitors; ++x) {
1155 for (it = client_list; it; it = g_list_next(it)) {
1156 ObClient *c = it->data;
1157 screen_area_add_strut_right(&c->strut,
1158 &monitor_area[x],
1159 (area[i][x].x +
1160 area[i][x].width - 1) -
1161 (o - c->strut.right),
1162 &struts[x]);
1163 }
1164 screen_area_add_strut_right(&dock_strut,
1165 &monitor_area[x],
1166 (area[i][x].x +
1167 area[i][x].width - 1) -
1168 (o - dock_strut.right),
1169 &struts[x]);
1170
1171 area[i][x].width -= struts[x].right;
1172 }
1173
1174 /* find the bottom-most xin heads, i do this in 2 loops :| */
1175 o = area[i][0].y + area[i][0].height - 1;
1176 for (x = 1; x < screen_num_monitors; ++x)
1177 o = MAX(o, area[i][x].y + area[i][x].height - 1);
1178
1179 for (x = 0; x < screen_num_monitors; ++x) {
1180 for (it = client_list; it; it = g_list_next(it)) {
1181 ObClient *c = it->data;
1182 screen_area_add_strut_bottom(&c->strut,
1183 &monitor_area[x],
1184 (area[i][x].y +
1185 area[i][x].height - 1) - \
1186 (o - c->strut.bottom),
1187 &struts[x]);
1188 }
1189 screen_area_add_strut_bottom(&dock_strut,
1190 &monitor_area[x],
1191 (area[i][x].y +
1192 area[i][x].height - 1) - \
1193 (o - dock_strut.bottom),
1194 &struts[x]);
1195
1196 area[i][x].height -= struts[x].bottom;
1197 }
1198
1199 l = RECT_LEFT(area[i][0]);
1200 t = RECT_TOP(area[i][0]);
1201 r = RECT_RIGHT(area[i][0]);
1202 b = RECT_BOTTOM(area[i][0]);
1203 for (x = 1; x < screen_num_monitors; ++x) {
1204 l = MIN(l, RECT_LEFT(area[i][x]));
1205 t = MIN(l, RECT_TOP(area[i][x]));
1206 r = MAX(r, RECT_RIGHT(area[i][x]));
1207 b = MAX(b, RECT_BOTTOM(area[i][x]));
1208 }
1209 RECT_SET(area[i][screen_num_monitors], l, t,
1210 r - l + 1, b - t + 1);
1211
1212 /* XXX optimize when this is run? */
1213
1214 /* the area has changed, adjust all the maximized
1215 windows */
1216 for (it = client_list; it; it = g_list_next(it)) {
1217 ObClient *c = it->data;
1218 if (i < screen_num_desktops) {
1219 if (c->desktop == i)
1220 client_reconfigure(c);
1221 } else if (c->desktop == DESKTOP_ALL)
1222 client_reconfigure(c);
1223 }
1224 if (i < screen_num_desktops) {
1225 /* don't set these for the 'all desktops' area */
1226 dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1227 dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1228 dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1229 dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1230 }
1231
1232 g_free(struts);
1233 }
1234
1235 PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1236 dims, 4 * screen_num_desktops);
1237
1238 g_free(dims);
1239 }
1240
1241 Rect *screen_area(guint desktop)
1242 {
1243 return screen_area_monitor(desktop, screen_num_monitors);
1244 }
1245
1246 Rect *screen_area_monitor(guint desktop, guint head)
1247 {
1248 if (head > screen_num_monitors)
1249 return NULL;
1250 if (desktop >= screen_num_desktops) {
1251 if (desktop == DESKTOP_ALL)
1252 return &area[screen_num_desktops][head];
1253 return NULL;
1254 }
1255 return &area[desktop][head];
1256 }
1257
1258 guint screen_find_monitor(Rect *search)
1259 {
1260 guint i;
1261 guint most = 0;
1262 guint mostv = 0;
1263
1264 for (i = 0; i < screen_num_monitors; ++i) {
1265 Rect *area = screen_physical_area_monitor(i);
1266 if (RECT_INTERSECTS_RECT(*area, *search)) {
1267 Rect r;
1268 guint v;
1269
1270 RECT_SET_INTERSECTION(r, *area, *search);
1271 v = r.width * r.height;
1272
1273 if (v > mostv) {
1274 mostv = v;
1275 most = i;
1276 }
1277 }
1278 }
1279 return most;
1280 }
1281
1282 Rect *screen_physical_area()
1283 {
1284 return screen_physical_area_monitor(screen_num_monitors);
1285 }
1286
1287 Rect *screen_physical_area_monitor(guint head)
1288 {
1289 if (head > screen_num_monitors)
1290 return NULL;
1291 return &monitor_area[head];
1292 }
1293
1294 void screen_set_root_cursor()
1295 {
1296 if (sn_app_starting())
1297 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1298 ob_cursor(OB_CURSOR_BUSYPOINTER));
1299 else
1300 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1301 ob_cursor(OB_CURSOR_POINTER));
1302 }
1303
1304 gboolean screen_pointer_pos(gint *x, gint *y)
1305 {
1306 Window w;
1307 gint i;
1308 guint u;
1309 gboolean ret;
1310
1311 ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1312 &w, &w, x, y, &i, &i, &u);
1313 if (!ret) {
1314 for (i = 0; i < ScreenCount(ob_display); ++i)
1315 if (i != ob_screen)
1316 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1317 &w, &w, x, y, &i, &i, &u))
1318 break;
1319 }
1320 return ret;
1321 }
This page took 0.102197 seconds and 4 git commands to generate.