Maintain allocated memory for lists of windows
[evilwm.git] / new.c
blobf03b4db253a4a0b2610e0494fa4168a8e3a41bd2
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. */
5 #include <stdlib.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include "evilwm.h"
9 #include "log.h"
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);
17 #ifdef XDEBUG
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);
21 #else
22 # define debug_wm_normal_hints(s)
23 #endif
25 void make_new_client(Window w, ScreenInfo *s) {
26 Client *c;
27 char *name;
28 XClassHint *class;
30 LOG_ENTER("make_new_client(window=%lx, screen=%d)", w, s->screen);
32 XGrabServer(dpy);
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. */
41 initialising = w;
42 XFetchName(dpy, w, &name);
43 XSync(dpy, False);
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");
49 XUngrabServer(dpy);
50 LOG_LEAVE();
51 return;
53 initialising = None;
54 LOG_DEBUG("name=%s\n", name ? name : "Untitled");
55 if (name)
56 XFree(name);
58 c = malloc(sizeof(Client));
59 /* Don't crash the window manager, just fail the operation. */
60 if (!c) {
61 LOG_ERROR("out of memory in new_client; limping onward\n");
62 LOG_LEAVE();
63 return;
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);
69 c->screen = s;
70 c->window = w;
71 c->ignore_unmap = 0;
72 c->remove = 0;
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. */
77 XUngrabServer(dpy);
79 c->border = opt_bw;
81 init_geometry(c);
83 #ifdef DEBUG
85 struct list *iter;
86 int i = 0;
87 for (iter = clients_tab_order; iter; iter = iter->next)
88 i++;
89 LOG_DEBUG("new window %dx%d+%d+%d, wincount=%d\n", c->width, c->height, c->x, c->y, i);
91 #endif
93 XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask);
95 reparent(c);
97 #ifdef SHAPE
98 if (have_shape) {
99 XShapeSelectInput(dpy, c->window, ShapeNotifyMask);
100 set_shape(c);
102 #endif
104 /* Read instance/class information for client and check against list
105 * built with -app options */
106 class = XAllocClassHint();
107 if (class) {
108 struct list *aiter = applications;
109 XGetClassHint(dpy, w, class);
110 while (aiter) {
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;
121 else
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;
127 else
128 c->y = a->y + c->border;
130 moveresize(c);
131 if (a->is_dock) c->is_dock = 1;
132 #ifdef VWM
133 if (a->vdesk != VDESK_NONE) c->vdesk = a->vdesk;
134 #endif
136 aiter = aiter->next;
138 XFree(class->res_name);
139 XFree(class->res_class);
140 XFree(class);
142 ewmh_init_client(c);
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. */
147 #ifdef VWM
148 if (is_fixed(c) || c->vdesk == s->vdesk)
149 #endif
151 unhide(c, RAISE);
152 #ifndef MOUSE
153 select_client(c);
154 discard_enter_events();
155 #endif
157 #ifdef VWM
158 else {
159 set_wm_state(c, IconicState);
161 ewmh_set_net_wm_desktop(c);
162 #endif
163 LOG_LEAVE();
166 /* Calls XGetWindowAttributes, XGetWMHints and XGetWMNormalHints to determine
167 * window's initial geometry.
169 * XGetWindowAttributes
171 static void init_geometry(Client *c) {
172 long size_flags;
173 XWindowAttributes attr;
174 unsigned long *eprop;
175 unsigned long nitems;
176 PropMwmHints *mprop;
177 #ifdef VWM
178 unsigned long *lprop;
179 #endif
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)) {
186 c->border = 0;
188 XFree(mprop);
191 #ifdef VWM
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]))
195 c->vdesk = lprop[0];
196 XFree(lprop);
198 #endif
200 get_window_type(c);
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);
206 LOG_XLEAVE();
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)) ) {
212 if (nitems == 2) {
213 c->oldx = eprop[0];
214 c->oldw = eprop[1];
216 XFree(eprop);
218 if ( (eprop = get_property(c->window, xa_evilwm_unmaximised_vert, XA_CARDINAL, &nitems)) ) {
219 if (nitems == 2) {
220 c->oldy = eprop[0];
221 c->oldh = eprop[1];
223 XFree(eprop);
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;
232 } else {
233 c->width = c->min_width;
234 c->height = c->min_height;
235 send_config(c);
237 if ((attr.map_state == IsViewable)
238 || (size_flags & (/*PPosition |*/ USPosition))) {
239 c->x = attr.x;
240 c->y = attr.y;
241 } else {
242 #ifdef MOUSE
243 int xmax = DisplayWidth(dpy, c->screen->screen);
244 int ymax = DisplayHeight(dpy, c->screen->screen);
245 int x, y;
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;
249 #else
250 c->x = c->y = c->border;
251 #endif
252 send_config(c);
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 */
258 c->ignore_unmap++;
260 gravitate(c, 0);
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);
279 #ifdef MOUSE
280 grab_button(c->parent, grabmask2, AnyButton);
281 #endif
284 /* Get WM_NORMAL_HINTS property */
285 long get_wm_normal_hints(Client *c) {
286 XSizeHints *size;
287 long flags;
288 long dummy;
289 size = XAllocSizeHints();
291 LOG_XENTER("XGetWMNormalHints(window=%lx)", c->window);
292 XGetWMNormalHints(dpy, c->window, size, &dummy);
293 debug_wm_normal_hints(size);
294 LOG_XLEAVE();
295 flags = size->flags;
296 if (flags & PMinSize) {
297 c->min_width = size->min_width;
298 c->min_height = size->min_height;
299 } else {
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;
305 } else {
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;
311 } else {
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;
326 } else {
327 c->win_gravity_hint = NorthWestGravity;
329 c->win_gravity = c->win_gravity_hint;
330 XFree(size);
331 return flags;
334 /* Determine if client thinks of itself as a dock */
335 void get_window_type(Client *c) {
336 Atom *aprop;
337 unsigned long nitems, i;
338 c->is_dock = 0;
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)
342 c->is_dock = 1;
344 XFree(aprop);
348 static void *get_property(Window w, Atom property, Atom req_type,
349 unsigned long *nitems_return) {
350 Atom actual_type;
351 int actual_format;
352 unsigned long bytes_after;
353 unsigned char *prop;
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)
358 == Success) {
359 if (actual_type == req_type)
360 return (void *)prop;
361 XFree(prop);
363 return NULL;
366 #ifdef XDEBUG
367 static const char *map_state_string(int map_state) {
368 const char *map_states[4] = {
369 "IsUnmapped",
370 "IsUnviewable",
371 "IsViewable",
372 "Unknown"
374 return ((unsigned int)map_state < 3)
375 ? map_states[map_state]
376 : map_states[3];
379 static const char *gravity_string(int gravity) {
380 const char *gravities[12] = {
381 "ForgetGravity",
382 "NorthWestGravity",
383 "NorthGravity",
384 "NorthEastGravity",
385 "WestGravity",
386 "CenterGravity",
387 "EastGravity",
388 "SouthWestGravity",
389 "SouthGravity",
390 "SouthEastGravity",
391 "StaticGravity",
392 "Unknown"
394 return ((unsigned int)gravity < 11) ? gravities[gravity] : gravities[11];
397 static void debug_wm_normal_hints(XSizeHints *size) {
398 if (size->flags & 15) {
399 LOG_XDEBUG("");
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");
412 LOG_XDEBUG_("\n");
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));
437 #endif