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