Maintain allocated memory for lists of windows
[evilwm.git] / screen.c
blobbb590a5f2d5d49a03360a0ca460bce44cbe55097
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 <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include "evilwm.h"
9 #include "log.h"
11 #ifdef INFOBANNER
12 Window info_window = None;
14 static void create_info_window(Client *c);
15 static void update_info_window(Client *c);
16 static void remove_info_window(void);
17 static void grab_keysym(Window w, unsigned int mask, KeySym keysym);
19 static void create_info_window(Client *c) {
20 info_window = XCreateSimpleWindow(dpy, c->screen->root, -4, -4, 2, 2,
21 0, c->screen->fg.pixel, c->screen->fg.pixel);
22 XMapRaised(dpy, info_window);
23 update_info_window(c);
26 static void update_info_window(Client *c) {
27 char *name;
28 char buf[27];
29 int namew, iwinx, iwiny, iwinw, iwinh;
30 int width_inc = c->width_inc, height_inc = c->height_inc;
32 if (!info_window)
33 return;
34 snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc,
35 (c->height-c->base_height)/height_inc, c->x, c->y);
36 iwinw = XTextWidth(font, buf, strlen(buf)) + 2;
37 iwinh = font->max_bounds.ascent + font->max_bounds.descent;
38 XFetchName(dpy, c->window, &name);
39 if (name) {
40 namew = XTextWidth(font, name, strlen(name));
41 if (namew > iwinw)
42 iwinw = namew + 2;
43 iwinh = iwinh * 2;
45 iwinx = c->x + c->border + c->width - iwinw;
46 iwiny = c->y - c->border;
47 if (iwinx + iwinw > DisplayWidth(dpy, c->screen->screen))
48 iwinx = DisplayWidth(dpy, c->screen->screen) - iwinw;
49 if (iwinx < 0)
50 iwinx = 0;
51 if (iwiny + iwinh > DisplayHeight(dpy, c->screen->screen))
52 iwiny = DisplayHeight(dpy, c->screen->screen) - iwinh;
53 if (iwiny < 0)
54 iwiny = 0;
55 XMoveResizeWindow(dpy, info_window, iwinx, iwiny, iwinw, iwinh);
56 XClearWindow(dpy, info_window);
57 if (name) {
58 XDrawString(dpy, info_window, c->screen->invert_gc,
59 1, iwinh / 2 - 1, name, strlen(name));
60 XFree(name);
62 XDrawString(dpy, info_window, c->screen->invert_gc, 1, iwinh - 1,
63 buf, strlen(buf));
66 static void remove_info_window(void) {
67 if (info_window)
68 XDestroyWindow(dpy, info_window);
69 info_window = None;
71 #endif /* INFOBANNER */
73 #if defined(MOUSE) || !defined(INFOBANNER)
74 static void draw_outline(Client *c) {
75 #ifndef INFOBANNER_MOVERESIZE
76 char buf[27];
77 int width_inc = c->width_inc, height_inc = c->height_inc;
78 #endif /* ndef INFOBANNER_MOVERESIZE */
80 XDrawRectangle(dpy, c->screen->root, c->screen->invert_gc,
81 c->x - c->border, c->y - c->border,
82 c->width + c->border, c->height + c->border);
84 #ifndef INFOBANNER_MOVERESIZE
85 snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc,
86 (c->height-c->base_height)/height_inc, c->x, c->y);
87 XDrawString(dpy, c->screen->root, c->screen->invert_gc,
88 c->x + c->width - XTextWidth(font, buf, strlen(buf)) - SPACE,
89 c->y + c->height - SPACE,
90 buf, strlen(buf));
91 #endif /* ndef INFOBANNER_MOVERESIZE */
93 #endif
95 #ifdef MOUSE
96 static void recalculate_sweep(Client *c, int x1, int y1, int x2, int y2) {
97 if (c->oldw == 0) {
98 c->width = abs(x1 - x2);
99 c->width -= (c->width - c->base_width) % c->width_inc;
100 if (c->min_width && c->width < c->min_width)
101 c->width = c->min_width;
102 if (c->max_width && c->width > c->max_width)
103 c->width = c->max_width;
104 c->x = (x1 <= x2) ? x1 : x1 - c->width;
106 if (c->oldh == 0) {
107 c->height = abs(y1 - y2);
108 c->height -= (c->height - c->base_height) % c->height_inc;
109 if (c->min_height && c->height < c->min_height)
110 c->height = c->min_height;
111 if (c->max_height && c->height > c->max_height)
112 c->height = c->max_height;
113 c->y = (y1 <= y2) ? y1 : y1 - c->height;
117 void sweep(Client *c) {
118 XEvent ev;
119 int old_cx = c->x;
120 int old_cy = c->y;
122 if (!grab_pointer(c->screen->root, MouseMask, resize_curs)) return;
124 XRaiseWindow(dpy, c->parent);
125 ewmh_raise_client(c);
126 #ifdef INFOBANNER_MOVERESIZE
127 create_info_window(c);
128 #endif
129 XGrabServer(dpy);
130 draw_outline(c);
132 setmouse(c->window, c->width, c->height);
133 for (;;) {
134 XMaskEvent(dpy, MouseMask, &ev);
135 switch (ev.type) {
136 case MotionNotify:
137 if (ev.xmotion.root != c->screen->root)
138 break;
139 draw_outline(c); /* clear */
140 XUngrabServer(dpy);
141 recalculate_sweep(c, old_cx, old_cy, ev.xmotion.x, ev.xmotion.y);
142 #ifdef INFOBANNER_MOVERESIZE
143 update_info_window(c);
144 #endif
145 XSync(dpy, False);
146 XGrabServer(dpy);
147 draw_outline(c);
148 break;
149 case ButtonRelease:
150 draw_outline(c); /* clear */
151 XUngrabServer(dpy);
152 #ifdef INFOBANNER_MOVERESIZE
153 remove_info_window();
154 #endif
155 XUngrabPointer(dpy, CurrentTime);
156 moveresize(c);
157 return;
158 default: break;
162 #endif
164 void show_info(Client *c, unsigned int keycode) {
165 XEvent ev;
166 XKeyboardState keyboard;
168 XGetKeyboardControl(dpy, &keyboard);
169 XAutoRepeatOff(dpy);
170 #ifdef INFOBANNER
171 create_info_window(c);
172 #else
173 XGrabServer(dpy);
174 draw_outline(c);
175 #endif
176 do {
177 XMaskEvent(dpy, KeyReleaseMask, &ev);
178 } while (ev.xkey.keycode != keycode);
179 #ifdef INFOBANNER
180 remove_info_window();
181 #else
182 draw_outline(c);
183 XUngrabServer(dpy);
184 #endif
185 if (keyboard.global_auto_repeat == AutoRepeatModeOn)
186 XAutoRepeatOn(dpy);
189 #ifdef MOUSE
190 static int absmin(int a, int b) {
191 if (abs(a) < abs(b))
192 return a;
193 return b;
196 static void snap_client(Client *c) {
197 int dx, dy;
198 int dpy_width = DisplayWidth(dpy, c->screen->screen);
199 int dpy_height = DisplayHeight(dpy, c->screen->screen);
200 struct list *iter;
201 Client *ci;
203 /* snap to other windows */
204 dx = dy = opt_snap;
205 for (iter = clients_tab_order; iter; iter = iter->next) {
206 ci = iter->data;
207 if (ci == c) continue;
208 if (ci->screen != ci->screen) continue;
209 #ifdef VWM
210 if (!is_fixed(ci) && ci->vdesk != c->vdesk) continue;
211 #endif
212 if (ci->is_dock && !c->screen->docks_visible) continue;
213 if (ci->y - ci->border - c->border - c->height - c->y <= opt_snap && c->y - c->border - ci->border - ci->height - ci->y <= opt_snap) {
214 dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border);
215 dx = absmin(dx, ci->x + ci->width - c->x - c->width);
216 dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border);
217 dx = absmin(dx, ci->x - c->x);
219 if (ci->x - ci->border - c->border - c->width - c->x <= opt_snap && c->x - c->border - ci->border - ci->width - ci->x <= opt_snap) {
220 dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border);
221 dy = absmin(dy, ci->y + ci->height - c->y - c->height);
222 dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border);
223 dy = absmin(dy, ci->y - c->y);
226 if (abs(dx) < opt_snap)
227 c->x += dx;
228 if (abs(dy) < opt_snap)
229 c->y += dy;
231 /* snap to screen border */
232 if (abs(c->x - c->border) < opt_snap) c->x = c->border;
233 if (abs(c->y - c->border) < opt_snap) c->y = c->border;
234 if (abs(c->x + c->width + c->border - dpy_width) < opt_snap)
235 c->x = dpy_width - c->width - c->border;
236 if (abs(c->y + c->height + c->border - dpy_height) < opt_snap)
237 c->y = dpy_height - c->height - c->border;
239 if (abs(c->x) == c->border && c->width == dpy_width)
240 c->x = 0;
241 if (abs(c->y) == c->border && c->height == dpy_height)
242 c->y = 0;
245 void drag(Client *c) {
246 XEvent ev;
247 int x1, y1;
248 int old_cx = c->x;
249 int old_cy = c->y;
251 if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return;
252 XRaiseWindow(dpy, c->parent);
253 ewmh_raise_client(c);
254 get_mouse_position(&x1, &y1, c->screen->root);
255 #ifdef INFOBANNER_MOVERESIZE
256 create_info_window(c);
257 #endif
258 if (no_solid_drag) {
259 XGrabServer(dpy);
260 draw_outline(c);
262 for (;;) {
263 XMaskEvent(dpy, MouseMask, &ev);
264 switch (ev.type) {
265 case MotionNotify:
266 if (ev.xmotion.root != c->screen->root)
267 break;
268 if (no_solid_drag) {
269 draw_outline(c); /* clear */
270 XUngrabServer(dpy);
272 c->x = old_cx + (ev.xmotion.x - x1);
273 c->y = old_cy + (ev.xmotion.y - y1);
274 if (opt_snap)
275 snap_client(c);
277 #ifdef INFOBANNER_MOVERESIZE
278 update_info_window(c);
279 #endif
280 if (no_solid_drag) {
281 XSync(dpy, False);
282 XGrabServer(dpy);
283 draw_outline(c);
284 } else {
285 XMoveWindow(dpy, c->parent,
286 c->x - c->border,
287 c->y - c->border);
288 send_config(c);
290 break;
291 case ButtonRelease:
292 if (no_solid_drag) {
293 draw_outline(c); /* clear */
294 XUngrabServer(dpy);
296 #ifdef INFOBANNER_MOVERESIZE
297 remove_info_window();
298 #endif
299 XUngrabPointer(dpy, CurrentTime);
300 if (no_solid_drag) {
301 moveresize(c);
303 return;
304 default: break;
308 #endif /* def MOUSE */
310 void moveresize(Client *c) {
311 XRaiseWindow(dpy, c->parent);
312 ewmh_raise_client(c);
313 XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border,
314 c->width, c->height);
315 XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height);
316 send_config(c);
319 void maximise_client(Client *c, int action, int hv) {
320 if (hv & MAXIMISE_HORZ) {
321 if (c->oldw) {
322 if (action == NET_WM_STATE_REMOVE
323 || action == NET_WM_STATE_TOGGLE) {
324 c->x = c->oldx;
325 c->width = c->oldw;
326 c->oldw = 0;
327 XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_horz);
329 } else {
330 if (action == NET_WM_STATE_ADD
331 || action == NET_WM_STATE_TOGGLE) {
332 unsigned long props[2];
333 c->oldx = c->x;
334 c->oldw = c->width;
335 c->x = 0;
336 c->width = DisplayWidth(dpy, c->screen->screen);
337 props[0] = c->oldx;
338 props[1] = c->oldw;
339 XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_horz,
340 XA_CARDINAL, 32, PropModeReplace,
341 (unsigned char *)&props, 2);
345 if (hv & MAXIMISE_VERT) {
346 if (c->oldh) {
347 if (action == NET_WM_STATE_REMOVE
348 || action == NET_WM_STATE_TOGGLE) {
349 c->y = c->oldy;
350 c->height = c->oldh;
351 c->oldh = 0;
352 XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_vert);
354 } else {
355 if (action == NET_WM_STATE_ADD
356 || action == NET_WM_STATE_TOGGLE) {
357 unsigned long props[2];
358 c->oldy = c->y;
359 c->oldh = c->height;
360 c->y = 0;
361 c->height = DisplayHeight(dpy, c->screen->screen);
362 props[0] = c->oldy;
363 props[1] = c->oldh;
364 XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_vert,
365 XA_CARDINAL, 32, PropModeReplace,
366 (unsigned char *)&props, 2);
370 ewmh_set_net_wm_state(c);
371 moveresize(c);
372 discard_enter_events();
375 void hide(Client *c) {
376 /* This will generate an unmap event. Tell event handler
377 * to ignore it. */
378 c->ignore_unmap++;
379 LOG_XENTER("XUnmapWindow(parent=%lx)", c->parent);
380 XUnmapWindow(dpy, c->parent);
381 LOG_XLEAVE();
382 set_wm_state(c, IconicState);
385 void unhide(Client *c, int raise_win) {
386 if (raise_win) {
387 XMapRaised(dpy, c->parent);
388 clients_stacking_order = list_to_tail(clients_stacking_order, c);
389 ewmh_set_net_client_list_stacking(c->screen);
390 } else {
391 XMapWindow(dpy, c->parent);
393 set_wm_state(c, NormalState);
396 void next(void) {
397 struct list *newl = list_find(clients_tab_order, current);
398 Client *newc = current;
399 do {
400 if (newl) {
401 newl = newl->next;
402 if (!newl && !current)
403 return;
405 if (!newl)
406 newl = clients_tab_order;
407 if (!newl)
408 return;
409 newc = newl->data;
410 if (newc == current)
411 return;
413 #ifdef VWM
414 /* NOTE: Checking against newc->screen->vdesk implies we can Alt+Tab
415 * across screen boundaries. Is this what we want? */
416 while ((!is_fixed(newc) && (newc->vdesk != newc->screen->vdesk)) || (newc->is_dock && !newc->screen->docks_visible));
417 #else
418 while (0);
419 #endif
420 if (!newc)
421 return;
422 unhide(newc, RAISE);
423 select_client(newc);
424 discard_enter_events();
427 #ifdef VWM
428 void switch_vdesk(ScreenInfo *s, unsigned int v) {
429 struct list *iter;
430 #ifdef DEBUG
431 int hidden = 0, raised = 0;
432 #endif
434 if (v == s->vdesk)
435 return;
436 LOG_ENTER("switch_vdesk(screen=%d, from=%d, to=%d)", s->screen, s->vdesk, v);
437 if (current && !is_fixed(current)) {
438 select_client(NULL);
440 for (iter = clients_tab_order; iter; iter = iter->next) {
441 Client *c = iter->data;
442 if (c->screen != s)
443 continue;
444 if (c->vdesk == s->vdesk) {
445 hide(c);
446 #ifdef DEBUG
447 hidden++;
448 #endif
449 } else if (c->vdesk == v) {
450 if (!c->is_dock || s->docks_visible)
451 unhide(c, NO_RAISE);
452 #ifdef DEBUG
453 raised++;
454 #endif
457 s->vdesk = v;
458 ewmh_set_net_current_desktop(s);
459 LOG_DEBUG("%d hidden, %d raised\n", hidden, raised);
460 LOG_LEAVE();
462 #endif /* def VWM */
464 void set_docks_visible(ScreenInfo *s, int is_visible) {
465 struct list *iter;
467 LOG_ENTER("set_docks_visible(screen=%d, is_visible=%d)", s->screen, is_visible);
468 s->docks_visible = is_visible;
469 for (iter = clients_tab_order; iter; iter = iter->next) {
470 Client *c = iter->data;
471 if (c->screen != s)
472 continue;
473 if (c->is_dock) {
474 if (is_visible) {
475 #ifdef VWM
476 if (is_fixed(c) || (c->vdesk == s->vdesk))
477 #endif
478 unhide(c, RAISE);
479 } else {
480 hide(c);
484 LOG_LEAVE();
487 ScreenInfo *find_screen(Window root) {
488 int i;
489 for (i = 0; i < num_screens; i++) {
490 if (screens[i].root == root)
491 return &screens[i];
493 return NULL;
496 ScreenInfo *find_current_screen(void) {
497 Window cur_root, dw;
498 int di;
499 unsigned int dui;
501 /* XQueryPointer is useful for getting the current pointer root */
502 XQueryPointer(dpy, screens[0].root, &cur_root, &dw, &di, &di, &di, &di, &dui);
503 return find_screen(cur_root);
506 static void grab_keysym(Window w, unsigned int mask, KeySym keysym) {
507 KeyCode keycode = XKeysymToKeycode(dpy, keysym);
508 XGrabKey(dpy, keycode, mask, w, True,
509 GrabModeAsync, GrabModeAsync);
510 XGrabKey(dpy, keycode, mask|LockMask, w, True,
511 GrabModeAsync, GrabModeAsync);
512 if (numlockmask) {
513 XGrabKey(dpy, keycode, mask|numlockmask, w, True,
514 GrabModeAsync, GrabModeAsync);
515 XGrabKey(dpy, keycode, mask|numlockmask|LockMask, w, True,
516 GrabModeAsync, GrabModeAsync);
520 static KeySym keys_to_grab[] = {
521 #ifdef VWM
522 KEY_FIX, KEY_PREVDESK, KEY_NEXTDESK,
523 XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8,
524 #endif
525 KEY_NEW, KEY_KILL,
526 KEY_TOPLEFT, KEY_TOPRIGHT, KEY_BOTTOMLEFT, KEY_BOTTOMRIGHT,
527 KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP,
528 KEY_LOWER, KEY_ALTLOWER, KEY_INFO, KEY_MAXVERT, KEY_MAX,
529 KEY_DOCK_TOGGLE
531 #define NUM_GRABS (int)(sizeof(keys_to_grab) / sizeof(KeySym))
533 static KeySym alt_keys_to_grab[] = {
534 KEY_KILL, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP
536 #define NUM_ALT_GRABS (int)(sizeof(alt_keys_to_grab) / sizeof(KeySym))
538 void grab_keys_for_screen(ScreenInfo *s) {
539 int i;
540 /* Release any previous grabs */
541 XUngrabKey(dpy, AnyKey, AnyModifier, s->root);
542 /* Grab key combinations we're interested in */
543 for (i = 0; i < NUM_GRABS; i++) {
544 grab_keysym(s->root, grabmask1, keys_to_grab[i]);
546 for (i = 0; i < NUM_ALT_GRABS; i++) {
547 grab_keysym(s->root, grabmask1 | altmask, alt_keys_to_grab[i]);
549 grab_keysym(s->root, grabmask2, KEY_NEXT);