]> Dogcows Code - chaz/openbox/blob - openbox/client.c
from r6017, we no longer need to fallback in client_unmanage. yay
[chaz/openbox] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.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 "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "xerror.h"
25 #include "screen.h"
26 #include "moveresize.h"
27 #include "place.h"
28 #include "prop.h"
29 #include "extensions.h"
30 #include "frame.h"
31 #include "session.h"
32 #include "event.h"
33 #include "grab.h"
34 #include "focus.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "render/render.h"
43
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47
48 #include <glib.h>
49 #include <X11/Xutil.h>
50
51 /*! The event mask to grab on client windows */
52 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
53 ColormapChangeMask)
54
55 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ButtonMotionMask)
57
58 typedef struct
59 {
60 ObClientDestructor func;
61 gpointer data;
62 } Destructor;
63
64 GList *client_list = NULL;
65
66 static GSList *client_destructors = NULL;
67
68 static void client_get_all(ObClient *self);
69 static void client_toggle_border(ObClient *self, gboolean show);
70 static void client_get_startup_id(ObClient *self);
71 static void client_get_area(ObClient *self);
72 static void client_get_desktop(ObClient *self);
73 static void client_get_state(ObClient *self);
74 static void client_get_layer(ObClient *self);
75 static void client_get_shaped(ObClient *self);
76 static void client_get_mwm_hints(ObClient *self);
77 static void client_get_gravity(ObClient *self);
78 static void client_get_client_machine(ObClient *self);
79 static void client_get_colormap(ObClient *self);
80 static void client_change_allowed_actions(ObClient *self);
81 static void client_change_state(ObClient *self);
82 static void client_change_wm_state(ObClient *self);
83 static void client_apply_startup_state(ObClient *self, gint x, gint y);
84 static void client_restore_session_state(ObClient *self);
85 static void client_restore_session_stacking(ObClient *self);
86 static ObAppSettings *client_get_settings_state(ObClient *self);
87
88 void client_startup(gboolean reconfig)
89 {
90 if (reconfig) return;
91
92 client_set_list();
93 }
94
95 void client_shutdown(gboolean reconfig)
96 {
97 }
98
99 void client_add_destructor(ObClientDestructor func, gpointer data)
100 {
101 Destructor *d = g_new(Destructor, 1);
102 d->func = func;
103 d->data = data;
104 client_destructors = g_slist_prepend(client_destructors, d);
105 }
106
107 void client_remove_destructor(ObClientDestructor func)
108 {
109 GSList *it;
110
111 for (it = client_destructors; it; it = g_slist_next(it)) {
112 Destructor *d = it->data;
113 if (d->func == func) {
114 g_free(d);
115 client_destructors = g_slist_delete_link(client_destructors, it);
116 break;
117 }
118 }
119 }
120
121 void client_set_list()
122 {
123 Window *windows, *win_it;
124 GList *it;
125 guint size = g_list_length(client_list);
126
127 /* create an array of the window ids */
128 if (size > 0) {
129 windows = g_new(Window, size);
130 win_it = windows;
131 for (it = client_list; it; it = g_list_next(it), ++win_it)
132 *win_it = ((ObClient*)it->data)->window;
133 } else
134 windows = NULL;
135
136 PROP_SETA32(RootWindow(ob_display, ob_screen),
137 net_client_list, window, (gulong*)windows, size);
138
139 if (windows)
140 g_free(windows);
141
142 stacking_set_list();
143 }
144
145 /*
146 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
147 {
148 GSList *it;
149
150 for (it = self->transients; it; it = g_slist_next(it)) {
151 if (!func(it->data, data)) return;
152 client_foreach_transient(it->data, func, data);
153 }
154 }
155
156 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
157 {
158 if (self->transient_for) {
159 if (self->transient_for != OB_TRAN_GROUP) {
160 if (!func(self->transient_for, data)) return;
161 client_foreach_ancestor(self->transient_for, func, data);
162 } else {
163 GSList *it;
164
165 for (it = self->group->members; it; it = g_slist_next(it))
166 if (it->data != self &&
167 !((ObClient*)it->data)->transient_for) {
168 if (!func(it->data, data)) return;
169 client_foreach_ancestor(it->data, func, data);
170 }
171 }
172 }
173 }
174 */
175
176 void client_manage_all()
177 {
178 guint i, j, nchild;
179 Window w, *children;
180 XWMHints *wmhints;
181 XWindowAttributes attrib;
182
183 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
184 &w, &w, &children, &nchild);
185
186 /* remove all icon windows from the list */
187 for (i = 0; i < nchild; i++) {
188 if (children[i] == None) continue;
189 wmhints = XGetWMHints(ob_display, children[i]);
190 if (wmhints) {
191 if ((wmhints->flags & IconWindowHint) &&
192 (wmhints->icon_window != children[i]))
193 for (j = 0; j < nchild; j++)
194 if (children[j] == wmhints->icon_window) {
195 children[j] = None;
196 break;
197 }
198 XFree(wmhints);
199 }
200 }
201
202 for (i = 0; i < nchild; ++i) {
203 if (children[i] == None)
204 continue;
205 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
206 if (attrib.override_redirect) continue;
207
208 if (attrib.map_state != IsUnmapped)
209 client_manage(children[i]);
210 }
211 }
212 XFree(children);
213 }
214
215 void client_manage(Window window)
216 {
217 ObClient *self;
218 XEvent e;
219 XWindowAttributes attrib;
220 XSetWindowAttributes attrib_set;
221 XWMHints *wmhint;
222 gboolean activate = FALSE;
223 ObAppSettings *settings;
224 gint newx, newy;
225
226 grab_server(TRUE);
227
228 /* check if it has already been unmapped by the time we started mapping.
229 the grab does a sync so we don't have to here */
230 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
231 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
232 {
233 XPutBackEvent(ob_display, &e);
234
235 ob_debug("Trying to manage unmapped window. Aborting that.\n");
236 grab_server(FALSE);
237 return; /* don't manage it */
238 }
239
240 /* make sure it isn't an override-redirect window */
241 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
242 attrib.override_redirect)
243 {
244 grab_server(FALSE);
245 return; /* don't manage it */
246 }
247
248 /* is the window a docking app */
249 if ((wmhint = XGetWMHints(ob_display, window))) {
250 if ((wmhint->flags & StateHint) &&
251 wmhint->initial_state == WithdrawnState)
252 {
253 dock_add(window, wmhint);
254 grab_server(FALSE);
255 XFree(wmhint);
256 return;
257 }
258 XFree(wmhint);
259 }
260
261 ob_debug("Managing window: %lx\n", window);
262
263 /* choose the events we want to receive on the CLIENT window */
264 attrib_set.event_mask = CLIENT_EVENTMASK;
265 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
266 XChangeWindowAttributes(ob_display, window,
267 CWEventMask|CWDontPropagate, &attrib_set);
268
269
270 /* create the ObClient struct, and populate it from the hints on the
271 window */
272 self = g_new0(ObClient, 1);
273 self->obwin.type = Window_Client;
274 self->window = window;
275
276 /* non-zero defaults */
277 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
278 self->layer = -1;
279 self->desktop = screen_num_desktops; /* always an invalid value */
280 self->user_time = CurrentTime;
281
282 client_get_all(self);
283 /* per-app settings override stuff, and return the settings for other
284 uses too */
285 settings = client_get_settings_state(self);
286 /* the session should get the last say */
287 client_restore_session_state(self);
288
289 client_calc_layer(self);
290
291 {
292 Time t = sn_app_started(self->startup_id, self->class);
293 if (t) self->user_time = t;
294 }
295
296 /* update the focus lists, do this before the call to change_state or
297 it can end up in the list twice! */
298 focus_order_add_new(self);
299
300 /* remove the client's border (and adjust re gravity) */
301 client_toggle_border(self, FALSE);
302
303 /* specify that if we exit, the window should not be destroyed and should
304 be reparented back to root automatically */
305 XChangeSaveSet(ob_display, window, SetModeInsert);
306
307 /* create the decoration frame for the client window */
308 self->frame = frame_new(self);
309
310 frame_grab_client(self->frame, self);
311
312 /* do this after we have a frame.. it uses the frame to help determine the
313 WM_STATE to apply. */
314 client_change_state(self);
315
316 grab_server(FALSE);
317
318 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
319 client_restore_session_stacking(self);
320
321 /* focus the new window? */
322 if (ob_state() != OB_STATE_STARTING &&
323 !self->iconic &&
324 /* this means focus=true for window is same as config_focus_new=true */
325 ((config_focus_new || (settings && settings->focus == 1)) ||
326 client_search_focus_parent(self)) &&
327 /* this checks for focus=false for the window */
328 (!settings || settings->focus != 0) &&
329 /* note the check against Type_Normal/Dialog, not client_normal(self),
330 which would also include other types. in this case we want more
331 strict rules for focus */
332 (self->type == OB_CLIENT_TYPE_NORMAL ||
333 self->type == OB_CLIENT_TYPE_DIALOG))
334 {
335 activate = TRUE;
336 }
337
338 /* get the current position */
339 newx = self->area.x;
340 newy = self->area.y;
341
342 /* figure out placement for the window */
343 if (ob_state() == OB_STATE_RUNNING) {
344 gboolean transient;
345
346 transient = place_client(self, &newx, &newy, settings);
347
348 /* make sure the window is visible. */
349 client_find_onscreen(self, &newx, &newy,
350 self->area.width,
351 self->area.height,
352 /* non-normal clients has less rules, and
353 windows that are being restored from a
354 session do also. we can assume you want
355 it back where you saved it. Clients saying
356 they placed themselves are subjected to
357 harder rules, ones that are placed by
358 place.c or by the user are allowed partially
359 off-screen and on xinerama divides (ie,
360 it is up to the placement routines to avoid
361 the xinerama divides) */
362 transient ||
363 (((self->positioned & PPosition) &&
364 !(self->positioned & USPosition)) &&
365 client_normal(self) &&
366 !self->session));
367 }
368
369 /* do this after the window is placed, so the premax/prefullscreen numbers
370 won't be all wacko!!
371 also, this moves the window to the position where it has been placed
372 */
373 ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
374 self->window, newx, newy, self->area.width, self->area.height);
375 client_apply_startup_state(self, newx, newy);
376
377 mouse_grab_for_client(self, TRUE);
378
379 if (activate) {
380 guint32 last_time = focus_client ?
381 focus_client->user_time : CurrentTime;
382
383 /* This is focus stealing prevention */
384 ob_debug_type(OB_DEBUG_FOCUS,
385 "Want to focus new window 0x%x with time %u "
386 "(last time %u)\n",
387 self->window, self->user_time, last_time);
388
389 /* if it's on another desktop */
390 if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
391 && /* the timestamp is from before you changed desktops */
392 self->user_time && screen_desktop_user_time &&
393 !event_time_after(self->user_time, screen_desktop_user_time))
394 {
395 activate = FALSE;
396 ob_debug_type(OB_DEBUG_FOCUS,
397 "Not focusing the window because its on another "
398 "desktop\n");
399 }
400 /* If something is focused, and it's not our parent... */
401 else if (focus_client && client_search_focus_parent(self) == NULL)
402 {
403 /* If time stamp is old, don't steal focus */
404 if (self->user_time && last_time &&
405 !event_time_after(self->user_time, last_time))
406 {
407 activate = FALSE;
408 ob_debug_type(OB_DEBUG_FOCUS,
409 "Not focusing the window because the time is "
410 "too old\n");
411 }
412 /* Don't steal focus from globally active clients.
413 I stole this idea from KWin. It seems nice.
414 */
415 if (!(focus_client->can_focus || focus_client->focus_notify)) {
416 activate = FALSE;
417 ob_debug_type(OB_DEBUG_FOCUS,
418 "Not focusing the window because a globally "
419 "active client has focus\n");
420 }
421 }
422
423 if (!activate) {
424 ob_debug_type(OB_DEBUG_FOCUS,
425 "Focus stealing prevention activated for %s with "
426 "time %u (last time %u)\n",
427 self->title, self->user_time, last_time);
428 /* if the client isn't focused, then hilite it so the user
429 knows it is there */
430 client_hilite(self, TRUE);
431 }
432 }
433 else {
434 /* This may look rather odd. Well it's because new windows are added
435 to the stacking order non-intrusively. If we're not going to focus
436 the new window or hilite it, then we raise it to the top. This will
437 take affect for things that don't get focused like splash screens.
438 Also if you don't have focus_new enabled, then it's going to get
439 raised to the top. Legacy begets legacy I guess?
440 */
441 client_raise(self);
442 }
443
444 /* this has to happen before we try focus the window, but we want it to
445 happen after the client's stacking has been determined or it looks bad
446 */
447 client_show(self);
448
449 /* use client_focus instead of client_activate cuz client_activate does
450 stuff like switch desktops etc and I'm not interested in all that when
451 a window maps since its not based on an action from the user like
452 clicking a window to activate it. so keep the new window out of the way
453 but do focus it. */
454 if (activate)
455 client_activate(self, FALSE, TRUE);
456
457 /* add to client list/map */
458 client_list = g_list_append(client_list, self);
459 g_hash_table_insert(window_map, &self->window, self);
460
461 /* this has to happen after we're in the client_list */
462 if (STRUT_EXISTS(self->strut))
463 screen_update_areas();
464
465 /* update the list hints */
466 client_set_list();
467
468 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
469 }
470
471 void client_unmanage_all()
472 {
473 while (client_list != NULL)
474 client_unmanage(client_list->data);
475 }
476
477 void client_unmanage(ObClient *self)
478 {
479 guint j;
480 GSList *it;
481
482 ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
483 self->title ? self->title : "");
484
485 g_assert(self != NULL);
486
487 /* we dont want events no more. do this before hiding the frame so we
488 don't generate more events */
489 XSelectInput(ob_display, self->window, NoEventMask);
490
491 frame_hide(self->frame);
492 /* flush to send the hide to the server quickly */
493 XFlush(ob_display);
494
495 /* ignore enter events from the unmap so it doesnt mess with the focus */
496 event_ignore_queued_enters();
497
498 mouse_grab_for_client(self, FALSE);
499
500 /* remove the window from our save set */
501 XChangeSaveSet(ob_display, self->window, SetModeDelete);
502
503 /* update the focus lists */
504 focus_order_remove(self);
505 if (client_focused(self)) {
506 /* don't leave an invalid focus_client */
507 focus_client = NULL;
508 }
509
510 client_list = g_list_remove(client_list, self);
511 stacking_remove(self);
512 g_hash_table_remove(window_map, &self->window);
513
514 /* once the client is out of the list, update the struts to remove its
515 influence */
516 if (STRUT_EXISTS(self->strut))
517 screen_update_areas();
518
519 for (it = client_destructors; it; it = g_slist_next(it)) {
520 Destructor *d = it->data;
521 d->func(self, d->data);
522 }
523
524 /* tell our parent(s) that we're gone */
525 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
526 for (it = self->group->members; it; it = g_slist_next(it))
527 if (it->data != self)
528 ((ObClient*)it->data)->transients =
529 g_slist_remove(((ObClient*)it->data)->transients, self);
530 } else if (self->transient_for) { /* transient of window */
531 self->transient_for->transients =
532 g_slist_remove(self->transient_for->transients, self);
533 }
534
535 /* tell our transients that we're gone */
536 for (it = self->transients; it; it = g_slist_next(it)) {
537 if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
538 ((ObClient*)it->data)->transient_for = NULL;
539 client_calc_layer(it->data);
540 }
541 }
542
543 /* remove from its group */
544 if (self->group) {
545 group_remove(self->group, self);
546 self->group = NULL;
547 }
548
549 /* restore the window's original geometry so it is not lost */
550 {
551 Rect a = self->area;
552
553 if (self->fullscreen)
554 a = self->pre_fullscreen_area;
555 else if (self->max_horz || self->max_vert) {
556 if (self->max_horz) {
557 a.x = self->pre_max_area.x;
558 a.width = self->pre_max_area.width;
559 }
560 if (self->max_vert) {
561 a.y = self->pre_max_area.y;
562 a.height = self->pre_max_area.height;
563 }
564 }
565
566 /* give the client its border back */
567 client_toggle_border(self, TRUE);
568
569 self->fullscreen = self->max_horz = self->max_vert = FALSE;
570 self->decorations = 0; /* unmanaged windows have no decor */
571
572 client_move_resize(self, a.x, a.y, a.width, a.height);
573 }
574
575 /* reparent the window out of the frame, and free the frame */
576 frame_release_client(self->frame, self);
577 self->frame = NULL;
578
579 if (ob_state() != OB_STATE_EXITING) {
580 /* these values should not be persisted across a window
581 unmapping/mapping */
582 PROP_ERASE(self->window, net_wm_desktop);
583 PROP_ERASE(self->window, net_wm_state);
584 PROP_ERASE(self->window, wm_state);
585 } else {
586 /* if we're left in an unmapped state, the client wont be mapped. this
587 is bad, since we will no longer be managing the window on restart */
588 XMapWindow(ob_display, self->window);
589 }
590
591 ob_debug("Unmanaged window 0x%lx\n", self->window);
592
593 /* free all data allocated in the client struct */
594 g_slist_free(self->transients);
595 for (j = 0; j < self->nicons; ++j)
596 g_free(self->icons[j].data);
597 if (self->nicons > 0)
598 g_free(self->icons);
599 g_free(self->title);
600 g_free(self->icon_title);
601 g_free(self->name);
602 g_free(self->class);
603 g_free(self->role);
604 g_free(self->client_machine);
605 g_free(self->sm_client_id);
606 g_free(self);
607
608 /* update the list hints */
609 client_set_list();
610 }
611
612 static ObAppSettings *client_get_settings_state(ObClient *self)
613 {
614 ObAppSettings *settings = NULL;
615 GSList *it;
616
617 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
618 ObAppSettings *app = it->data;
619
620 if ((app->name && !app->class && !strcmp(app->name, self->name))
621 || (app->class && !app->name && !strcmp(app->class, self->class))
622 || (app->class && app->name && !strcmp(app->class, self->class)
623 && !strcmp(app->name, self->name)))
624 {
625 ob_debug("Window matching: %s\n", app->name);
626 /* Match if no role was specified in the per app setting, or if the
627 * string matches the beginning of the role, since apps like to set
628 * the role to things like browser-window-23c4b2f */
629 if (!app->role
630 || !strncmp(app->role, self->role, strlen(app->role)))
631 {
632 /* use this one */
633 settings = app;
634 break;
635 }
636 }
637 }
638
639 if (settings) {
640 if (settings->shade != -1)
641 self->shaded = !!settings->shade;
642 if (settings->decor != -1)
643 self->undecorated = !settings->decor;
644 if (settings->iconic != -1)
645 self->iconic = !!settings->iconic;
646 if (settings->skip_pager != -1)
647 self->skip_pager = !!settings->skip_pager;
648 if (settings->skip_taskbar != -1)
649 self->skip_taskbar = !!settings->skip_taskbar;
650
651 if (settings->max_vert != -1)
652 self->max_vert = !!settings->max_vert;
653 if (settings->max_horz != -1)
654 self->max_horz = !!settings->max_horz;
655
656 if (settings->fullscreen != -1)
657 self->fullscreen = !!settings->fullscreen;
658
659 if (settings->desktop < screen_num_desktops
660 || settings->desktop == DESKTOP_ALL)
661 self->desktop = settings->desktop;
662
663 if (settings->layer == -1) {
664 self->below = TRUE;
665 self->above = FALSE;
666 }
667 else if (settings->layer == 0) {
668 self->below = FALSE;
669 self->above = FALSE;
670 }
671 else if (settings->layer == 1) {
672 self->below = FALSE;
673 self->above = TRUE;
674 }
675 }
676 return settings;
677 }
678
679 static void client_restore_session_state(ObClient *self)
680 {
681 GList *it;
682
683 if (!(it = session_state_find(self)))
684 return;
685
686 self->session = it->data;
687
688 RECT_SET_POINT(self->area, self->session->x, self->session->y);
689 self->positioned = PPosition;
690 if (self->session->w > 0)
691 self->area.width = self->session->w;
692 if (self->session->h > 0)
693 self->area.height = self->session->h;
694 XResizeWindow(ob_display, self->window,
695 self->area.width, self->area.height);
696
697 self->desktop = (self->session->desktop == DESKTOP_ALL ?
698 self->session->desktop :
699 MIN(screen_num_desktops - 1, self->session->desktop));
700 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
701
702 self->shaded = self->session->shaded;
703 self->iconic = self->session->iconic;
704 self->skip_pager = self->session->skip_pager;
705 self->skip_taskbar = self->session->skip_taskbar;
706 self->fullscreen = self->session->fullscreen;
707 self->above = self->session->above;
708 self->below = self->session->below;
709 self->max_horz = self->session->max_horz;
710 self->max_vert = self->session->max_vert;
711 }
712
713 static void client_restore_session_stacking(ObClient *self)
714 {
715 GList *it;
716
717 if (!self->session) return;
718
719 it = g_list_find(session_saved_state, self->session);
720 for (it = g_list_previous(it); it; it = g_list_previous(it)) {
721 GList *cit;
722
723 for (cit = client_list; cit; cit = g_list_next(cit))
724 if (session_state_cmp(it->data, cit->data))
725 break;
726 if (cit) {
727 client_calc_layer(self);
728 stacking_below(CLIENT_AS_WINDOW(self),
729 CLIENT_AS_WINDOW(cit->data));
730 break;
731 }
732 }
733 }
734
735 void client_move_onscreen(ObClient *self, gboolean rude)
736 {
737 gint x = self->area.x;
738 gint y = self->area.y;
739 if (client_find_onscreen(self, &x, &y,
740 self->area.width,
741 self->area.height, rude)) {
742 client_move(self, x, y);
743 }
744 }
745
746 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
747 gboolean rude)
748 {
749 Rect *mon_a, *all_a;
750 gint ox = *x, oy = *y;
751 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
752 gint fw, fh;
753
754 all_a = screen_area(self->desktop);
755 mon_a = screen_area_monitor(self->desktop, client_monitor(self));
756
757 /* get where the frame would be */
758 frame_client_gravity(self->frame, x, y, w, h);
759
760 /* get the requested size of the window with decorations */
761 fw = self->frame->size.left + w + self->frame->size.right;
762 fh = self->frame->size.top + h + self->frame->size.bottom;
763
764 /* This makes sure windows aren't entirely outside of the screen so you
765 can't see them at all.
766 It makes sure 10% of the window is on the screen at least. At don't let
767 it move itself off the top of the screen, which would hide the titlebar
768 on you. (The user can still do this if they want too, it's only limiting
769 the application.
770
771 XXX watch for xinerama dead areas...
772 */
773 if (client_normal(self)) {
774 if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1)
775 *x = all_a->x + all_a->width - fw/10;
776 if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1)
777 *y = all_a->y + all_a->height - fh/10;
778 if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x)
779 *x = all_a->x - fw*9/10;
780 if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y)
781 *y = all_a->y - fw*9/10;
782 }
783
784 /* If rudeness wasn't requested, then figure out of the client is currently
785 entirely on the screen. If it is, and the position isn't changing by
786 request, and it is enlarging, then be rude even though it wasn't
787 requested */
788 if (!rude) {
789 Point oldtl, oldtr, oldbl, oldbr;
790 Point newtl, newtr, newbl, newbr;
791 gboolean stationary;
792
793 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
794 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
795 self->frame->area.y + self->frame->area.height - 1);
796 POINT_SET(oldtr, oldbr.x, oldtl.y);
797 POINT_SET(oldbl, oldtl.x, oldbr.y);
798
799 POINT_SET(newtl, *x, *y);
800 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
801 POINT_SET(newtr, newbr.x, newtl.y);
802 POINT_SET(newbl, newtl.x, newbr.y);
803
804 /* is it moving or just resizing from some corner? */
805 stationary = (POINT_EQUAL(oldtl, newtl) || POINT_EQUAL(oldtr, newtr) ||
806 POINT_EQUAL(oldbl, newbl) || POINT_EQUAL(oldbr, newbr));
807
808 /* if left edge is growing */
809 if (stationary && newtl.x < oldtl.x)
810 rudel = TRUE;
811 /* if right edge is growing */
812 if (stationary && newtr.x > oldtr.x)
813 ruder = TRUE;
814 /* if top edge is growing */
815 if (stationary && newtl.y < oldtl.y)
816 rudet = TRUE;
817 /* if bottom edge is growing */
818 if (stationary && newbl.y > oldbl.y)
819 rudeb = TRUE;
820 }
821
822 /* This here doesn't let windows even a pixel outside the struts/screen.
823 * When called from client_manage, programs placing themselves are
824 * forced completely onscreen, while things like
825 * xterm -geometry resolution-width/2 will work fine. Trying to
826 * place it completely offscreen will be handled in the above code.
827 * Sorry for this confused comment, i am tired. */
828 if (fw <= mon_a->width) {
829 if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
830 if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
831 *x = mon_a->x + mon_a->width - fw;
832 }
833 if (fh <= mon_a->height) {
834 if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
835 if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
836 *y = mon_a->y + mon_a->height - fh;
837 }
838
839 /* get where the client should be */
840 frame_frame_gravity(self->frame, x, y, w, h);
841
842 return ox != *x || oy != *y;
843 }
844
845 static void client_toggle_border(ObClient *self, gboolean show)
846 {
847 /* adjust our idea of where the client is, based on its border. When the
848 border is removed, the client should now be considered to be in a
849 different position.
850 when re-adding the border to the client, the same operation needs to be
851 reversed. */
852 gint oldx = self->area.x, oldy = self->area.y;
853 gint x = oldx, y = oldy;
854 switch(self->gravity) {
855 default:
856 case NorthWestGravity:
857 case WestGravity:
858 case SouthWestGravity:
859 break;
860 case NorthEastGravity:
861 case EastGravity:
862 case SouthEastGravity:
863 if (show) x -= self->border_width * 2;
864 else x += self->border_width * 2;
865 break;
866 case NorthGravity:
867 case SouthGravity:
868 case CenterGravity:
869 case ForgetGravity:
870 case StaticGravity:
871 if (show) x -= self->border_width;
872 else x += self->border_width;
873 break;
874 }
875 switch(self->gravity) {
876 default:
877 case NorthWestGravity:
878 case NorthGravity:
879 case NorthEastGravity:
880 break;
881 case SouthWestGravity:
882 case SouthGravity:
883 case SouthEastGravity:
884 if (show) y -= self->border_width * 2;
885 else y += self->border_width * 2;
886 break;
887 case WestGravity:
888 case EastGravity:
889 case CenterGravity:
890 case ForgetGravity:
891 case StaticGravity:
892 if (show) y -= self->border_width;
893 else y += self->border_width;
894 break;
895 }
896 self->area.x = x;
897 self->area.y = y;
898
899 if (show) {
900 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
901
902 /* set border_width to 0 because there is no border to add into
903 calculations anymore */
904 self->border_width = 0;
905 } else
906 XSetWindowBorderWidth(ob_display, self->window, 0);
907 }
908
909
910 static void client_get_all(ObClient *self)
911 {
912 client_get_area(self);
913 client_get_mwm_hints(self);
914
915 /* The transient hint is used to pick a type, but the type can also affect
916 transiency (dialogs are always made transients of their group if they
917 have one). This is Havoc's idea, but it is needed to make some apps
918 work right (eg tsclient). */
919 client_update_transient_for(self);
920 client_get_type(self);/* this can change the mwmhints for special cases */
921 client_get_state(self);
922 client_update_transient_for(self);
923
924 client_update_wmhints(self);
925 client_get_startup_id(self);
926 client_get_desktop(self);/* uses transient data/group/startup id if a
927 desktop is not specified */
928 client_get_shaped(self);
929
930 client_get_layer(self); /* if layer hasn't been specified, get it from
931 other sources if possible */
932
933 {
934 /* a couple type-based defaults for new windows */
935
936 /* this makes sure that these windows appear on all desktops */
937 if (self->type == OB_CLIENT_TYPE_DESKTOP)
938 self->desktop = DESKTOP_ALL;
939 }
940
941 client_update_protocols(self);
942
943 client_get_gravity(self); /* get the attribute gravity */
944 client_update_normal_hints(self); /* this may override the attribute
945 gravity */
946
947 /* got the type, the mwmhints, the protocols, and the normal hints
948 (min/max sizes), so we're ready to set up the decorations/functions */
949 client_setup_decor_and_functions(self);
950
951 #ifdef SYNC
952 client_update_sync_request_counter(self);
953 #endif
954 client_get_client_machine(self);
955 client_get_colormap(self);
956 client_update_title(self);
957 client_update_class(self);
958 client_update_sm_client_id(self);
959 client_update_strut(self);
960 client_update_icons(self);
961 client_update_user_time(self);
962 }
963
964 static void client_get_startup_id(ObClient *self)
965 {
966 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
967 if (self->group)
968 PROP_GETS(self->group->leader,
969 net_startup_id, utf8, &self->startup_id);
970 }
971
972 static void client_get_area(ObClient *self)
973 {
974 XWindowAttributes wattrib;
975 Status ret;
976
977 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
978 g_assert(ret != BadWindow);
979
980 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
981 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
982 self->border_width = wattrib.border_width;
983
984 ob_debug("client area: %d %d %d %d\n", wattrib.x, wattrib.y,
985 wattrib.width, wattrib.height);
986 }
987
988 static void client_get_desktop(ObClient *self)
989 {
990 guint32 d = screen_num_desktops; /* an always-invalid value */
991
992 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
993 if (d >= screen_num_desktops && d != DESKTOP_ALL)
994 self->desktop = screen_num_desktops - 1;
995 else
996 self->desktop = d;
997 } else {
998 gboolean trdesk = FALSE;
999
1000 if (self->transient_for) {
1001 if (self->transient_for != OB_TRAN_GROUP) {
1002 self->desktop = self->transient_for->desktop;
1003 trdesk = TRUE;
1004 } else {
1005 GSList *it;
1006
1007 for (it = self->group->members; it; it = g_slist_next(it))
1008 if (it->data != self &&
1009 !((ObClient*)it->data)->transient_for) {
1010 self->desktop = ((ObClient*)it->data)->desktop;
1011 trdesk = TRUE;
1012 break;
1013 }
1014 }
1015 }
1016 if (!trdesk) {
1017 /* try get from the startup-notification protocol */
1018 if (sn_get_desktop(self->startup_id, &self->desktop)) {
1019 if (self->desktop >= screen_num_desktops &&
1020 self->desktop != DESKTOP_ALL)
1021 self->desktop = screen_num_desktops - 1;
1022 } else
1023 /* defaults to the current desktop */
1024 self->desktop = screen_desktop;
1025 }
1026 }
1027 }
1028
1029 static void client_get_layer(ObClient *self)
1030 {
1031 if (!(self->above || self->below)) {
1032 if (self->group) {
1033 /* apply stuff from the group */
1034 GSList *it;
1035 gint layer = -2;
1036
1037 for (it = self->group->members; it; it = g_slist_next(it)) {
1038 ObClient *c = it->data;
1039 if (c != self && !client_search_transient(self, c) &&
1040 client_normal(self) && client_normal(c))
1041 {
1042 layer = MAX(layer,
1043 (c->above ? 1 : (c->below ? -1 : 0)));
1044 }
1045 }
1046 switch (layer) {
1047 case -1:
1048 self->below = TRUE;
1049 break;
1050 case -2:
1051 case 0:
1052 break;
1053 case 1:
1054 self->above = TRUE;
1055 break;
1056 default:
1057 g_assert_not_reached();
1058 break;
1059 }
1060 }
1061 }
1062 }
1063
1064 static void client_get_state(ObClient *self)
1065 {
1066 guint32 *state;
1067 guint num;
1068
1069 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
1070 gulong i;
1071 for (i = 0; i < num; ++i) {
1072 if (state[i] == prop_atoms.net_wm_state_modal)
1073 self->modal = TRUE;
1074 else if (state[i] == prop_atoms.net_wm_state_shaded)
1075 self->shaded = TRUE;
1076 else if (state[i] == prop_atoms.net_wm_state_hidden)
1077 self->iconic = TRUE;
1078 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
1079 self->skip_taskbar = TRUE;
1080 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
1081 self->skip_pager = TRUE;
1082 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
1083 self->fullscreen = TRUE;
1084 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
1085 self->max_vert = TRUE;
1086 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
1087 self->max_horz = TRUE;
1088 else if (state[i] == prop_atoms.net_wm_state_above)
1089 self->above = TRUE;
1090 else if (state[i] == prop_atoms.net_wm_state_below)
1091 self->below = TRUE;
1092 else if (state[i] == prop_atoms.net_wm_state_demands_attention)
1093 self->demands_attention = TRUE;
1094 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
1095 self->undecorated = TRUE;
1096 }
1097
1098 g_free(state);
1099 }
1100 }
1101
1102 static void client_get_shaped(ObClient *self)
1103 {
1104 self->shaped = FALSE;
1105 #ifdef SHAPE
1106 if (extensions_shape) {
1107 gint foo;
1108 guint ufoo;
1109 gint s;
1110
1111 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
1112
1113 XShapeQueryExtents(ob_display, self->window, &s, &foo,
1114 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1115 &ufoo);
1116 self->shaped = (s != 0);
1117 }
1118 #endif
1119 }
1120
1121 void client_update_transient_for(ObClient *self)
1122 {
1123 Window t = None;
1124 ObClient *target = NULL;
1125
1126 if (XGetTransientForHint(ob_display, self->window, &t)) {
1127 self->transient = TRUE;
1128 if (t != self->window) { /* cant be transient to itself! */
1129 target = g_hash_table_lookup(window_map, &t);
1130 /* if this happens then we need to check for it*/
1131 g_assert(target != self);
1132 if (target && !WINDOW_IS_CLIENT(target)) {
1133 /* this can happen when a dialog is a child of
1134 a dockapp, for example */
1135 target = NULL;
1136 }
1137
1138 /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1139
1140 Setting the transient_for to Root is actually illegal, however
1141 applications from time have done this to specify transient for
1142 their group.
1143
1144 Now you can do that by being a TYPE_DIALOG and not setting
1145 the transient_for hint at all on your window. But people still
1146 use Root, and Kwin is very strange in this regard.
1147
1148 KWin 3.0 will not consider windows with transient_for set to
1149 Root as transient for their group *UNLESS* they are also modal.
1150 In that case, it will make them transient for the group. This
1151 leads to all sorts of weird behavior from KDE apps which are
1152 only tested in KWin. I'd like to follow their behavior just to
1153 make this work right with KDE stuff, but that seems wrong.
1154 */
1155 if (!target && self->group) {
1156 /* not transient to a client, see if it is transient for a
1157 group */
1158 if (t == RootWindow(ob_display, ob_screen)) {
1159 /* window is a transient for its group! */
1160 target = OB_TRAN_GROUP;
1161 }
1162 }
1163 }
1164 } else if (self->group) {
1165 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1166 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1167 self->type == OB_CLIENT_TYPE_MENU ||
1168 self->type == OB_CLIENT_TYPE_UTILITY)
1169 {
1170 self->transient = TRUE;
1171 target = OB_TRAN_GROUP;
1172 }
1173 } else
1174 self->transient = FALSE;
1175
1176 /* if anything has changed... */
1177 if (target != self->transient_for) {
1178 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1179 GSList *it;
1180
1181 /* remove from old parents */
1182 for (it = self->group->members; it; it = g_slist_next(it)) {
1183 ObClient *c = it->data;
1184 if (c != self && (!c->transient_for ||
1185 c->transient_for != OB_TRAN_GROUP))
1186 c->transients = g_slist_remove(c->transients, self);
1187 }
1188 } else if (self->transient_for != NULL) { /* transient of window */
1189 /* remove from old parent */
1190 self->transient_for->transients =
1191 g_slist_remove(self->transient_for->transients, self);
1192 }
1193 self->transient_for = target;
1194 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
1195 GSList *it;
1196
1197 /* add to new parents */
1198 for (it = self->group->members; it; it = g_slist_next(it)) {
1199 ObClient *c = it->data;
1200 if (c != self && (!c->transient_for ||
1201 c->transient_for != OB_TRAN_GROUP))
1202 c->transients = g_slist_append(c->transients, self);
1203 }
1204
1205 /* remove all transients which are in the group, that causes
1206 circlular pointer hell of doom */
1207 for (it = self->group->members; it; it = g_slist_next(it)) {
1208 GSList *sit, *next;
1209 for (sit = self->transients; sit; sit = next) {
1210 next = g_slist_next(sit);
1211 if (sit->data == it->data)
1212 self->transients =
1213 g_slist_delete_link(self->transients, sit);
1214 }
1215 }
1216 } else if (self->transient_for != NULL) { /* transient of window */
1217 /* add to new parent */
1218 self->transient_for->transients =
1219 g_slist_append(self->transient_for->transients, self);
1220 }
1221 }
1222 }
1223
1224 static void client_get_mwm_hints(ObClient *self)
1225 {
1226 guint num;
1227 guint32 *hints;
1228
1229 self->mwmhints.flags = 0; /* default to none */
1230
1231 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1232 &hints, &num)) {
1233 if (num >= OB_MWM_ELEMENTS) {
1234 self->mwmhints.flags = hints[0];
1235 self->mwmhints.functions = hints[1];
1236 self->mwmhints.decorations = hints[2];
1237 }
1238 g_free(hints);
1239 }
1240 }
1241
1242 void client_get_type(ObClient *self)
1243 {
1244 guint num, i;
1245 guint32 *val;
1246
1247 self->type = -1;
1248
1249 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1250 /* use the first value that we know about in the array */
1251 for (i = 0; i < num; ++i) {
1252 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1253 self->type = OB_CLIENT_TYPE_DESKTOP;
1254 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1255 self->type = OB_CLIENT_TYPE_DOCK;
1256 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1257 self->type = OB_CLIENT_TYPE_TOOLBAR;
1258 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1259 self->type = OB_CLIENT_TYPE_MENU;
1260 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1261 self->type = OB_CLIENT_TYPE_UTILITY;
1262 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1263 self->type = OB_CLIENT_TYPE_SPLASH;
1264 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1265 self->type = OB_CLIENT_TYPE_DIALOG;
1266 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1267 self->type = OB_CLIENT_TYPE_NORMAL;
1268 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1269 /* prevent this window from getting any decor or
1270 functionality */
1271 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1272 OB_MWM_FLAG_DECORATIONS);
1273 self->mwmhints.decorations = 0;
1274 self->mwmhints.functions = 0;
1275 }
1276 if (self->type != (ObClientType) -1)
1277 break; /* grab the first legit type */
1278 }
1279 g_free(val);
1280 }
1281
1282 if (self->type == (ObClientType) -1) {
1283 /*the window type hint was not set, which means we either classify
1284 ourself as a normal window or a dialog, depending on if we are a
1285 transient. */
1286 if (self->transient)
1287 self->type = OB_CLIENT_TYPE_DIALOG;
1288 else
1289 self->type = OB_CLIENT_TYPE_NORMAL;
1290 }
1291 }
1292
1293 void client_update_protocols(ObClient *self)
1294 {
1295 guint32 *proto;
1296 guint num_return, i;
1297
1298 self->focus_notify = FALSE;
1299 self->delete_window = FALSE;
1300
1301 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1302 for (i = 0; i < num_return; ++i) {
1303 if (proto[i] == prop_atoms.wm_delete_window)
1304 /* this means we can request the window to close */
1305 self->delete_window = TRUE;
1306 else if (proto[i] == prop_atoms.wm_take_focus)
1307 /* if this protocol is requested, then the window will be
1308 notified whenever we want it to receive focus */
1309 self->focus_notify = TRUE;
1310 #ifdef SYNC
1311 else if (proto[i] == prop_atoms.net_wm_sync_request)
1312 /* if this protocol is requested, then resizing the
1313 window will be synchronized between the frame and the
1314 client */
1315 self->sync_request = TRUE;
1316 #endif
1317 }
1318 g_free(proto);
1319 }
1320 }
1321
1322 #ifdef SYNC
1323 void client_update_sync_request_counter(ObClient *self)
1324 {
1325 guint32 i;
1326
1327 if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
1328 self->sync_counter = i;
1329 } else
1330 self->sync_counter = None;
1331 }
1332 #endif
1333
1334 static void client_get_gravity(ObClient *self)
1335 {
1336 XWindowAttributes wattrib;
1337 Status ret;
1338
1339 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1340 g_assert(ret != BadWindow);
1341 self->gravity = wattrib.win_gravity;
1342 }
1343
1344 void client_get_colormap(ObClient *self)
1345 {
1346 XWindowAttributes wa;
1347
1348 if (XGetWindowAttributes(ob_display, self->window, &wa))
1349 client_update_colormap(self, wa.colormap);
1350 }
1351
1352 void client_update_colormap(ObClient *self, Colormap colormap)
1353 {
1354 self->colormap = colormap;
1355 }
1356
1357 void client_update_normal_hints(ObClient *self)
1358 {
1359 XSizeHints size;
1360 glong ret;
1361 gint oldgravity = self->gravity;
1362
1363 /* defaults */
1364 self->min_ratio = 0.0f;
1365 self->max_ratio = 0.0f;
1366 SIZE_SET(self->size_inc, 1, 1);
1367 SIZE_SET(self->base_size, 0, 0);
1368 SIZE_SET(self->min_size, 0, 0);
1369 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1370
1371 /* get the hints from the window */
1372 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1373 /* normal windows can't request placement! har har
1374 if (!client_normal(self))
1375 */
1376 self->positioned = (size.flags & (PPosition|USPosition));
1377
1378 if (size.flags & PWinGravity) {
1379 self->gravity = size.win_gravity;
1380
1381 /* if the client has a frame, i.e. has already been mapped and
1382 is changing its gravity */
1383 if (self->frame && self->gravity != oldgravity) {
1384 /* move our idea of the client's position based on its new
1385 gravity */
1386 client_convert_gravity(self, oldgravity,
1387 &self->area.x, &self->area.y,
1388 self->area.width, self->area.height);
1389 }
1390 }
1391
1392 if (size.flags & PAspect) {
1393 if (size.min_aspect.y)
1394 self->min_ratio =
1395 (gfloat) size.min_aspect.x / size.min_aspect.y;
1396 if (size.max_aspect.y)
1397 self->max_ratio =
1398 (gfloat) size.max_aspect.x / size.max_aspect.y;
1399 }
1400
1401 if (size.flags & PMinSize)
1402 SIZE_SET(self->min_size, size.min_width, size.min_height);
1403
1404 if (size.flags & PMaxSize)
1405 SIZE_SET(self->max_size, size.max_width, size.max_height);
1406
1407 if (size.flags & PBaseSize)
1408 SIZE_SET(self->base_size, size.base_width, size.base_height);
1409
1410 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1411 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1412 }
1413 }
1414
1415 void client_setup_decor_and_functions(ObClient *self)
1416 {
1417 /* start with everything (cept fullscreen) */
1418 self->decorations =
1419 (OB_FRAME_DECOR_TITLEBAR |
1420 OB_FRAME_DECOR_HANDLE |
1421 OB_FRAME_DECOR_GRIPS |
1422 OB_FRAME_DECOR_BORDER |
1423 OB_FRAME_DECOR_ICON |
1424 OB_FRAME_DECOR_ALLDESKTOPS |
1425 OB_FRAME_DECOR_ICONIFY |
1426 OB_FRAME_DECOR_MAXIMIZE |
1427 OB_FRAME_DECOR_SHADE |
1428 OB_FRAME_DECOR_CLOSE);
1429 self->functions =
1430 (OB_CLIENT_FUNC_RESIZE |
1431 OB_CLIENT_FUNC_MOVE |
1432 OB_CLIENT_FUNC_ICONIFY |
1433 OB_CLIENT_FUNC_MAXIMIZE |
1434 OB_CLIENT_FUNC_SHADE |
1435 OB_CLIENT_FUNC_CLOSE);
1436
1437 if (!(self->min_size.width < self->max_size.width ||
1438 self->min_size.height < self->max_size.height))
1439 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1440
1441 switch (self->type) {
1442 case OB_CLIENT_TYPE_NORMAL:
1443 /* normal windows retain all of the possible decorations and
1444 functionality, and are the only windows that you can fullscreen */
1445 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1446 break;
1447
1448 case OB_CLIENT_TYPE_DIALOG:
1449 case OB_CLIENT_TYPE_UTILITY:
1450 /* these windows cannot be maximized */
1451 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1452 break;
1453
1454 case OB_CLIENT_TYPE_MENU:
1455 case OB_CLIENT_TYPE_TOOLBAR:
1456 /* these windows get less functionality */
1457 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1458 break;
1459
1460 case OB_CLIENT_TYPE_DESKTOP:
1461 case OB_CLIENT_TYPE_DOCK:
1462 case OB_CLIENT_TYPE_SPLASH:
1463 /* none of these windows are manipulated by the window manager */
1464 self->decorations = 0;
1465 self->functions = 0;
1466 break;
1467 }
1468
1469 /* Mwm Hints are applied subtractively to what has already been chosen for
1470 decor and functionality */
1471 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1472 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1473 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1474 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1475 {
1476 /* if the mwm hints request no handle or title, then all
1477 decorations are disabled, but keep the border if that's
1478 specified */
1479 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1480 self->decorations = OB_FRAME_DECOR_BORDER;
1481 else
1482 self->decorations = 0;
1483 }
1484 }
1485 }
1486
1487 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1488 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1489 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1490 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1491 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1492 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1493 /* dont let mwm hints kill any buttons
1494 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1495 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1496 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1497 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1498 */
1499 /* dont let mwm hints kill the close button
1500 if (! (self->mwmhints.functions & MwmFunc_Close))
1501 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1502 }
1503 }
1504
1505 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1506 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1507 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1508 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1509 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1510 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1511
1512 /* can't maximize without moving/resizing */
1513 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1514 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1515 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1516 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1517 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1518 }
1519
1520 /* kill the handle on fully maxed windows */
1521 if (self->max_vert && self->max_horz)
1522 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1523
1524 /* finally, the user can have requested no decorations, which overrides
1525 everything (but doesnt give it a border if it doesnt have one) */
1526 if (self->undecorated) {
1527 if (config_theme_keepborder)
1528 self->decorations &= OB_FRAME_DECOR_BORDER;
1529 else
1530 self->decorations = 0;
1531 }
1532
1533 /* if we don't have a titlebar, then we cannot shade! */
1534 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1535 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1536
1537 /* now we need to check against rules for the client's current state */
1538 if (self->fullscreen) {
1539 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1540 OB_CLIENT_FUNC_FULLSCREEN |
1541 OB_CLIENT_FUNC_ICONIFY);
1542 self->decorations = 0;
1543 }
1544
1545 client_change_allowed_actions(self);
1546
1547 if (self->frame) {
1548 /* adjust the client's decorations, etc. */
1549 client_reconfigure(self);
1550 }
1551 }
1552
1553 static void client_change_allowed_actions(ObClient *self)
1554 {
1555 gulong actions[9];
1556 gint num = 0;
1557
1558 /* desktop windows are kept on all desktops */
1559 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1560 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1561
1562 if (self->functions & OB_CLIENT_FUNC_SHADE)
1563 actions[num++] = prop_atoms.net_wm_action_shade;
1564 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1565 actions[num++] = prop_atoms.net_wm_action_close;
1566 if (self->functions & OB_CLIENT_FUNC_MOVE)
1567 actions[num++] = prop_atoms.net_wm_action_move;
1568 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1569 actions[num++] = prop_atoms.net_wm_action_minimize;
1570 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1571 actions[num++] = prop_atoms.net_wm_action_resize;
1572 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1573 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1574 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1575 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1576 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1577 }
1578
1579 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1580
1581 /* make sure the window isn't breaking any rules now */
1582
1583 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1584 if (self->frame) client_shade(self, FALSE);
1585 else self->shaded = FALSE;
1586 }
1587 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1588 if (self->frame) client_iconify(self, FALSE, TRUE);
1589 else self->iconic = FALSE;
1590 }
1591 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1592 if (self->frame) client_fullscreen(self, FALSE);
1593 else self->fullscreen = FALSE;
1594 }
1595 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1596 self->max_vert)) {
1597 if (self->frame) client_maximize(self, FALSE, 0);
1598 else self->max_vert = self->max_horz = FALSE;
1599 }
1600 }
1601
1602 void client_reconfigure(ObClient *self)
1603 {
1604 /* by making this pass FALSE for user, we avoid the emacs event storm where
1605 every configurenotify causes an update in its normal hints, i think this
1606 is generally what we want anyways... */
1607 client_configure(self, self->area.x, self->area.y,
1608 self->area.width, self->area.height, FALSE, TRUE);
1609 }
1610
1611 void client_update_wmhints(ObClient *self)
1612 {
1613 XWMHints *hints;
1614 GSList *it;
1615
1616 /* assume a window takes input if it doesnt specify */
1617 self->can_focus = TRUE;
1618
1619 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1620 gboolean ur;
1621
1622 if (hints->flags & InputHint)
1623 self->can_focus = hints->input;
1624
1625 /* only do this when first managing the window *AND* when we aren't
1626 starting up! */
1627 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1628 if (hints->flags & StateHint)
1629 self->iconic = hints->initial_state == IconicState;
1630
1631 ur = self->urgent;
1632 self->urgent = (hints->flags & XUrgencyHint);
1633 if (self->urgent && !ur)
1634 client_hilite(self, TRUE);
1635 else if (!self->urgent && ur && self->demands_attention)
1636 client_hilite(self, FALSE);
1637
1638 if (!(hints->flags & WindowGroupHint))
1639 hints->window_group = None;
1640
1641 /* did the group state change? */
1642 if (hints->window_group !=
1643 (self->group ? self->group->leader : None)) {
1644 /* remove from the old group if there was one */
1645 if (self->group != NULL) {
1646 /* remove transients of the group */
1647 for (it = self->group->members; it; it = g_slist_next(it))
1648 self->transients = g_slist_remove(self->transients,
1649 it->data);
1650
1651 /* remove myself from parents in the group */
1652 if (self->transient_for == OB_TRAN_GROUP) {
1653 for (it = self->group->members; it;
1654 it = g_slist_next(it))
1655 {
1656 ObClient *c = it->data;
1657
1658 if (c != self && !c->transient_for)
1659 c->transients = g_slist_remove(c->transients,
1660 self);
1661 }
1662 }
1663
1664 group_remove(self->group, self);
1665 self->group = NULL;
1666 }
1667 if (hints->window_group != None) {
1668 self->group = group_add(hints->window_group, self);
1669
1670 /* i can only have transients from the group if i am not
1671 transient myself */
1672 if (!self->transient_for) {
1673 /* add other transients of the group that are already
1674 set up */
1675 for (it = self->group->members; it;
1676 it = g_slist_next(it))
1677 {
1678 ObClient *c = it->data;
1679 if (c != self && c->transient_for == OB_TRAN_GROUP)
1680 self->transients =
1681 g_slist_append(self->transients, c);
1682 }
1683 }
1684 }
1685
1686 /* because the self->transient flag wont change from this call,
1687 we don't need to update the window's type and such, only its
1688 transient_for, and the transients lists of other windows in
1689 the group may be affected */
1690 client_update_transient_for(self);
1691 }
1692
1693 /* the WM_HINTS can contain an icon */
1694 client_update_icons(self);
1695
1696 XFree(hints);
1697 }
1698 }
1699
1700 void client_update_title(ObClient *self)
1701 {
1702 gchar *data = NULL;
1703 gchar *visible = NULL;
1704
1705 g_free(self->title);
1706
1707 /* try netwm */
1708 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1709 /* try old x stuff */
1710 if (!(PROP_GETS(self->window, wm_name, locale, &data)
1711 || PROP_GETS(self->window, wm_name, utf8, &data))) {
1712 if (self->transient) {
1713 /*
1714 GNOME alert windows are not given titles:
1715 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1716 */
1717 data = g_strdup("");
1718 } else
1719 data = g_strdup("Unnamed Window");
1720 }
1721 }
1722
1723 if (self->client_machine) {
1724 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1725 g_free(data);
1726 } else
1727 visible = data;
1728
1729 PROP_SETS(self->window, net_wm_visible_name, visible);
1730 self->title = visible;
1731
1732 if (self->frame)
1733 frame_adjust_title(self->frame);
1734
1735 /* update the icon title */
1736 data = NULL;
1737 g_free(self->icon_title);
1738
1739 /* try netwm */
1740 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1741 /* try old x stuff */
1742 if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
1743 PROP_GETS(self->window, wm_icon_name, utf8, &data)))
1744 data = g_strdup(self->title);
1745
1746 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1747 self->icon_title = data;
1748 }
1749
1750 void client_update_class(ObClient *self)
1751 {
1752 gchar **data;
1753 gchar *s;
1754
1755 if (self->name) g_free(self->name);
1756 if (self->class) g_free(self->class);
1757 if (self->role) g_free(self->role);
1758
1759 self->name = self->class = self->role = NULL;
1760
1761 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1762 if (data[0]) {
1763 self->name = g_strdup(data[0]);
1764 if (data[1])
1765 self->class = g_strdup(data[1]);
1766 }
1767 g_strfreev(data);
1768 }
1769
1770 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1771 self->role = s;
1772
1773 if (self->name == NULL) self->name = g_strdup("");
1774 if (self->class == NULL) self->class = g_strdup("");
1775 if (self->role == NULL) self->role = g_strdup("");
1776 }
1777
1778 void client_update_strut(ObClient *self)
1779 {
1780 guint num;
1781 guint32 *data;
1782 gboolean got = FALSE;
1783 StrutPartial strut;
1784
1785 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1786 &data, &num)) {
1787 if (num == 12) {
1788 got = TRUE;
1789 STRUT_PARTIAL_SET(strut,
1790 data[0], data[2], data[1], data[3],
1791 data[4], data[5], data[8], data[9],
1792 data[6], data[7], data[10], data[11]);
1793 }
1794 g_free(data);
1795 }
1796
1797 if (!got &&
1798 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1799 if (num == 4) {
1800 const Rect *a;
1801
1802 got = TRUE;
1803
1804 /* use the screen's width/height */
1805 a = screen_physical_area();
1806
1807 STRUT_PARTIAL_SET(strut,
1808 data[0], data[2], data[1], data[3],
1809 a->y, a->y + a->height - 1,
1810 a->x, a->x + a->width - 1,
1811 a->y, a->y + a->height - 1,
1812 a->x, a->x + a->width - 1);
1813 }
1814 g_free(data);
1815 }
1816
1817 if (!got)
1818 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1819 0, 0, 0, 0, 0, 0, 0, 0);
1820
1821 if (!STRUT_EQUAL(strut, self->strut)) {
1822 self->strut = strut;
1823
1824 /* updating here is pointless while we're being mapped cuz we're not in
1825 the client list yet */
1826 if (self->frame)
1827 screen_update_areas();
1828 }
1829 }
1830
1831 void client_update_icons(ObClient *self)
1832 {
1833 guint num;
1834 guint32 *data;
1835 guint w, h, i, j;
1836
1837 for (i = 0; i < self->nicons; ++i)
1838 g_free(self->icons[i].data);
1839 if (self->nicons > 0)
1840 g_free(self->icons);
1841 self->nicons = 0;
1842
1843 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1844 /* figure out how many valid icons are in here */
1845 i = 0;
1846 while (num - i > 2) {
1847 w = data[i++];
1848 h = data[i++];
1849 i += w * h;
1850 if (i > num || w*h == 0) break;
1851 ++self->nicons;
1852 }
1853
1854 self->icons = g_new(ObClientIcon, self->nicons);
1855
1856 /* store the icons */
1857 i = 0;
1858 for (j = 0; j < self->nicons; ++j) {
1859 guint x, y, t;
1860
1861 w = self->icons[j].width = data[i++];
1862 h = self->icons[j].height = data[i++];
1863
1864 if (w*h == 0) continue;
1865
1866 self->icons[j].data = g_new(RrPixel32, w * h);
1867 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1868 if (x >= w) {
1869 x = 0;
1870 ++y;
1871 }
1872 self->icons[j].data[t] =
1873 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1874 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1875 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1876 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1877 }
1878 g_assert(i <= num);
1879 }
1880
1881 g_free(data);
1882 } else {
1883 XWMHints *hints;
1884
1885 if ((hints = XGetWMHints(ob_display, self->window))) {
1886 if (hints->flags & IconPixmapHint) {
1887 self->nicons++;
1888 self->icons = g_new(ObClientIcon, self->nicons);
1889 xerror_set_ignore(TRUE);
1890 if (!RrPixmapToRGBA(ob_rr_inst,
1891 hints->icon_pixmap,
1892 (hints->flags & IconMaskHint ?
1893 hints->icon_mask : None),
1894 &self->icons[self->nicons-1].width,
1895 &self->icons[self->nicons-1].height,
1896 &self->icons[self->nicons-1].data)){
1897 g_free(&self->icons[self->nicons-1]);
1898 self->nicons--;
1899 }
1900 xerror_set_ignore(FALSE);
1901 }
1902 XFree(hints);
1903 }
1904 }
1905
1906 /* set the default icon onto the window
1907 in theory, this could be a race, but if a window doesn't set an icon
1908 or removes it entirely, it's not very likely it is going to set one
1909 right away afterwards */
1910 if (self->nicons == 0) {
1911 RrPixel32 *icon = ob_rr_theme->def_win_icon;
1912 gulong *data;
1913
1914 data = g_new(gulong, 48*48+2);
1915 data[0] = data[1] = 48;
1916 for (i = 0; i < 48*48; ++i)
1917 data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
1918 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
1919 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
1920 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
1921 PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
1922 g_free(data);
1923 } else if (self->frame)
1924 /* don't draw the icon empty if we're just setting one now anyways,
1925 we'll get the property change any second */
1926 frame_adjust_icon(self->frame);
1927 }
1928
1929 void client_update_user_time(ObClient *self)
1930 {
1931 guint32 time;
1932
1933 if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
1934 /* we set this every time, not just when it grows, because in practice
1935 sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
1936 backward we don't want all windows to stop focusing. we'll just
1937 assume noone is setting times older than the last one, cuz that
1938 would be pretty stupid anyways
1939 */
1940 self->user_time = time;
1941
1942 /*
1943 ob_debug("window %s user time %u\n", self->title, time);
1944 */
1945 }
1946 }
1947
1948 static void client_get_client_machine(ObClient *self)
1949 {
1950 gchar *data = NULL;
1951 gchar localhost[128];
1952
1953 g_free(self->client_machine);
1954
1955 if (PROP_GETS(self->window, wm_client_machine, locale, &data)) {
1956 gethostname(localhost, 127);
1957 localhost[127] = '\0';
1958 if (strcmp(localhost, data))
1959 self->client_machine = data;
1960 }
1961 }
1962
1963 static void client_change_wm_state(ObClient *self)
1964 {
1965 gulong state[2];
1966 glong old;
1967
1968 old = self->wmstate;
1969
1970 if (self->shaded || self->iconic || !self->frame->visible)
1971 self->wmstate = IconicState;
1972 else
1973 self->wmstate = NormalState;
1974
1975 if (old != self->wmstate) {
1976 PROP_MSG(self->window, kde_wm_change_state,
1977 self->wmstate, 1, 0, 0);
1978
1979 state[0] = self->wmstate;
1980 state[1] = None;
1981 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1982 }
1983 }
1984
1985 static void client_change_state(ObClient *self)
1986 {
1987 gulong netstate[11];
1988 guint num;
1989
1990 num = 0;
1991 if (self->modal)
1992 netstate[num++] = prop_atoms.net_wm_state_modal;
1993 if (self->shaded)
1994 netstate[num++] = prop_atoms.net_wm_state_shaded;
1995 if (self->iconic)
1996 netstate[num++] = prop_atoms.net_wm_state_hidden;
1997 if (self->skip_taskbar)
1998 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1999 if (self->skip_pager)
2000 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
2001 if (self->fullscreen)
2002 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
2003 if (self->max_vert)
2004 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
2005 if (self->max_horz)
2006 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
2007 if (self->above)
2008 netstate[num++] = prop_atoms.net_wm_state_above;
2009 if (self->below)
2010 netstate[num++] = prop_atoms.net_wm_state_below;
2011 if (self->demands_attention)
2012 netstate[num++] = prop_atoms.net_wm_state_demands_attention;
2013 if (self->undecorated)
2014 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
2015 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
2016
2017 if (self->frame)
2018 frame_adjust_state(self->frame);
2019 }
2020
2021 ObClient *client_search_focus_tree(ObClient *self)
2022 {
2023 GSList *it;
2024 ObClient *ret;
2025
2026 for (it = self->transients; it; it = g_slist_next(it)) {
2027 if (client_focused(it->data)) return it->data;
2028 if ((ret = client_search_focus_tree(it->data))) return ret;
2029 }
2030 return NULL;
2031 }
2032
2033 ObClient *client_search_focus_tree_full(ObClient *self)
2034 {
2035 if (self->transient_for) {
2036 if (self->transient_for != OB_TRAN_GROUP) {
2037 return client_search_focus_tree_full(self->transient_for);
2038 } else {
2039 GSList *it;
2040 gboolean recursed = FALSE;
2041
2042 for (it = self->group->members; it; it = g_slist_next(it))
2043 if (!((ObClient*)it->data)->transient_for) {
2044 ObClient *c;
2045 if ((c = client_search_focus_tree_full(it->data)))
2046 return c;
2047 recursed = TRUE;
2048 }
2049 if (recursed)
2050 return NULL;
2051 }
2052 }
2053
2054 /* this function checks the whole tree, the client_search_focus_tree~
2055 does not, so we need to check this window */
2056 if (client_focused(self))
2057 return self;
2058 return client_search_focus_tree(self);
2059 }
2060
2061 static ObStackingLayer calc_layer(ObClient *self)
2062 {
2063 ObStackingLayer l;
2064
2065 if (self->fullscreen &&
2066 (client_focused(self) || client_search_focus_tree(self)))
2067 l = OB_STACKING_LAYER_FULLSCREEN;
2068 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
2069 l = OB_STACKING_LAYER_DESKTOP;
2070 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2071 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2072 else l = OB_STACKING_LAYER_ABOVE;
2073 }
2074 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2075 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2076 else l = OB_STACKING_LAYER_NORMAL;
2077
2078 return l;
2079 }
2080
2081 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2082 ObStackingLayer min, gboolean raised)
2083 {
2084 ObStackingLayer old, own;
2085 GSList *it;
2086
2087 old = self->layer;
2088 own = calc_layer(self);
2089 self->layer = MAX(own, min);
2090
2091 for (it = self->transients; it; it = g_slist_next(it))
2092 client_calc_layer_recursive(it->data, orig,
2093 self->layer,
2094 raised ? raised : self->layer != old);
2095
2096 if (!raised && self->layer != old)
2097 if (orig->frame) { /* only restack if the original window is managed */
2098 stacking_remove(CLIENT_AS_WINDOW(self));
2099 stacking_add(CLIENT_AS_WINDOW(self));
2100 }
2101 }
2102
2103 void client_calc_layer(ObClient *self)
2104 {
2105 ObClient *orig;
2106 GSList *it;
2107
2108 orig = self;
2109
2110 /* transients take on the layer of their parents */
2111 it = client_search_all_top_parents(self);
2112
2113 for (; it; it = g_slist_next(it))
2114 client_calc_layer_recursive(it->data, orig, 0, FALSE);
2115 }
2116
2117 gboolean client_should_show(ObClient *self)
2118 {
2119 if (self->iconic)
2120 return FALSE;
2121 if (client_normal(self) && screen_showing_desktop)
2122 return FALSE;
2123 /*
2124 if (self->transient_for) {
2125 if (self->transient_for != OB_TRAN_GROUP)
2126 return client_should_show(self->transient_for);
2127 else {
2128 GSList *it;
2129
2130 for (it = self->group->members; it; it = g_slist_next(it)) {
2131 ObClient *c = it->data;
2132 if (c != self && !c->transient_for) {
2133 if (client_should_show(c))
2134 return TRUE;
2135 }
2136 }
2137 }
2138 }
2139 */
2140 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2141 return TRUE;
2142
2143 return FALSE;
2144 }
2145
2146 void client_show(ObClient *self)
2147 {
2148
2149 if (client_should_show(self)) {
2150 frame_show(self->frame);
2151 }
2152
2153 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2154 needs to be in IconicState. This includes when it is on another
2155 desktop!
2156 */
2157 client_change_wm_state(self);
2158 }
2159
2160 void client_hide(ObClient *self)
2161 {
2162 if (!client_should_show(self)) {
2163 frame_hide(self->frame);
2164 }
2165
2166 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2167 needs to be in IconicState. This includes when it is on another
2168 desktop!
2169 */
2170 client_change_wm_state(self);
2171 }
2172
2173 void client_showhide(ObClient *self)
2174 {
2175
2176 if (client_should_show(self)) {
2177 frame_show(self->frame);
2178 }
2179 else {
2180 frame_hide(self->frame);
2181 }
2182
2183 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2184 needs to be in IconicState. This includes when it is on another
2185 desktop!
2186 */
2187 client_change_wm_state(self);
2188 }
2189
2190 gboolean client_normal(ObClient *self) {
2191 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2192 self->type == OB_CLIENT_TYPE_DOCK ||
2193 self->type == OB_CLIENT_TYPE_SPLASH);
2194 }
2195
2196 gboolean client_application(ObClient *self)
2197 {
2198 return (self->type == OB_CLIENT_TYPE_NORMAL ||
2199 self->type == OB_CLIENT_TYPE_DIALOG);
2200 }
2201
2202 static void client_apply_startup_state(ObClient *self, gint x, gint y)
2203 {
2204 gboolean pos = FALSE; /* has the window's position been configured? */
2205 gint ox, oy;
2206
2207 /* save the position, and set self->area for these to use */
2208 ox = self->area.x;
2209 oy = self->area.y;
2210 self->area.x = x;
2211 self->area.y = y;
2212
2213 /* set the desktop hint, to make sure that it always exists */
2214 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
2215
2216 /* these are in a carefully crafted order.. */
2217
2218 if (self->iconic) {
2219 self->iconic = FALSE;
2220 client_iconify(self, TRUE, FALSE);
2221 }
2222 if (self->fullscreen) {
2223 self->fullscreen = FALSE;
2224 client_fullscreen(self, TRUE);
2225 pos = TRUE;
2226 }
2227 if (self->undecorated) {
2228 self->undecorated = FALSE;
2229 client_set_undecorated(self, TRUE);
2230 }
2231 if (self->shaded) {
2232 self->shaded = FALSE;
2233 client_shade(self, TRUE);
2234 }
2235 if (self->demands_attention) {
2236 self->demands_attention = FALSE;
2237 client_hilite(self, TRUE);
2238 }
2239
2240 if (self->max_vert && self->max_horz) {
2241 self->max_vert = self->max_horz = FALSE;
2242 client_maximize(self, TRUE, 0);
2243 pos = TRUE;
2244 } else if (self->max_vert) {
2245 self->max_vert = FALSE;
2246 client_maximize(self, TRUE, 2);
2247 pos = TRUE;
2248 } else if (self->max_horz) {
2249 self->max_horz = FALSE;
2250 client_maximize(self, TRUE, 1);
2251 pos = TRUE;
2252 }
2253
2254 /* if the client didn't get positioned yet, then do so now
2255 call client_move even if the window is not being moved anywhere, because
2256 when we reparent it and decorate it, it is getting moved and we need to
2257 be telling it so with a ConfigureNotify event.
2258 */
2259 if (!pos) {
2260 /* use the saved position */
2261 self->area.x = ox;
2262 self->area.y = oy;
2263 client_move(self, x, y);
2264 }
2265
2266 /* nothing to do for the other states:
2267 skip_taskbar
2268 skip_pager
2269 modal
2270 above
2271 below
2272 */
2273 }
2274
2275 void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y,
2276 gint w, gint h)
2277 {
2278 gint oldg = self->gravity;
2279
2280 /* get the frame's position from the requested stuff */
2281 self->gravity = gravity;
2282 frame_client_gravity(self->frame, x, y, w, h);
2283 self->gravity = oldg;
2284
2285 /* get the client's position in its true gravity from that */
2286 frame_frame_gravity(self->frame, x, y, w, h);
2287 }
2288
2289 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2290 gint *logicalw, gint *logicalh,
2291 gboolean user)
2292 {
2293 Rect desired_area = {*x, *y, *w, *h};
2294
2295 /* make the frame recalculate its dimentions n shit without changing
2296 anything visible for real, this way the constraints below can work with
2297 the updated frame dimensions. */
2298 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
2299
2300 /* work within the prefered sizes given by the window */
2301 if (!(*w == self->area.width && *h == self->area.height)) {
2302 gint basew, baseh, minw, minh;
2303
2304 /* base size is substituted with min size if not specified */
2305 if (self->base_size.width || self->base_size.height) {
2306 basew = self->base_size.width;
2307 baseh = self->base_size.height;
2308 } else {
2309 basew = self->min_size.width;
2310 baseh = self->min_size.height;
2311 }
2312 /* min size is substituted with base size if not specified */
2313 if (self->min_size.width || self->min_size.height) {
2314 minw = self->min_size.width;
2315 minh = self->min_size.height;
2316 } else {
2317 minw = self->base_size.width;
2318 minh = self->base_size.height;
2319 }
2320
2321 /* if this is a user-requested resize, then check against min/max
2322 sizes */
2323
2324 /* smaller than min size or bigger than max size? */
2325 if (*w > self->max_size.width) *w = self->max_size.width;
2326 if (*w < minw) *w = minw;
2327 if (*h > self->max_size.height) *h = self->max_size.height;
2328 if (*h < minh) *h = minh;
2329
2330 *w -= basew;
2331 *h -= baseh;
2332
2333 /* keep to the increments */
2334 *w /= self->size_inc.width;
2335 *h /= self->size_inc.height;
2336
2337 /* you cannot resize to nothing */
2338 if (basew + *w < 1) *w = 1 - basew;
2339 if (baseh + *h < 1) *h = 1 - baseh;
2340
2341 /* save the logical size */
2342 *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
2343 *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
2344
2345 *w *= self->size_inc.width;
2346 *h *= self->size_inc.height;
2347
2348 *w += basew;
2349 *h += baseh;
2350
2351 /* adjust the height to match the width for the aspect ratios.
2352 for this, min size is not substituted for base size ever. */
2353 *w -= self->base_size.width;
2354 *h -= self->base_size.height;
2355
2356 if (!self->fullscreen) {
2357 if (self->min_ratio)
2358 if (*h * self->min_ratio > *w) {
2359 *h = (gint)(*w / self->min_ratio);
2360
2361 /* you cannot resize to nothing */
2362 if (*h < 1) {
2363 *h = 1;
2364 *w = (gint)(*h * self->min_ratio);
2365 }
2366 }
2367 if (self->max_ratio)
2368 if (*h * self->max_ratio < *w) {
2369 *h = (gint)(*w / self->max_ratio);
2370
2371 /* you cannot resize to nothing */
2372 if (*h < 1) {
2373 *h = 1;
2374 *w = (gint)(*h * self->min_ratio);
2375 }
2376 }
2377 }
2378
2379 *w += self->base_size.width;
2380 *h += self->base_size.height;
2381 }
2382
2383 /* gets the frame's position */
2384 frame_client_gravity(self->frame, x, y, *w, *h);
2385
2386 /* these positions are frame positions, not client positions */
2387
2388 /* set the size and position if fullscreen */
2389 if (self->fullscreen) {
2390 Rect *a;
2391 guint i;
2392
2393 i = screen_find_monitor(&desired_area);
2394 a = screen_physical_area_monitor(i);
2395
2396 *x = a->x;
2397 *y = a->y;
2398 *w = a->width;
2399 *h = a->height;
2400
2401 user = FALSE; /* ignore if the client can't be moved/resized when it
2402 is entering fullscreen */
2403 } else if (self->max_horz || self->max_vert) {
2404 Rect *a;
2405 guint i;
2406
2407 i = screen_find_monitor(&desired_area);
2408 a = screen_area_monitor(self->desktop, i);
2409
2410 /* set the size and position if maximized */
2411 if (self->max_horz) {
2412 *x = a->x;
2413 *w = a->width - self->frame->size.left - self->frame->size.right;
2414 }
2415 if (self->max_vert) {
2416 *y = a->y;
2417 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2418 }
2419
2420 /* maximizing is not allowed if the user can't move+resize the window
2421 */
2422 }
2423
2424 /* gets the client's position */
2425 frame_frame_gravity(self->frame, x, y, *w, *h);
2426
2427 /* these override the above states! if you cant move you can't move! */
2428 if (user) {
2429 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2430 *x = self->area.x;
2431 *y = self->area.y;
2432 }
2433 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2434 *w = self->area.width;
2435 *h = self->area.height;
2436 }
2437 }
2438
2439 g_assert(*w > 0);
2440 g_assert(*h > 0);
2441 }
2442
2443
2444 void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
2445 gboolean user, gboolean final,
2446 gboolean force_reply)
2447 {
2448 gint oldw, oldh, oldrx, oldry;
2449 gboolean send_resize_client;
2450 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
2451 guint fdecor = self->frame->decorations;
2452 gboolean fhorz = self->frame->max_horz;
2453 gint logicalw, logicalh;
2454
2455 /* find the new x, y, width, and height (and logical size) */
2456 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
2457
2458 /* set the logical size if things changed */
2459 if (!(w == self->area.width && h == self->area.height))
2460 SIZE_SET(self->logical_size, logicalw, logicalh);
2461
2462 /* figure out if we moved or resized or what */
2463 moved = x != self->area.x || y != self->area.y;
2464 resized = w != self->area.width || h != self->area.height;
2465
2466 oldw = self->area.width;
2467 oldh = self->area.height;
2468 RECT_SET(self->area, x, y, w, h);
2469
2470 /* for app-requested resizes, always resize if 'resized' is true.
2471 for user-requested ones, only resize if final is true, or when
2472 resizing in redraw mode */
2473 send_resize_client = ((!user && resized) ||
2474 (user && (final ||
2475 (resized && config_resize_redraw))));
2476
2477 /* if the client is enlarging, then resize the client before the frame */
2478 if (send_resize_client && user && (w > oldw || h > oldh)) {
2479 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2480 /* resize the plate to show the client padding color underneath */
2481 frame_adjust_client_area(self->frame);
2482 }
2483
2484 /* find the frame's dimensions and move/resize it */
2485 if (self->decorations != fdecor || self->max_horz != fhorz)
2486 moved = resized = TRUE;
2487 if (moved || resized)
2488 frame_adjust_area(self->frame, moved, resized, FALSE);
2489
2490 /* find the client's position relative to the root window */
2491 oldrx = self->root_pos.x;
2492 oldry = self->root_pos.y;
2493 rootmoved = (oldrx != (signed)(self->frame->area.x +
2494 self->frame->size.left -
2495 self->border_width) ||
2496 oldry != (signed)(self->frame->area.y +
2497 self->frame->size.top -
2498 self->border_width));
2499
2500 if (force_reply || ((!user || (user && final)) && rootmoved))
2501 {
2502 XEvent event;
2503
2504 POINT_SET(self->root_pos,
2505 self->frame->area.x + self->frame->size.left -
2506 self->border_width,
2507 self->frame->area.y + self->frame->size.top -
2508 self->border_width);
2509
2510 event.type = ConfigureNotify;
2511 event.xconfigure.display = ob_display;
2512 event.xconfigure.event = self->window;
2513 event.xconfigure.window = self->window;
2514
2515 /* root window real coords */
2516 event.xconfigure.x = self->root_pos.x;
2517 event.xconfigure.y = self->root_pos.y;
2518 event.xconfigure.width = w;
2519 event.xconfigure.height = h;
2520 event.xconfigure.border_width = 0;
2521 event.xconfigure.above = self->frame->plate;
2522 event.xconfigure.override_redirect = FALSE;
2523 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2524 FALSE, StructureNotifyMask, &event);
2525 }
2526
2527 /* if the client is shrinking, then resize the frame before the client */
2528 if (send_resize_client && (!user || (w <= oldw || h <= oldh))) {
2529 /* resize the plate to show the client padding color underneath */
2530 frame_adjust_client_area(self->frame);
2531
2532 XResizeWindow(ob_display, self->window, w, h);
2533 }
2534
2535 XFlush(ob_display);
2536 }
2537
2538 void client_fullscreen(ObClient *self, gboolean fs)
2539 {
2540 gint x, y, w, h;
2541
2542 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2543 self->fullscreen == fs) return; /* already done */
2544
2545 self->fullscreen = fs;
2546 client_change_state(self); /* change the state hints on the client */
2547 client_calc_layer(self); /* and adjust out layer/stacking */
2548
2549 if (fs) {
2550 self->pre_fullscreen_area = self->area;
2551 /* if the window is maximized, its area isn't all that meaningful.
2552 save it's premax area instead. */
2553 if (self->max_horz) {
2554 self->pre_fullscreen_area.x = self->pre_max_area.x;
2555 self->pre_fullscreen_area.width = self->pre_max_area.width;
2556 }
2557 if (self->max_vert) {
2558 self->pre_fullscreen_area.y = self->pre_max_area.y;
2559 self->pre_fullscreen_area.height = self->pre_max_area.height;
2560 }
2561
2562 /* these are not actually used cuz client_configure will set them
2563 as appropriate when the window is fullscreened */
2564 x = y = w = h = 0;
2565 } else {
2566 Rect *a;
2567
2568 if (self->pre_fullscreen_area.width > 0 &&
2569 self->pre_fullscreen_area.height > 0)
2570 {
2571 x = self->pre_fullscreen_area.x;
2572 y = self->pre_fullscreen_area.y;
2573 w = self->pre_fullscreen_area.width;
2574 h = self->pre_fullscreen_area.height;
2575 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2576 } else {
2577 /* pick some fallbacks... */
2578 a = screen_area_monitor(self->desktop, 0);
2579 x = a->x + a->width / 4;
2580 y = a->y + a->height / 4;
2581 w = a->width / 2;
2582 h = a->height / 2;
2583 }
2584 }
2585
2586 client_setup_decor_and_functions(self);
2587
2588 client_move_resize(self, x, y, w, h);
2589
2590 /* try focus us when we go into fullscreen mode */
2591 client_focus(self);
2592 }
2593
2594 static void client_iconify_recursive(ObClient *self,
2595 gboolean iconic, gboolean curdesk)
2596 {
2597 GSList *it;
2598 gboolean changed = FALSE;
2599
2600
2601 if (self->iconic != iconic) {
2602 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2603 self->window);
2604
2605 if (iconic) {
2606 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2607 self->iconic = iconic;
2608
2609 /* update the focus lists.. iconic windows go to the bottom of
2610 the list, put the new iconic window at the 'top of the
2611 bottom'. */
2612 focus_order_to_top(self);
2613
2614 changed = TRUE;
2615 }
2616 } else {
2617 self->iconic = iconic;
2618
2619 if (curdesk)
2620 client_set_desktop(self, screen_desktop, FALSE);
2621
2622 /* this puts it after the current focused window */
2623 focus_order_remove(self);
2624 focus_order_add_new(self);
2625
2626 changed = TRUE;
2627 }
2628 }
2629
2630 if (changed) {
2631 client_change_state(self);
2632 client_showhide(self);
2633 if (STRUT_EXISTS(self->strut))
2634 screen_update_areas();
2635 }
2636
2637 /* iconify all direct transients, and deiconify all transients
2638 (non-direct too) */
2639 for (it = self->transients; it; it = g_slist_next(it))
2640 if (it->data != self)
2641 if (client_is_direct_child(self, it->data) || !iconic)
2642 client_iconify_recursive(it->data, iconic, curdesk);
2643 }
2644
2645 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2646 {
2647 /* move up the transient chain as far as possible first */
2648 self = client_search_top_parent(self);
2649 client_iconify_recursive(self, iconic, curdesk);
2650 }
2651
2652 void client_maximize(ObClient *self, gboolean max, gint dir)
2653 {
2654 gint x, y, w, h;
2655
2656 g_assert(dir == 0 || dir == 1 || dir == 2);
2657 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2658
2659 /* check if already done */
2660 if (max) {
2661 if (dir == 0 && self->max_horz && self->max_vert) return;
2662 if (dir == 1 && self->max_horz) return;
2663 if (dir == 2 && self->max_vert) return;
2664 } else {
2665 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2666 if (dir == 1 && !self->max_horz) return;
2667 if (dir == 2 && !self->max_vert) return;
2668 }
2669
2670 /* we just tell it to configure in the same place and client_configure
2671 worries about filling the screen with the window */
2672 x = self->area.x;
2673 y = self->area.y;
2674 w = self->area.width;
2675 h = self->area.height;
2676
2677 if (max) {
2678 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2679 RECT_SET(self->pre_max_area,
2680 self->area.x, self->pre_max_area.y,
2681 self->area.width, self->pre_max_area.height);
2682 }
2683 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2684 RECT_SET(self->pre_max_area,
2685 self->pre_max_area.x, self->area.y,
2686 self->pre_max_area.width, self->area.height);
2687 }
2688 } else {
2689 Rect *a;
2690
2691 a = screen_area_monitor(self->desktop, 0);
2692 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2693 if (self->pre_max_area.width > 0) {
2694 x = self->pre_max_area.x;
2695 w = self->pre_max_area.width;
2696
2697 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2698 0, self->pre_max_area.height);
2699 } else {
2700 /* pick some fallbacks... */
2701 x = a->x + a->width / 4;
2702 w = a->width / 2;
2703 }
2704 }
2705 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2706 if (self->pre_max_area.height > 0) {
2707 y = self->pre_max_area.y;
2708 h = self->pre_max_area.height;
2709
2710 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2711 self->pre_max_area.width, 0);
2712 } else {
2713 /* pick some fallbacks... */
2714 y = a->y + a->height / 4;
2715 h = a->height / 2;
2716 }
2717 }
2718 }
2719
2720 if (dir == 0 || dir == 1) /* horz */
2721 self->max_horz = max;
2722 if (dir == 0 || dir == 2) /* vert */
2723 self->max_vert = max;
2724
2725 client_change_state(self); /* change the state hints on the client */
2726
2727 client_setup_decor_and_functions(self);
2728
2729 client_move_resize(self, x, y, w, h);
2730 }
2731
2732 void client_shade(ObClient *self, gboolean shade)
2733 {
2734 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2735 shade) || /* can't shade */
2736 self->shaded == shade) return; /* already done */
2737
2738 self->shaded = shade;
2739 client_change_state(self);
2740 client_change_wm_state(self); /* the window is being hidden/shown */
2741 /* resize the frame to just the titlebar */
2742 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2743 }
2744
2745 void client_close(ObClient *self)
2746 {
2747 XEvent ce;
2748
2749 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2750
2751 /* in the case that the client provides no means to requesting that it
2752 close, we just kill it */
2753 if (!self->delete_window)
2754 client_kill(self);
2755
2756 /*
2757 XXX: itd be cool to do timeouts and shit here for killing the client's
2758 process off
2759 like... if the window is around after 5 seconds, then the close button
2760 turns a nice red, and if this function is called again, the client is
2761 explicitly killed.
2762 */
2763
2764 ce.xclient.type = ClientMessage;
2765 ce.xclient.message_type = prop_atoms.wm_protocols;
2766 ce.xclient.display = ob_display;
2767 ce.xclient.window = self->window;
2768 ce.xclient.format = 32;
2769 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2770 ce.xclient.data.l[1] = event_curtime;
2771 ce.xclient.data.l[2] = 0l;
2772 ce.xclient.data.l[3] = 0l;
2773 ce.xclient.data.l[4] = 0l;
2774 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2775 }
2776
2777 void client_kill(ObClient *self)
2778 {
2779 XKillClient(ob_display, self->window);
2780 }
2781
2782 void client_hilite(ObClient *self, gboolean hilite)
2783 {
2784 if (self->demands_attention == hilite)
2785 return; /* no change */
2786
2787 /* don't allow focused windows to hilite */
2788 self->demands_attention = hilite && !client_focused(self);
2789 if (self->frame != NULL) { /* if we're mapping, just set the state */
2790 if (self->demands_attention)
2791 frame_flash_start(self->frame);
2792 else
2793 frame_flash_stop(self->frame);
2794 client_change_state(self);
2795 }
2796 }
2797
2798 void client_set_desktop_recursive(ObClient *self,
2799 guint target, gboolean donthide)
2800 {
2801 guint old;
2802 GSList *it;
2803
2804 if (target != self->desktop) {
2805
2806 ob_debug("Setting desktop %u\n", target+1);
2807
2808 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2809
2810 /* remove from the old desktop(s) */
2811 focus_order_remove(self);
2812
2813 old = self->desktop;
2814 self->desktop = target;
2815 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2816 /* the frame can display the current desktop state */
2817 frame_adjust_state(self->frame);
2818 /* 'move' the window to the new desktop */
2819 if (!donthide)
2820 client_showhide(self);
2821 /* raise if it was not already on the desktop */
2822 if (old != DESKTOP_ALL)
2823 client_raise(self);
2824 if (STRUT_EXISTS(self->strut))
2825 screen_update_areas();
2826
2827 /* add to the new desktop(s) */
2828 if (config_focus_new)
2829 focus_order_to_top(self);
2830 else
2831 focus_order_to_bottom(self);
2832 }
2833
2834 /* move all transients */
2835 for (it = self->transients; it; it = g_slist_next(it))
2836 if (it->data != self)
2837 if (client_is_direct_child(self, it->data))
2838 client_set_desktop_recursive(it->data, target, donthide);
2839 }
2840
2841 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2842 {
2843 self = client_search_top_parent(self);
2844 client_set_desktop_recursive(self, target, donthide);
2845 }
2846
2847 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
2848 {
2849 while (child != parent &&
2850 child->transient_for && child->transient_for != OB_TRAN_GROUP)
2851 child = child->transient_for;
2852 return child == parent;
2853 }
2854
2855 ObClient *client_search_modal_child(ObClient *self)
2856 {
2857 GSList *it;
2858 ObClient *ret;
2859
2860 for (it = self->transients; it; it = g_slist_next(it)) {
2861 ObClient *c = it->data;
2862 if ((ret = client_search_modal_child(c))) return ret;
2863 if (c->modal) return c;
2864 }
2865 return NULL;
2866 }
2867
2868 gboolean client_validate(ObClient *self)
2869 {
2870 XEvent e;
2871
2872 XSync(ob_display, FALSE); /* get all events on the server */
2873
2874 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2875 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2876 XPutBackEvent(ob_display, &e);
2877 return FALSE;
2878 }
2879
2880 return TRUE;
2881 }
2882
2883 void client_set_wm_state(ObClient *self, glong state)
2884 {
2885 if (state == self->wmstate) return; /* no change */
2886
2887 switch (state) {
2888 case IconicState:
2889 client_iconify(self, TRUE, TRUE);
2890 break;
2891 case NormalState:
2892 client_iconify(self, FALSE, TRUE);
2893 break;
2894 }
2895 }
2896
2897 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2898 {
2899 gboolean shaded = self->shaded;
2900 gboolean fullscreen = self->fullscreen;
2901 gboolean undecorated = self->undecorated;
2902 gboolean max_horz = self->max_horz;
2903 gboolean max_vert = self->max_vert;
2904 gboolean modal = self->modal;
2905 gboolean iconic = self->iconic;
2906 gboolean demands_attention = self->demands_attention;
2907 gint i;
2908
2909 if (!(action == prop_atoms.net_wm_state_add ||
2910 action == prop_atoms.net_wm_state_remove ||
2911 action == prop_atoms.net_wm_state_toggle))
2912 /* an invalid action was passed to the client message, ignore it */
2913 return;
2914
2915 for (i = 0; i < 2; ++i) {
2916 Atom state = i == 0 ? data1 : data2;
2917
2918 if (!state) continue;
2919
2920 /* if toggling, then pick whether we're adding or removing */
2921 if (action == prop_atoms.net_wm_state_toggle) {
2922 if (state == prop_atoms.net_wm_state_modal)
2923 action = modal ? prop_atoms.net_wm_state_remove :
2924 prop_atoms.net_wm_state_add;
2925 else if (state == prop_atoms.net_wm_state_maximized_vert)
2926 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2927 prop_atoms.net_wm_state_add;
2928 else if (state == prop_atoms.net_wm_state_maximized_horz)
2929 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2930 prop_atoms.net_wm_state_add;
2931 else if (state == prop_atoms.net_wm_state_shaded)
2932 action = shaded ? prop_atoms.net_wm_state_remove :
2933 prop_atoms.net_wm_state_add;
2934 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2935 action = self->skip_taskbar ?
2936 prop_atoms.net_wm_state_remove :
2937 prop_atoms.net_wm_state_add;
2938 else if (state == prop_atoms.net_wm_state_skip_pager)
2939 action = self->skip_pager ?
2940 prop_atoms.net_wm_state_remove :
2941 prop_atoms.net_wm_state_add;
2942 else if (state == prop_atoms.net_wm_state_hidden)
2943 action = self->iconic ?
2944 prop_atoms.net_wm_state_remove :
2945 prop_atoms.net_wm_state_add;
2946 else if (state == prop_atoms.net_wm_state_fullscreen)
2947 action = fullscreen ?
2948 prop_atoms.net_wm_state_remove :
2949 prop_atoms.net_wm_state_add;
2950 else if (state == prop_atoms.net_wm_state_above)
2951 action = self->above ? prop_atoms.net_wm_state_remove :
2952 prop_atoms.net_wm_state_add;
2953 else if (state == prop_atoms.net_wm_state_below)
2954 action = self->below ? prop_atoms.net_wm_state_remove :
2955 prop_atoms.net_wm_state_add;
2956 else if (state == prop_atoms.net_wm_state_demands_attention)
2957 action = self->demands_attention ?
2958 prop_atoms.net_wm_state_remove :
2959 prop_atoms.net_wm_state_add;
2960 else if (state == prop_atoms.ob_wm_state_undecorated)
2961 action = undecorated ? prop_atoms.net_wm_state_remove :
2962 prop_atoms.net_wm_state_add;
2963 }
2964
2965 if (action == prop_atoms.net_wm_state_add) {
2966 if (state == prop_atoms.net_wm_state_modal) {
2967 modal = TRUE;
2968 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2969 max_vert = TRUE;
2970 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2971 max_horz = TRUE;
2972 } else if (state == prop_atoms.net_wm_state_shaded) {
2973 shaded = TRUE;
2974 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2975 self->skip_taskbar = TRUE;
2976 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2977 self->skip_pager = TRUE;
2978 } else if (state == prop_atoms.net_wm_state_hidden) {
2979 iconic = TRUE;
2980 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2981 fullscreen = TRUE;
2982 } else if (state == prop_atoms.net_wm_state_above) {
2983 self->above = TRUE;
2984 self->below = FALSE;
2985 } else if (state == prop_atoms.net_wm_state_below) {
2986 self->above = FALSE;
2987 self->below = TRUE;
2988 } else if (state == prop_atoms.net_wm_state_demands_attention) {
2989 demands_attention = TRUE;
2990 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2991 undecorated = TRUE;
2992 }
2993
2994 } else { /* action == prop_atoms.net_wm_state_remove */
2995 if (state == prop_atoms.net_wm_state_modal) {
2996 modal = FALSE;
2997 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2998 max_vert = FALSE;
2999 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3000 max_horz = FALSE;
3001 } else if (state == prop_atoms.net_wm_state_shaded) {
3002 shaded = FALSE;
3003 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3004 self->skip_taskbar = FALSE;
3005 } else if (state == prop_atoms.net_wm_state_skip_pager) {
3006 self->skip_pager = FALSE;
3007 } else if (state == prop_atoms.net_wm_state_hidden) {
3008 iconic = FALSE;
3009 } else if (state == prop_atoms.net_wm_state_fullscreen) {
3010 fullscreen = FALSE;
3011 } else if (state == prop_atoms.net_wm_state_above) {
3012 self->above = FALSE;
3013 } else if (state == prop_atoms.net_wm_state_below) {
3014 self->below = FALSE;
3015 } else if (state == prop_atoms.net_wm_state_demands_attention) {
3016 demands_attention = FALSE;
3017 } else if (state == prop_atoms.ob_wm_state_undecorated) {
3018 undecorated = FALSE;
3019 }
3020 }
3021 }
3022 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3023 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3024 /* toggling both */
3025 if (max_horz == max_vert) { /* both going the same way */
3026 client_maximize(self, max_horz, 0);
3027 } else {
3028 client_maximize(self, max_horz, 1);
3029 client_maximize(self, max_vert, 2);
3030 }
3031 } else {
3032 /* toggling one */
3033 if (max_horz != self->max_horz)
3034 client_maximize(self, max_horz, 1);
3035 else
3036 client_maximize(self, max_vert, 2);
3037 }
3038 }
3039 /* change fullscreen state before shading, as it will affect if the window
3040 can shade or not */
3041 if (fullscreen != self->fullscreen)
3042 client_fullscreen(self, fullscreen);
3043 if (shaded != self->shaded)
3044 client_shade(self, shaded);
3045 if (undecorated != self->undecorated)
3046 client_set_undecorated(self, undecorated);
3047 if (modal != self->modal) {
3048 self->modal = modal;
3049 /* when a window changes modality, then its stacking order with its
3050 transients needs to change */
3051 client_raise(self);
3052 }
3053 if (iconic != self->iconic)
3054 client_iconify(self, iconic, FALSE);
3055
3056 if (demands_attention != self->demands_attention)
3057 client_hilite(self, demands_attention);
3058
3059 client_change_state(self); /* change the hint to reflect these changes */
3060 }
3061
3062 ObClient *client_focus_target(ObClient *self)
3063 {
3064 ObClient *child = NULL;
3065
3066 child = client_search_modal_child(self);
3067 if (child) return child;
3068 return self;
3069 }
3070
3071 gboolean client_can_focus(ObClient *self)
3072 {
3073 XEvent ev;
3074
3075 /* choose the correct target */
3076 self = client_focus_target(self);
3077
3078 if (!self->frame->visible)
3079 return FALSE;
3080
3081 if (!(self->can_focus || self->focus_notify))
3082 return FALSE;
3083
3084 /* do a check to see if the window has already been unmapped or destroyed
3085 do this intelligently while watching out for unmaps we've generated
3086 (ignore_unmaps > 0) */
3087 if (XCheckTypedWindowEvent(ob_display, self->window,
3088 DestroyNotify, &ev)) {
3089 XPutBackEvent(ob_display, &ev);
3090 return FALSE;
3091 }
3092 while (XCheckTypedWindowEvent(ob_display, self->window,
3093 UnmapNotify, &ev)) {
3094 if (self->ignore_unmaps) {
3095 self->ignore_unmaps--;
3096 } else {
3097 XPutBackEvent(ob_display, &ev);
3098 return FALSE;
3099 }
3100 }
3101
3102 return TRUE;
3103 }
3104
3105 gboolean client_focus(ObClient *self)
3106 {
3107 /* choose the correct target */
3108 self = client_focus_target(self);
3109
3110 if (!client_can_focus(self)) {
3111 if (!self->frame->visible) {
3112 /* update the focus lists */
3113 focus_order_to_top(self);
3114 }
3115 return FALSE;
3116 }
3117
3118 ob_debug_type(OB_DEBUG_FOCUS,
3119 "Focusing client \"%s\" at time %u\n",
3120 self->title, event_curtime);
3121
3122 if (self->can_focus) {
3123 /* This can cause a BadMatch error with CurrentTime, or if an app
3124 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3125 xerror_set_ignore(TRUE);
3126 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3127 event_curtime);
3128 xerror_set_ignore(FALSE);
3129 }
3130
3131 if (self->focus_notify) {
3132 XEvent ce;
3133 ce.xclient.type = ClientMessage;
3134 ce.xclient.message_type = prop_atoms.wm_protocols;
3135 ce.xclient.display = ob_display;
3136 ce.xclient.window = self->window;
3137 ce.xclient.format = 32;
3138 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3139 ce.xclient.data.l[1] = event_curtime;
3140 ce.xclient.data.l[2] = 0l;
3141 ce.xclient.data.l[3] = 0l;
3142 ce.xclient.data.l[4] = 0l;
3143 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3144 }
3145
3146 #ifdef DEBUG_FOCUS
3147 ob_debug("%sively focusing %lx at %d\n",
3148 (self->can_focus ? "act" : "pass"),
3149 self->window, (gint) event_curtime);
3150 #endif
3151
3152 /* Cause the FocusIn to come back to us. Important for desktop switches,
3153 since otherwise we'll have no FocusIn on the queue and send it off to
3154 the focus_backup. */
3155 XSync(ob_display, FALSE);
3156 return TRUE;
3157 }
3158
3159 void client_activate(ObClient *self, gboolean here, gboolean user)
3160 {
3161 guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
3162
3163 /* XXX do some stuff here if user is false to determine if we really want
3164 to activate it or not (a parent or group member is currently
3165 active)?
3166 */
3167 ob_debug_type(OB_DEBUG_FOCUS,
3168 "Want to activate window 0x%x with time %u (last time %u), "
3169 "source=%s\n",
3170 self->window, event_curtime, last_time,
3171 (user ? "user" : "application"));
3172
3173 if (!user && event_curtime && last_time &&
3174 !event_time_after(event_curtime, last_time))
3175 {
3176 client_hilite(self, TRUE);
3177 } else {
3178 if (event_curtime != CurrentTime)
3179 self->user_time = event_curtime;
3180
3181 /* if using focus_delay, stop the timer now so that focus doesn't
3182 go moving on us */
3183 event_halt_focus_delay();
3184
3185 if (client_normal(self) && screen_showing_desktop)
3186 screen_show_desktop(FALSE);
3187 if (self->iconic)
3188 client_iconify(self, FALSE, here);
3189 if (self->desktop != DESKTOP_ALL &&
3190 self->desktop != screen_desktop) {
3191 if (here)
3192 client_set_desktop(self, screen_desktop, FALSE);
3193 else
3194 screen_set_desktop(self->desktop);
3195 } else if (!self->frame->visible)
3196 /* if its not visible for other reasons, then don't mess
3197 with it */
3198 return;
3199 if (self->shaded)
3200 client_shade(self, FALSE);
3201
3202 client_focus(self);
3203
3204 /* we do this as an action here. this is rather important. this is
3205 because we want the results from the focus change to take place
3206 BEFORE we go about raising the window. when a fullscreen window
3207 loses focus, we need this or else the raise wont be able to raise
3208 above the to-lose-focus fullscreen window. */
3209 client_raise(self);
3210 }
3211 }
3212
3213 void client_raise(ObClient *self)
3214 {
3215 action_run_string("Raise", self, CurrentTime);
3216 }
3217
3218 void client_lower(ObClient *self)
3219 {
3220 action_run_string("Lower", self, CurrentTime);
3221 }
3222
3223 gboolean client_focused(ObClient *self)
3224 {
3225 return self == focus_client;
3226 }
3227
3228 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3229 {
3230 guint i;
3231 /* si is the smallest image >= req */
3232 /* li is the largest image < req */
3233 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
3234
3235 if (!self->nicons) {
3236 ObClientIcon *parent = NULL;
3237
3238 if (self->transient_for) {
3239 if (self->transient_for != OB_TRAN_GROUP)
3240 parent = client_icon_recursive(self->transient_for, w, h);
3241 else {
3242 GSList *it;
3243 for (it = self->group->members; it; it = g_slist_next(it)) {
3244 ObClient *c = it->data;
3245 if (c != self && !c->transient_for) {
3246 if ((parent = client_icon_recursive(c, w, h)))
3247 break;
3248 }
3249 }
3250 }
3251 }
3252
3253 return parent;
3254 }
3255
3256 for (i = 0; i < self->nicons; ++i) {
3257 size = self->icons[i].width * self->icons[i].height;
3258 if (size < smallest && size >= (unsigned)(w * h)) {
3259 smallest = size;
3260 si = i;
3261 }
3262 if (size > largest && size <= (unsigned)(w * h)) {
3263 largest = size;
3264 li = i;
3265 }
3266 }
3267 if (largest == 0) /* didnt find one smaller than the requested size */
3268 return &self->icons[si];
3269 return &self->icons[li];
3270 }
3271
3272 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3273 {
3274 ObClientIcon *ret;
3275 static ObClientIcon deficon;
3276
3277 if (!(ret = client_icon_recursive(self, w, h))) {
3278 deficon.width = deficon.height = 48;
3279 deficon.data = ob_rr_theme->def_win_icon;
3280 ret = &deficon;
3281 }
3282 return ret;
3283 }
3284
3285 void client_set_layer(ObClient *self, gint layer)
3286 {
3287 if (layer < 0) {
3288 self->below = TRUE;
3289 self->above = FALSE;
3290 } else if (layer == 0) {
3291 self->below = self->above = FALSE;
3292 } else {
3293 self->below = FALSE;
3294 self->above = TRUE;
3295 }
3296 client_calc_layer(self);
3297 client_change_state(self); /* reflect this in the state hints */
3298 }
3299
3300 void client_set_undecorated(ObClient *self, gboolean undecorated)
3301 {
3302 if (self->undecorated != undecorated) {
3303 self->undecorated = undecorated;
3304 client_setup_decor_and_functions(self);
3305 /* Make sure the client knows it might have moved. Maybe there is a
3306 * better way of doing this so only one client_configure is sent, but
3307 * since 125 of these are sent per second when moving the window (with
3308 * user = FALSE) i doubt it matters much.
3309 */
3310 client_configure(self, self->area.x, self->area.y,
3311 self->area.width, self->area.height, TRUE, TRUE);
3312 client_change_state(self); /* reflect this in the state hints */
3313 }
3314 }
3315
3316 guint client_monitor(ObClient *self)
3317 {
3318 return screen_find_monitor(&self->frame->area);
3319 }
3320
3321 ObClient *client_search_top_parent(ObClient *self)
3322 {
3323 while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
3324 client_normal(self))
3325 self = self->transient_for;
3326 return self;
3327 }
3328
3329 GSList *client_search_all_top_parents(ObClient *self)
3330 {
3331 GSList *ret = NULL;
3332
3333 /* move up the direct transient chain as far as possible */
3334 while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
3335 self = self->transient_for;
3336
3337 if (!self->transient_for)
3338 ret = g_slist_prepend(ret, self);
3339 else {
3340 GSList *it;
3341
3342 g_assert(self->group);
3343
3344 for (it = self->group->members; it; it = g_slist_next(it)) {
3345 ObClient *c = it->data;
3346
3347 if (!c->transient_for && client_normal(c))
3348 ret = g_slist_prepend(ret, c);
3349 }
3350
3351 if (ret == NULL) /* no group parents */
3352 ret = g_slist_prepend(ret, self);
3353 }
3354
3355 return ret;
3356 }
3357
3358 ObClient *client_search_focus_parent(ObClient *self)
3359 {
3360 if (self->transient_for) {
3361 if (self->transient_for != OB_TRAN_GROUP) {
3362 if (client_focused(self->transient_for))
3363 return self->transient_for;
3364 } else {
3365 GSList *it;
3366
3367 for (it = self->group->members; it; it = g_slist_next(it)) {
3368 ObClient *c = it->data;
3369
3370 /* checking transient_for prevents infinate loops! */
3371 if (c != self && !c->transient_for)
3372 if (client_focused(c))
3373 return c;
3374 }
3375 }
3376 }
3377
3378 return NULL;
3379 }
3380
3381 ObClient *client_search_parent(ObClient *self, ObClient *search)
3382 {
3383 if (self->transient_for) {
3384 if (self->transient_for != OB_TRAN_GROUP) {
3385 if (self->transient_for == search)
3386 return search;
3387 } else {
3388 GSList *it;
3389
3390 for (it = self->group->members; it; it = g_slist_next(it)) {
3391 ObClient *c = it->data;
3392
3393 /* checking transient_for prevents infinate loops! */
3394 if (c != self && !c->transient_for)
3395 if (c == search)
3396 return search;
3397 }
3398 }
3399 }
3400
3401 return NULL;
3402 }
3403
3404 ObClient *client_search_transient(ObClient *self, ObClient *search)
3405 {
3406 GSList *sit;
3407
3408 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3409 if (sit->data == search)
3410 return search;
3411 if (client_search_transient(sit->data, search))
3412 return search;
3413 }
3414 return NULL;
3415 }
3416
3417 void client_update_sm_client_id(ObClient *self)
3418 {
3419 g_free(self->sm_client_id);
3420 self->sm_client_id = NULL;
3421
3422 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3423 self->group)
3424 PROP_GETS(self->group->leader, sm_client_id, locale,
3425 &self->sm_client_id);
3426 }
3427
3428 #define WANT_EDGE(cur, c) \
3429 if(cur == c) \
3430 continue; \
3431 if(!client_normal(cur)) \
3432 continue; \
3433 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3434 continue; \
3435 if(cur->iconic) \
3436 continue; \
3437 if(cur->layer < c->layer && !config_resist_layers_below) \
3438 continue;
3439
3440 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3441 if ((his_edge_start >= my_edge_start && \
3442 his_edge_start <= my_edge_end) || \
3443 (my_edge_start >= his_edge_start && \
3444 my_edge_start <= his_edge_end)) \
3445 dest = his_offset;
3446
3447 /* finds the nearest edge in the given direction from the current client
3448 * note to self: the edge is the -frame- edge (the actual one), not the
3449 * client edge.
3450 */
3451 gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
3452 {
3453 gint dest, monitor_dest;
3454 gint my_edge_start, my_edge_end, my_offset;
3455 GList *it;
3456 Rect *a, *monitor;
3457
3458 if(!client_list)
3459 return -1;
3460
3461 a = screen_area(c->desktop);
3462 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3463
3464 switch(dir) {
3465 case OB_DIRECTION_NORTH:
3466 my_edge_start = c->frame->area.x;
3467 my_edge_end = c->frame->area.x + c->frame->area.width;
3468 my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
3469
3470 /* default: top of screen */
3471 dest = a->y + (hang ? c->frame->area.height : 0);
3472 monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
3473 /* if the monitor edge comes before the screen edge, */
3474 /* use that as the destination instead. (For xinerama) */
3475 if (monitor_dest != dest && my_offset > monitor_dest)
3476 dest = monitor_dest;
3477
3478 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3479 gint his_edge_start, his_edge_end, his_offset;
3480 ObClient *cur = it->data;
3481
3482 WANT_EDGE(cur, c)
3483
3484 his_edge_start = cur->frame->area.x;
3485 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3486 his_offset = cur->frame->area.y +
3487 (hang ? 0 : cur->frame->area.height);
3488
3489 if(his_offset + 1 > my_offset)
3490 continue;
3491
3492 if(his_offset < dest)
3493 continue;
3494
3495 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3496 }
3497 break;
3498 case OB_DIRECTION_SOUTH:
3499 my_edge_start = c->frame->area.x;
3500 my_edge_end = c->frame->area.x + c->frame->area.width;
3501 my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
3502
3503 /* default: bottom of screen */
3504 dest = a->y + a->height - (hang ? c->frame->area.height : 0);
3505 monitor_dest = monitor->y + monitor->height -
3506 (hang ? c->frame->area.height : 0);
3507 /* if the monitor edge comes before the screen edge, */
3508 /* use that as the destination instead. (For xinerama) */
3509 if (monitor_dest != dest && my_offset < monitor_dest)
3510 dest = monitor_dest;
3511
3512 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3513 gint his_edge_start, his_edge_end, his_offset;
3514 ObClient *cur = it->data;
3515
3516 WANT_EDGE(cur, c)
3517
3518 his_edge_start = cur->frame->area.x;
3519 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3520 his_offset = cur->frame->area.y +
3521 (hang ? cur->frame->area.height : 0);
3522
3523
3524 if(his_offset - 1 < my_offset)
3525 continue;
3526
3527 if(his_offset > dest)
3528 continue;
3529
3530 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3531 }
3532 break;
3533 case OB_DIRECTION_WEST:
3534 my_edge_start = c->frame->area.y;
3535 my_edge_end = c->frame->area.y + c->frame->area.height;
3536 my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
3537
3538 /* default: leftmost egde of screen */
3539 dest = a->x + (hang ? c->frame->area.width : 0);
3540 monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
3541 /* if the monitor edge comes before the screen edge, */
3542 /* use that as the destination instead. (For xinerama) */
3543 if (monitor_dest != dest && my_offset > monitor_dest)
3544 dest = monitor_dest;
3545
3546 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3547 gint his_edge_start, his_edge_end, his_offset;
3548 ObClient *cur = it->data;
3549
3550 WANT_EDGE(cur, c)
3551
3552 his_edge_start = cur->frame->area.y;
3553 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3554 his_offset = cur->frame->area.x +
3555 (hang ? 0 : cur->frame->area.width);
3556
3557 if(his_offset + 1 > my_offset)
3558 continue;
3559
3560 if(his_offset < dest)
3561 continue;
3562
3563 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3564 }
3565 break;
3566 case OB_DIRECTION_EAST:
3567 my_edge_start = c->frame->area.y;
3568 my_edge_end = c->frame->area.y + c->frame->area.height;
3569 my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
3570
3571 /* default: rightmost edge of screen */
3572 dest = a->x + a->width - (hang ? c->frame->area.width : 0);
3573 monitor_dest = monitor->x + monitor->width -
3574 (hang ? c->frame->area.width : 0);
3575 /* if the monitor edge comes before the screen edge, */
3576 /* use that as the destination instead. (For xinerama) */
3577 if (monitor_dest != dest && my_offset < monitor_dest)
3578 dest = monitor_dest;
3579
3580 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3581 gint his_edge_start, his_edge_end, his_offset;
3582 ObClient *cur = it->data;
3583
3584 WANT_EDGE(cur, c)
3585
3586 his_edge_start = cur->frame->area.y;
3587 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3588 his_offset = cur->frame->area.x +
3589 (hang ? cur->frame->area.width : 0);
3590
3591 if(his_offset - 1 < my_offset)
3592 continue;
3593
3594 if(his_offset > dest)
3595 continue;
3596
3597 HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3598 }
3599 break;
3600 case OB_DIRECTION_NORTHEAST:
3601 case OB_DIRECTION_SOUTHEAST:
3602 case OB_DIRECTION_NORTHWEST:
3603 case OB_DIRECTION_SOUTHWEST:
3604 /* not implemented */
3605 default:
3606 g_assert_not_reached();
3607 dest = 0; /* suppress warning */
3608 }
3609 return dest;
3610 }
3611
3612 ObClient* client_under_pointer()
3613 {
3614 gint x, y;
3615 GList *it;
3616 ObClient *ret = NULL;
3617
3618 if (screen_pointer_pos(&x, &y)) {
3619 for (it = stacking_list; it; it = g_list_next(it)) {
3620 if (WINDOW_IS_CLIENT(it->data)) {
3621 ObClient *c = WINDOW_AS_CLIENT(it->data);
3622 if (c->frame->visible &&
3623 RECT_CONTAINS(c->frame->area, x, y)) {
3624 ret = c;
3625 break;
3626 }
3627 }
3628 }
3629 }
3630 return ret;
3631 }
3632
3633 gboolean client_has_group_siblings(ObClient *self)
3634 {
3635 return self->group && self->group->members->next;
3636 }
3637
3638 gboolean client_has_application_group_siblings(ObClient *self)
3639 {
3640 GSList *it;
3641
3642 if (!self->group) return FALSE;
3643
3644 for (it = self->group->members; it; it = g_slist_next(it)) {
3645 ObClient *c = it->data;
3646 if (c != self && client_application(c))
3647 return TRUE;
3648 }
3649 return FALSE;
3650 }
This page took 0.200184 seconds and 5 git commands to generate.