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. */
8 #include <sys/select.h>
12 static int interruptibleXNextEvent(XEvent
*event
);
15 const char *debug_atom_name(Atom a
);
16 const char *debug_atom_name(Atom a
) {
18 char *atom_name
= XGetAtomName(dpy
, a
);
19 strncpy(buf
, atom_name
, sizeof(buf
));
20 buf
[sizeof(buf
)-1] = 0;
25 static void handle_key_event(XKeyEvent
*e
) {
26 KeySym key
= XKeycodeToKeysym(dpy
,e
->keycode
,0);
28 int width_inc
, height_inc
;
29 ScreenInfo
*current_screen
= find_current_screen();
37 if (XGrabKeyboard(dpy
, e
->root
, False
, GrabModeAsync
, GrabModeAsync
, CurrentTime
) == GrabSuccess
) {
40 XMaskEvent(dpy
, KeyPressMask
|KeyReleaseMask
, &ev
);
41 if (ev
.type
== KeyPress
&& XKeycodeToKeysym(dpy
,ev
.xkey
.keycode
,0) == KEY_NEXT
)
43 } while (ev
.type
== KeyPress
|| XKeycodeToKeysym(dpy
,ev
.xkey
.keycode
,0) == KEY_NEXT
);
44 XUngrabKeyboard(dpy
, CurrentTime
);
46 ewmh_select_client(current
);
49 set_docks_visible(current_screen
, !current_screen
->docks_visible
);
52 case XK_1
: case XK_2
: case XK_3
: case XK_4
:
53 case XK_5
: case XK_6
: case XK_7
: case XK_8
:
54 switch_vdesk(current_screen
, KEY_TO_VDESK(key
));
57 if (current_screen
->vdesk
> 0) {
58 switch_vdesk(current_screen
,
59 current_screen
->vdesk
- 1);
63 if (current_screen
->vdesk
< VDESK_MAX
) {
64 switch_vdesk(current_screen
,
65 current_screen
->vdesk
+ 1);
72 if (c
== NULL
) return;
73 width_inc
= (c
->width_inc
> 1) ? c
->width_inc
: 16;
74 height_inc
= (c
->height_inc
> 1) ? c
->height_inc
: 16;
77 if (e
->state
& altmask
) {
78 if ((c
->width
- width_inc
) >= c
->min_width
)
79 c
->width
-= width_inc
;
85 if (e
->state
& altmask
) {
86 if (!c
->max_height
|| (c
->height
+ height_inc
) <= c
->max_height
)
87 c
->height
+= height_inc
;
93 if (e
->state
& altmask
) {
94 if ((c
->height
- height_inc
) >= c
->min_height
)
95 c
->height
-= height_inc
;
101 if (e
->state
& altmask
) {
102 if (!c
->max_width
|| (c
->width
+ width_inc
) <= c
->max_width
)
103 c
->width
+= width_inc
;
113 c
->x
= DisplayWidth(dpy
, c
->screen
->screen
)
114 - c
->width
-c
->border
;
119 c
->y
= DisplayHeight(dpy
, c
->screen
->screen
)
120 - c
->height
-c
->border
;
122 case KEY_BOTTOMRIGHT
:
123 c
->x
= DisplayWidth(dpy
, c
->screen
->screen
)
124 - c
->width
-c
->border
;
125 c
->y
= DisplayHeight(dpy
, c
->screen
->screen
)
126 - c
->height
-c
->border
;
129 send_wm_delete(c
, e
->state
& altmask
);
131 case KEY_LOWER
: case KEY_ALTLOWER
:
132 XLowerWindow(dpy
, c
->parent
);
133 ewmh_lower_client(c
);
136 show_info(c
, e
->keycode
);
139 maximise_client(c
, NET_WM_STATE_TOGGLE
, MAXIMISE_HORZ
|MAXIMISE_VERT
);
142 maximise_client(c
, NET_WM_STATE_TOGGLE
, MAXIMISE_VERT
);
147 client_to_vdesk(c
, current_screen
->vdesk
);
149 client_to_vdesk(c
, VDESK_FIXED
);
157 if (abs(c
->x
) == c
->border
&& c
->oldw
!= 0)
159 if (abs(c
->y
) == c
->border
&& c
->oldh
!= 0)
162 discard_enter_events();
167 static void handle_button_event(XButtonEvent
*e
) {
168 Client
*c
= find_client(e
->window
);
177 XLowerWindow(dpy
, c
->parent
);
178 ewmh_lower_client(c
);
186 static void do_window_changes(int value_mask
, XWindowChanges
*wc
, Client
*c
,
189 if (value_mask
& CWX
) c
->x
= wc
->x
;
190 if (value_mask
& CWY
) c
->y
= wc
->y
;
191 if (value_mask
& CWWidth
) {
192 c
->width
= wc
->width
;
193 if (c
->width
< c
->min_width
)
194 c
->width
= c
->min_width
;
195 if (c
->max_width
&& c
->width
> c
->max_width
)
196 c
->width
= c
->max_width
;
198 if (value_mask
& CWHeight
) {
199 c
->height
= wc
->height
;
200 if (c
->height
< c
->min_height
)
201 c
->height
= c
->min_height
;
202 if (c
->max_height
&& c
->height
> c
->max_height
)
203 c
->height
= c
->max_height
;
205 if (c
->x
== 0 && c
->width
>= DisplayWidth(dpy
, c
->screen
->screen
)) {
208 if (c
->y
== 0 && c
->height
>= DisplayHeight(dpy
, c
->screen
->screen
)) {
211 gravitate(c
, gravity
);
212 wc
->x
= c
->x
- c
->border
;
213 wc
->y
= c
->y
- c
->border
;
214 wc
->border_width
= c
->border
;
215 XConfigureWindow(dpy
, c
->parent
, value_mask
, wc
);
216 XMoveResizeWindow(dpy
, c
->window
, 0, 0, c
->width
, c
->height
);
217 if ((value_mask
& (CWX
|CWY
)) && !(value_mask
& (CWWidth
|CWHeight
))) {
222 static void handle_configure_request(XConfigureRequestEvent
*e
) {
223 Client
*c
= find_client(e
->window
);
229 wc
.height
= e
->height
;
231 wc
.sibling
= e
->above
;
232 wc
.stack_mode
= e
->detail
;
234 if (e
->value_mask
& CWStackMode
&& e
->value_mask
& CWSibling
) {
235 Client
*sibling
= find_client(e
->above
);
237 wc
.sibling
= sibling
->parent
;
240 do_window_changes(e
->value_mask
, &wc
, c
, 0);
242 LOG_XENTER("XConfigureWindow(window=%lx, value_mask=%lx)", (unsigned int)e
->window
, e
->value_mask
);
243 XConfigureWindow(dpy
, e
->window
, e
->value_mask
, &wc
);
248 static void handle_map_request(XMapRequestEvent
*e
) {
249 Client
*c
= find_client(e
->window
);
251 LOG_ENTER("handle_map_request(window=%lx)", e
->window
);
254 if (!is_fixed(c
) && c
->vdesk
!= c
->screen
->vdesk
)
255 switch_vdesk(c
->screen
, c
->vdesk
);
259 XWindowAttributes attr
;
260 XGetWindowAttributes(dpy
, e
->window
, &attr
);
261 make_new_client(e
->window
, find_screen(attr
.root
));
266 static void handle_unmap_event(XUnmapEvent
*e
) {
267 Client
*c
= find_client(e
->window
);
269 LOG_ENTER("handle_unmap_event(window=%lx)", e
->window
);
271 if (c
->ignore_unmap
) {
273 LOG_DEBUG("ignored (%d ignores remaining)\n", c
->ignore_unmap
);
275 LOG_DEBUG("flagging client for removal\n");
277 need_client_tidy
= 1;
280 LOG_DEBUG("unknown client!\n");
285 static void handle_colormap_change(XColormapEvent
*e
) {
286 Client
*c
= find_client(e
->window
);
289 c
->cmap
= e
->colormap
;
290 XInstallColormap(dpy
, c
->cmap
);
294 static void handle_property_change(XPropertyEvent
*e
) {
295 Client
*c
= find_client(e
->window
);
298 LOG_ENTER("handle_property_change(window=%lx, atom=%s)", e
->window
, debug_atom_name(e
->atom
));
299 if (e
->atom
== XA_WM_NORMAL_HINTS
) {
300 get_wm_normal_hints(c
);
301 LOG_DEBUG("geometry=%dx%d+%d+%d\n", c
->width
, c
->height
, c
->x
, c
->y
);
302 } else if (e
->atom
== xa_net_wm_window_type
) {
306 && (is_fixed(c
) || (c
->vdesk
== c
->screen
->vdesk
))
316 static void handle_enter_event(XCrossingEvent
*e
) {
319 if ((c
= find_client(e
->window
))) {
321 if (!is_fixed(c
) && c
->vdesk
!= c
->screen
->vdesk
)
325 ewmh_select_client(c
);
329 static void handle_mappingnotify_event(XMappingEvent
*e
) {
330 XRefreshKeyboardMapping(e
);
331 if (e
->request
== MappingKeyboard
) {
333 for (i
= 0; i
< num_screens
; i
++) {
334 grab_keys_for_screen(&screens
[i
]);
340 static void handle_shape_event(XShapeEvent
*e
) {
341 Client
*c
= find_client(e
->window
);
347 static void handle_client_message(XClientMessageEvent
*e
) {
349 ScreenInfo
*s
= find_current_screen();
353 LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", e
->window
, e
->format
, debug_atom_name(e
->message_type
));
356 if (e
->message_type
== xa_net_current_desktop
) {
357 switch_vdesk(s
, e
->data
.l
[0]);
362 c
= find_client(e
->window
);
363 if (!c
&& e
->message_type
== xa_net_request_frame_extents
) {
364 ewmh_set_net_frame_extents(e
->window
);
372 if (e
->message_type
== xa_net_active_window
) {
373 /* Only do this if it came from direct user action */
374 if (e
->data
.l
[0] == 2) {
383 if (e
->message_type
== xa_net_close_window
) {
384 /* Only do this if it came from direct user action */
385 if (e
->data
.l
[1] == 2) {
386 send_wm_delete(c
, 0);
391 if (e
->message_type
== xa_net_moveresize_window
) {
392 /* Only do this if it came from direct user action */
393 int source_indication
= (e
->data
.l
[0] >> 12) & 3;
394 if (source_indication
== 2) {
395 int value_mask
= (e
->data
.l
[0] >> 8) & 0x0f;
396 int gravity
= e
->data
.l
[0] & 0xff;
401 wc
.width
= e
->data
.l
[3];
402 wc
.height
= e
->data
.l
[4];
403 do_window_changes(value_mask
, &wc
, c
, gravity
);
408 if (e
->message_type
== xa_net_restack_window
) {
409 /* Only do this if it came from direct user action */
410 if (e
->data
.l
[0] == 2) {
413 wc
.sibling
= e
->data
.l
[1];
414 wc
.stack_mode
= e
->data
.l
[2];
415 do_window_changes(CWSibling
| CWStackMode
, &wc
, c
, c
->win_gravity
);
421 if (e
->message_type
== xa_net_wm_desktop
) {
422 /* Only do this if it came from direct user action */
423 if (e
->data
.l
[1] == 2) {
424 client_to_vdesk(c
, e
->data
.l
[0]);
430 if (e
->message_type
== xa_net_wm_state
) {
431 int i
, maximise_hv
= 0;
432 /* Message can contain up to two state changes: */
433 for (i
= 1; i
<= 2; i
++) {
434 if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_maximized_vert
) {
435 maximise_hv
|= MAXIMISE_VERT
;
436 } else if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_maximized_horz
) {
437 maximise_hv
|= MAXIMISE_HORZ
;
438 } else if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_fullscreen
) {
439 maximise_hv
|= MAXIMISE_VERT
|MAXIMISE_HORZ
;
443 maximise_client(c
, e
->data
.l
[0], maximise_hv
);
451 void event_main_loop(void) {
453 /* main event loop here */
455 if (interruptibleXNextEvent(&ev
)) {
458 handle_key_event(&ev
.xkey
); break;
461 handle_button_event(&ev
.xbutton
); break;
463 case ConfigureRequest
:
464 handle_configure_request(&ev
.xconfigurerequest
); break;
466 handle_map_request(&ev
.xmaprequest
); break;
468 handle_colormap_change(&ev
.xcolormap
); break;
470 handle_enter_event(&ev
.xcrossing
); break;
472 handle_property_change(&ev
.xproperty
); break;
474 handle_unmap_event(&ev
.xunmap
); break;
476 handle_mappingnotify_event(&ev
.xmapping
); break;
478 handle_client_message(&ev
.xclient
); break;
481 if (have_shape
&& ev
.type
== shape_event
) {
482 handle_shape_event((XShapeEvent
*)&ev
);
486 if (have_randr
&& ev
.type
== randr_event_base
+ RRScreenChangeNotify
) {
487 XRRUpdateConfiguration(&ev
);
493 if (need_client_tidy
) {
494 struct list
*iter
, *niter
;
495 need_client_tidy
= 0;
496 for (iter
= clients_tab_order
; iter
; iter
= niter
) {
497 Client
*c
= iter
->data
;
506 /* interruptibleXNextEvent() is taken from the Blender source and comes with
507 * the following copyright notice: */
509 /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */
511 /* This program is freely distributable without licensing fees
512 * and is provided without guarantee or warrantee expressed or
513 * implied. This program is -not- in the public domain. */
515 /* Unlike XNextEvent, if a signal arrives, interruptibleXNextEvent will
518 static int interruptibleXNextEvent(XEvent
*event
) {
521 int dpy_fd
= ConnectionNumber(dpy
);
524 XNextEvent(dpy
, event
);
528 FD_SET(dpy_fd
, &fds
);
529 rc
= select(dpy_fd
+ 1, &fds
, NULL
, NULL
, NULL
);
531 if (errno
== EINTR
) {
534 LOG_ERROR("interruptibleXNextEvent(): select()\n");