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. */
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
) {
29 int namew
, iwinx
, iwiny
, iwinw
, iwinh
;
30 int width_inc
= c
->width_inc
, height_inc
= c
->height_inc
;
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
);
40 namew
= XTextWidth(font
, name
, strlen(name
));
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
;
51 if (iwiny
+ iwinh
> DisplayHeight(dpy
, c
->screen
->screen
))
52 iwiny
= DisplayHeight(dpy
, c
->screen
->screen
) - iwinh
;
55 XMoveResizeWindow(dpy
, info_window
, iwinx
, iwiny
, iwinw
, iwinh
);
56 XClearWindow(dpy
, info_window
);
58 XDrawString(dpy
, info_window
, c
->screen
->invert_gc
,
59 1, iwinh
/ 2 - 1, name
, strlen(name
));
62 XDrawString(dpy
, info_window
, c
->screen
->invert_gc
, 1, iwinh
- 1,
66 static void remove_info_window(void) {
68 XDestroyWindow(dpy
, info_window
);
71 #endif /* INFOBANNER */
73 #if defined(MOUSE) || !defined(INFOBANNER)
74 static void draw_outline(Client
*c
) {
75 #ifndef INFOBANNER_MOVERESIZE
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
,
91 #endif /* ndef INFOBANNER_MOVERESIZE */
96 static void recalculate_sweep(Client
*c
, int x1
, int y1
, int x2
, int y2
) {
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
;
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
) {
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
);
132 setmouse(c
->window
, c
->width
, c
->height
);
134 XMaskEvent(dpy
, MouseMask
, &ev
);
137 if (ev
.xmotion
.root
!= c
->screen
->root
)
139 draw_outline(c
); /* clear */
141 recalculate_sweep(c
, old_cx
, old_cy
, ev
.xmotion
.x
, ev
.xmotion
.y
);
142 #ifdef INFOBANNER_MOVERESIZE
143 update_info_window(c
);
150 draw_outline(c
); /* clear */
152 #ifdef INFOBANNER_MOVERESIZE
153 remove_info_window();
155 XUngrabPointer(dpy
, CurrentTime
);
164 void show_info(Client
*c
, unsigned int keycode
) {
166 XKeyboardState keyboard
;
168 XGetKeyboardControl(dpy
, &keyboard
);
171 create_info_window(c
);
177 XMaskEvent(dpy
, KeyReleaseMask
, &ev
);
178 } while (ev
.xkey
.keycode
!= keycode
);
180 remove_info_window();
185 if (keyboard
.global_auto_repeat
== AutoRepeatModeOn
)
190 static int absmin(int a
, int b
) {
196 static void snap_client(Client
*c
) {
198 int dpy_width
= DisplayWidth(dpy
, c
->screen
->screen
);
199 int dpy_height
= DisplayHeight(dpy
, c
->screen
->screen
);
203 /* snap to other windows */
205 for (iter
= clients_tab_order
; iter
; iter
= iter
->next
) {
207 if (ci
== c
) continue;
208 if (ci
->screen
!= ci
->screen
) continue;
210 if (!is_fixed(ci
) && ci
->vdesk
!= c
->vdesk
) continue;
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
)
228 if (abs(dy
) < opt_snap
)
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
)
241 if (abs(c
->y
) == c
->border
&& c
->height
== dpy_height
)
245 void drag(Client
*c
) {
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
);
263 XMaskEvent(dpy
, MouseMask
, &ev
);
266 if (ev
.xmotion
.root
!= c
->screen
->root
)
269 draw_outline(c
); /* clear */
272 c
->x
= old_cx
+ (ev
.xmotion
.x
- x1
);
273 c
->y
= old_cy
+ (ev
.xmotion
.y
- y1
);
277 #ifdef INFOBANNER_MOVERESIZE
278 update_info_window(c
);
285 XMoveWindow(dpy
, c
->parent
,
293 draw_outline(c
); /* clear */
296 #ifdef INFOBANNER_MOVERESIZE
297 remove_info_window();
299 XUngrabPointer(dpy
, CurrentTime
);
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
);
319 void maximise_client(Client
*c
, int action
, int hv
) {
320 if (hv
& MAXIMISE_HORZ
) {
322 if (action
== NET_WM_STATE_REMOVE
323 || action
== NET_WM_STATE_TOGGLE
) {
327 XDeleteProperty(dpy
, c
->window
, xa_evilwm_unmaximised_horz
);
330 if (action
== NET_WM_STATE_ADD
331 || action
== NET_WM_STATE_TOGGLE
) {
332 unsigned long props
[2];
336 c
->width
= DisplayWidth(dpy
, c
->screen
->screen
);
339 XChangeProperty(dpy
, c
->window
, xa_evilwm_unmaximised_horz
,
340 XA_CARDINAL
, 32, PropModeReplace
,
341 (unsigned char *)&props
, 2);
345 if (hv
& MAXIMISE_VERT
) {
347 if (action
== NET_WM_STATE_REMOVE
348 || action
== NET_WM_STATE_TOGGLE
) {
352 XDeleteProperty(dpy
, c
->window
, xa_evilwm_unmaximised_vert
);
355 if (action
== NET_WM_STATE_ADD
356 || action
== NET_WM_STATE_TOGGLE
) {
357 unsigned long props
[2];
361 c
->height
= DisplayHeight(dpy
, c
->screen
->screen
);
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
);
372 discard_enter_events();
375 void hide(Client
*c
) {
376 /* This will generate an unmap event. Tell event handler
379 LOG_XENTER("XUnmapWindow(parent=%lx)", c
->parent
);
380 XUnmapWindow(dpy
, c
->parent
);
382 set_wm_state(c
, IconicState
);
385 void unhide(Client
*c
, int 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
);
391 XMapWindow(dpy
, c
->parent
);
393 set_wm_state(c
, NormalState
);
397 struct list
*newl
= list_find(clients_tab_order
, current
);
398 Client
*newc
= current
;
402 if (!newl
&& !current
)
406 newl
= clients_tab_order
;
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
));
424 discard_enter_events();
428 void switch_vdesk(ScreenInfo
*s
, unsigned int v
) {
431 int hidden
= 0, raised
= 0;
436 LOG_ENTER("switch_vdesk(screen=%d, from=%d, to=%d)", s
->screen
, s
->vdesk
, v
);
437 if (current
&& !is_fixed(current
)) {
440 for (iter
= clients_tab_order
; iter
; iter
= iter
->next
) {
441 Client
*c
= iter
->data
;
444 if (c
->vdesk
== s
->vdesk
) {
449 } else if (c
->vdesk
== v
) {
450 if (!c
->is_dock
|| s
->docks_visible
)
458 ewmh_set_net_current_desktop(s
);
459 LOG_DEBUG("%d hidden, %d raised\n", hidden
, raised
);
464 void set_docks_visible(ScreenInfo
*s
, int is_visible
) {
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
;
476 if (is_fixed(c
) || (c
->vdesk
== s
->vdesk
))
487 ScreenInfo
*find_screen(Window root
) {
489 for (i
= 0; i
< num_screens
; i
++) {
490 if (screens
[i
].root
== root
)
496 ScreenInfo
*find_current_screen(void) {
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
);
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
[] = {
522 KEY_FIX
, KEY_PREVDESK
, KEY_NEXTDESK
,
523 XK_1
, XK_2
, XK_3
, XK_4
, XK_5
, XK_6
, XK_7
, XK_8
,
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
,
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
) {
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
);