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