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