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
:
135 show_info(c
, e
->keycode
);
138 maximise_client(c
, NET_WM_STATE_TOGGLE
, MAXIMISE_HORZ
|MAXIMISE_VERT
);
141 maximise_client(c
, NET_WM_STATE_TOGGLE
, MAXIMISE_VERT
);
146 client_to_vdesk(c
, current_screen
->vdesk
);
148 client_to_vdesk(c
, VDESK_FIXED
);
156 if (abs(c
->x
) == c
->border
&& c
->oldw
!= 0)
158 if (abs(c
->y
) == c
->border
&& c
->oldh
!= 0)
162 setmouse(c
->window
, c
->width
+ c
->border
- 1,
163 c
->height
+ c
->border
- 1);
165 discard_enter_events();
170 static void handle_button_event(XButtonEvent
*e
) {
171 Client
*c
= find_client(e
->window
);
180 client_lower(c
); break;
187 static void do_window_changes(int value_mask
, XWindowChanges
*wc
, Client
*c
,
190 if (value_mask
& CWX
) c
->x
= wc
->x
;
191 if (value_mask
& CWY
) c
->y
= wc
->y
;
192 if (value_mask
& CWWidth
) {
193 c
->width
= wc
->width
;
194 if (c
->width
< c
->min_width
)
195 c
->width
= c
->min_width
;
196 if (c
->max_width
&& c
->width
> c
->max_width
)
197 c
->width
= c
->max_width
;
199 if (value_mask
& CWHeight
) {
200 c
->height
= wc
->height
;
201 if (c
->height
< c
->min_height
)
202 c
->height
= c
->min_height
;
203 if (c
->max_height
&& c
->height
> c
->max_height
)
204 c
->height
= c
->max_height
;
206 if (c
->x
== 0 && c
->width
>= DisplayWidth(dpy
, c
->screen
->screen
)) {
209 if (c
->y
== 0 && c
->height
>= DisplayHeight(dpy
, c
->screen
->screen
)) {
212 gravitate(c
, gravity
);
213 wc
->x
= c
->x
- c
->border
;
214 wc
->y
= c
->y
- c
->border
;
215 wc
->border_width
= c
->border
;
216 XConfigureWindow(dpy
, c
->parent
, value_mask
, wc
);
217 XMoveResizeWindow(dpy
, c
->window
, 0, 0, c
->width
, c
->height
);
218 if ((value_mask
& (CWX
|CWY
)) && !(value_mask
& (CWWidth
|CWHeight
))) {
223 static void handle_configure_request(XConfigureRequestEvent
*e
) {
224 Client
*c
= find_client(e
->window
);
230 wc
.height
= e
->height
;
232 wc
.sibling
= e
->above
;
233 wc
.stack_mode
= e
->detail
;
235 if (e
->value_mask
& CWStackMode
&& e
->value_mask
& CWSibling
) {
236 Client
*sibling
= find_client(e
->above
);
238 wc
.sibling
= sibling
->parent
;
241 do_window_changes(e
->value_mask
, &wc
, c
, 0);
243 LOG_XENTER("XConfigureWindow(window=%lx, value_mask=%lx)", (unsigned int)e
->window
, e
->value_mask
);
244 XConfigureWindow(dpy
, e
->window
, e
->value_mask
, &wc
);
249 static void handle_map_request(XMapRequestEvent
*e
) {
250 Client
*c
= find_client(e
->window
);
252 LOG_ENTER("handle_map_request(window=%lx)", e
->window
);
255 if (!is_fixed(c
) && c
->vdesk
!= c
->screen
->vdesk
)
256 switch_vdesk(c
->screen
, c
->vdesk
);
261 XWindowAttributes attr
;
262 XGetWindowAttributes(dpy
, e
->window
, &attr
);
263 make_new_client(e
->window
, find_screen(attr
.root
));
268 static void handle_unmap_event(XUnmapEvent
*e
) {
269 Client
*c
= find_client(e
->window
);
271 LOG_ENTER("handle_unmap_event(window=%lx)", e
->window
);
273 if (c
->ignore_unmap
) {
275 LOG_DEBUG("ignored (%d ignores remaining)\n", c
->ignore_unmap
);
277 LOG_DEBUG("flagging client for removal\n");
279 need_client_tidy
= 1;
282 LOG_DEBUG("unknown client!\n");
287 static void handle_colormap_change(XColormapEvent
*e
) {
288 Client
*c
= find_client(e
->window
);
291 c
->cmap
= e
->colormap
;
292 XInstallColormap(dpy
, c
->cmap
);
296 static void handle_property_change(XPropertyEvent
*e
) {
297 Client
*c
= find_client(e
->window
);
300 LOG_ENTER("handle_property_change(window=%lx, atom=%s)", e
->window
, debug_atom_name(e
->atom
));
301 if (e
->atom
== XA_WM_NORMAL_HINTS
) {
302 get_wm_normal_hints(c
);
303 LOG_DEBUG("geometry=%dx%d+%d+%d\n", c
->width
, c
->height
, c
->x
, c
->y
);
304 } else if (e
->atom
== xa_net_wm_window_type
) {
308 && (is_fixed(c
) || (c
->vdesk
== c
->screen
->vdesk
))
318 static void handle_enter_event(XCrossingEvent
*e
) {
321 if ((c
= find_client(e
->window
))) {
323 if (!is_fixed(c
) && c
->vdesk
!= c
->screen
->vdesk
)
327 ewmh_select_client(c
);
331 static void handle_mappingnotify_event(XMappingEvent
*e
) {
332 XRefreshKeyboardMapping(e
);
333 if (e
->request
== MappingKeyboard
) {
335 for (i
= 0; i
< num_screens
; i
++) {
336 grab_keys_for_screen(&screens
[i
]);
342 static void handle_shape_event(XShapeEvent
*e
) {
343 Client
*c
= find_client(e
->window
);
349 static void handle_client_message(XClientMessageEvent
*e
) {
351 ScreenInfo
*s
= find_current_screen();
355 LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", e
->window
, e
->format
, debug_atom_name(e
->message_type
));
358 if (e
->message_type
== xa_net_current_desktop
) {
359 switch_vdesk(s
, e
->data
.l
[0]);
364 c
= find_client(e
->window
);
365 if (!c
&& e
->message_type
== xa_net_request_frame_extents
) {
366 ewmh_set_net_frame_extents(e
->window
);
374 if (e
->message_type
== xa_net_active_window
) {
375 /* Only do this if it came from direct user action */
376 if (e
->data
.l
[0] == 2) {
385 if (e
->message_type
== xa_net_close_window
) {
386 /* Only do this if it came from direct user action */
387 if (e
->data
.l
[1] == 2) {
388 send_wm_delete(c
, 0);
393 if (e
->message_type
== xa_net_moveresize_window
) {
394 /* Only do this if it came from direct user action */
395 int source_indication
= (e
->data
.l
[0] >> 12) & 3;
396 if (source_indication
== 2) {
397 int value_mask
= (e
->data
.l
[0] >> 8) & 0x0f;
398 int gravity
= e
->data
.l
[0] & 0xff;
403 wc
.width
= e
->data
.l
[3];
404 wc
.height
= e
->data
.l
[4];
405 do_window_changes(value_mask
, &wc
, c
, gravity
);
410 if (e
->message_type
== xa_net_restack_window
) {
411 /* Only do this if it came from direct user action */
412 if (e
->data
.l
[0] == 2) {
415 wc
.sibling
= e
->data
.l
[1];
416 wc
.stack_mode
= e
->data
.l
[2];
417 do_window_changes(CWSibling
| CWStackMode
, &wc
, c
, c
->win_gravity
);
423 if (e
->message_type
== xa_net_wm_desktop
) {
424 /* Only do this if it came from direct user action */
425 if (e
->data
.l
[1] == 2) {
426 client_to_vdesk(c
, e
->data
.l
[0]);
432 if (e
->message_type
== xa_net_wm_state
) {
433 int i
, maximise_hv
= 0;
434 /* Message can contain up to two state changes: */
435 for (i
= 1; i
<= 2; i
++) {
436 if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_maximized_vert
) {
437 maximise_hv
|= MAXIMISE_VERT
;
438 } else if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_maximized_horz
) {
439 maximise_hv
|= MAXIMISE_HORZ
;
440 } else if ((Atom
)e
->data
.l
[i
] == xa_net_wm_state_fullscreen
) {
441 maximise_hv
|= MAXIMISE_VERT
|MAXIMISE_HORZ
;
445 maximise_client(c
, e
->data
.l
[0], maximise_hv
);
453 void event_main_loop(void) {
455 /* main event loop here */
457 if (interruptibleXNextEvent(&ev
)) {
460 handle_key_event(&ev
.xkey
); break;
463 handle_button_event(&ev
.xbutton
); break;
465 case ConfigureRequest
:
466 handle_configure_request(&ev
.xconfigurerequest
); break;
468 handle_map_request(&ev
.xmaprequest
); break;
470 handle_colormap_change(&ev
.xcolormap
); break;
472 handle_enter_event(&ev
.xcrossing
); break;
474 handle_property_change(&ev
.xproperty
); break;
476 handle_unmap_event(&ev
.xunmap
); break;
478 handle_mappingnotify_event(&ev
.xmapping
); break;
480 handle_client_message(&ev
.xclient
); break;
483 if (have_shape
&& ev
.type
== shape_event
) {
484 handle_shape_event((XShapeEvent
*)&ev
);
488 if (have_randr
&& ev
.type
== randr_event_base
+ RRScreenChangeNotify
) {
489 XRRUpdateConfiguration(&ev
);
495 if (need_client_tidy
) {
496 struct list
*iter
, *niter
;
497 need_client_tidy
= 0;
498 for (iter
= clients_tab_order
; iter
; iter
= niter
) {
499 Client
*c
= iter
->data
;
508 /* interruptibleXNextEvent() is taken from the Blender source and comes with
509 * the following copyright notice: */
511 /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */
513 /* This program is freely distributable without licensing fees
514 * and is provided without guarantee or warrantee expressed or
515 * implied. This program is -not- in the public domain. */
517 /* Unlike XNextEvent, if a signal arrives, interruptibleXNextEvent will
520 static int interruptibleXNextEvent(XEvent
*event
) {
523 int dpy_fd
= ConnectionNumber(dpy
);
526 XNextEvent(dpy
, event
);
530 FD_SET(dpy_fd
, &fds
);
531 rc
= select(dpy_fd
+ 1, &fds
, NULL
, NULL
, NULL
);
533 if (errno
== EINTR
) {
536 LOG_ERROR("interruptibleXNextEvent(): select()\n");