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