1 /* evilwm - Minimalist Window Manager for X
2 * Copyright (C) 1999-2009 Ciaran Anscomb <evilwm@6809.org.uk>
3 * see README for license and other details. */
11 #define MAXIMUM_PROPERTY_LENGTH 4096
13 static void init_geometry(Client
*c
);
14 static void reparent(Client
*c
);
15 static void *get_property(Window w
, Atom property
, Atom req_type
,
16 unsigned long *nitems_return
);
18 static const char *map_state_string(int map_state
);
19 static const char *gravity_string(int gravity
);
20 static void debug_wm_normal_hints(XSizeHints
*size
);
22 # define debug_wm_normal_hints(s)
25 void make_new_client(Window w
, ScreenInfo
*s
) {
30 LOG_ENTER("make_new_client(window=%lx, screen=%d)", w
, s
->screen
);
34 /* First a bit of interaction with the error handler due to X's
35 * tendency to batch event notifications. We set a global variable to
36 * the id of the window we're initialising then do simple X call on
37 * that window. If an error is raised by this (and nothing else should
38 * do so as we've grabbed the server), the error handler resets the
39 * variable indicating the window has already disappeared, so we stop
40 * trying to manage it. */
42 XFetchName(dpy
, w
, &name
);
44 /* If 'initialising' is now set to None, that means doing the
45 * XFetchName raised BadWindow - the window has been removed before
46 * we got a chance to grab the server. */
47 if (initialising
== None
) {
48 LOG_DEBUG("XError occurred for initialising window - aborting...\n");
54 LOG_DEBUG("name=%s\n", name
? name
: "Untitled");
58 c
= malloc(sizeof(Client
));
59 /* Don't crash the window manager, just fail the operation. */
61 LOG_ERROR("out of memory in new_client; limping onward\n");
65 clients_tab_order
= list_prepend(clients_tab_order
, c
);
66 clients_mapping_order
= list_append(clients_mapping_order
, c
);
67 clients_stacking_order
= list_append(clients_stacking_order
, c
);
74 /* Ungrab the X server as soon as possible. Now that the client is
75 * malloc()ed and attached to the list, it is safe for any subsequent
76 * X calls to raise an X error and thus flag it for removal. */
87 for (iter
= clients_tab_order
; iter
; iter
= iter
->next
)
89 LOG_DEBUG("new window %dx%d+%d+%d, wincount=%d\n", c
->width
, c
->height
, c
->x
, c
->y
, i
);
93 XSelectInput(dpy
, c
->window
, ColormapChangeMask
| EnterWindowMask
| PropertyChangeMask
);
99 XShapeSelectInput(dpy
, c
->window
, ShapeNotifyMask
);
104 /* Read instance/class information for client and check against list
105 * built with -app options */
106 class = XAllocClassHint();
108 struct list
*aiter
= applications
;
109 XGetClassHint(dpy
, w
, class);
111 Application
*a
= aiter
->data
;
112 if ((!a
->res_name
|| (class->res_name
&& !strcmp(class->res_name
, a
->res_name
)))
113 && (!a
->res_class
|| (class->res_class
&& !strcmp(class->res_class
, a
->res_class
)))) {
114 if (a
->geometry_mask
& WidthValue
)
115 c
->width
= a
->width
* c
->width_inc
;
116 if (a
->geometry_mask
& HeightValue
)
117 c
->height
= a
->height
* c
->height_inc
;
118 if (a
->geometry_mask
& XValue
) {
119 if (a
->geometry_mask
& XNegative
)
120 c
->x
= a
->x
+ DisplayWidth(dpy
, s
->screen
)-c
->width
-c
->border
;
122 c
->x
= a
->x
+ c
->border
;
124 if (a
->geometry_mask
& YValue
) {
125 if (a
->geometry_mask
& YNegative
)
126 c
->y
= a
->y
+ DisplayHeight(dpy
, s
->screen
)-c
->height
-c
->border
;
128 c
->y
= a
->y
+ c
->border
;
131 if (a
->is_dock
) c
->is_dock
= 1;
133 if (a
->vdesk
!= VDESK_NONE
) c
->vdesk
= a
->vdesk
;
138 XFree(class->res_name
);
139 XFree(class->res_class
);
143 ewmh_set_net_client_list(c
->screen
);
145 /* Only map the window frame (and thus the window) if it's supposed
146 * to be visible on this virtual desktop. */
148 if (is_fixed(c
) || c
->vdesk
== s
->vdesk
)
154 discard_enter_events();
159 set_wm_state(c
, IconicState
);
161 ewmh_set_net_wm_desktop(c
);
166 /* Calls XGetWindowAttributes, XGetWMHints and XGetWMNormalHints to determine
167 * window's initial geometry.
169 * XGetWindowAttributes
171 static void init_geometry(Client
*c
) {
173 XWindowAttributes attr
;
174 unsigned long *eprop
;
175 unsigned long nitems
;
178 unsigned long *lprop
;
181 if ( (mprop
= get_property(c
->window
, mwm_hints
, mwm_hints
, &nitems
)) ) {
182 if (nitems
>= PROP_MWM_HINTS_ELEMENTS
183 && (mprop
->flags
& MWM_HINTS_DECORATIONS
)
184 && !(mprop
->decorations
& MWM_DECOR_ALL
)
185 && !(mprop
->decorations
& MWM_DECOR_BORDER
)) {
192 c
->vdesk
= c
->screen
->vdesk
;
193 if ( (lprop
= get_property(c
->window
, xa_net_wm_desktop
, XA_CARDINAL
, &nitems
)) ) {
194 if (nitems
&& valid_vdesk(lprop
[0]))
202 /* Get current window attributes */
203 LOG_XENTER("XGetWindowAttributes(window=%lx)", c
->window
);
204 XGetWindowAttributes(dpy
, c
->window
, &attr
);
205 LOG_XDEBUG("(%s) %dx%d+%d+%d, bw = %d\n", map_state_string(attr
.map_state
), attr
.width
, attr
.height
, attr
.x
, attr
.y
, attr
.border_width
);
207 c
->old_border
= attr
.border_width
;
208 c
->oldw
= c
->oldh
= 0;
209 c
->cmap
= attr
.colormap
;
211 if ( (eprop
= get_property(c
->window
, xa_evilwm_unmaximised_horz
, XA_CARDINAL
, &nitems
)) ) {
218 if ( (eprop
= get_property(c
->window
, xa_evilwm_unmaximised_vert
, XA_CARDINAL
, &nitems
)) ) {
226 size_flags
= get_wm_normal_hints(c
);
228 if ((attr
.width
>= c
->min_width
) && (attr
.height
>= c
->min_height
)) {
229 /* if (attr.map_state == IsViewable || (size_flags & (PSize | USSize))) { */
230 c
->width
= attr
.width
;
231 c
->height
= attr
.height
;
233 c
->width
= c
->min_width
;
234 c
->height
= c
->min_height
;
237 if ((attr
.map_state
== IsViewable
)
238 || (size_flags
& (/*PPosition |*/ USPosition
))) {
243 int xmax
= DisplayWidth(dpy
, c
->screen
->screen
);
244 int ymax
= DisplayHeight(dpy
, c
->screen
->screen
);
246 get_mouse_position(&x
, &y
, c
->screen
->root
);
247 c
->x
= (x
* (xmax
- c
->border
- c
->width
)) / xmax
;
248 c
->y
= (y
* (ymax
- c
->border
- c
->height
)) / ymax
;
250 c
->x
= c
->y
= c
->border
;
255 LOG_DEBUG("window started as %dx%d +%d+%d\n", c
->width
, c
->height
, c
->x
, c
->y
);
256 if (attr
.map_state
== IsViewable
) {
257 /* The reparent that is to come would trigger an unmap event */
263 static void reparent(Client
*c
) {
264 XSetWindowAttributes p_attr
;
266 p_attr
.border_pixel
= c
->screen
->bg
.pixel
;
267 p_attr
.override_redirect
= True
;
268 p_attr
.event_mask
= ChildMask
| ButtonPressMask
| EnterWindowMask
;
269 c
->parent
= XCreateWindow(dpy
, c
->screen
->root
, c
->x
-c
->border
, c
->y
-c
->border
,
270 c
->width
, c
->height
, c
->border
,
271 DefaultDepth(dpy
, c
->screen
->screen
), CopyFromParent
,
272 DefaultVisual(dpy
, c
->screen
->screen
),
273 CWOverrideRedirect
| CWBorderPixel
| CWEventMask
, &p_attr
);
275 XAddToSaveSet(dpy
, c
->window
);
276 XSetWindowBorderWidth(dpy
, c
->window
, 0);
277 XReparentWindow(dpy
, c
->window
, c
->parent
, 0, 0);
278 XMapWindow(dpy
, c
->window
);
280 grab_button(c
->parent
, grabmask2
, AnyButton
);
284 /* Get WM_NORMAL_HINTS property */
285 long get_wm_normal_hints(Client
*c
) {
289 size
= XAllocSizeHints();
291 LOG_XENTER("XGetWMNormalHints(window=%lx)", c
->window
);
292 XGetWMNormalHints(dpy
, c
->window
, size
, &dummy
);
293 debug_wm_normal_hints(size
);
296 if (flags
& PMinSize
) {
297 c
->min_width
= size
->min_width
;
298 c
->min_height
= size
->min_height
;
300 c
->min_width
= c
->min_height
= 0;
302 if (flags
& PMaxSize
) {
303 c
->max_width
= size
->max_width
;
304 c
->max_height
= size
->max_height
;
306 c
->max_width
= c
->max_height
= 0;
308 if (flags
& PBaseSize
) {
309 c
->base_width
= size
->base_width
;
310 c
->base_height
= size
->base_height
;
312 c
->base_width
= c
->min_width
;
313 c
->base_height
= c
->min_height
;
315 c
->width_inc
= c
->height_inc
= 1;
316 if (flags
& PResizeInc
) {
317 c
->width_inc
= size
->width_inc
? size
->width_inc
: 1;
318 c
->height_inc
= size
->height_inc
? size
->height_inc
: 1;
320 if (!(flags
& PMinSize
)) {
321 c
->min_width
= c
->base_width
+ c
->width_inc
;
322 c
->min_height
= c
->base_height
+ c
->height_inc
;
324 if (flags
& PWinGravity
) {
325 c
->win_gravity_hint
= size
->win_gravity
;
327 c
->win_gravity_hint
= NorthWestGravity
;
329 c
->win_gravity
= c
->win_gravity_hint
;
334 /* Determine if client thinks of itself as a dock */
335 void get_window_type(Client
*c
) {
337 unsigned long nitems
, i
;
339 if ( (aprop
= get_property(c
->window
, xa_net_wm_window_type
, XA_ATOM
, &nitems
)) ) {
340 for (i
= 0; i
< nitems
; i
++) {
341 if (aprop
[i
] == xa_net_wm_window_type_dock
)
348 static void *get_property(Window w
, Atom property
, Atom req_type
,
349 unsigned long *nitems_return
) {
352 unsigned long bytes_after
;
354 if (XGetWindowProperty(dpy
, w
, property
,
355 0L, MAXIMUM_PROPERTY_LENGTH
/ 4, False
,
356 req_type
, &actual_type
, &actual_format
,
357 nitems_return
, &bytes_after
, &prop
)
359 if (actual_type
== req_type
)
367 static const char *map_state_string(int map_state
) {
368 const char *map_states
[4] = {
374 return ((unsigned int)map_state
< 3)
375 ? map_states
[map_state
]
379 static const char *gravity_string(int gravity
) {
380 const char *gravities
[12] = {
394 return ((unsigned int)gravity
< 11) ? gravities
[gravity
] : gravities
[11];
397 static void debug_wm_normal_hints(XSizeHints
*size
) {
398 if (size
->flags
& 15) {
400 if (size
->flags
& USPosition
) {
401 LOG_XDEBUG_("USPosition ");
403 if (size
->flags
& USSize
) {
404 LOG_XDEBUG_("USSize ");
406 if (size
->flags
& PPosition
) {
407 LOG_XDEBUG_("PPosition ");
409 if (size
->flags
& PSize
) {
410 LOG_XDEBUG_("PSize");
414 if (size
->flags
& PMinSize
) {
415 LOG_XDEBUG("PMinSize: min_width = %d, min_height = %d\n", size
->min_width
, size
->min_height
);
417 if (size
->flags
& PMaxSize
) {
418 LOG_XDEBUG("PMaxSize: max_width = %d, max_height = %d\n", size
->max_width
, size
->max_height
);
420 if (size
->flags
& PResizeInc
) {
421 LOG_XDEBUG("PResizeInc: width_inc = %d, height_inc = %d\n",
422 size
->width_inc
, size
->height_inc
);
424 if (size
->flags
& PAspect
) {
425 LOG_XDEBUG("PAspect: min_aspect = %d/%d, max_aspect = %d/%d\n",
426 size
->min_aspect
.x
, size
->min_aspect
.y
,
427 size
->max_aspect
.x
, size
->max_aspect
.y
);
429 if (size
->flags
& PBaseSize
) {
430 LOG_XDEBUG("PBaseSize: base_width = %d, base_height = %d\n",
431 size
->base_width
, size
->base_height
);
433 if (size
->flags
& PWinGravity
) {
434 LOG_XDEBUG("PWinGravity: %s\n", gravity_string(size
->win_gravity
));