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