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