Refactor client raise, lower, hide and show
[evilwm.git] / screen.c
blob45d9b280c702d925b26a6343a526bd9e1ed4a751
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 client_raise(c);
125 #ifdef INFOBANNER_MOVERESIZE
126 create_info_window(c);
127 #endif
128 XGrabServer(dpy);
129 draw_outline(c);
131 setmouse(c->window, c->width, c->height);
132 for (;;) {
133 XMaskEvent(dpy, MouseMask, &ev);
134 switch (ev.type) {
135 case MotionNotify:
136 if (ev.xmotion.root != c->screen->root)
137 break;
138 draw_outline(c); /* clear */
139 XUngrabServer(dpy);
140 recalculate_sweep(c, old_cx, old_cy, ev.xmotion.x, ev.xmotion.y);
141 #ifdef INFOBANNER_MOVERESIZE
142 update_info_window(c);
143 #endif
144 XSync(dpy, False);
145 XGrabServer(dpy);
146 draw_outline(c);
147 break;
148 case ButtonRelease:
149 draw_outline(c); /* clear */
150 XUngrabServer(dpy);
151 #ifdef INFOBANNER_MOVERESIZE
152 remove_info_window();
153 #endif
154 XUngrabPointer(dpy, CurrentTime);
155 moveresize(c);
156 return;
157 default: break;
161 #endif
163 void show_info(Client *c, unsigned int keycode) {
164 XEvent ev;
165 XKeyboardState keyboard;
167 XGetKeyboardControl(dpy, &keyboard);
168 XAutoRepeatOff(dpy);
169 #ifdef INFOBANNER
170 create_info_window(c);
171 #else
172 XGrabServer(dpy);
173 draw_outline(c);
174 #endif
175 do {
176 XMaskEvent(dpy, KeyReleaseMask, &ev);
177 } while (ev.xkey.keycode != keycode);
178 #ifdef INFOBANNER
179 remove_info_window();
180 #else
181 draw_outline(c);
182 XUngrabServer(dpy);
183 #endif
184 if (keyboard.global_auto_repeat == AutoRepeatModeOn)
185 XAutoRepeatOn(dpy);
188 #ifdef MOUSE
189 static int absmin(int a, int b) {
190 if (abs(a) < abs(b))
191 return a;
192 return b;
195 static void snap_client(Client *c) {
196 int dx, dy;
197 int dpy_width = DisplayWidth(dpy, c->screen->screen);
198 int dpy_height = DisplayHeight(dpy, c->screen->screen);
199 struct list *iter;
200 Client *ci;
202 /* snap to other windows */
203 dx = dy = opt_snap;
204 for (iter = clients_tab_order; iter; iter = iter->next) {
205 ci = iter->data;
206 if (ci == c) continue;
207 if (ci->screen != ci->screen) continue;
208 #ifdef VWM
209 if (!is_fixed(ci) && ci->vdesk != c->vdesk) continue;
210 #endif
211 if (ci->is_dock && !c->screen->docks_visible) continue;
212 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) {
213 dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border);
214 dx = absmin(dx, ci->x + ci->width - c->x - c->width);
215 dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border);
216 dx = absmin(dx, ci->x - c->x);
218 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) {
219 dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border);
220 dy = absmin(dy, ci->y + ci->height - c->y - c->height);
221 dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border);
222 dy = absmin(dy, ci->y - c->y);
225 if (abs(dx) < opt_snap)
226 c->x += dx;
227 if (abs(dy) < opt_snap)
228 c->y += dy;
230 /* snap to screen border */
231 if (abs(c->x - c->border) < opt_snap) c->x = c->border;
232 if (abs(c->y - c->border) < opt_snap) c->y = c->border;
233 if (abs(c->x + c->width + c->border - dpy_width) < opt_snap)
234 c->x = dpy_width - c->width - c->border;
235 if (abs(c->y + c->height + c->border - dpy_height) < opt_snap)
236 c->y = dpy_height - c->height - c->border;
238 if (abs(c->x) == c->border && c->width == dpy_width)
239 c->x = 0;
240 if (abs(c->y) == c->border && c->height == dpy_height)
241 c->y = 0;
244 void drag(Client *c) {
245 XEvent ev;
246 int x1, y1;
247 int old_cx = c->x;
248 int old_cy = c->y;
250 if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return;
251 client_raise(c);
252 get_mouse_position(&x1, &y1, c->screen->root);
253 #ifdef INFOBANNER_MOVERESIZE
254 create_info_window(c);
255 #endif
256 if (no_solid_drag) {
257 XGrabServer(dpy);
258 draw_outline(c);
260 for (;;) {
261 XMaskEvent(dpy, MouseMask, &ev);
262 switch (ev.type) {
263 case MotionNotify:
264 if (ev.xmotion.root != c->screen->root)
265 break;
266 if (no_solid_drag) {
267 draw_outline(c); /* clear */
268 XUngrabServer(dpy);
270 c->x = old_cx + (ev.xmotion.x - x1);
271 c->y = old_cy + (ev.xmotion.y - y1);
272 if (opt_snap)
273 snap_client(c);
275 #ifdef INFOBANNER_MOVERESIZE
276 update_info_window(c);
277 #endif
278 if (no_solid_drag) {
279 XSync(dpy, False);
280 XGrabServer(dpy);
281 draw_outline(c);
282 } else {
283 XMoveWindow(dpy, c->parent,
284 c->x - c->border,
285 c->y - c->border);
286 send_config(c);
288 break;
289 case ButtonRelease:
290 if (no_solid_drag) {
291 draw_outline(c); /* clear */
292 XUngrabServer(dpy);
294 #ifdef INFOBANNER_MOVERESIZE
295 remove_info_window();
296 #endif
297 XUngrabPointer(dpy, CurrentTime);
298 if (no_solid_drag) {
299 moveresize(c);
301 return;
302 default: break;
306 #endif /* def MOUSE */
308 void moveresize(Client *c) {
309 client_raise(c);
310 XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border,
311 c->width, c->height);
312 XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height);
313 send_config(c);
316 void maximise_client(Client *c, int action, int hv) {
317 if (hv & MAXIMISE_HORZ) {
318 if (c->oldw) {
319 if (action == NET_WM_STATE_REMOVE
320 || action == NET_WM_STATE_TOGGLE) {
321 c->x = c->oldx;
322 c->width = c->oldw;
323 c->oldw = 0;
324 XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_horz);
326 } else {
327 if (action == NET_WM_STATE_ADD
328 || action == NET_WM_STATE_TOGGLE) {
329 unsigned long props[2];
330 c->oldx = c->x;
331 c->oldw = c->width;
332 c->x = 0;
333 c->width = DisplayWidth(dpy, c->screen->screen);
334 props[0] = c->oldx;
335 props[1] = c->oldw;
336 XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_horz,
337 XA_CARDINAL, 32, PropModeReplace,
338 (unsigned char *)&props, 2);
342 if (hv & MAXIMISE_VERT) {
343 if (c->oldh) {
344 if (action == NET_WM_STATE_REMOVE
345 || action == NET_WM_STATE_TOGGLE) {
346 c->y = c->oldy;
347 c->height = c->oldh;
348 c->oldh = 0;
349 XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_vert);
351 } else {
352 if (action == NET_WM_STATE_ADD
353 || action == NET_WM_STATE_TOGGLE) {
354 unsigned long props[2];
355 c->oldy = c->y;
356 c->oldh = c->height;
357 c->y = 0;
358 c->height = DisplayHeight(dpy, c->screen->screen);
359 props[0] = c->oldy;
360 props[1] = c->oldh;
361 XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_vert,
362 XA_CARDINAL, 32, PropModeReplace,
363 (unsigned char *)&props, 2);
367 ewmh_set_net_wm_state(c);
368 moveresize(c);
369 discard_enter_events();
372 void next(void) {
373 struct list *newl = list_find(clients_tab_order, current);
374 Client *newc = current;
375 do {
376 if (newl) {
377 newl = newl->next;
378 if (!newl && !current)
379 return;
381 if (!newl)
382 newl = clients_tab_order;
383 if (!newl)
384 return;
385 newc = newl->data;
386 if (newc == current)
387 return;
389 #ifdef VWM
390 /* NOTE: Checking against newc->screen->vdesk implies we can Alt+Tab
391 * across screen boundaries. Is this what we want? */
392 while ((!is_fixed(newc) && (newc->vdesk != newc->screen->vdesk)) || (newc->is_dock && !newc->screen->docks_visible));
393 #else
394 while (0);
395 #endif
396 if (!newc)
397 return;
398 client_show(newc);
399 client_raise(newc);
400 select_client(newc);
401 #ifdef WARP_POINTER
402 setmouse(newc->window, newc->width + newc->border - 1,
403 newc->height + newc->border - 1);
404 #endif
405 discard_enter_events();
408 #ifdef VWM
409 void switch_vdesk(ScreenInfo *s, unsigned int v) {
410 struct list *iter;
411 #ifdef DEBUG
412 int hidden = 0, raised = 0;
413 #endif
415 if (v == s->vdesk)
416 return;
417 LOG_ENTER("switch_vdesk(screen=%d, from=%d, to=%d)", s->screen, s->vdesk, v);
418 if (current && !is_fixed(current)) {
419 select_client(NULL);
421 for (iter = clients_tab_order; iter; iter = iter->next) {
422 Client *c = iter->data;
423 if (c->screen != s)
424 continue;
425 if (c->vdesk == s->vdesk) {
426 client_hide(c);
427 #ifdef DEBUG
428 hidden++;
429 #endif
430 } else if (c->vdesk == v) {
431 if (!c->is_dock || s->docks_visible)
432 client_show(c);
433 #ifdef DEBUG
434 raised++;
435 #endif
438 s->vdesk = v;
439 ewmh_set_net_current_desktop(s);
440 LOG_DEBUG("%d hidden, %d raised\n", hidden, raised);
441 LOG_LEAVE();
443 #endif /* def VWM */
445 void set_docks_visible(ScreenInfo *s, int is_visible) {
446 struct list *iter;
448 LOG_ENTER("set_docks_visible(screen=%d, is_visible=%d)", s->screen, is_visible);
449 s->docks_visible = is_visible;
450 for (iter = clients_tab_order; iter; iter = iter->next) {
451 Client *c = iter->data;
452 if (c->screen != s)
453 continue;
454 if (c->is_dock) {
455 if (is_visible) {
456 #ifdef VWM
457 if (is_fixed(c) || (c->vdesk == s->vdesk)) {
458 #endif
459 client_show(c);
460 client_raise(c);
461 #ifdef VWM
463 #endif
464 } else {
465 client_hide(c);
469 LOG_LEAVE();
472 ScreenInfo *find_screen(Window root) {
473 int i;
474 for (i = 0; i < num_screens; i++) {
475 if (screens[i].root == root)
476 return &screens[i];
478 return NULL;
481 ScreenInfo *find_current_screen(void) {
482 Window cur_root, dw;
483 int di;
484 unsigned int dui;
486 /* XQueryPointer is useful for getting the current pointer root */
487 XQueryPointer(dpy, screens[0].root, &cur_root, &dw, &di, &di, &di, &di, &dui);
488 return find_screen(cur_root);
491 static void grab_keysym(Window w, unsigned int mask, KeySym keysym) {
492 KeyCode keycode = XKeysymToKeycode(dpy, keysym);
493 XGrabKey(dpy, keycode, mask, w, True,
494 GrabModeAsync, GrabModeAsync);
495 XGrabKey(dpy, keycode, mask|LockMask, w, True,
496 GrabModeAsync, GrabModeAsync);
497 if (numlockmask) {
498 XGrabKey(dpy, keycode, mask|numlockmask, w, True,
499 GrabModeAsync, GrabModeAsync);
500 XGrabKey(dpy, keycode, mask|numlockmask|LockMask, w, True,
501 GrabModeAsync, GrabModeAsync);
505 static KeySym keys_to_grab[] = {
506 #ifdef VWM
507 KEY_FIX, KEY_PREVDESK, KEY_NEXTDESK,
508 XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8,
509 #endif
510 KEY_NEW, KEY_KILL,
511 KEY_TOPLEFT, KEY_TOPRIGHT, KEY_BOTTOMLEFT, KEY_BOTTOMRIGHT,
512 KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP,
513 KEY_LOWER, KEY_ALTLOWER, KEY_INFO, KEY_MAXVERT, KEY_MAX,
514 KEY_DOCK_TOGGLE
516 #define NUM_GRABS (int)(sizeof(keys_to_grab) / sizeof(KeySym))
518 static KeySym alt_keys_to_grab[] = {
519 KEY_KILL, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP
521 #define NUM_ALT_GRABS (int)(sizeof(alt_keys_to_grab) / sizeof(KeySym))
523 void grab_keys_for_screen(ScreenInfo *s) {
524 int i;
525 /* Release any previous grabs */
526 XUngrabKey(dpy, AnyKey, AnyModifier, s->root);
527 /* Grab key combinations we're interested in */
528 for (i = 0; i < NUM_GRABS; i++) {
529 grab_keysym(s->root, grabmask1, keys_to_grab[i]);
531 for (i = 0; i < NUM_ALT_GRABS; i++) {
532 grab_keysym(s->root, grabmask1 | altmask, alt_keys_to_grab[i]);
534 grab_keysym(s->root, grabmask2, KEY_NEXT);