1
/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3
client.c for the Openbox window manager
4
Copyright (c) 2006 Mikael Magnusson
5
Copyright (c) 2003-2007 Dana Jansens
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.
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.
17
See the COPYING file for a copy of the GNU General Public License.
22
#include "startupnotify.h"
25
#include "moveresize.h"
34
#include "focus_cycle.h"
39
#include "menuframe.h"
42
#include "obrender/render.h"
44
#include "obt/display.h"
45
#include "obt/xqueue.h"
53
# include <signal.h> /* for kill() */
57
#include <X11/Xutil.h>
59
/*! The event mask to grab on client windows */
60
#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
63
#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
68
ObClientCallback func;
72
GList *client_list = NULL;
74
static GSList *client_destroy_notifies = NULL;
75
static RrImage *client_default_icon = NULL;
77
static void client_get_all(ObClient *self, gboolean real);
78
static void client_get_startup_id(ObClient *self);
79
static void client_get_session_ids(ObClient *self);
80
static void client_save_app_rule_values(ObClient *self);
81
static void client_get_area(ObClient *self);
82
static void client_get_desktop(ObClient *self);
83
static void client_get_state(ObClient *self);
84
static void client_get_shaped(ObClient *self);
85
static void client_get_colormap(ObClient *self);
86
static void client_set_desktop_recursive(ObClient *self,
90
static void client_change_allowed_actions(ObClient *self);
91
static void client_change_state(ObClient *self);
92
static void client_change_wm_state(ObClient *self);
93
static void client_apply_startup_state(ObClient *self,
94
gint x, gint y, gint w, gint h);
95
static void client_restore_session_state(ObClient *self);
96
static gboolean client_restore_session_stacking(ObClient *self);
97
static ObAppSettings *client_get_settings_state(ObClient *self);
98
static void client_update_transient_tree(ObClient *self,
99
ObGroup *oldgroup, ObGroup *newgroup,
100
gboolean oldgtran, gboolean newgtran,
102
ObClient *newparent);
103
static void client_present(ObClient *self, gboolean here, gboolean raise,
105
static GSList *client_search_all_top_parents_internal(ObClient *self,
107
ObStackingLayer layer);
108
static void client_call_notifies(ObClient *self, GSList *list);
109
static void client_ping_event(ObClient *self, gboolean dead);
110
static void client_prompt_kill(ObClient *self);
111
static gboolean client_can_steal_focus(ObClient *self,
112
gboolean allow_other_desktop,
113
gboolean request_from_user,
114
Time steal_time, Time launch_time);
115
static void client_setup_default_decor_and_functions(ObClient *self);
116
static void client_setup_decor_undecorated(ObClient *self);
118
void client_startup(gboolean reconfig)
120
client_default_icon = RrImageNewFromData(
121
ob_rr_icons, ob_rr_theme->def_win_icon,
122
ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h);
124
if (reconfig) return;
129
void client_shutdown(gboolean reconfig)
131
RrImageUnref(client_default_icon);
132
client_default_icon = NULL;
134
if (reconfig) return;
137
static void client_call_notifies(ObClient *self, GSList *list)
141
for (it = list; it; it = g_slist_next(it)) {
142
ClientCallback *d = it->data;
143
d->func(self, d->data);
147
void client_add_destroy_notify(ObClientCallback func, gpointer data)
149
ClientCallback *d = g_slice_new(ClientCallback);
152
client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
155
void client_remove_destroy_notify(ObClientCallback func)
159
for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
160
ClientCallback *d = it->data;
161
if (d->func == func) {
162
g_slice_free(ClientCallback, d);
163
client_destroy_notifies =
164
g_slist_delete_link(client_destroy_notifies, it);
170
void client_set_list(void)
172
Window *windows, *win_it;
174
guint size = g_list_length(client_list);
176
/* create an array of the window ids */
178
windows = g_new(Window, size);
180
for (it = client_list; it; it = g_list_next(it), ++win_it)
181
*win_it = ((ObClient*)it->data)->window;
185
OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
186
(gulong*)windows, size);
194
void client_manage(Window window, ObPrompt *prompt)
197
XSetWindowAttributes attrib_set;
198
gboolean try_activate = FALSE;
199
gboolean do_activate;
200
ObAppSettings *settings;
201
gboolean transient = FALSE;
207
ob_debug("Managing window: 0x%lx", window);
209
/* choose the events we want to receive on the CLIENT window
210
(ObPrompt windows can request events too) */
211
attrib_set.event_mask = CLIENT_EVENTMASK |
212
(prompt ? prompt->event_mask : 0);
213
attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
214
XChangeWindowAttributes(obt_display, window,
215
CWEventMask|CWDontPropagate, &attrib_set);
217
/* create the ObClient struct, and populate it from the hints on the
219
self = g_slice_new0(ObClient);
220
self->obwin.type = OB_WINDOW_CLASS_CLIENT;
221
self->window = window;
222
self->prompt = prompt;
223
self->managed = TRUE;
225
/* non-zero defaults */
226
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
227
self->gravity = NorthWestGravity;
228
self->desktop = screen_num_desktops; /* always an invalid value */
230
/* get all the stuff off the window */
231
client_get_all(self, TRUE);
233
ob_debug("Window type: %d", self->type);
234
ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
235
ob_debug("Window name: %s class: %s role: %s title: %s",
236
self->name, self->class, self->role, self->title);
238
/* per-app settings override stuff from client_get_all, and return the
239
settings for other uses too. the returned settings is a shallow copy,
240
that needs to be freed with g_free(). */
241
settings = client_get_settings_state(self);
243
/* the session should get the last say though */
244
client_restore_session_state(self);
246
/* the per-app settings/session may have changed the decorations for
247
the window, so we setup decorations for that here. this is a special
248
case because we want to place the window according to these decoration
250
we do this before setting up the frame so that it will reflect the
251
decorations of the window as it will be placed on screen.
253
client_setup_decor_undecorated(self);
255
/* specify that if we exit, the window should not be destroyed and
256
should be reparented back to root automatically, unless we are managing
257
an internal ObPrompt window */
259
XChangeSaveSet(obt_display, window, SetModeInsert);
261
/* create the decoration frame for the client window */
262
self->frame = frame_new(self);
264
frame_grab_client(self->frame);
266
/* we've grabbed everything and set everything that we need to at mapping
270
/* tell startup notification that this app started */
271
launch_time = sn_app_started(self->startup_id, self->class, self->name);
273
if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
274
user_time = event_time();
276
/* do this after we have a frame.. it uses the frame to help determine the
277
WM_STATE to apply. */
278
client_change_state(self);
280
/* add ourselves to the focus order */
281
focus_order_add_new(self);
283
/* do this to add ourselves to the stacking list in a non-intrusive way */
284
client_calc_layer(self);
286
/* focus the new window? */
287
if (ob_state() != OB_STATE_STARTING &&
288
(!self->session || self->session->focused) &&
289
/* this means focus=true for window is same as config_focus_new=true */
290
((config_focus_new || settings->focus == 1) ||
291
client_search_focus_tree_full(self)) &&
292
/* NET_WM_USER_TIME 0 when mapping means don't focus */
294
/* this checks for focus=false for the window */
295
settings->focus != 0 &&
296
focus_valid_target(self, self->desktop,
297
FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
298
settings->focus == 1))
303
/* remove the client's border */
304
XSetWindowBorderWidth(obt_display, self->window, 0);
306
/* adjust the frame to the client's size before showing or placing
308
frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
309
frame_adjust_client_area(self->frame);
311
/* where the frame was placed is where the window was originally */
314
ob_debug("Going to try activate new window? %s",
315
try_activate ? "yes" : "no");
317
do_activate = client_can_steal_focus(
318
self, settings->focus == 1,
319
(!!launch_time || settings->focus == 1),
320
event_time(), launch_time);
324
/* figure out placement for the window if the window is new */
325
if (ob_state() == OB_STATE_RUNNING) {
326
ob_debug("Positioned: %s @ %d %d",
327
(!self->positioned ? "no" :
328
(self->positioned == PPosition ? "program specified" :
329
(self->positioned == USPosition ? "user specified" :
330
(self->positioned == (PPosition | USPosition) ?
331
"program + user specified" :
332
"BADNESS !?")))), place.x, place.y);
334
ob_debug("Sized: %s @ %d %d",
335
(!self->sized ? "no" :
336
(self->sized == PSize ? "program specified" :
337
(self->sized == USSize ? "user specified" :
338
(self->sized == (PSize | USSize) ?
339
"program + user specified" :
340
"BADNESS !?")))), place.width, place.height);
342
obplaced = place_client(self, do_activate, &place.x, &place.y,
345
/* watch for buggy apps that ask to be placed at (0,0) when there is
347
if (!obplaced && place.x == 0 && place.y == 0 &&
348
/* non-normal windows are allowed */
349
client_normal(self) &&
350
/* oldschool fullscreen windows are allowed */
351
!client_is_oldfullscreen(self, &place))
355
r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
359
ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
361
g_slice_free(Rect, r);
364
/* make sure the window is visible. */
365
client_find_onscreen(self, &place.x, &place.y,
366
place.width, place.height,
367
/* non-normal clients has less rules, and
368
windows that are being restored from a
369
session do also. we can assume you want
370
it back where you saved it. Clients saying
371
they placed themselves are subjected to
372
harder rules, ones that are placed by
373
place.c or by the user are allowed partially
374
off-screen and on xinerama divides (ie,
375
it is up to the placement routines to avoid
376
the xinerama divides)
378
children and splash screens are forced on
379
screen, but i don't remember why i decided to
382
ob_state() == OB_STATE_RUNNING &&
383
(self->type == OB_CLIENT_TYPE_DIALOG ||
384
self->type == OB_CLIENT_TYPE_SPLASH ||
385
(!((self->positioned & USPosition) ||
386
settings->pos_given) &&
387
client_normal(self) &&
389
/* don't move oldschool fullscreen windows to
390
fit inside the struts (fixes Acroread, which
391
makes its fullscreen window fit the screen
392
but it is not USSize'd or USPosition'd) */
393
!client_is_oldfullscreen(self, &place))));
396
/* if the window isn't user-sized, then make it fit inside
397
the visible screen area on its monitor. Use basically the same rules
398
for forcing the window on screen in the client_find_onscreen call.
400
do this after place_client, it chooses the monitor!
402
splash screens get "transient" set to TRUE by
403
the place_client call
405
if (ob_state() == OB_STATE_RUNNING &&
407
(!(self->sized & USSize || self->positioned & USPosition) &&
408
client_normal(self) &&
410
/* don't shrink oldschool fullscreen windows to fit inside the
411
struts (fixes Acroread, which makes its fullscreen window
412
fit the screen but it is not USSize'd or USPosition'd) */
413
!client_is_oldfullscreen(self, &place))))
415
Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
417
/* get the size of the frame */
418
place.width += self->frame->size.left + self->frame->size.right;
419
place.height += self->frame->size.top + self->frame->size.bottom;
421
/* fit the window inside the area */
422
place.width = MIN(place.width, a->width);
423
place.height = MIN(place.height, a->height);
425
ob_debug("setting window size to %dx%d", place.width, place.height);
427
/* get the size of the client back */
428
place.width -= self->frame->size.left + self->frame->size.right;
429
place.height -= self->frame->size.top + self->frame->size.bottom;
431
g_slice_free(Rect, a);
434
ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
435
"some restrictions may apply",
436
self->window, place.x, place.y, place.width, place.height);
438
ob_debug(" but session requested %d, %d %d x %d instead, "
440
self->session->x, self->session->y,
441
self->session->w, self->session->h);
443
/* do this after the window is placed, so the premax/prefullscreen numbers
446
this also places the window
448
client_apply_startup_state(self, place.x, place.y,
449
place.width, place.height);
451
/* set the initial value of the desktop hint, when one wasn't requested
453
OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
455
/* grab mouse bindings before showing the window */
456
mouse_grab_for_client(self, TRUE);
458
/* this has to happen before we try focus the window, but we want it to
459
happen after the client's stacking has been determined or it looks bad
463
if (!config_focus_under_mouse)
464
ignore_start = event_start_ignore_all_enters();
468
if (!config_focus_under_mouse)
469
event_end_ignore_all_enters(ignore_start);
472
/* activate/hilight/raise the window */
475
gboolean stacked = client_restore_session_stacking(self);
476
client_present(self, FALSE, !stacked, TRUE);
479
/* if the client isn't stealing focus, then hilite it so the user
480
knows it is there, but don't do this if we're restoring from a
482
if (!client_restore_session_stacking(self))
483
client_hilite(self, TRUE);
487
/* This may look rather odd. Well it's because new windows are added
488
to the stacking order non-intrusively. If we're not going to focus
489
the new window or hilite it, then we raise it to the top. This will
490
take affect for things that don't get focused like splash screens.
491
Also if you don't have focus_new enabled, then it's going to get
492
raised to the top. Legacy begets legacy I guess?
494
if (!client_restore_session_stacking(self))
495
stacking_raise(CLIENT_AS_WINDOW(self));
498
/* add to client list/map */
499
client_list = g_list_append(client_list, self);
500
window_add(&self->window, CLIENT_AS_WINDOW(self));
502
/* this has to happen after we're in the client_list */
503
if (STRUT_EXISTS(self->strut))
504
screen_update_areas();
506
/* update the list hints */
509
/* free the ObAppSettings shallow copy */
510
g_slice_free(ObAppSettings, settings);
512
ob_debug("Managed window 0x%lx plate 0x%x (%s)",
513
window, self->frame->window, self->class);
516
ObClient *client_fake_manage(Window window)
519
ObAppSettings *settings;
521
ob_debug("Pretend-managing window: %lx", window);
523
/* do this minimal stuff to figure out the client's decorations */
525
self = g_slice_new0(ObClient);
526
self->window = window;
528
client_get_all(self, FALSE);
529
/* per-app settings override stuff, and return the settings for other
530
uses too. this returns a shallow copy that needs to be freed */
531
settings = client_get_settings_state(self);
533
/* create the decoration frame for the client window and adjust its size */
534
self->frame = frame_new(self);
536
client_apply_startup_state(self, self->area.x, self->area.y,
537
self->area.width, self->area.height);
539
ob_debug("gave extents left %d right %d top %d bottom %d",
540
self->frame->size.left, self->frame->size.right,
541
self->frame->size.top, self->frame->size.bottom);
543
/* free the ObAppSettings shallow copy */
544
g_slice_free(ObAppSettings, settings);
549
void client_unmanage_all(void)
552
client_unmanage(client_list->data);
555
void client_unmanage(ObClient *self)
560
ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
561
self->window, self->frame->window,
562
self->class, self->title ? self->title : "");
564
g_assert(self != NULL);
566
/* we dont want events no more. do this before hiding the frame so we
567
don't generate more events */
568
XSelectInput(obt_display, self->window, NoEventMask);
570
/* ignore enter events from the unmap so it doesnt mess with the focus */
571
if (!config_focus_under_mouse)
572
ignore_start = event_start_ignore_all_enters();
574
frame_hide(self->frame);
575
/* flush to send the hide to the server quickly */
578
if (!config_focus_under_mouse)
579
event_end_ignore_all_enters(ignore_start);
581
mouse_grab_for_client(self, FALSE);
583
self->managed = FALSE;
585
/* remove the window from our save set, unless we are managing an internal
588
XChangeSaveSet(obt_display, self->window, SetModeDelete);
590
/* update the focus lists */
591
focus_order_remove(self);
592
if (client_focused(self)) {
593
/* don't leave an invalid focus_client */
597
/* if we're prompting to kill the client, close that */
598
prompt_unref(self->kill_prompt);
599
self->kill_prompt = NULL;
601
client_list = g_list_remove(client_list, self);
602
stacking_remove(self);
603
window_remove(self->window);
605
/* once the client is out of the list, update the struts to remove its
607
if (STRUT_EXISTS(self->strut))
608
screen_update_areas();
610
client_call_notifies(self, client_destroy_notifies);
612
/* tell our parent(s) that we're gone */
613
for (it = self->parents; it; it = g_slist_next(it))
614
((ObClient*)it->data)->transients =
615
g_slist_remove(((ObClient*)it->data)->transients,self);
617
/* tell our transients that we're gone */
618
for (it = self->transients; it; it = g_slist_next(it)) {
619
((ObClient*)it->data)->parents =
620
g_slist_remove(((ObClient*)it->data)->parents, self);
621
/* we could be keeping our children in a higher layer */
622
client_calc_layer(it->data);
625
/* remove from its group */
627
group_remove(self->group, self);
631
/* restore the window's original geometry so it is not lost */
637
if (self->fullscreen)
638
a = self->pre_fullscreen_area;
639
else if (self->max_horz || self->max_vert) {
640
if (self->max_horz) {
641
a.x = self->pre_max_area.x;
642
a.width = self->pre_max_area.width;
644
if (self->max_vert) {
645
a.y = self->pre_max_area.y;
646
a.height = self->pre_max_area.height;
650
self->fullscreen = self->max_horz = self->max_vert = FALSE;
651
/* let it be moved and resized no matter what */
652
self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
653
self->decorations = 0; /* unmanaged windows have no decor */
655
/* give the client its border back */
656
XSetWindowBorderWidth(obt_display, self->window, self->border_width);
658
client_move_resize(self, a.x, a.y, a.width, a.height);
661
/* reparent the window out of the frame, and free the frame */
662
frame_release_client(self->frame);
663
frame_free(self->frame);
666
if (ob_state() != OB_STATE_EXITING) {
667
/* these values should not be persisted across a window
669
OBT_PROP_ERASE(self->window, NET_WM_DESKTOP);
670
OBT_PROP_ERASE(self->window, NET_WM_STATE);
671
OBT_PROP_ERASE(self->window, WM_STATE);
673
/* if we're left in an unmapped state, the client wont be mapped.
674
this is bad, since we will no longer be managing the window on
676
XMapWindow(obt_display, self->window);
679
/* these should not be left on the window ever. other window managers
680
don't necessarily use them and it will mess them up (like compiz) */
681
OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME);
682
OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME);
684
/* update the list hints */
687
ob_debug("Unmanaged window 0x%lx", self->window);
689
/* free all data allocated in the client struct */
690
RrImageUnref(self->icon_set);
691
g_slist_free(self->transients);
692
g_free(self->startup_id);
693
g_free(self->wm_command);
695
g_free(self->icon_title);
696
g_free(self->original_title);
700
g_free(self->client_machine);
701
g_free(self->sm_client_id);
702
g_slice_free(ObClient, self);
705
void client_fake_unmanage(ObClient *self)
707
/* this is all that got allocated to get the decorations */
709
frame_free(self->frame);
710
g_slice_free(ObClient, self);
713
static gboolean client_can_steal_focus(ObClient *self,
714
gboolean allow_other_desktop,
715
gboolean request_from_user,
720
gboolean relative_focused;
724
relative_focused = (focus_client != NULL &&
725
(client_search_focus_tree_full(self) != NULL ||
726
client_search_focus_group_full(self) != NULL));
728
/* This is focus stealing prevention */
729
ob_debug("Want to focus window 0x%x at time %u "
730
"launched at %u (last user interaction time %u) "
731
"request from %s, allow other desktop: %s",
732
self->window, steal_time, launch_time,
733
event_last_user_time,
734
(request_from_user ? "user" : "other"),
735
(allow_other_desktop ? "yes" : "no"));
738
if no launch time is provided for an application, make one up.
740
if the window is related to other existing windows
741
and one of those windows was the last used
742
then we will give it a launch time equal to the last user time,
743
which will end up giving the window focus probably.
745
the window is related to other windows, but you are not working in
747
seems suspicious, so we will give it a launch time of
748
NOW - STEAL_INTERVAL,
749
so it will be given focus only if we didn't use something else
750
during the steal interval.
752
the window is all on its own, so we can't judge it. give it a launch
753
time equal to the last user time, so it will probably take focus.
755
this way running things from a terminal will give them focus, but popups
756
without a launch time shouldn't steal focus so easily.
760
if (client_has_relative(self)) {
761
if (event_last_user_time && client_search_focus_group_full(self)) {
762
/* our relative is focused */
763
launch_time = event_last_user_time;
764
ob_debug("Unknown launch time, using %u - window in active "
765
"group", launch_time);
767
else if (!request_from_user) {
768
/* has relatives which are not being used. suspicious */
769
launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
770
ob_debug("Unknown launch time, using %u - window in inactive "
771
"group", launch_time);
774
/* has relatives which are not being used, but the user seems
775
to want to go there! */
776
launch_time = event_last_user_time;
777
ob_debug("Unknown launch time, using %u - user request",
782
/* the window is on its own, probably the user knows it is going
784
launch_time = event_last_user_time;
785
ob_debug("Unknown launch time, using %u - independent window",
790
/* if it's on another desktop
791
then if allow_other_desktop is true, we don't want to let it steal
792
focus, unless it was launched after we changed desktops and the request
795
if (!(self->desktop == screen_desktop ||
796
self->desktop == DESKTOP_ALL) &&
797
(!allow_other_desktop ||
798
(request_from_user && screen_desktop_user_time &&
799
!event_time_after(launch_time, screen_desktop_user_time))))
802
ob_debug("Not focusing the window because its on another desktop\n");
804
/* If something is focused... */
805
else if (focus_client) {
806
/* If the user is working in another window right now, then don't
808
if (!relative_focused &&
809
event_last_user_time &&
810
/* last user time must be strictly > launch_time to block focus */
811
(event_time_after(event_last_user_time, launch_time) &&
812
event_last_user_time != launch_time) &&
813
event_time_after(event_last_user_time,
814
steal_time - OB_EVENT_USER_TIME_DELAY))
817
ob_debug("Not focusing the window because the user is "
818
"working in another window that is not its relative");
820
/* Don't move focus if it's not going to go to this window
822
else if (client_focus_target(self) != self) {
824
ob_debug("Not focusing the window because another window "
825
"would get the focus anyway");
827
/* For requests that don't come from the user */
828
else if (!request_from_user) {
829
/* If the new window is a transient (and its relatives aren't
831
if (client_has_parent(self) && !relative_focused) {
833
ob_debug("Not focusing the window because it is a "
834
"transient, and its relatives aren't focused");
836
/* Don't steal focus from globally active clients.
837
I stole this idea from KWin. It seems nice.
839
else if (!(focus_client->can_focus || focus_client->focus_notify))
842
ob_debug("Not focusing the window because a globally "
843
"active client has focus");
845
/* Don't move focus if the window is not visible on the current
846
desktop and none of its relatives are focused */
847
else if (!allow_other_desktop &&
848
!screen_compare_desktops(self->desktop, screen_desktop) &&
852
ob_debug("Not focusing the window because it is on "
853
"another desktop and no relatives are focused ");
859
ob_debug("Focus stealing prevention activated for %s at "
860
"time %u (last user interaction time %u)",
861
self->title, steal_time, event_last_user_time);
863
ob_debug("Allowing focus stealing for %s at time %u (last user "
864
"interaction time %u)",
865
self->title, steal_time, event_last_user_time);
869
/*! Returns a new structure containing the per-app settings for this client.
870
The returned structure needs to be freed with g_free. */
871
static ObAppSettings *client_get_settings_state(ObClient *self)
873
ObAppSettings *settings;
876
settings = config_create_app_settings();
878
for (it = config_per_app_settings; it; it = g_slist_next(it)) {
879
ObAppSettings *app = it->data;
880
gboolean match = TRUE;
882
g_assert(app->name != NULL || app->class != NULL ||
883
app->role != NULL || app->title != NULL ||
884
(signed)app->type >= 0);
887
!g_pattern_match(app->name, strlen(self->name), self->name, NULL))
889
else if (app->class &&
890
!g_pattern_match(app->class,
891
strlen(self->class), self->class, NULL))
893
else if (app->role &&
894
!g_pattern_match(app->role,
895
strlen(self->role), self->role, NULL))
897
else if (app->title &&
898
!g_pattern_match(app->title,
899
strlen(self->title), self->title, NULL))
901
else if ((signed)app->type >= 0 && app->type != self->type) {
906
ob_debug("Window matching: %s", app->name);
908
/* copy the settings to our struct, overriding the existing
909
settings if they are not defaults */
910
config_app_settings_copy_non_defaults(app, settings);
914
if (settings->shade != -1)
915
self->shaded = !!settings->shade;
916
if (settings->decor != -1)
917
self->undecorated = !settings->decor;
918
if (settings->iconic != -1)
919
self->iconic = !!settings->iconic;
920
if (settings->skip_pager != -1)
921
self->skip_pager = !!settings->skip_pager;
922
if (settings->skip_taskbar != -1)
923
self->skip_taskbar = !!settings->skip_taskbar;
925
if (settings->max_vert != -1)
926
self->max_vert = !!settings->max_vert;
927
if (settings->max_horz != -1)
928
self->max_horz = !!settings->max_horz;
930
if (settings->fullscreen != -1)
931
self->fullscreen = !!settings->fullscreen;
933
if (settings->desktop) {
934
if (settings->desktop == DESKTOP_ALL)
935
self->desktop = settings->desktop;
936
else if (settings->desktop > 0 &&
937
settings->desktop <= screen_num_desktops)
938
self->desktop = settings->desktop - 1;
941
if (settings->layer == -1) {
945
else if (settings->layer == 0) {
949
else if (settings->layer == 1) {
956
static void client_restore_session_state(ObClient *self)
960
ob_debug_type(OB_DEBUG_SM,
961
"Restore session for client %s", self->title);
963
if (!(it = session_state_find(self))) {
964
ob_debug_type(OB_DEBUG_SM,
965
"Session data not found for client %s", self->title);
969
self->session = it->data;
971
ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
974
RECT_SET_POINT(self->area, self->session->x, self->session->y);
975
self->positioned = USPosition;
976
self->sized = USSize;
977
if (self->session->w > 0)
978
self->area.width = self->session->w;
979
if (self->session->h > 0)
980
self->area.height = self->session->h;
981
XResizeWindow(obt_display, self->window,
982
self->area.width, self->area.height);
984
self->desktop = (self->session->desktop == DESKTOP_ALL ?
985
self->session->desktop :
986
MIN(screen_num_desktops - 1, self->session->desktop));
987
OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
989
self->shaded = self->session->shaded;
990
self->iconic = self->session->iconic;
991
self->skip_pager = self->session->skip_pager;
992
self->skip_taskbar = self->session->skip_taskbar;
993
self->fullscreen = self->session->fullscreen;
994
self->above = self->session->above;
995
self->below = self->session->below;
996
self->max_horz = self->session->max_horz;
997
self->max_vert = self->session->max_vert;
998
self->undecorated = self->session->undecorated;
1001
static gboolean client_restore_session_stacking(ObClient *self)
1005
if (!self->session) return FALSE;
1007
mypos = g_list_find(session_saved_state, self->session);
1008
if (!mypos) return FALSE;
1010
/* start above me and look for the first client */
1011
for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
1014
for (cit = client_list; cit; cit = g_list_next(cit)) {
1015
ObClient *c = cit->data;
1016
/* found a client that was in the session, so go below it */
1017
if (c->session == it->data) {
1018
stacking_below(CLIENT_AS_WINDOW(self),
1019
CLIENT_AS_WINDOW(cit->data));
1027
void client_move_onscreen(ObClient *self, gboolean rude)
1029
gint x = self->area.x;
1030
gint y = self->area.y;
1031
if (client_find_onscreen(self, &x, &y,
1033
self->area.height, rude)) {
1034
client_move(self, x, y);
1038
gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1041
gint ox = *x, oy = *y;
1042
gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1048
RECT_SET(desired, *x, *y, w, h);
1049
frame_rect_to_frame(self->frame, &desired);
1051
/* get where the frame would be */
1052
frame_client_gravity(self->frame, x, y);
1054
/* get the requested size of the window with decorations */
1055
fw = self->frame->size.left + w + self->frame->size.right;
1056
fh = self->frame->size.top + h + self->frame->size.bottom;
1058
/* If rudeness wasn't requested, then still be rude in a given direction
1059
if the client is not moving, only resizing in that direction */
1061
Point oldtl, oldtr, oldbl, oldbr;
1062
Point newtl, newtr, newbl, newbr;
1063
gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1065
POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
1066
POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
1067
self->frame->area.y + self->frame->area.height - 1);
1068
POINT_SET(oldtr, oldbr.x, oldtl.y);
1069
POINT_SET(oldbl, oldtl.x, oldbr.y);
1071
POINT_SET(newtl, *x, *y);
1072
POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1073
POINT_SET(newtr, newbr.x, newtl.y);
1074
POINT_SET(newbl, newtl.x, newbr.y);
1076
/* is it moving or just resizing from some corner? */
1077
stationary_l = oldtl.x == newtl.x;
1078
stationary_r = oldtr.x == newtr.x;
1079
stationary_t = oldtl.y == newtl.y;
1080
stationary_b = oldbl.y == newbl.y;
1082
/* if left edge is growing and didnt move right edge */
1083
if (stationary_r && newtl.x < oldtl.x)
1085
/* if right edge is growing and didnt move left edge */
1086
if (stationary_l && newtr.x > oldtr.x)
1088
/* if top edge is growing and didnt move bottom edge */
1089
if (stationary_b && newtl.y < oldtl.y)
1091
/* if bottom edge is growing and didnt move top edge */
1092
if (stationary_t && newbl.y > oldbl.y)
1096
/* we iterate through every monitor that the window is at least partially
1097
on, to make sure it is obeying the rules on them all
1099
if the window does not appear on any monitors, then use the first one
1102
for (i = 0; i < screen_num_monitors; ++i) {
1105
if (!screen_physical_area_monitor_contains(i, &desired)) {
1106
if (i < screen_num_monitors - 1 || found_mon)
1109
/* the window is not inside any monitor! so just use the first
1111
a = screen_area(self->desktop, 0, NULL);
1114
a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1117
/* This makes sure windows aren't entirely outside of the screen so you
1118
can't see them at all.
1119
It makes sure 10% of the window is on the screen at least. And don't
1120
let it move itself off the top of the screen, which would hide the
1121
titlebar on you. (The user can still do this if they want too, it's
1122
only limiting the application.
1124
if (client_normal(self)) {
1125
if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1126
*x = a->x + a->width - fw/10;
1127
if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1128
*y = a->y + a->height - fh/10;
1129
if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1130
*x = a->x - fw*9/10;
1131
if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1132
*y = a->y - fh*9/10;
1135
/* This here doesn't let windows even a pixel outside the
1136
struts/screen. When called from client_manage, programs placing
1137
themselves are forced completely onscreen, while things like
1138
xterm -geometry resolution-width/2 will work fine. Trying to
1139
place it completely offscreen will be handled in the above code.
1140
Sorry for this confused comment, i am tired. */
1141
if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1142
if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1143
*x = a->x + MAX(0, a->width - fw);
1145
if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1146
if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1147
*y = a->y + MAX(0, a->height - fh);
1149
g_slice_free(Rect, a);
1152
/* get where the client should be */
1153
frame_frame_gravity(self->frame, x, y);
1155
return ox != *x || oy != *y;
1158
static void client_get_all(ObClient *self, gboolean real)
1160
/* this is needed for the frame to set itself up */
1161
client_get_area(self);
1163
/* these things can change the decor and functions of the window */
1165
client_get_mwm_hints(self);
1166
/* this can change the mwmhints for special cases */
1167
client_get_type_and_transientness(self);
1168
client_update_normal_hints(self);
1170
/* set up the maximum possible decor/functions */
1171
client_setup_default_decor_and_functions(self);
1173
client_get_state(self);
1175
/* get the session related properties, these can change decorations
1176
from per-app settings */
1177
client_get_session_ids(self);
1179
/* now we got everything that can affect the decorations */
1183
/* get this early so we have it for debugging */
1184
client_update_title(self);
1186
/* save the values of the variables used for app rule matching */
1187
client_save_app_rule_values(self);
1189
client_update_protocols(self);
1191
client_update_wmhints(self);
1192
/* this may have already been called from client_update_wmhints */
1193
if (!self->parents && !self->transient_for_group)
1194
client_update_transient_for(self);
1196
client_get_startup_id(self);
1197
client_get_desktop(self);/* uses transient data/group/startup id if a
1198
desktop is not specified */
1199
client_get_shaped(self);
1202
/* a couple type-based defaults for new windows */
1204
/* this makes sure that these windows appear on all desktops */
1205
if (self->type == OB_CLIENT_TYPE_DESKTOP)
1206
self->desktop = DESKTOP_ALL;
1210
client_update_sync_request_counter(self);
1213
client_get_colormap(self);
1214
client_update_strut(self);
1215
client_update_icons(self);
1216
client_update_icon_geometry(self);
1219
static void client_get_startup_id(ObClient *self)
1221
if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
1223
OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
1227
static void client_get_area(ObClient *self)
1229
XWindowAttributes wattrib;
1232
ret = XGetWindowAttributes(obt_display, self->window, &wattrib);
1233
g_assert(ret != BadWindow);
1235
RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1236
POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1237
self->border_width = wattrib.border_width;
1239
ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y,
1240
wattrib.width, wattrib.height, wattrib.border_width);
1243
static void client_get_desktop(ObClient *self)
1245
guint32 d = screen_num_desktops; /* an always-invalid value */
1247
if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) {
1248
if (d >= screen_num_desktops && d != DESKTOP_ALL)
1249
self->desktop = screen_num_desktops - 1;
1252
ob_debug("client requested desktop 0x%x", self->desktop);
1255
gboolean first = TRUE;
1256
guint all = screen_num_desktops; /* not a valid value */
1258
/* if they are all on one desktop, then open it on the
1260
for (it = self->parents; it; it = g_slist_next(it)) {
1261
ObClient *c = it->data;
1263
if (c->desktop == DESKTOP_ALL) continue;
1269
else if (all != c->desktop)
1270
all = screen_num_desktops; /* make it invalid */
1272
if (all != screen_num_desktops) {
1273
self->desktop = all;
1275
ob_debug("client desktop set from parents: 0x%x",
1278
/* try get from the startup-notification protocol */
1279
else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1280
if (self->desktop >= screen_num_desktops &&
1281
self->desktop != DESKTOP_ALL)
1282
self->desktop = screen_num_desktops - 1;
1283
ob_debug("client desktop set from startup-notification: 0x%x",
1286
/* defaults to the current desktop */
1288
self->desktop = screen_desktop;
1289
ob_debug("client desktop set to the current desktop: %d",
1295
static void client_get_state(ObClient *self)
1300
if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) {
1302
for (i = 0; i < num; ++i) {
1303
if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1305
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1306
self->shaded = TRUE;
1307
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1308
self->iconic = TRUE;
1309
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1310
self->skip_taskbar = TRUE;
1311
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1312
self->skip_pager = TRUE;
1313
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1314
self->fullscreen = TRUE;
1315
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
1316
self->max_vert = TRUE;
1317
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
1318
self->max_horz = TRUE;
1319
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1321
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1323
else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1324
self->demands_attention = TRUE;
1325
else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1326
self->undecorated = TRUE;
1333
static void client_get_shaped(ObClient *self)
1335
self->shaped = FALSE;
1337
if (obt_display_extension_shape) {
1342
XShapeSelectInput(obt_display, self->window, ShapeNotifyMask);
1344
XShapeQueryExtents(obt_display, self->window, &s, &foo,
1345
&foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1352
void client_update_transient_for(ObClient *self)
1355
ObClient *target = NULL;
1356
gboolean trangroup = FALSE;
1358
if (XGetTransientForHint(obt_display, self->window, &t)) {
1359
if (t != self->window) { /* can't be transient to itself! */
1360
ObWindow *tw = window_find(t);
1361
/* if this happens then we need to check for it */
1362
g_assert(tw != CLIENT_AS_WINDOW(self));
1363
if (tw && WINDOW_IS_CLIENT(tw)) {
1364
/* watch out for windows with a parent that is something
1365
different, like a dockapp for example */
1366
target = WINDOW_AS_CLIENT(tw);
1370
/* Setting the transient_for to Root is actually illegal, however
1371
applications from time have done this to specify transient for
1373
if (!target && self->group && t == obt_root(ob_screen))
1375
} else if (self->group && self->transient)
1378
client_update_transient_tree(self, self->group, self->group,
1379
self->transient_for_group, trangroup,
1380
client_direct_parent(self), target);
1381
self->transient_for_group = trangroup;
1385
static void client_update_transient_tree(ObClient *self,
1386
ObGroup *oldgroup, ObGroup *newgroup,
1387
gboolean oldgtran, gboolean newgtran,
1388
ObClient* oldparent,
1389
ObClient *newparent)
1394
g_assert(!oldgtran || oldgroup);
1395
g_assert(!newgtran || newgroup);
1396
g_assert((!oldgtran && !oldparent) ||
1397
(oldgtran && !oldparent) ||
1398
(!oldgtran && oldparent));
1399
g_assert((!newgtran && !newparent) ||
1400
(newgtran && !newparent) ||
1401
(!newgtran && newparent));
1404
Group transient windows are not allowed to have other group
1405
transient windows as their children.
1408
/* No change has occured */
1409
if (oldgroup == newgroup &&
1410
oldgtran == newgtran &&
1411
oldparent == newparent) return;
1413
/** Remove the client from the transient tree **/
1415
for (it = self->transients; it; it = next) {
1416
next = g_slist_next(it);
1418
self->transients = g_slist_delete_link(self->transients, it);
1419
c->parents = g_slist_remove(c->parents, self);
1421
for (it = self->parents; it; it = next) {
1422
next = g_slist_next(it);
1424
self->parents = g_slist_delete_link(self->parents, it);
1425
c->transients = g_slist_remove(c->transients, self);
1428
/** Re-add the client to the transient tree **/
1430
/* If we're transient for a group then we need to add ourselves to all our
1433
for (it = newgroup->members; it; it = g_slist_next(it)) {
1436
!client_search_top_direct_parent(c)->transient_for_group &&
1439
c->transients = g_slist_prepend(c->transients, self);
1440
self->parents = g_slist_prepend(self->parents, c);
1445
/* If we are now transient for a single window we need to add ourselves to
1448
WARNING: Cyclical transient-ness is possible if two windows are
1449
transient for eachother.
1451
else if (newparent &&
1452
/* don't make ourself its child if it is already our child */
1453
!client_is_direct_child(self, newparent) &&
1454
client_normal(newparent))
1456
newparent->transients = g_slist_prepend(newparent->transients, self);
1457
self->parents = g_slist_prepend(self->parents, newparent);
1460
/* Add any group transient windows to our children. But if we're transient
1461
for the group, then other group transients are not our children.
1463
WARNING: Cyclical transient-ness is possible. For e.g. if:
1464
A is transient for the group
1465
B is transient for A
1466
C is transient for B
1467
A can't be transient for C or we have a cycle
1469
if (!newgtran && newgroup &&
1471
!client_search_top_direct_parent(newparent)->transient_for_group) &&
1472
client_normal(self))
1474
for (it = newgroup->members; it; it = g_slist_next(it)) {
1476
if (c != self && c->transient_for_group &&
1477
/* Don't make it our child if it is already our parent */
1478
!client_is_direct_child(c, self))
1480
self->transients = g_slist_prepend(self->transients, c);
1481
c->parents = g_slist_prepend(c->parents, self);
1486
/** If we change our group transient-ness, our children change their
1487
effective group transient-ness, which affects how they relate to other
1490
for (it = self->transients; it; it = g_slist_next(it)) {
1492
if (!c->transient_for_group)
1493
client_update_transient_tree(c, c->group, c->group,
1494
c->transient_for_group,
1495
c->transient_for_group,
1496
client_direct_parent(c),
1497
client_direct_parent(c));
1501
void client_get_mwm_hints(ObClient *self)
1506
self->mwmhints.flags = 0; /* default to none */
1508
if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1510
if (num >= OB_MWM_ELEMENTS) {
1511
self->mwmhints.flags = hints[0];
1512
self->mwmhints.functions = hints[1];
1513
self->mwmhints.decorations = hints[2];
1519
void client_get_type_and_transientness(ObClient *self)
1526
self->transient = FALSE;
1528
if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1529
/* use the first value that we know about in the array */
1530
for (i = 0; i < num; ++i) {
1531
if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1532
self->type = OB_CLIENT_TYPE_DESKTOP;
1533
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1534
self->type = OB_CLIENT_TYPE_DOCK;
1535
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1536
self->type = OB_CLIENT_TYPE_TOOLBAR;
1537
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1538
self->type = OB_CLIENT_TYPE_MENU;
1539
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1540
self->type = OB_CLIENT_TYPE_UTILITY;
1541
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1542
self->type = OB_CLIENT_TYPE_SPLASH;
1543
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1544
self->type = OB_CLIENT_TYPE_DIALOG;
1545
else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1546
self->type = OB_CLIENT_TYPE_NORMAL;
1547
else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1549
/* prevent this window from getting any decor or
1551
self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1552
OB_MWM_FLAG_DECORATIONS);
1553
self->mwmhints.decorations = 0;
1554
self->mwmhints.functions = 0;
1556
if (self->type != (ObClientType) -1)
1557
break; /* grab the first legit type */
1562
if (XGetTransientForHint(obt_display, self->window, &t))
1563
self->transient = TRUE;
1565
if (self->type == (ObClientType) -1) {
1566
/*the window type hint was not set, which means we either classify
1567
ourself as a normal window or a dialog, depending on if we are a
1569
if (self->transient)
1570
self->type = OB_CLIENT_TYPE_DIALOG;
1572
self->type = OB_CLIENT_TYPE_NORMAL;
1575
/* then, based on our type, we can update our transientness.. */
1576
if (self->type == OB_CLIENT_TYPE_DIALOG ||
1577
self->type == OB_CLIENT_TYPE_TOOLBAR ||
1578
self->type == OB_CLIENT_TYPE_MENU ||
1579
self->type == OB_CLIENT_TYPE_UTILITY)
1581
self->transient = TRUE;
1585
void client_update_protocols(ObClient *self)
1590
self->focus_notify = FALSE;
1591
self->delete_window = FALSE;
1593
if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1594
for (i = 0; i < num_ret; ++i) {
1595
if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1596
/* this means we can request the window to close */
1597
self->delete_window = TRUE;
1598
else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1599
/* if this protocol is requested, then the window will be
1600
notified whenever we want it to receive focus */
1601
self->focus_notify = TRUE;
1602
else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1603
/* if this protocol is requested, then the window will allow
1604
pings to determine if it is still alive */
1607
else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1608
/* if this protocol is requested, then resizing the
1609
window will be synchronized between the frame and the
1611
self->sync_request = TRUE;
1619
void client_update_sync_request_counter(ObClient *self)
1623
if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1627
self->sync_counter = i;
1629
/* this must be set when managing a new window according to EWMH */
1630
XSyncIntToValue(&val, 0);
1631
XSyncSetCounter(obt_display, self->sync_counter, val);
1633
self->sync_counter = None;
1637
static void client_get_colormap(ObClient *self)
1639
XWindowAttributes wa;
1641
if (XGetWindowAttributes(obt_display, self->window, &wa))
1642
client_update_colormap(self, wa.colormap);
1645
void client_update_colormap(ObClient *self, Colormap colormap)
1647
if (colormap == self->colormap) return;
1649
ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1651
if (client_focused(self)) {
1652
screen_install_colormap(self, FALSE); /* uninstall old one */
1653
self->colormap = colormap;
1654
screen_install_colormap(self, TRUE); /* install new one */
1656
self->colormap = colormap;
1659
void client_update_normal_hints(ObClient *self)
1665
self->min_ratio = 0.0f;
1666
self->max_ratio = 0.0f;
1667
SIZE_SET(self->size_inc, 1, 1);
1668
SIZE_SET(self->base_size, -1, -1);
1669
SIZE_SET(self->min_size, 0, 0);
1670
SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1672
/* get the hints from the window */
1673
if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) {
1674
/* normal windows can't request placement! har har
1675
if (!client_normal(self))
1677
self->positioned = (size.flags & (PPosition|USPosition));
1678
self->sized = (size.flags & (PSize|USSize));
1680
if (size.flags & PWinGravity)
1681
self->gravity = size.win_gravity;
1683
if (size.flags & PAspect) {
1684
if (size.min_aspect.y)
1686
(gfloat) size.min_aspect.x / size.min_aspect.y;
1687
if (size.max_aspect.y)
1689
(gfloat) size.max_aspect.x / size.max_aspect.y;
1692
if (size.flags & PMinSize)
1693
SIZE_SET(self->min_size, size.min_width, size.min_height);
1695
if (size.flags & PMaxSize)
1696
SIZE_SET(self->max_size, size.max_width, size.max_height);
1698
if (size.flags & PBaseSize)
1699
SIZE_SET(self->base_size, size.base_width, size.base_height);
1701
if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1702
SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1704
ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1705
self->min_size.width, self->min_size.height,
1706
self->max_size.width, self->max_size.height);
1707
ob_debug("size inc (%d %d) base size (%d %d)",
1708
self->size_inc.width, self->size_inc.height,
1709
self->base_size.width, self->base_size.height);
1712
ob_debug("Normal hints: not set");
1715
static void client_setup_default_decor_and_functions(ObClient *self)
1717
/* start with everything (cept fullscreen) */
1719
(OB_FRAME_DECOR_TITLEBAR |
1720
OB_FRAME_DECOR_HANDLE |
1721
OB_FRAME_DECOR_GRIPS |
1722
OB_FRAME_DECOR_BORDER |
1723
OB_FRAME_DECOR_ICON |
1724
OB_FRAME_DECOR_ALLDESKTOPS |
1725
OB_FRAME_DECOR_ICONIFY |
1726
OB_FRAME_DECOR_MAXIMIZE |
1727
OB_FRAME_DECOR_SHADE |
1728
OB_FRAME_DECOR_CLOSE);
1730
(OB_CLIENT_FUNC_RESIZE |
1731
OB_CLIENT_FUNC_MOVE |
1732
OB_CLIENT_FUNC_ICONIFY |
1733
OB_CLIENT_FUNC_MAXIMIZE |
1734
OB_CLIENT_FUNC_SHADE |
1735
OB_CLIENT_FUNC_CLOSE |
1736
OB_CLIENT_FUNC_BELOW |
1737
OB_CLIENT_FUNC_ABOVE |
1738
OB_CLIENT_FUNC_UNDECORATE);
1740
if (!(self->min_size.width < self->max_size.width ||
1741
self->min_size.height < self->max_size.height))
1742
self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1744
switch (self->type) {
1745
case OB_CLIENT_TYPE_NORMAL:
1746
/* normal windows retain all of the possible decorations and
1747
functionality, and can be fullscreen */
1748
self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1751
case OB_CLIENT_TYPE_DIALOG:
1752
/* sometimes apps make dialog windows fullscreen for some reason (for
1753
e.g. kpdf does this..) */
1754
self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1757
case OB_CLIENT_TYPE_UTILITY:
1758
/* these windows don't have anything added or removed by default */
1761
case OB_CLIENT_TYPE_MENU:
1762
case OB_CLIENT_TYPE_TOOLBAR:
1763
/* these windows can't iconify or maximize */
1764
self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1765
OB_FRAME_DECOR_MAXIMIZE);
1766
self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1767
OB_CLIENT_FUNC_MAXIMIZE);
1770
case OB_CLIENT_TYPE_SPLASH:
1771
/* these don't get get any decorations, and the only thing you can
1772
do with them is move them */
1773
self->decorations = 0;
1774
self->functions = OB_CLIENT_FUNC_MOVE;
1777
case OB_CLIENT_TYPE_DESKTOP:
1778
/* these windows are not manipulated by the window manager */
1779
self->decorations = 0;
1780
self->functions = 0;
1783
case OB_CLIENT_TYPE_DOCK:
1784
/* these windows are not manipulated by the window manager, but they
1785
can set below layer which has a special meaning */
1786
self->decorations = 0;
1787
self->functions = OB_CLIENT_FUNC_BELOW;
1791
/* If the client has no decor from its type (which never changes) then
1792
don't allow the user to "undecorate" the window. Otherwise, allow them
1793
to, even if there are motif hints removing the decor, because those
1794
may change these days (e.g. chromium) */
1795
if (self->decorations == 0)
1796
self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1798
/* Mwm Hints are applied subtractively to what has already been chosen for
1799
decor and functionality */
1800
if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1801
if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1802
if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1803
(self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1805
/* if the mwm hints request no handle or title, then all
1806
decorations are disabled, but keep the border if that's
1808
if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1809
self->decorations = OB_FRAME_DECOR_BORDER;
1811
self->decorations = 0;
1816
if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1817
if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1818
if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1819
self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1820
if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1821
self->functions &= ~OB_CLIENT_FUNC_MOVE;
1822
/* dont let mwm hints kill any buttons
1823
if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1824
self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1825
if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1826
self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1828
/* dont let mwm hints kill the close button
1829
if (! (self->mwmhints.functions & MwmFunc_Close))
1830
self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1834
if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1835
self->decorations &= ~OB_FRAME_DECOR_SHADE;
1836
if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1837
self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1838
if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1839
self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1841
/* can't maximize without moving/resizing */
1842
if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1843
(self->functions & OB_CLIENT_FUNC_MOVE) &&
1844
(self->functions & OB_CLIENT_FUNC_RESIZE))) {
1845
self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1846
self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1850
/*! Set up decor for a client based on its undecorated state. */
1851
static void client_setup_decor_undecorated(ObClient *self)
1853
/* If the user requested no decorations, then remove all the decorations,
1854
except the border. But don't add a border if there wasn't one. */
1855
if (self->undecorated)
1856
self->decorations &= (config_theme_keepborder ?
1857
OB_FRAME_DECOR_BORDER : 0);
1860
void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1862
client_setup_default_decor_and_functions(self);
1864
client_setup_decor_undecorated(self);
1866
if (self->max_horz && self->max_vert) {
1867
/* once upon a time you couldn't resize maximized windows, that is not
1868
the case any more though !
1870
but do kill the handle on fully maxed windows */
1871
self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1874
/* if we don't have a titlebar, then we cannot shade! */
1875
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1876
self->functions &= ~OB_CLIENT_FUNC_SHADE;
1878
/* now we need to check against rules for the client's current state */
1879
if (self->fullscreen) {
1880
self->functions &= (OB_CLIENT_FUNC_CLOSE |
1881
OB_CLIENT_FUNC_FULLSCREEN |
1882
OB_CLIENT_FUNC_ICONIFY);
1883
self->decorations = 0;
1886
client_change_allowed_actions(self);
1889
/* reconfigure to make sure decorations are updated */
1890
client_reconfigure(self, FALSE);
1893
static void client_change_allowed_actions(ObClient *self)
1898
/* desktop windows are kept on all desktops */
1899
if (self->type != OB_CLIENT_TYPE_DESKTOP)
1900
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1902
if (self->functions & OB_CLIENT_FUNC_SHADE)
1903
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1904
if (self->functions & OB_CLIENT_FUNC_CLOSE)
1905
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1906
if (self->functions & OB_CLIENT_FUNC_MOVE)
1907
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1908
if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1909
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1910
if (self->functions & OB_CLIENT_FUNC_RESIZE)
1911
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1912
if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1913
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1914
if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1915
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1916
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1918
if (self->functions & OB_CLIENT_FUNC_ABOVE)
1919
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1920
if (self->functions & OB_CLIENT_FUNC_BELOW)
1921
actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1922
if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1923
actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1925
OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1927
/* make sure the window isn't breaking any rules now
1929
don't check ICONIFY here. just cuz a window can't iconify doesnt mean
1930
it can't be iconified with its parent
1933
if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1934
if (self->frame) client_shade(self, FALSE);
1935
else self->shaded = FALSE;
1937
if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1938
if (self->frame) client_fullscreen(self, FALSE);
1939
else self->fullscreen = FALSE;
1941
if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1943
if (self->frame) client_maximize(self, FALSE, 0);
1944
else self->max_vert = self->max_horz = FALSE;
1948
void client_update_wmhints(ObClient *self)
1952
/* assume a window takes input if it doesn't specify */
1953
self->can_focus = TRUE;
1955
if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
1958
if (hints->flags & InputHint)
1959
self->can_focus = hints->input;
1961
/* only do this when first managing the window *AND* when we aren't
1963
if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1964
if (hints->flags & StateHint)
1965
self->iconic = hints->initial_state == IconicState;
1968
self->urgent = (hints->flags & XUrgencyHint);
1969
if (self->urgent && !ur)
1970
client_hilite(self, TRUE);
1971
else if (!self->urgent && ur && self->demands_attention)
1972
client_hilite(self, FALSE);
1974
if (!(hints->flags & WindowGroupHint))
1975
hints->window_group = None;
1977
/* did the group state change? */
1978
if (hints->window_group !=
1979
(self->group ? self->group->leader : None))
1981
ObGroup *oldgroup = self->group;
1983
/* remove from the old group if there was one */
1985
group_remove(self->group, self);
1989
/* add ourself to the group if we have one */
1990
if (hints->window_group != None) {
1991
self->group = group_add(hints->window_group, self);
1994
/* Put ourselves into the new group's transient tree, and remove
1995
ourselves from the old group's */
1996
client_update_transient_tree(self, oldgroup, self->group,
1997
self->transient_for_group,
1998
self->transient_for_group,
1999
client_direct_parent(self),
2000
client_direct_parent(self));
2002
/* Lastly, being in a group, or not, can change if the window is
2003
transient for anything.
2005
The logic for this is:
2006
self->transient = TRUE always if the window wants to be
2007
transient for something, even if transient_for was NULL because
2008
it wasn't in a group before.
2010
If parents was NULL and oldgroup was NULL we can assume
2011
that when we add the new group, it will become transient for
2014
If transient_for_group is TRUE, then it must have already
2015
had a group. If it is getting a new group, the above call to
2016
client_update_transient_tree has already taken care of
2017
everything ! If it is losing all group status then it will
2018
no longer be transient for anything and that needs to be
2021
if (self->transient &&
2022
((self->parents == NULL && oldgroup == NULL) ||
2023
(self->transient_for_group && !self->group)))
2024
client_update_transient_for(self);
2027
/* the WM_HINTS can contain an icon */
2028
if (hints->flags & IconPixmapHint)
2029
client_update_icons(self);
2034
focus_cycle_addremove(self, TRUE);
2037
void client_update_title(ObClient *self)
2040
gchar *visible = NULL;
2042
g_free(self->title);
2043
g_free(self->original_title);
2046
if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
2047
/* try old x stuff */
2048
if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
2049
if (self->transient) {
2051
GNOME alert windows are not given titles:
2052
http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2054
data = g_strdup("");
2056
data = g_strdup(_("Unnamed Window"));
2059
self->original_title = g_strdup(data);
2061
if (self->client_machine) {
2062
visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2067
if (self->not_responding) {
2069
if (self->kill_level > 0)
2070
visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2072
visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2076
OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
2077
self->title = visible;
2080
frame_adjust_title(self->frame);
2082
/* update the icon title */
2084
g_free(self->icon_title);
2087
if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
2088
/* try old x stuff */
2089
if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
2090
data = g_strdup(self->title);
2092
if (self->client_machine) {
2093
visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2098
if (self->not_responding) {
2100
if (self->kill_level > 0)
2101
visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2103
visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2107
OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
2108
self->icon_title = visible;
2111
void client_update_strut(ObClient *self)
2115
gboolean got = FALSE;
2118
if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL,
2123
STRUT_PARTIAL_SET(strut,
2124
data[0], data[2], data[1], data[3],
2125
data[4], data[5], data[8], data[9],
2126
data[6], data[7], data[10], data[11]);
2132
OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
2138
/* use the screen's width/height */
2139
a = screen_physical_area_all_monitors();
2141
STRUT_PARTIAL_SET(strut,
2142
data[0], data[2], data[1], data[3],
2143
a->y, a->y + a->height - 1,
2144
a->x, a->x + a->width - 1,
2145
a->y, a->y + a->height - 1,
2146
a->x, a->x + a->width - 1);
2152
STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2153
0, 0, 0, 0, 0, 0, 0, 0);
2155
if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
2156
self->strut = strut;
2158
/* updating here is pointless while we're being mapped cuz we're not in
2159
the client list yet */
2161
screen_update_areas();
2165
void client_update_icons(ObClient *self)
2174
/* grab the server, because we might be setting the window's icon and
2175
we don't want them to set it in between and we overwrite their own
2179
if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) {
2180
/* figure out how many valid icons are in here */
2182
while (i + 2 < num) { /* +2 is to make sure there is a w and h */
2185
/* watch for the data being too small for the specified size,
2186
or for zero sized icons. */
2187
if (i + w*h > num || w == 0 || h == 0) {
2192
/* convert it to the right bit order for ObRender */
2193
for (j = 0; j < w*h; ++j)
2195
(((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2196
(((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) +
2197
(((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) +
2198
(((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset);
2200
/* add it to the image cache as an original */
2202
img = RrImageNewFromData(ob_rr_icons, &data[i], w, h);
2204
RrImageAddFromData(img, &data[i], w, h);
2212
/* if we didn't find an image from the NET_WM_ICON stuff, then try the
2217
if ((hints = XGetWMHints(obt_display, self->window))) {
2218
if (hints->flags & IconPixmapHint) {
2220
obt_display_ignore_errors(TRUE);
2221
xicon = RrPixmapToRGBA(ob_rr_inst,
2223
(hints->flags & IconMaskHint ?
2224
hints->icon_mask : None),
2225
(gint*)&w, (gint*)&h, &data);
2226
obt_display_ignore_errors(FALSE);
2229
if (w > 0 && h > 0) {
2231
img = RrImageNewFromData(ob_rr_icons, data, w, h);
2233
RrImageAddFromData(img, data, w, h);
2243
/* set the client's icons to be whatever we found */
2244
RrImageUnref(self->icon_set);
2245
self->icon_set = img;
2247
/* if the client has no icon at all, then we set a default icon onto it.
2248
but, if it has parents, then one of them will have an icon already
2250
if (!self->icon_set && !self->parents) {
2251
RrPixel32 *icon = ob_rr_theme->def_win_icon;
2252
gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */
2254
w = ob_rr_theme->def_win_icon_w;
2255
h = ob_rr_theme->def_win_icon_h;
2256
ldata = g_new(gulong, w*h+2);
2259
for (i = 0; i < w*h; ++i)
2260
ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2261
(((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2262
(((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2263
(((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2264
OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2);
2266
} else if (self->frame)
2267
/* don't draw the icon empty if we're just setting one now anyways,
2268
we'll get the property change any second */
2269
frame_adjust_icon(self->frame);
2274
void client_update_icon_geometry(ObClient *self)
2279
RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2281
if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL,
2285
/* don't let them set it with an area < 0 */
2286
RECT_SET(self->icon_geometry, data[0], data[1],
2287
MAX(data[2],0), MAX(data[3],0));
2292
static void client_get_session_ids(ObClient *self)
2299
if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader))
2302
/* get the SM_CLIENT_ID */
2303
if (leader && leader != self->window)
2304
OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
2306
OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
2308
/* get the WM_CLASS (name and class). make them "" if they are not
2310
got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
2314
self->name = g_strdup(ss[0]);
2316
self->class = g_strdup(ss[1]);
2321
if (self->name == NULL) self->name = g_strdup("");
2322
if (self->class == NULL) self->class = g_strdup("");
2324
/* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2325
got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
2330
self->role = g_strdup("");
2332
/* get the WM_COMMAND */
2336
got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
2338
got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
2341
/* merge/mash them all together */
2342
gchar *merge = NULL;
2345
for (i = 0; ss[i]; ++i) {
2348
merge = g_strconcat(merge, ss[i], NULL);
2350
merge = g_strconcat(ss[i], NULL);
2355
self->wm_command = merge;
2358
/* get the WM_CLIENT_MACHINE */
2361
got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
2363
got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
2366
gchar localhost[128];
2369
gethostname(localhost, 127);
2370
localhost[127] = '\0';
2371
if (strcmp(localhost, s) != 0)
2372
self->client_machine = s;
2376
/* see if it has the PID set too (the PID requires that the
2377
WM_CLIENT_MACHINE be set) */
2378
if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid))
2383
/*! Save the properties used for app matching rules, as seen by Openbox when
2384
the window mapped, so that users can still access them later if the app
2386
static void client_save_app_rule_values(ObClient *self)
2390
OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
2391
OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
2392
OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
2393
OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
2395
switch (self->type) {
2396
case OB_CLIENT_TYPE_NORMAL:
2397
type = "normal"; break;
2398
case OB_CLIENT_TYPE_DIALOG:
2399
type = "dialog"; break;
2400
case OB_CLIENT_TYPE_UTILITY:
2401
type = "utility"; break;
2402
case OB_CLIENT_TYPE_MENU:
2403
type = "menu"; break;
2404
case OB_CLIENT_TYPE_TOOLBAR:
2405
type = "toolbar"; break;
2406
case OB_CLIENT_TYPE_SPLASH:
2407
type = "splash"; break;
2408
case OB_CLIENT_TYPE_DESKTOP:
2409
type = "desktop"; break;
2410
case OB_CLIENT_TYPE_DOCK:
2411
type = "dock"; break;
2413
OBT_PROP_SETS(self->window, OB_APP_TYPE, type);
2416
static void client_change_wm_state(ObClient *self)
2421
old = self->wmstate;
2423
if (self->shaded || self->iconic ||
2424
(self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2426
self->wmstate = IconicState;
2428
self->wmstate = NormalState;
2430
if (old != self->wmstate) {
2431
OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE,
2432
self->wmstate, 1, 0, 0, 0);
2434
state[0] = self->wmstate;
2436
OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2);
2440
static void client_change_state(ObClient *self)
2442
gulong netstate[12];
2447
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2449
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2451
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2452
if (self->skip_taskbar)
2453
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2454
if (self->skip_pager)
2455
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2456
if (self->fullscreen)
2457
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2459
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2461
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2463
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2465
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2466
if (self->demands_attention)
2467
netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2468
if (self->undecorated)
2469
netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2470
OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num);
2473
frame_adjust_state(self->frame);
2476
ObClient *client_search_focus_tree(ObClient *self)
2481
for (it = self->transients; it; it = g_slist_next(it)) {
2482
if (client_focused(it->data)) return it->data;
2483
if ((ret = client_search_focus_tree(it->data))) return ret;
2488
ObClient *client_search_focus_tree_full(ObClient *self)
2490
if (self->parents) {
2493
for (it = self->parents; it; it = g_slist_next(it)) {
2494
ObClient *c = it->data;
2495
if ((c = client_search_focus_tree_full(c))) return c;
2501
/* this function checks the whole tree, the client_search_focus_tree
2502
does not, so we need to check this window */
2503
if (client_focused(self))
2505
return client_search_focus_tree(self);
2509
ObClient *client_search_focus_group_full(ObClient *self)
2514
for (it = self->group->members; it; it = g_slist_next(it)) {
2515
ObClient *c = it->data;
2517
if (client_focused(c)) return c;
2518
if ((c = client_search_focus_tree(it->data))) return c;
2521
if (client_focused(self)) return self;
2525
gboolean client_has_parent(ObClient *self)
2527
return self->parents != NULL;
2530
gboolean client_has_children(ObClient *self)
2532
return self->transients != NULL;
2535
gboolean client_is_oldfullscreen(const ObClient *self,
2538
const Rect *monitor, *allmonitors;
2540
/* No decorations and fills the monitor = oldskool fullscreen.
2541
But not for maximized windows.
2544
if (self->decorations || self->max_horz || self->max_vert) return FALSE;
2546
monitor = screen_physical_area_monitor(screen_find_monitor(area));
2547
allmonitors = screen_physical_area_all_monitors();
2549
return (RECT_EQUAL(*area, *monitor) ||
2550
RECT_EQUAL(*area, *allmonitors));
2553
static ObStackingLayer calc_layer(ObClient *self)
2557
if (self->type == OB_CLIENT_TYPE_DESKTOP)
2558
l = OB_STACKING_LAYER_DESKTOP;
2559
else if (self->type == OB_CLIENT_TYPE_DOCK) {
2560
if (self->below) l = OB_STACKING_LAYER_NORMAL;
2561
else l = OB_STACKING_LAYER_ABOVE;
2563
else if ((self->fullscreen ||
2564
client_is_oldfullscreen(self, &self->area)) &&
2565
/* you are fullscreen while you or your children are focused.. */
2566
(client_focused(self) || client_search_focus_tree(self) ||
2567
/* you can be fullscreen if you're on another desktop */
2568
(self->desktop != screen_desktop &&
2569
self->desktop != DESKTOP_ALL) ||
2570
/* and you can also be fullscreen if the focused client is on
2571
another monitor, or nothing else is focused */
2573
client_monitor(focus_client) != client_monitor(self))))
2574
l = OB_STACKING_LAYER_FULLSCREEN;
2575
else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2576
else if (self->below) l = OB_STACKING_LAYER_BELOW;
2577
else l = OB_STACKING_LAYER_NORMAL;
2582
static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2583
ObStackingLayer min)
2585
ObStackingLayer old, own;
2589
own = calc_layer(self);
2590
self->layer = MAX(own, min);
2592
if (self->layer != old) {
2593
stacking_remove(CLIENT_AS_WINDOW(self));
2594
stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2597
/* we've been restacked */
2598
self->visited = TRUE;
2600
for (it = self->transients; it; it = g_slist_next(it))
2601
client_calc_layer_recursive(it->data, orig,
2605
static void client_calc_layer_internal(ObClient *self)
2609
/* transients take on the layer of their parents */
2610
sit = client_search_all_top_parents(self);
2612
for (; sit; sit = g_slist_next(sit))
2613
client_calc_layer_recursive(sit->data, self, 0);
2616
void client_calc_layer(ObClient *self)
2620
/* skip over stuff above fullscreen layer */
2621
for (it = stacking_list; it; it = g_list_next(it))
2622
if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2624
/* find the windows in the fullscreen layer, and mark them not-visited */
2625
for (; it; it = g_list_next(it)) {
2626
if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2627
else if (WINDOW_IS_CLIENT(it->data))
2628
WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2631
client_calc_layer_internal(self);
2633
/* skip over stuff above fullscreen layer */
2634
for (it = stacking_list; it; it = g_list_next(it))
2635
if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2637
/* now recalc any windows in the fullscreen layer which have not
2638
had their layer recalced already */
2639
for (; it; it = g_list_next(it)) {
2640
if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2641
else if (WINDOW_IS_CLIENT(it->data) &&
2642
!WINDOW_AS_CLIENT(it->data)->visited)
2643
client_calc_layer_internal(it->data);
2647
gboolean client_should_show(ObClient *self)
2651
if (client_normal(self) && screen_showing_desktop)
2653
if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2659
gboolean client_show(ObClient *self)
2661
gboolean show = FALSE;
2663
if (client_should_show(self)) {
2664
/* replay pending pointer event before showing the window, in case it
2665
should be going to something under the window */
2666
mouse_replay_pointer();
2668
frame_show(self->frame);
2671
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2672
it needs to be in IconicState. This includes when it is on another
2675
client_change_wm_state(self);
2680
gboolean client_hide(ObClient *self)
2682
gboolean hide = FALSE;
2684
if (!client_should_show(self)) {
2685
/* We don't need to ignore enter events here.
2686
The window can hide/iconify in 3 different ways:
2687
1 - through an x message. in this case we ignore all enter events
2688
caused by responding to the x message (unless underMouse)
2689
2 - by a keyboard action. in this case we ignore all enter events
2690
caused by the action
2691
3 - by a mouse action. in this case they are doing stuff with the
2692
mouse and focus _should_ move.
2694
Also in action_end, we simulate an enter event that can't be ignored
2695
so trying to ignore them is futile in case 3 anyways
2698
/* replay pending pointer event before hiding the window, in case it
2699
should be going to the window */
2700
mouse_replay_pointer();
2702
frame_hide(self->frame);
2705
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2706
it needs to be in IconicState. This includes when it is on another
2709
client_change_wm_state(self);
2714
void client_showhide(ObClient *self)
2716
if (!client_show(self))
2720
gboolean client_normal(ObClient *self) {
2721
return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2722
self->type == OB_CLIENT_TYPE_DOCK ||
2723
self->type == OB_CLIENT_TYPE_SPLASH);
2726
gboolean client_helper(ObClient *self)
2728
return (self->type == OB_CLIENT_TYPE_UTILITY ||
2729
self->type == OB_CLIENT_TYPE_MENU ||
2730
self->type == OB_CLIENT_TYPE_TOOLBAR);
2733
gboolean client_mouse_focusable(ObClient *self)
2735
return !(self->type == OB_CLIENT_TYPE_MENU ||
2736
self->type == OB_CLIENT_TYPE_TOOLBAR ||
2737
self->type == OB_CLIENT_TYPE_SPLASH ||
2738
self->type == OB_CLIENT_TYPE_DOCK);
2741
gboolean client_enter_focusable(ObClient *self)
2743
/* you can focus desktops but it shouldn't on enter */
2744
return (client_mouse_focusable(self) &&
2745
self->type != OB_CLIENT_TYPE_DESKTOP);
2748
static void client_apply_startup_state(ObClient *self,
2749
gint x, gint y, gint w, gint h)
2751
/* save the states that we are going to apply */
2752
gboolean iconic = self->iconic;
2753
gboolean fullscreen = self->fullscreen;
2754
gboolean undecorated = self->undecorated;
2755
gboolean shaded = self->shaded;
2756
gboolean demands_attention = self->demands_attention;
2757
gboolean max_horz = self->max_horz;
2758
gboolean max_vert = self->max_vert;
2762
/* turn them all off in the client, so they won't affect the window
2764
self->iconic = self->fullscreen = self->undecorated = self->shaded =
2765
self->demands_attention = self->max_horz = self->max_vert = FALSE;
2767
/* move the client to its placed position, or it it's already there,
2768
generate a ConfigureNotify telling the client where it is.
2770
do this after adjusting the frame. otherwise it gets all weird and
2771
clients don't work right
2773
do this before applying the states so they have the correct
2774
pre-max/pre-fullscreen values
2776
client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2777
ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2778
self->window, x, y, w, h);
2779
/* save the area, and make it where it should be for the premax stuff */
2780
oldarea = self->area;
2781
RECT_SET(self->area, x, y, w, h);
2783
/* apply the states. these are in a carefully crafted order.. */
2786
client_iconify(self, TRUE, FALSE, TRUE);
2788
client_set_undecorated(self, TRUE);
2790
client_shade(self, TRUE);
2791
if (demands_attention)
2792
client_hilite(self, TRUE);
2794
if (max_vert && max_horz)
2795
client_maximize(self, TRUE, 0);
2797
client_maximize(self, TRUE, 2);
2799
client_maximize(self, TRUE, 1);
2801
/* fullscreen removes the ability to apply other states */
2803
client_fullscreen(self, TRUE);
2805
/* make sure client_setup_decor_and_functions() is called at least once */
2806
client_setup_decor_and_functions(self, FALSE);
2808
/* if the window hasn't been configured yet, then do so now, in fact the
2809
x,y,w,h may _not_ be the same as the area rect, which can end up
2810
meaning that the client isn't properly moved/resized by the fullscreen
2812
pho can cause this because it maps at size of the screen but not 0,0
2813
so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2814
then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2815
cuz thats where the pre-fullscreen will be. however the actual area is
2816
not, so this needs to be called even if we have fullscreened/maxed
2818
self->area = oldarea;
2819
client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2821
/* nothing to do for the other states:
2830
void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2832
/* these should be the current values. this is for when you're not moving,
2834
g_assert(*x == self->area.x);
2835
g_assert(oldw == self->area.width);
2838
switch (self->gravity) {
2840
case NorthWestGravity:
2842
case SouthWestGravity:
2849
*x -= (neww - oldw) / 2;
2851
case NorthEastGravity:
2853
case SouthEastGravity:
2859
void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2861
/* these should be the current values. this is for when you're not moving,
2863
g_assert(*y == self->area.y);
2864
g_assert(oldh == self->area.height);
2867
switch (self->gravity) {
2869
case NorthWestGravity:
2871
case NorthEastGravity:
2878
*y -= (newh - oldh) / 2;
2880
case SouthWestGravity:
2882
case SouthEastGravity:
2888
void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2889
gint *logicalw, gint *logicalh,
2892
Rect desired = {*x, *y, *w, *h};
2893
frame_rect_to_frame(self->frame, &desired);
2895
/* make the frame recalculate its dimensions n shit without changing
2896
anything visible for real, this way the constraints below can work with
2897
the updated frame dimensions. */
2898
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2900
/* cap any X windows at the size of an unsigned short */
2902
G_MAXUSHORT - self->frame->size.left - self->frame->size.right);
2904
G_MAXUSHORT - self->frame->size.top - self->frame->size.bottom);
2907
/* gets the frame's position */
2908
frame_client_gravity(self->frame, x, y);
2910
/* these positions are frame positions, not client positions */
2912
/* set the size and position if fullscreen */
2913
if (self->fullscreen) {
2917
i = screen_find_monitor(&desired);
2918
a = screen_physical_area_monitor(i);
2925
user = FALSE; /* ignore if the client can't be moved/resized when it
2927
} else if (self->max_horz || self->max_vert) {
2931
/* use all possible struts when maximizing to the full screen */
2932
i = screen_find_monitor(&desired);
2933
a = screen_area(self->desktop, i,
2934
(self->max_horz && self->max_vert ? NULL : &desired));
2936
/* set the size and position if maximized */
2937
if (self->max_horz) {
2939
*w = a->width - self->frame->size.left - self->frame->size.right;
2941
if (self->max_vert) {
2943
*h = a->height - self->frame->size.top - self->frame->size.bottom;
2946
user = FALSE; /* ignore if the client can't be moved/resized when it
2949
g_slice_free(Rect, a);
2952
/* gets the client's position */
2953
frame_frame_gravity(self->frame, x, y);
2955
/* work within the preferred sizes given by the window, these may have
2956
changed rather than it's requested width and height, so always run
2957
through this code */
2959
gint basew, baseh, minw, minh;
2960
gint incw, inch, maxw, maxh;
2961
gfloat minratio, maxratio;
2963
incw = self->size_inc.width;
2964
inch = self->size_inc.height;
2965
minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2966
0 : self->min_ratio;
2967
maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2968
0 : self->max_ratio;
2970
/* base size is substituted with min size if not specified */
2971
if (self->base_size.width >= 0 || self->base_size.height >= 0) {
2972
basew = self->base_size.width;
2973
baseh = self->base_size.height;
2975
basew = self->min_size.width;
2976
baseh = self->min_size.height;
2978
/* min size is substituted with base size if not specified */
2979
if (self->min_size.width || self->min_size.height) {
2980
minw = self->min_size.width;
2981
minh = self->min_size.height;
2983
minw = self->base_size.width;
2984
minh = self->base_size.height;
2987
/* This comment is no longer true */
2988
/* if this is a user-requested resize, then check against min/max
2991
/* smaller than min size or bigger than max size? */
2992
if (*w > self->max_size.width) *w = self->max_size.width;
2993
if (*w < minw) *w = minw;
2994
if (*h > self->max_size.height) *h = self->max_size.height;
2995
if (*h < minh) *h = minh;
3000
/* the sizes to used for maximized */
3004
/* keep to the increments */
3008
/* you cannot resize to nothing */
3009
if (basew + *w < 1) *w = 1 - basew;
3010
if (baseh + *h < 1) *h = 1 - baseh;
3012
/* save the logical size */
3013
*logicalw = incw > 1 ? *w : *w + basew;
3014
*logicalh = inch > 1 ? *h : *h + baseh;
3019
/* if maximized/fs then don't use the size increments */
3020
if (self->fullscreen || self->max_horz) *w = maxw;
3021
if (self->fullscreen || self->max_vert) *h = maxh;
3026
/* adjust the height to match the width for the aspect ratios.
3027
for this, min size is not substituted for base size ever. */
3028
*w -= self->base_size.width;
3029
*h -= self->base_size.height;
3032
if (*h * minratio > *w) {
3033
*h = (gint)(*w / minratio);
3035
/* you cannot resize to nothing */
3038
*w = (gint)(*h * minratio);
3042
if (*h * maxratio < *w) {
3043
*h = (gint)(*w / maxratio);
3045
/* you cannot resize to nothing */
3048
*w = (gint)(*h * minratio);
3052
*w += self->base_size.width;
3053
*h += self->base_size.height;
3056
/* these override the above states! if you cant move you can't move! */
3058
if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3062
if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3063
*w = self->area.width;
3064
*h = self->area.height;
3072
void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3073
gboolean user, gboolean final, gboolean force_reply)
3075
Rect oldframe, oldclient;
3076
gboolean send_resize_client;
3077
gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3078
gboolean fmoved, fresized;
3079
guint fdecor = self->frame->decorations;
3080
gboolean fhorz = self->frame->max_horz;
3081
gboolean fvert = self->frame->max_vert;
3082
gint logicalw, logicalh;
3084
/* find the new x, y, width, and height (and logical size) */
3085
client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3087
/* set the logical size if things changed */
3088
if (!(w == self->area.width && h == self->area.height))
3089
SIZE_SET(self->logical_size, logicalw, logicalh);
3091
/* figure out if we moved or resized or what */
3092
moved = (x != self->area.x || y != self->area.y);
3093
resized = (w != self->area.width || h != self->area.height);
3095
oldframe = self->frame->area;
3096
oldclient = self->area;
3097
RECT_SET(self->area, x, y, w, h);
3099
/* for app-requested resizes, always resize if 'resized' is true.
3100
for user-requested ones, only resize if final is true, or when
3101
resizing in redraw mode */
3102
send_resize_client = ((!user && resized) ||
3104
(resized && config_resize_redraw))));
3106
/* if the client is enlarging, then resize the client before the frame */
3107
if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
3108
XMoveResizeWindow(obt_display, self->window,
3109
self->frame->size.left, self->frame->size.top,
3110
MAX(w, oldclient.width), MAX(h, oldclient.height));
3111
frame_adjust_client_area(self->frame);
3114
/* find the frame's dimensions and move/resize it */
3118
/* if decorations changed, then readjust everything for the frame */
3119
if (self->decorations != fdecor ||
3120
self->max_horz != fhorz || self->max_vert != fvert)
3122
fmoved = fresized = TRUE;
3125
/* adjust the frame */
3126
if (fmoved || fresized) {
3127
gulong ignore_start;
3129
ignore_start = event_start_ignore_all_enters();
3131
/* replay pending pointer event before move the window, in case it
3132
would change what window gets the event */
3133
mouse_replay_pointer();
3135
frame_adjust_area(self->frame, fmoved, fresized, FALSE);
3138
event_end_ignore_all_enters(ignore_start);
3141
if (!user || final) {
3142
gint oldrx = self->root_pos.x;
3143
gint oldry = self->root_pos.y;
3144
/* we have reset the client to 0 border width, so don't include
3145
it in these coords */
3146
POINT_SET(self->root_pos,
3147
self->frame->area.x + self->frame->size.left -
3149
self->frame->area.y + self->frame->size.top -
3150
self->border_width);
3151
if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3155
/* This is kinda tricky and should not be changed.. let me explain!
3157
When user = FALSE, then the request is coming from the application
3158
itself, and we are more strict about when to send a synthetic
3159
ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
3160
in this case (or send one if force_reply is true)
3162
When user = TRUE, then the request is coming from "us", like when we
3163
maximize a window or something. In this case we are more lenient. We
3164
used to follow the same rules as above, but _Java_ Swing can't handle
3165
this. So just to appease Swing, when user = TRUE, we always send
3166
a synthetic ConfigureNotify to give the window its root coordinates.
3167
Lastly, if force_reply is TRUE, we always send a
3168
ConfigureNotify, which is needed during a resize with XSYNCronization.
3170
if ((!user && !resized && (rootmoved || force_reply)) ||
3171
(user && ((!resized && force_reply) || (final && rootmoved))))
3175
event.type = ConfigureNotify;
3176
event.xconfigure.display = obt_display;
3177
event.xconfigure.event = self->window;
3178
event.xconfigure.window = self->window;
3180
ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3181
self->title, self->root_pos.x, self->root_pos.y, w, h);
3183
/* root window real coords */
3184
event.xconfigure.x = self->root_pos.x;
3185
event.xconfigure.y = self->root_pos.y;
3186
event.xconfigure.width = w;
3187
event.xconfigure.height = h;
3188
event.xconfigure.border_width = self->border_width;
3189
event.xconfigure.above = None;
3190
event.xconfigure.override_redirect = FALSE;
3191
XSendEvent(event.xconfigure.display, event.xconfigure.window,
3192
FALSE, StructureNotifyMask, &event);
3195
/* if the client is shrinking, then resize the frame before the client.
3197
both of these resize sections may run, because the top one only resizes
3198
in the direction that is growing
3200
if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
3202
frame_adjust_client_area(self->frame);
3203
XMoveResizeWindow(obt_display, self->window,
3204
self->frame->size.left, self->frame->size.top, w, h);
3207
XFlush(obt_display);
3209
/* if it moved between monitors, then this can affect the stacking
3210
layer of this window or others - for fullscreen windows.
3211
also if it changed to/from oldschool fullscreen then its layer may
3214
watch out tho, don't try change stacking stuff if the window is no
3215
longer being managed !
3217
if (self->managed &&
3218
(screen_find_monitor(&self->frame->area) !=
3219
screen_find_monitor(&oldframe) ||
3220
(final && (client_is_oldfullscreen(self, &oldclient) !=
3221
client_is_oldfullscreen(self, &self->area)))))
3223
client_calc_layer(self);
3227
void client_fullscreen(ObClient *self, gboolean fs)
3231
if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3232
self->fullscreen == fs) return; /* already done */
3234
self->fullscreen = fs;
3235
client_change_state(self); /* change the state hints on the client */
3238
self->pre_fullscreen_area = self->area;
3239
self->pre_fullscreen_max_horz = self->max_horz;
3240
self->pre_fullscreen_max_vert = self->max_vert;
3242
/* if the window is maximized, its area isn't all that meaningful.
3243
save its premax area instead. */
3244
if (self->max_horz) {
3245
self->pre_fullscreen_area.x = self->pre_max_area.x;
3246
self->pre_fullscreen_area.width = self->pre_max_area.width;
3248
if (self->max_vert) {
3249
self->pre_fullscreen_area.y = self->pre_max_area.y;
3250
self->pre_fullscreen_area.height = self->pre_max_area.height;
3253
/* these will help configure_full figure out where to fullscreen
3257
w = self->area.width;
3258
h = self->area.height;
3260
g_assert(self->pre_fullscreen_area.width > 0 &&
3261
self->pre_fullscreen_area.height > 0);
3263
self->max_horz = self->pre_fullscreen_max_horz;
3264
self->max_vert = self->pre_fullscreen_max_vert;
3265
if (self->max_horz) {
3266
self->pre_max_area.x = self->pre_fullscreen_area.x;
3267
self->pre_max_area.width = self->pre_fullscreen_area.width;
3269
if (self->max_vert) {
3270
self->pre_max_area.y = self->pre_fullscreen_area.y;
3271
self->pre_max_area.height = self->pre_fullscreen_area.height;
3274
x = self->pre_fullscreen_area.x;
3275
y = self->pre_fullscreen_area.y;
3276
w = self->pre_fullscreen_area.width;
3277
h = self->pre_fullscreen_area.height;
3278
RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3281
ob_debug("Window %s going fullscreen (%d)",
3282
self->title, self->fullscreen);
3285
/* make sure the window is on some monitor */
3286
client_find_onscreen(self, &x, &y, w, h, FALSE);
3289
client_setup_decor_and_functions(self, FALSE);
3290
client_move_resize(self, x, y, w, h);
3292
/* and adjust our layer/stacking. do this after resizing the window,
3293
and applying decorations, because windows which fill the screen are
3294
considered "fullscreen" and it affects their layer */
3295
client_calc_layer(self);
3298
/* try focus us when we go into fullscreen mode */
3303
static void client_iconify_recursive(ObClient *self,
3304
gboolean iconic, gboolean curdesk,
3305
gboolean hide_animation)
3308
gboolean changed = FALSE;
3310
if (self->iconic != iconic) {
3311
ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3315
/* don't let non-normal windows iconify along with their parents
3317
if (client_normal(self)) {
3318
self->iconic = iconic;
3320
/* update the focus lists.. iconic windows go to the bottom of
3321
the list. this will also call focus_cycle_addremove(). */
3322
focus_order_to_bottom(self);
3327
self->iconic = iconic;
3329
if (curdesk && self->desktop != screen_desktop &&
3330
self->desktop != DESKTOP_ALL)
3331
client_set_desktop(self, screen_desktop, FALSE, FALSE);
3333
/* this puts it after the current focused window, this will
3334
also cause focus_cycle_addremove() to be called for the
3336
focus_order_like_new(self);
3343
client_change_state(self);
3344
if (config_animate_iconify && !hide_animation)
3345
frame_begin_iconify_animation(self->frame, iconic);
3346
/* do this after starting the animation so it doesn't flash */
3347
client_showhide(self);
3350
/* iconify all direct transients, and deiconify all transients
3352
for (it = self->transients; it; it = g_slist_next(it))
3353
if (it->data != self)
3354
if (client_is_direct_child(self, it->data) || !iconic)
3355
client_iconify_recursive(it->data, iconic, curdesk,
3359
void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3360
gboolean hide_animation)
3362
if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3363
/* move up the transient chain as far as possible first */
3364
self = client_search_top_direct_parent(self);
3365
client_iconify_recursive(self, iconic, curdesk, hide_animation);
3369
void client_maximize(ObClient *self, gboolean max, gint dir)
3373
g_assert(dir == 0 || dir == 1 || dir == 2);
3374
if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
3376
/* check if already done */
3378
if (dir == 0 && self->max_horz && self->max_vert) return;
3379
if (dir == 1 && self->max_horz) return;
3380
if (dir == 2 && self->max_vert) return;
3382
if (dir == 0 && !self->max_horz && !self->max_vert) return;
3383
if (dir == 1 && !self->max_horz) return;
3384
if (dir == 2 && !self->max_vert) return;
3387
/* these will help configure_full figure out which screen to fill with
3391
w = self->area.width;
3392
h = self->area.height;
3395
if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3396
RECT_SET(self->pre_max_area,
3397
self->area.x, self->pre_max_area.y,
3398
self->area.width, self->pre_max_area.height);
3400
if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3401
RECT_SET(self->pre_max_area,
3402
self->pre_max_area.x, self->area.y,
3403
self->pre_max_area.width, self->area.height);
3406
if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3407
g_assert(self->pre_max_area.width > 0);
3409
x = self->pre_max_area.x;
3410
w = self->pre_max_area.width;
3412
RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3413
0, self->pre_max_area.height);
3415
if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3416
g_assert(self->pre_max_area.height > 0);
3418
y = self->pre_max_area.y;
3419
h = self->pre_max_area.height;
3421
RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3422
self->pre_max_area.width, 0);
3426
if (dir == 0 || dir == 1) /* horz */
3427
self->max_horz = max;
3428
if (dir == 0 || dir == 2) /* vert */
3429
self->max_vert = max;
3432
/* make sure the window is on some monitor */
3433
client_find_onscreen(self, &x, &y, w, h, FALSE);
3436
client_change_state(self); /* change the state hints on the client */
3438
client_setup_decor_and_functions(self, FALSE);
3439
client_move_resize(self, x, y, w, h);
3442
void client_shade(ObClient *self, gboolean shade)
3444
if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3445
shade) || /* can't shade */
3446
self->shaded == shade) return; /* already done */
3448
self->shaded = shade;
3449
client_change_state(self);
3450
client_change_wm_state(self); /* the window is being hidden/shown */
3451
/* resize the frame to just the titlebar */
3452
frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3455
static void client_ping_event(ObClient *self, gboolean dead)
3457
if (self->not_responding != dead) {
3458
self->not_responding = dead;
3459
client_update_title(self);
3462
/* the client isn't responding, so ask to kill it */
3463
client_prompt_kill(self);
3465
/* it came back to life ! */
3467
if (self->kill_prompt) {
3468
prompt_unref(self->kill_prompt);
3469
self->kill_prompt = NULL;
3472
self->kill_level = 0;
3477
void client_close(ObClient *self)
3479
if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3481
/* if closing an internal obprompt, that is just cancelling it */
3483
prompt_cancel(self->prompt);
3487
/* in the case that the client provides no means to requesting that it
3488
close, we just kill it */
3489
if (!self->delete_window)
3490
/* don't use client_kill(), we should only kill based on PID in
3491
response to a lack of PING replies */
3492
XKillClient(obt_display, self->window);
3494
/* request the client to close with WM_DELETE_WINDOW */
3495
OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
3496
OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
3497
0, 0, 0, NoEventMask);
3499
/* we're trying to close the window, so see if it is responding. if it
3500
is not, then we will let them kill the window */
3502
ping_start(self, client_ping_event);
3504
/* if we already know the window isn't responding (maybe they clicked
3505
no in the kill dialog but it hasn't come back to life), then show
3507
if (self->not_responding)
3508
client_prompt_kill(self);
3512
#define OB_KILL_RESULT_NO 0
3513
#define OB_KILL_RESULT_YES 1
3515
static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
3517
ObClient *self = data;
3519
if (result == OB_KILL_RESULT_YES)
3521
return TRUE; /* call the cleanup func */
3524
static void client_kill_cleanup(ObPrompt *p, gpointer data)
3526
ObClient *self = data;
3528
g_assert(p == self->kill_prompt);
3530
prompt_unref(self->kill_prompt);
3531
self->kill_prompt = NULL;
3534
static void client_prompt_kill(ObClient *self)
3536
/* check if we're already prompting */
3537
if (!self->kill_prompt) {
3538
ObPromptAnswer answers[] = {
3539
{ 0, OB_KILL_RESULT_NO },
3540
{ 0, OB_KILL_RESULT_YES }
3543
const gchar *y, *title;
3545
title = self->original_title;
3546
if (title[0] == '\0') {
3547
/* empty string, so use its parent */
3548
ObClient *p = client_search_top_direct_parent(self);
3549
if (p) title = p->original_title;
3552
if (client_on_localhost(self)) {
3555
if (self->kill_level == 0)
3561
(_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
3563
y = _("End Process");
3567
(_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
3569
y = _("Disconnect");
3571
/* set the dialog buttons' text */
3572
answers[0].text = _("Cancel"); /* "no" */
3573
answers[1].text = y; /* "yes" */
3575
self->kill_prompt = prompt_new(m, NULL, answers,
3576
sizeof(answers)/sizeof(answers[0]),
3577
OB_KILL_RESULT_NO, /* default = no */
3578
OB_KILL_RESULT_NO, /* cancel = no */
3579
client_kill_requested,
3580
client_kill_cleanup,
3585
prompt_show(self->kill_prompt, self, TRUE);
3588
void client_kill(ObClient *self)
3590
/* don't kill our own windows */
3591
if (self->prompt) return;
3593
if (client_on_localhost(self) && self->pid) {
3594
/* running on the local host */
3595
if (self->kill_level == 0) {
3596
ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3597
self->window, self->pid);
3598
kill(self->pid, SIGTERM);
3601
/* show that we're trying to kill it */
3602
client_update_title(self);
3605
ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3606
self->window, self->pid);
3607
kill(self->pid, SIGKILL); /* kill -9 */
3611
/* running on a remote host */
3612
XKillClient(obt_display, self->window);
3616
void client_hilite(ObClient *self, gboolean hilite)
3618
if (self->demands_attention == hilite)
3619
return; /* no change */
3621
/* don't allow focused windows to hilite */
3622
self->demands_attention = hilite && !client_focused(self);
3623
if (self->frame != NULL) { /* if we're mapping, just set the state */
3624
if (self->demands_attention) {
3625
frame_flash_start(self->frame);
3627
/* if the window is on another desktop then raise it and make it
3628
the most recently used window */
3629
if (self->desktop != screen_desktop &&
3630
self->desktop != DESKTOP_ALL)
3632
stacking_raise(CLIENT_AS_WINDOW(self));
3633
focus_order_to_top(self);
3637
frame_flash_stop(self->frame);
3638
client_change_state(self);
3642
static void client_set_desktop_recursive(ObClient *self,
3650
if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3652
ob_debug("Setting desktop %u", target+1);
3654
g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3656
old = self->desktop;
3657
self->desktop = target;
3658
OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target);
3659
/* the frame can display the current desktop state */
3660
frame_adjust_state(self->frame);
3661
/* 'move' the window to the new desktop */
3665
/* raise if it was not already on the desktop */
3666
if (old != DESKTOP_ALL && !dontraise)
3667
stacking_raise(CLIENT_AS_WINDOW(self));
3668
if (STRUT_EXISTS(self->strut))
3669
screen_update_areas();
3671
/* the new desktop's geometry may be different, so we may need to
3672
resize, for example if we are maximized */
3673
client_reconfigure(self, FALSE);
3675
focus_cycle_addremove(self, FALSE);
3678
/* move all transients */
3679
for (it = self->transients; it; it = g_slist_next(it))
3680
if (it->data != self)
3681
if (client_is_direct_child(self, it->data))
3682
client_set_desktop_recursive(it->data, target,
3683
donthide, dontraise);
3686
void client_set_desktop(ObClient *self, guint target,
3687
gboolean donthide, gboolean dontraise)
3689
self = client_search_top_direct_parent(self);
3690
client_set_desktop_recursive(self, target, donthide, dontraise);
3692
focus_cycle_addremove(NULL, TRUE);
3695
gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3697
while (child != parent && (child = client_direct_parent(child)));
3698
return child == parent;
3701
ObClient *client_search_modal_child(ObClient *self)
3706
for (it = self->transients; it; it = g_slist_next(it)) {
3707
ObClient *c = it->data;
3708
if ((ret = client_search_modal_child(c))) return ret;
3709
if (c->modal) return c;
3714
struct ObClientFindDestroyUnmap {
3719
static gboolean find_destroy_unmap(XEvent *e, gpointer data)
3721
struct ObClientFindDestroyUnmap *find = data;
3722
if (e->type == DestroyNotify)
3723
return e->xdestroywindow.window == find->window;
3724
if (e->type == UnmapNotify && e->xunmap.window == find->window)
3725
/* ignore the first $find->ignore_unmaps$ many unmap events */
3726
return --find->ignore_unmaps < 0;
3730
gboolean client_validate(ObClient *self)
3732
struct ObClientFindDestroyUnmap find;
3734
XSync(obt_display, FALSE); /* get all events on the server */
3736
find.window = self->window;
3737
find.ignore_unmaps = self->ignore_unmaps;
3738
if (xqueue_exists_local(find_destroy_unmap, &find))
3744
void client_set_wm_state(ObClient *self, glong state)
3746
if (state == self->wmstate) return; /* no change */
3750
client_iconify(self, TRUE, TRUE, FALSE);
3753
client_iconify(self, FALSE, TRUE, FALSE);
3758
void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3760
gboolean shaded = self->shaded;
3761
gboolean fullscreen = self->fullscreen;
3762
gboolean undecorated = self->undecorated;
3763
gboolean max_horz = self->max_horz;
3764
gboolean max_vert = self->max_vert;
3765
gboolean modal = self->modal;
3766
gboolean iconic = self->iconic;
3767
gboolean demands_attention = self->demands_attention;
3768
gboolean above = self->above;
3769
gboolean below = self->below;
3773
if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3774
action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3775
action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3776
/* an invalid action was passed to the client message, ignore it */
3779
for (i = 0; i < 2; ++i) {
3780
Atom state = i == 0 ? data1 : data2;
3782
if (!state) continue;
3784
/* if toggling, then pick whether we're adding or removing */
3785
if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3786
if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3788
else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3789
value = self->max_vert;
3790
else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3791
value = self->max_horz;
3792
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3794
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3795
value = self->skip_taskbar;
3796
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3797
value = self->skip_pager;
3798
else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3799
value = self->iconic;
3800
else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3802
else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3803
value = self->above;
3804
else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3805
value = self->below;
3806
else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3807
value = self->demands_attention;
3808
else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3809
value = undecorated;
3811
g_assert_not_reached();
3812
action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3813
OBT_PROP_ATOM(NET_WM_STATE_ADD);
3816
value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
3817
if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3819
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3821
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3823
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3825
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3826
self->skip_taskbar = value;
3827
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3828
self->skip_pager = value;
3829
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3831
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3833
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3835
/* only unset below when setting above, otherwise you can't get to
3839
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3840
/* and vice versa */
3844
} else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3845
demands_attention = value;
3846
} else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3847
undecorated = value;
3851
if (max_horz != self->max_horz || max_vert != self->max_vert) {
3852
if (max_horz != self->max_horz && max_vert != self->max_vert) {
3854
if (max_horz == max_vert) { /* both going the same way */
3855
client_maximize(self, max_horz, 0);
3857
client_maximize(self, max_horz, 1);
3858
client_maximize(self, max_vert, 2);
3862
if (max_horz != self->max_horz)
3863
client_maximize(self, max_horz, 1);
3865
client_maximize(self, max_vert, 2);
3868
/* change fullscreen state before shading, as it will affect if the window
3870
if (fullscreen != self->fullscreen)
3871
client_fullscreen(self, fullscreen);
3872
if (shaded != self->shaded)
3873
client_shade(self, shaded);
3874
if (undecorated != self->undecorated)
3875
client_set_undecorated(self, undecorated);
3876
if (above != self->above || below != self->below) {
3877
self->above = above;
3878
self->below = below;
3879
client_calc_layer(self);
3882
if (modal != self->modal) {
3883
self->modal = modal;
3884
/* when a window changes modality, then its stacking order with its
3885
transients needs to change */
3886
stacking_raise(CLIENT_AS_WINDOW(self));
3888
/* it also may get focused. if something is focused that shouldn't
3889
be focused anymore, then move the focus */
3890
if (focus_client && client_focus_target(focus_client) != focus_client)
3891
client_focus(focus_client);
3894
if (iconic != self->iconic)
3895
client_iconify(self, iconic, FALSE, FALSE);
3897
if (demands_attention != self->demands_attention)
3898
client_hilite(self, demands_attention);
3900
client_change_state(self); /* change the hint to reflect these changes */
3902
focus_cycle_addremove(self, TRUE);
3905
ObClient *client_focus_target(ObClient *self)
3907
ObClient *child = NULL;
3909
child = client_search_modal_child(self);
3910
if (child) return child;
3914
gboolean client_can_focus(ObClient *self)
3916
/* choose the correct target */
3917
self = client_focus_target(self);
3919
if (!self->frame->visible)
3922
if (!(self->can_focus || self->focus_notify))
3928
gboolean client_focus(ObClient *self)
3930
if (!client_validate(self)) return FALSE;
3932
/* we might not focus this window, so if we have modal children which would
3933
be focused instead, bring them to this desktop */
3934
client_bring_modal_windows(self);
3936
/* choose the correct target */
3937
self = client_focus_target(self);
3939
if (!client_can_focus(self)) {
3940
ob_debug_type(OB_DEBUG_FOCUS,
3941
"Client %s can't be focused", self->title);
3945
ob_debug_type(OB_DEBUG_FOCUS,
3946
"Focusing client \"%s\" (0x%x) at time %u",
3947
self->title, self->window, event_time());
3949
/* if using focus_delay, stop the timer now so that focus doesn't
3951
event_halt_focus_delay();
3953
obt_display_ignore_errors(TRUE);
3955
if (self->can_focus) {
3956
/* This can cause a BadMatch error with CurrentTime, or if an app
3957
passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3958
XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
3962
if (self->focus_notify) {
3964
ce.xclient.type = ClientMessage;
3965
ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3966
ce.xclient.display = obt_display;
3967
ce.xclient.window = self->window;
3968
ce.xclient.format = 32;
3969
ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3970
ce.xclient.data.l[1] = event_time();
3971
ce.xclient.data.l[2] = 0l;
3972
ce.xclient.data.l[3] = 0l;
3973
ce.xclient.data.l[4] = 0l;
3974
XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce);
3977
obt_display_ignore_errors(FALSE);
3979
ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3980
obt_display_error_occured);
3981
return !obt_display_error_occured;
3984
static void client_present(ObClient *self, gboolean here, gboolean raise,
3987
if (client_normal(self) && screen_showing_desktop)
3988
screen_show_desktop(FALSE, self);
3990
client_iconify(self, FALSE, here, FALSE);
3991
if (self->desktop != DESKTOP_ALL &&
3992
self->desktop != screen_desktop)
3995
client_set_desktop(self, screen_desktop, FALSE, TRUE);
3997
screen_set_desktop(self->desktop, FALSE);
3998
} else if (!self->frame->visible)
3999
/* if its not visible for other reasons, then don't mess
4002
if (self->shaded && unshade)
4003
client_shade(self, FALSE);
4005
stacking_raise(CLIENT_AS_WINDOW(self));
4010
/* this function exists to map to the net_active_window message in the ewmh */
4011
void client_activate(ObClient *self, gboolean desktop,
4012
gboolean here, gboolean raise,
4013
gboolean unshade, gboolean user)
4015
self = client_focus_target(self);
4017
if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
4018
client_present(self, here, raise, unshade);
4020
client_hilite(self, TRUE);
4023
static void client_bring_windows_recursive(ObClient *self,
4031
for (it = self->transients; it; it = g_slist_next(it))
4032
client_bring_windows_recursive(it->data, desktop,
4033
helpers, modals, iconic);
4035
if (((helpers && client_helper(self)) ||
4036
(modals && self->modal)) &&
4037
((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
4038
(iconic && self->iconic)))
4040
if (iconic && self->iconic)
4041
client_iconify(self, FALSE, TRUE, FALSE);
4043
client_set_desktop(self, desktop, FALSE, FALSE);
4047
void client_bring_helper_windows(ObClient *self)
4049
client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
4052
void client_bring_modal_windows(ObClient *self)
4054
client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
4057
gboolean client_focused(ObClient *self)
4059
return self == focus_client;
4062
RrImage* client_icon(ObClient *self)
4064
RrImage *ret = NULL;
4067
ret = self->icon_set;
4068
else if (self->parents) {
4070
for (it = self->parents; it && !ret; it = g_slist_next(it))
4071
ret = client_icon(it->data);
4074
ret = client_default_icon;
4078
void client_set_layer(ObClient *self, gint layer)
4082
self->above = FALSE;
4083
} else if (layer == 0) {
4084
self->below = self->above = FALSE;
4086
self->below = FALSE;
4089
client_calc_layer(self);
4090
client_change_state(self); /* reflect this in the state hints */
4093
void client_set_undecorated(ObClient *self, gboolean undecorated)
4095
if (self->undecorated != undecorated &&
4096
/* don't let it undecorate if the function is missing, but let
4098
(self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4100
self->undecorated = undecorated;
4101
client_setup_decor_and_functions(self, TRUE);
4102
client_change_state(self); /* reflect this in the state hints */
4106
guint client_monitor(ObClient *self)
4108
return screen_find_monitor(&self->frame->area);
4111
ObClient *client_direct_parent(ObClient *self)
4113
if (!self->parents) return NULL;
4114
if (self->transient_for_group) return NULL;
4115
return self->parents->data;
4118
ObClient *client_search_top_direct_parent(ObClient *self)
4121
while ((p = client_direct_parent(self))) self = p;
4125
static GSList *client_search_all_top_parents_internal(ObClient *self,
4127
ObStackingLayer layer)
4132
/* move up the direct transient chain as far as possible */
4133
while ((p = client_direct_parent(self)) &&
4134
(!bylayer || p->layer == layer))
4138
ret = g_slist_prepend(NULL, self);
4140
ret = g_slist_copy(self->parents);
4145
GSList *client_search_all_top_parents(ObClient *self)
4147
return client_search_all_top_parents_internal(self, FALSE, 0);
4150
GSList *client_search_all_top_parents_layer(ObClient *self)
4152
return client_search_all_top_parents_internal(self, TRUE, self->layer);
4155
ObClient *client_search_focus_parent(ObClient *self)
4159
for (it = self->parents; it; it = g_slist_next(it))
4160
if (client_focused(it->data)) return it->data;
4165
ObClient *client_search_focus_parent_full(ObClient *self)
4168
ObClient *ret = NULL;
4170
for (it = self->parents; it; it = g_slist_next(it)) {
4171
if (client_focused(it->data))
4174
ret = client_search_focus_parent_full(it->data);
4180
ObClient *client_search_parent(ObClient *self, ObClient *search)
4184
for (it = self->parents; it; it = g_slist_next(it))
4185
if (it->data == search) return search;
4190
ObClient *client_search_transient(ObClient *self, ObClient *search)
4194
for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4195
if (sit->data == search)
4197
if (client_search_transient(sit->data, search))
4203
static void detect_edge(Rect area, ObDirection dir,
4204
gint my_head, gint my_size,
4205
gint my_edge_start, gint my_edge_size,
4206
gint *dest, gboolean *near_edge)
4208
gint edge_start, edge_size, head, tail;
4209
gboolean skip_head = FALSE, skip_tail = FALSE;
4212
case OB_DIRECTION_NORTH:
4213
case OB_DIRECTION_SOUTH:
4214
edge_start = area.x;
4215
edge_size = area.width;
4217
case OB_DIRECTION_EAST:
4218
case OB_DIRECTION_WEST:
4219
edge_start = area.y;
4220
edge_size = area.height;
4223
g_assert_not_reached();
4226
/* do we collide with this window? */
4227
if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4228
edge_start, edge_size))
4232
case OB_DIRECTION_NORTH:
4233
head = RECT_BOTTOM(area);
4234
tail = RECT_TOP(area);
4236
case OB_DIRECTION_SOUTH:
4237
head = RECT_TOP(area);
4238
tail = RECT_BOTTOM(area);
4240
case OB_DIRECTION_WEST:
4241
head = RECT_RIGHT(area);
4242
tail = RECT_LEFT(area);
4244
case OB_DIRECTION_EAST:
4245
head = RECT_LEFT(area);
4246
tail = RECT_RIGHT(area);
4249
g_assert_not_reached();
4252
case OB_DIRECTION_NORTH:
4253
case OB_DIRECTION_WEST:
4254
/* check if our window is past the head of this window */
4255
if (my_head <= head + 1)
4257
/* check if our window's tail is past the tail of this window */
4258
if (my_head + my_size - 1 <= tail)
4260
/* check if the head of this window is closer than the previously
4261
chosen edge (take into account that the previously chosen
4262
edge might have been a tail, not a head) */
4263
if (head + (*near_edge ? 0 : my_size) <= *dest)
4265
/* check if the tail of this window is closer than the previously
4266
chosen edge (take into account that the previously chosen
4267
edge might have been a head, not a tail) */
4268
if (tail - (!*near_edge ? 0 : my_size) <= *dest)
4271
case OB_DIRECTION_SOUTH:
4272
case OB_DIRECTION_EAST:
4273
/* check if our window is past the head of this window */
4274
if (my_head >= head - 1)
4276
/* check if our window's tail is past the tail of this window */
4277
if (my_head - my_size + 1 >= tail)
4279
/* check if the head of this window is closer than the previously
4280
chosen edge (take into account that the previously chosen
4281
edge might have been a tail, not a head) */
4282
if (head - (*near_edge ? 0 : my_size) >= *dest)
4284
/* check if the tail of this window is closer than the previously
4285
chosen edge (take into account that the previously chosen
4286
edge might have been a head, not a tail) */
4287
if (tail + (!*near_edge ? 0 : my_size) >= *dest)
4291
g_assert_not_reached();
4294
ob_debug("my head %d size %d", my_head, my_size);
4295
ob_debug("head %d tail %d dest %d", head, tail, *dest);
4297
ob_debug("using near edge %d", head);
4301
else if (!skip_tail) {
4302
ob_debug("using far edge %d", tail);
4308
void client_find_edge_directional(ObClient *self, ObDirection dir,
4309
gint my_head, gint my_size,
4310
gint my_edge_start, gint my_edge_size,
4311
gint *dest, gboolean *near_edge)
4319
a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4320
&self->frame->area);
4323
case OB_DIRECTION_NORTH:
4324
edge = RECT_TOP(*a) - 1;
4326
case OB_DIRECTION_SOUTH:
4327
edge = RECT_BOTTOM(*a) + 1;
4329
case OB_DIRECTION_EAST:
4330
edge = RECT_RIGHT(*a) + 1;
4332
case OB_DIRECTION_WEST:
4333
edge = RECT_LEFT(*a) - 1;
4336
g_assert_not_reached();
4338
/* default to the far edge, then narrow it down */
4342
/* search for edges of monitors */
4343
for (i = 0; i < screen_num_monitors; ++i) {
4344
Rect *area = screen_area(self->desktop, i, NULL);
4345
detect_edge(*area, dir, my_head, my_size, my_edge_start,
4346
my_edge_size, dest, near_edge);
4347
g_slice_free(Rect, area);
4350
/* search for edges of clients */
4351
for (it = client_list; it; it = g_list_next(it)) {
4352
ObClient *cur = it->data;
4354
/* skip windows to not bump into */
4359
if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4360
cur->desktop != screen_desktop)
4363
ob_debug("trying window %s", cur->title);
4365
detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
4366
my_edge_size, dest, near_edge);
4368
dock_get_area(&dock_area);
4369
detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4370
my_edge_size, dest, near_edge);
4372
g_slice_free(Rect, a);
4375
void client_find_move_directional(ObClient *self, ObDirection dir,
4379
gint e, e_start, e_size;
4383
case OB_DIRECTION_EAST:
4384
head = RECT_RIGHT(self->frame->area);
4385
size = self->frame->area.width;
4386
e_start = RECT_TOP(self->frame->area);
4387
e_size = self->frame->area.height;
4389
case OB_DIRECTION_WEST:
4390
head = RECT_LEFT(self->frame->area);
4391
size = self->frame->area.width;
4392
e_start = RECT_TOP(self->frame->area);
4393
e_size = self->frame->area.height;
4395
case OB_DIRECTION_NORTH:
4396
head = RECT_TOP(self->frame->area);
4397
size = self->frame->area.height;
4398
e_start = RECT_LEFT(self->frame->area);
4399
e_size = self->frame->area.width;
4401
case OB_DIRECTION_SOUTH:
4402
head = RECT_BOTTOM(self->frame->area);
4403
size = self->frame->area.height;
4404
e_start = RECT_LEFT(self->frame->area);
4405
e_size = self->frame->area.width;
4408
g_assert_not_reached();
4411
client_find_edge_directional(self, dir, head, size,
4412
e_start, e_size, &e, &near);
4413
*x = self->frame->area.x;
4414
*y = self->frame->area.y;
4416
case OB_DIRECTION_EAST:
4417
if (near) e -= self->frame->area.width;
4421
case OB_DIRECTION_WEST:
4423
else e -= self->frame->area.width;
4426
case OB_DIRECTION_NORTH:
4428
else e -= self->frame->area.height;
4431
case OB_DIRECTION_SOUTH:
4432
if (near) e -= self->frame->area.height;
4437
g_assert_not_reached();
4439
frame_frame_gravity(self->frame, x, y);
4442
void client_find_resize_directional(ObClient *self, ObDirection side,
4444
gint *x, gint *y, gint *w, gint *h)
4447
gint e, e_start, e_size, delta;
4452
case OB_DIRECTION_EAST:
4453
head = RECT_RIGHT(self->frame->area) +
4454
(self->size_inc.width - 1) * (grow ? 1 : 0);
4455
e_start = RECT_TOP(self->frame->area);
4456
e_size = self->frame->area.height;
4457
dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4459
case OB_DIRECTION_WEST:
4460
head = RECT_LEFT(self->frame->area) -
4461
(self->size_inc.width - 1) * (grow ? 1 : 0);
4462
e_start = RECT_TOP(self->frame->area);
4463
e_size = self->frame->area.height;
4464
dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4466
case OB_DIRECTION_NORTH:
4467
head = RECT_TOP(self->frame->area) -
4468
(self->size_inc.height - 1) * (grow ? 1 : 0);
4469
e_start = RECT_LEFT(self->frame->area);
4470
e_size = self->frame->area.width;
4471
dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4473
case OB_DIRECTION_SOUTH:
4474
head = RECT_BOTTOM(self->frame->area) +
4475
(self->size_inc.height - 1) * (grow ? 1 : 0);
4476
e_start = RECT_LEFT(self->frame->area);
4477
e_size = self->frame->area.width;
4478
dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4481
g_assert_not_reached();
4484
ob_debug("head %d dir %d", head, dir);
4485
client_find_edge_directional(self, dir, head, 1,
4486
e_start, e_size, &e, &near);
4487
ob_debug("edge %d", e);
4488
*x = self->frame->area.x;
4489
*y = self->frame->area.y;
4490
*w = self->frame->area.width;
4491
*h = self->frame->area.height;
4493
case OB_DIRECTION_EAST:
4494
if (grow == near) --e;
4495
delta = e - RECT_RIGHT(self->frame->area);
4498
case OB_DIRECTION_WEST:
4499
if (grow == near) ++e;
4500
delta = RECT_LEFT(self->frame->area) - e;
4504
case OB_DIRECTION_NORTH:
4505
if (grow == near) ++e;
4506
delta = RECT_TOP(self->frame->area) - e;
4510
case OB_DIRECTION_SOUTH:
4511
if (grow == near) --e;
4512
delta = e - RECT_BOTTOM(self->frame->area);
4516
g_assert_not_reached();
4518
frame_frame_gravity(self->frame, x, y);
4519
*w -= self->frame->size.left + self->frame->size.right;
4520
*h -= self->frame->size.top + self->frame->size.bottom;
4523
ObClient* client_under_pointer(void)
4527
ObClient *ret = NULL;
4529
if (screen_pointer_pos(&x, &y)) {
4530
for (it = stacking_list; it; it = g_list_next(it)) {
4531
if (WINDOW_IS_CLIENT(it->data)) {
4532
ObClient *c = WINDOW_AS_CLIENT(it->data);
4533
if (c->frame->visible &&
4534
/* check the desktop, this is done during desktop
4535
switching and windows are shown/hidden status is not
4537
(c->desktop == screen_desktop ||
4538
c->desktop == DESKTOP_ALL) &&
4539
/* ignore all animating windows */
4540
!frame_iconify_animating(c->frame) &&
4541
RECT_CONTAINS(c->frame->area, x, y))
4552
gboolean client_has_group_siblings(ObClient *self)
4554
return self->group && self->group->members->next;
4557
gboolean client_has_relative(ObClient *self)
4559
return client_has_parent(self) ||
4560
client_has_group_siblings(self) ||
4561
client_has_children(self);
4564
/*! Returns TRUE if the client is running on the same machine as Openbox */
4565
gboolean client_on_localhost(ObClient *self)
4567
return self->client_machine == NULL;