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