A tiling window manager
1/*
2 * X events
3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <strings.h>
24#include <signal.h>
25#include <errno.h>
26#include <err.h>
27#include <unistd.h>
28#include <poll.h>
29#include <sys/time.h>
30#include <sys/wait.h>
31
32#include <X11/X.h>
33#include <X11/Xlib.h>
34#include <X11/Xutil.h>
35#include <X11/Xatom.h>
36#include <X11/keysym.h>
37#include <X11/Xmd.h> /* for CARD32. */
38
39#include "sdorfehs.h"
40
41/*
42 * The event currently being processed. Mostly used in functions from action.c
43 * which need to forward events to other windows.
44 */
45XEvent rp_current_event;
46
47/* RAISED is non zero if a raised message should be used 0 for a map message. */
48void
49show_rudeness_msg(rp_window *win, int raised)
50{
51 rp_vscreen *v = win->vscreen;
52 rp_window_elem *elem = vscreen_find_window(&v->mapped_windows, win);
53
54 if (v == rp_current_vscreen) {
55 if (win->transient)
56 marked_message_printf(0, 0, raised ?
57 MESSAGE_RAISE_TRANSIENT : MESSAGE_MAP_TRANSIENT,
58 elem->number, window_name(win));
59 else
60 marked_message_printf(0, 0, raised ?
61 MESSAGE_RAISE_WINDOW : MESSAGE_MAP_WINDOW,
62 elem->number, window_name(win));
63 } else {
64 if (win->transient)
65 marked_message_printf(0, 0, raised ?
66 MESSAGE_RAISE_TRANSIENT_VSCREEN :
67 MESSAGE_MAP_TRANSIENT_VSCREEN,
68 elem->number, window_name(win),
69 win->vscreen->number);
70 else
71 marked_message_printf(0, 0, raised ?
72 MESSAGE_RAISE_WINDOW_VSCREEN :
73 MESSAGE_MAP_WINDOW_VSCREEN,
74 elem->number, window_name(win),
75 win->vscreen->number);
76 }
77}
78
79static void
80new_window(XCreateWindowEvent *e)
81{
82 rp_window *win;
83
84 if (e->override_redirect)
85 return;
86
87 if (is_rp_window(e->window))
88 return;
89
90 win = find_window(e->window);
91 if (win == NULL) {
92 /* We'll figure out which vscreen to put this window in later */
93 win = add_to_window_list(rp_current_screen, e->window);
94 }
95 update_window_information(win);
96
97 PRINT_DEBUG(("created new window\n"));
98}
99
100static void
101unmap_notify(XEvent *ev)
102{
103 rp_frame *frame;
104 rp_window *win;
105
106 /* ignore SubstructureNotify unmaps. */
107 if (ev->xunmap.event != ev->xunmap.window
108 && ev->xunmap.send_event != True)
109 return;
110
111 /* FIXME: Should we only look in the mapped window list? */
112 win = find_window_in_list(ev->xunmap.window, &rp_mapped_window);
113
114 if (win == NULL)
115 return;
116
117 switch (win->state) {
118 case IconicState:
119 PRINT_DEBUG(("Withdrawing iconized window '%s'\n",
120 window_name(win)));
121 if (ev->xunmap.send_event)
122 withdraw_window(win);
123 break;
124 case NormalState:
125 PRINT_DEBUG(("Withdrawing normal window '%s'\n",
126 window_name(win)));
127 /*
128 * If the window was inside a frame, fill the frame with
129 * another window.
130 */
131 frame = find_windows_frame(win);
132 if (frame) {
133 cleanup_frame(frame);
134 if (frame->number == win->vscreen->current_frame
135 && rp_current_vscreen == win->vscreen)
136 set_active_frame(frame, 0);
137 /* Since we may have switched windows, call the hook. */
138 if (frame->win_number != EMPTY)
139 hook_run(&rp_switch_win_hook);
140 }
141 withdraw_window(win);
142 break;
143 }
144
145 update_window_names(win->vscreen->screen, defaults.window_fmt);
146}
147
148static void
149map_request(Window window)
150{
151 rp_window *win;
152
153 win = find_window(window);
154 if (win == NULL) {
155 PRINT_DEBUG(("Map request from an unknown window.\n"));
156 XMapWindow(dpy, window);
157 return;
158 }
159
160 if (unmanaged_window(window)) {
161 PRINT_DEBUG(("Map request from an unmanaged window\n"));
162 unmanage(win);
163 XMapRaised(dpy, window);
164 return;
165 }
166
167 PRINT_DEBUG(("Map request from a managed window\n"));
168
169 switch (win->state) {
170 case WithdrawnState:
171 if (unmanaged_window(win->w)) {
172 PRINT_DEBUG(("Mapping Unmanaged Window\n"));
173 XMapWindow(dpy, win->w);
174 break;
175 } else {
176 PRINT_DEBUG(("Mapping Withdrawn Window\n"));
177 map_window(win);
178 break;
179 }
180 break;
181 case IconicState:
182 PRINT_DEBUG(("Mapping Iconic window\n"));
183
184 if (win->vscreen != rp_current_vscreen) {
185 if (!rp_honour_vscreen_switch) {
186 show_rudeness_msg(win, 1);
187 break;
188 }
189 PRINT_DEBUG(("honouring raise from other vscreen\n"));
190 }
191
192 /* Depending on the rudeness level, actually map the window. */
193 if (win->last_access == 0) {
194 if ((rp_honour_transient_map && win->transient)
195 || (rp_honour_normal_map && !win->transient)) {
196 set_current_vscreen(win->vscreen);
197 set_active_window(win);
198 }
199 } else {
200 if ((rp_honour_transient_raise && win->transient)
201 || (rp_honour_normal_raise && !win->transient)) {
202 set_current_vscreen(win->vscreen);
203 set_active_window(win);
204 } else
205 show_rudeness_msg(win, 1);
206 }
207 break;
208 default:
209 PRINT_DEBUG(("Map request for window in unknown state %d\n",
210 win->state));
211 }
212}
213
214static void
215destroy_window(XDestroyWindowEvent *ev)
216{
217 rp_window *win;
218
219 win = find_window(ev->window);
220 if (win == NULL)
221 return;
222
223 ignore_badwindow++;
224
225 /*
226 * If, somehow, the window is not withdrawn before it is destroyed,
227 * perform the necessary steps to withdraw the window before it is
228 * unmanaged.
229 */
230 if (win->state == IconicState) {
231 PRINT_DEBUG(("Destroying Iconic Window (%s)\n",
232 window_name(win)));
233 withdraw_window(win);
234 } else if (win->state == NormalState) {
235 rp_frame *frame;
236
237 PRINT_DEBUG(("Destroying Normal Window (%s)\n",
238 window_name(win)));
239 frame = find_windows_frame(win);
240 if (frame) {
241 cleanup_frame(frame);
242 if (frame->number == win->vscreen->current_frame
243 && rp_current_vscreen == win->vscreen)
244 set_active_frame(frame, 0);
245 /* Since we may have switched windows, call the hook. */
246 if (frame->win_number != EMPTY)
247 hook_run(&rp_switch_win_hook);
248 }
249 withdraw_window(win);
250 }
251 /*
252 * Now that the window is guaranteed to be in the unmapped window list,
253 * we can safely stop managing it.
254 */
255 unmanage(win);
256 ignore_badwindow--;
257}
258
259static void
260configure_request(XConfigureRequestEvent *e)
261{
262 XWindowChanges changes;
263 rp_window *win;
264
265 win = find_window(e->window);
266
267 if (win) {
268 if (e->value_mask & CWStackMode) {
269 if (e->detail == Above && win->state != WithdrawnState) {
270 if (win->vscreen == rp_current_vscreen) {
271 /*
272 * Depending on the rudeness level,
273 * actually map the window.
274 */
275 if ((rp_honour_transient_raise &&
276 win->transient) ||
277 (rp_honour_normal_raise &&
278 !win->transient)) {
279 if (win->state == IconicState)
280 set_active_window(win);
281 else if (find_windows_frame(win))
282 goto_window(win);
283 } else if (current_window() != win) {
284 show_rudeness_msg(win, 1);
285 }
286 } else {
287 show_rudeness_msg(win, 1);
288 }
289 }
290 PRINT_DEBUG(("request CWStackMode %d\n", e->detail));
291 }
292 PRINT_DEBUG(("'%s' window size: %d %d %d %d %d\n",
293 window_name(win), win->x, win->y, win->width, win->height,
294 win->border));
295
296 /* Collect the changes to be granted. */
297 if (e->value_mask & CWBorderWidth) {
298 changes.border_width = e->border_width;
299 win->border = e->border_width;
300 PRINT_DEBUG(("request CWBorderWidth %d\n",
301 e->border_width));
302 }
303 if (e->value_mask & CWWidth) {
304 changes.width = e->width;
305 win->width = e->width;
306 PRINT_DEBUG(("request CWWidth %d\n", e->width));
307 }
308 if (e->value_mask & CWHeight) {
309 changes.height = e->height;
310 win->height = e->height;
311 PRINT_DEBUG(("request CWHeight %d\n", e->height));
312 }
313 if (e->value_mask & CWX) {
314 changes.x = e->x;
315 win->x = e->x;
316 PRINT_DEBUG(("request CWX %d\n", e->x));
317 }
318 if (e->value_mask & CWY) {
319 changes.y = e->y;
320 win->y = e->y;
321 PRINT_DEBUG(("request CWY %d\n", e->y));
322 }
323 if (e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight)) {
324 /* Grant the request, then immediately maximize it. */
325 XConfigureWindow(dpy, win->w,
326 e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight),
327 &changes);
328 XSync(dpy, False);
329 if (win->state == NormalState)
330 maximize(win);
331 }
332 } else {
333 /*
334 * Its an unmanaged window, so give it what it wants. But don't
335 * change the stack mode.
336 */
337 if (e->value_mask & CWX)
338 changes.x = e->x;
339 if (e->value_mask & CWY)
340 changes.x = e->x;
341 if (e->value_mask & CWWidth)
342 changes.x = e->x;
343 if (e->value_mask & CWHeight)
344 changes.x = e->x;
345 if (e->value_mask & CWBorderWidth)
346 changes.x = e->x;
347 XConfigureWindow(dpy, e->window,
348 e->value_mask & (CWX|CWY|CWBorderWidth|CWWidth|CWHeight),
349 &changes);
350 }
351}
352
353static void
354client_msg(XClientMessageEvent *ev)
355{
356 rp_screen *s = find_screen(ev->window);
357 rp_window *win = NULL;
358
359 PRINT_DEBUG(("Received client message for window 0x%lx.\n", ev->window));
360
361 if (s == NULL) {
362 win = find_window_in_list(ev->window, &rp_mapped_window);
363 if (!win) {
364 PRINT_DEBUG(("can't find screen or window for "
365 "XClientMessageEvent\n"));
366 return;
367 }
368
369 s = win->vscreen->screen;
370 }
371
372 if (ev->window == s->root) {
373 if (ev->format != 32)
374 return;
375
376 if (ev->message_type == _net_current_desktop) {
377 rp_vscreen *v;
378
379 PRINT_DEBUG(("Received _NET_CURRENT_DESKTOP = %ld\n",
380 ev->data.l[0]));
381
382 v = screen_find_vscreen_by_number(s, ev->data.l[0]);
383 if (v)
384 set_current_vscreen(v);
385 } else {
386 warnx("unsupported message type %ld\n",
387 ev->message_type);
388 }
389
390 return;
391 }
392
393 if (ev->message_type == wm_change_state) {
394 rp_window *win;
395
396 PRINT_DEBUG(("WM_CHANGE_STATE\n"));
397
398 win = find_window(ev->window);
399 if (win == NULL)
400 return;
401 if (ev->format == 32 && ev->data.l[0] == IconicState) {
402 /*
403 * FIXME: This means clients can hide themselves
404 * without the user's intervention. This is bad, but
405 * Emacs is the only program I know of that iconifies
406 * itself and this is generally from the user pressing
407 * C-z.
408 */
409 PRINT_DEBUG(("Iconify Request.\n"));
410 if (win->state == NormalState) {
411 rp_window *w = find_window_other(win->vscreen);
412
413 if (w)
414 set_active_window(w);
415 else
416 blank_frame(vscreen_get_frame(
417 win->vscreen,
418 win->vscreen->current_frame));
419 }
420 } else {
421 warnx("non-standard WM_CHANGE_STATE format");
422 }
423 } else if (win && ev->message_type == _net_wm_state) {
424 PRINT_DEBUG(("_NET_WM_STATE for window 0x%lx, action %ld, "
425 "property 0x%lx\n", win->w, ev->data.l[0], ev->data.l[1]));
426
427 if (ev->data.l[1] == _net_wm_state_fullscreen) {
428 if (ev->data.l[0] == _NET_WM_STATE_ADD ||
429 (ev->data.l[0] == _NET_WM_STATE_TOGGLE &&
430 win->full_screen == 0))
431 window_full_screen(win);
432 else
433 window_full_screen(NULL);
434 }
435 } else if (win && ev->message_type == _net_active_window) {
436 PRINT_DEBUG(("_NET_ACTIVE_WINDOW raise: 0x%lx\n", win->w));
437 map_request(win->w);
438 } else {
439 PRINT_DEBUG(("unknown client message type 0x%lx\n",
440 ev->message_type));
441 }
442}
443
444static void
445handle_key(KeySym ks, unsigned int mod, rp_screen *s)
446{
447 rp_action *key_action;
448 rp_keymap *map = find_keymap(defaults.top_kmap);
449
450 if (map == NULL) {
451 warnx("unable to find %s keymap", defaults.top_kmap);
452 return;
453 }
454 PRINT_DEBUG(("handling key...\n"));
455
456 /* All functions hide the program bar and the frame indicator. */
457 if (defaults.bar_timeout > 0 && !defaults.bar_sticky)
458 hide_bar(s, 1);
459 hide_frame_indicator();
460
461 /* Disable any alarm that was going to go off. */
462 alarm(0);
463 alarm_signalled = 0;
464
465 /* Call the top level key pressed hook. */
466 hook_run(&rp_key_hook);
467
468 PRINT_DEBUG(("handle_key\n"));
469
470 /*
471 * Read a key and execute the command associated with it on the default
472 * keymap. Ignore the key if it doesn't have a binding.
473 */
474 if ((key_action = find_keybinding(ks, x11_mask_to_rp_mask(mod), map))) {
475 cmdret *result;
476
477 PRINT_DEBUG(("%s\n", key_action->data));
478
479 if (defaults.bar_sticky)
480 hide_bar(s, 0);
481
482 result = command(1, key_action->data);
483
484 if (result) {
485 if (result->output)
486 message(result->output);
487 cmdret_free(result);
488 }
489 } else {
490 if (defaults.bar_sticky)
491 hide_bar(s, 0);
492
493 PRINT_DEBUG(("Impossible: No matching key"));
494 }
495}
496
497static void
498key_press(XEvent *ev)
499{
500 rp_screen *s;
501 unsigned int modifier;
502 KeySym ks;
503
504 s = rp_current_screen;
505 if (!s)
506 return;
507
508 modifier = ev->xkey.state;
509 cook_keycode(&ev->xkey, &ks, &modifier, NULL, 0, 1);
510
511 handle_key(ks, modifier, s);
512}
513
514static void
515button_press(XEvent *ev)
516{
517 rp_screen *s;
518 XButtonEvent *xbe = (XButtonEvent *)ev;
519
520 s = rp_current_screen;
521 if (!s)
522 return;
523
524 if (xbe->window == s->bar_window)
525 bar_handle_click(s, xbe);
526}
527
528static void
529property_notify(XEvent *ev)
530{
531 rp_window *win;
532
533 PRINT_DEBUG(("atom: %ld (%s)\n", ev->xproperty.atom,
534 XGetAtomName(dpy, ev->xproperty.atom)));
535
536 win = find_window(ev->xproperty.window);
537 if (!win)
538 return;
539
540 if (ev->xproperty.atom == _net_wm_pid) {
541 struct rp_child_info *child_info;
542
543 PRINT_DEBUG(("updating _NET_WM_PID\n"));
544 child_info = get_child_info(win->w, 1);
545 if (child_info && !child_info->window_mapped) {
546 if (child_info->frame) {
547 PRINT_DEBUG(("frame=%p\n", child_info->frame));
548 win->intended_frame_number =
549 child_info->frame->number;
550 /*
551 * Only map the first window in the launch
552 * frame.
553 */
554 child_info->window_mapped = 1;
555 }
556 }
557 } else if (ev->xproperty.atom == XA_WM_NAME) {
558 PRINT_DEBUG(("updating window name\n"));
559 if (update_window_name(win)) {
560 update_window_names(win->vscreen->screen,
561 defaults.window_fmt);
562 hook_run(&rp_title_changed_hook);
563 }
564 } else if (ev->xproperty.atom == XA_WM_NORMAL_HINTS) {
565 PRINT_DEBUG(("updating window normal hints\n"));
566 update_normal_hints(win);
567 if (win->state == NormalState)
568 maximize(win);
569 } else if (ev->xproperty.atom == XA_WM_TRANSIENT_FOR) {
570 PRINT_DEBUG(("Transient for\n"));
571 win->transient = XGetTransientForHint(dpy, win->w,
572 &win->transient_for);
573 } else if (ev->xproperty.atom == _net_wm_state) {
574 check_state(win);
575 } else {
576 PRINT_DEBUG(("Unhandled property notify event: %ld\n",
577 ev->xproperty.atom));
578 }
579}
580
581static void
582colormap_notify(XEvent *ev)
583{
584 rp_window *win;
585
586 win = find_window(ev->xcolormap.window);
587
588 if (win != NULL) {
589 XWindowAttributes attr;
590
591 /*
592 * SDL sets the colormap just before destroying the window, so
593 * ignore BadWindow errors.
594 */
595 ignore_badwindow++;
596
597 XGetWindowAttributes(dpy, win->w, &attr);
598 win->colormap = attr.colormap;
599
600 if (win == current_window()
601 && !rp_current_screen->bar_is_raised) {
602 XInstallColormap(dpy, win->colormap);
603 }
604 ignore_badwindow--;
605 }
606}
607
608static void
609focus_change(XFocusChangeEvent *ev)
610{
611 rp_window *win;
612
613 /* We're only interested in the NotifyGrab mode */
614 if (ev->mode != NotifyGrab)
615 return;
616
617 win = find_window(ev->window);
618
619 if (win != NULL) {
620 PRINT_DEBUG(("Re-grabbing prefix key\n"));
621 grab_top_level_keys(win->w);
622 }
623}
624
625static void
626mapping_notify(XMappingEvent *ev)
627{
628 ungrab_keys_all_wins();
629
630 switch (ev->request) {
631 case MappingModifier:
632 update_modifier_map();
633 /* This is meant to fall through. */
634 case MappingKeyboard:
635 XRefreshKeyboardMapping(ev);
636 break;
637 }
638
639 grab_keys_all_wins();
640}
641
642static void
643configure_notify(XConfigureEvent *ev)
644{
645 rp_screen *s;
646
647 s = find_screen(ev->window);
648 if (s != NULL)
649 /*
650 * This is a root window of a screen, look if its width or
651 * height changed:
652 */
653 screen_update(s, ev->x, ev->y, ev->width, ev->height);
654}
655
656/*
657 * This is called when an application has requested the selection. Copied from
658 * rxvt.
659 */
660static void
661selection_request(XSelectionRequestEvent *rq)
662{
663 XEvent ev;
664 CARD32 target_list[4];
665 Atom target;
666 static Atom xa_targets = None;
667 static Atom xa_text = None; /* XXX */
668 XTextProperty ct;
669 XICCEncodingStyle style;
670 char *cl[4];
671
672 if (xa_text == None)
673 xa_text = XInternAtom(dpy, "TEXT", False);
674 if (xa_targets == None)
675 xa_targets = XInternAtom(dpy, "TARGETS", False);
676
677 ev.xselection.type = SelectionNotify;
678 ev.xselection.property = None;
679 ev.xselection.display = rq->display;
680 ev.xselection.requestor = rq->requestor;
681 ev.xselection.selection = rq->selection;
682 ev.xselection.target = rq->target;
683 ev.xselection.time = rq->time;
684
685 if (rq->target == xa_targets) {
686 target_list[0] = (CARD32) xa_targets;
687 target_list[1] = (CARD32) xa_string;
688 target_list[2] = (CARD32) xa_text;
689 target_list[3] = (CARD32) xa_compound_text;
690 XChangeProperty(dpy, rq->requestor, rq->property, rq->target,
691 (8 * sizeof(target_list[0])), PropModeReplace,
692 (unsigned char *) target_list,
693 (sizeof(target_list) / sizeof(target_list[0])));
694 ev.xselection.property = rq->property;
695 } else if (rq->target == xa_string
696 || rq->target == xa_compound_text
697 || rq->target == xa_text) {
698 if (rq->target == xa_string) {
699 style = XStringStyle;
700 target = xa_string;
701 } else {
702 target = xa_compound_text;
703 style = (rq->target == xa_compound_text) ?
704 XCompoundTextStyle : XStdICCTextStyle;
705 }
706 cl[0] = selection.text;
707 XmbTextListToTextProperty(dpy, cl, 1, style, &ct);
708 XChangeProperty(dpy, rq->requestor, rq->property,
709 target, 8, PropModeReplace,
710 ct.value, ct.nitems);
711 ev.xselection.property = rq->property;
712 }
713 XSendEvent(dpy, rq->requestor, False, 0, &ev);
714}
715
716static void
717selection_clear(void)
718{
719 free(selection.text);
720 selection.text = NULL;
721 selection.len = 0;
722}
723
724/* Given an event, call the correct function to handle it. */
725static void
726delegate_event(XEvent *ev)
727{
728 if (rp_have_xrandr)
729 xrandr_notify(ev);
730
731 switch (ev->type) {
732 case ConfigureRequest:
733 PRINT_DEBUG(("--- Handling ConfigureRequest ---\n"));
734 configure_request(&ev->xconfigurerequest);
735 break;
736
737 case CreateNotify:
738 PRINT_DEBUG(("--- Handling CreateNotify ---\n"));
739 new_window(&ev->xcreatewindow);
740 break;
741
742 case DestroyNotify:
743 PRINT_DEBUG(("--- Handling DestroyNotify ---\n"));
744 destroy_window(&ev->xdestroywindow);
745 break;
746
747 case ClientMessage:
748 PRINT_DEBUG(("--- Handling ClientMessage ---\n"));
749 client_msg(&ev->xclient);
750 break;
751
752 case ColormapNotify:
753 PRINT_DEBUG(("--- Handling ColormapNotify ---\n"));
754 colormap_notify(ev);
755 break;
756
757 case PropertyNotify:
758 PRINT_DEBUG(("--- Handling PropertyNotify ---\n"));
759 property_notify(ev);
760 break;
761
762 case MapRequest:
763 PRINT_DEBUG(("--- Handling MapRequest ---\n"));
764 map_request(ev->xmap.window);
765 break;
766
767 case KeyPress:
768 PRINT_DEBUG(("--- Handling KeyPress ---\n"));
769 key_press(ev);
770 break;
771
772 case ButtonPress:
773 PRINT_DEBUG(("--- Handling ButtonPress ---\n"));
774 button_press(ev);
775 break;
776
777 case UnmapNotify:
778 PRINT_DEBUG(("--- Handling UnmapNotify ---\n"));
779 unmap_notify(ev);
780 break;
781
782 case FocusOut:
783 PRINT_DEBUG(("--- Handling FocusOut ---\n"));
784 focus_change(&ev->xfocus);
785 break;
786
787 case FocusIn:
788 PRINT_DEBUG(("--- Handling FocusIn ---\n"));
789 focus_change(&ev->xfocus);
790 break;
791
792 case MappingNotify:
793 PRINT_DEBUG(("--- Handling MappingNotify ---\n"));
794 mapping_notify(&ev->xmapping);
795 break;
796
797 case SelectionRequest:
798 selection_request(&ev->xselectionrequest);
799 break;
800
801 case SelectionClear:
802 selection_clear();
803 break;
804
805 case ConfigureNotify:
806 if (!rp_have_xrandr) {
807 PRINT_DEBUG(("--- Handling ConfigureNotify ---\n"));
808 configure_notify(&ev->xconfigure);
809 }
810 break;
811
812 case MapNotify:
813 case Expose:
814 case MotionNotify:
815 case KeyRelease:
816 case ReparentNotify:
817 case EnterNotify:
818 case SelectionNotify:
819 case CirculateRequest:
820 /* Ignore these events. */
821 break;
822
823 default:
824 PRINT_DEBUG(("--- Unknown event %d ---\n", -ev->type));
825 }
826}
827
828static void
829handle_signals(void)
830{
831 /* An alarm means we need to hide the popup windows. */
832 if (alarm_signalled > 0) {
833 rp_screen *cur;
834
835 PRINT_DEBUG(("SIGALRM received\n"));
836
837 /* Only hide the bar if it times out. */
838 if (defaults.bar_timeout > 0) {
839 list_for_each_entry(cur, &rp_screens, node) {
840 hide_bar(cur, 0);
841 }
842 }
843 hide_frame_indicator();
844 alarm_signalled = 0;
845 }
846 if (chld_signalled > 0) {
847 rp_child_info *cur;
848 struct list_head *iter, *tmp;
849
850 /* Report and remove terminated processes. */
851 list_for_each_safe_entry(cur, iter, tmp, &rp_children, node) {
852 if (cur->terminated) {
853 /* Report any child that didn't return 0. */
854 if (cur->status != 0)
855 marked_message_printf(0, 0,
856 "/bin/sh -c \"%s\" finished (%d)",
857 cur->cmd, cur->status);
858 list_del(&cur->node);
859 free(cur->cmd);
860 free(cur);
861 }
862 }
863
864 chld_signalled = 0;
865 }
866 if (hup_signalled > 0) {
867 PRINT_DEBUG(("restarting\n"));
868 hook_run(&rp_restart_hook);
869 clean_up();
870 execvp(myargv[0], myargv);
871 }
872 if (kill_signalled > 0) {
873 PRINT_DEBUG(("exiting\n"));
874 hook_run(&rp_quit_hook);
875 clean_up();
876 exit(0);
877 }
878 /* Report any X11 errors that have occurred. */
879 if (rp_error_msg) {
880 marked_message_printf(0, 6, "X error: %s", rp_error_msg);
881 free(rp_error_msg);
882 rp_error_msg = NULL;
883 }
884}
885
886/* The main loop. */
887void
888listen_for_events(void)
889{
890 struct pollfd pfd[3];
891 int pollfifo = 1;
892
893 memset(&pfd, 0, sizeof(pfd));
894 pfd[0].fd = ConnectionNumber(dpy);
895 pfd[0].events = POLLIN;
896 pfd[1].fd = rp_glob_screen.control_socket_fd;
897 pfd[1].events = POLLIN;
898 pfd[2].fd = rp_glob_screen.bar_fifo_fd;
899 pfd[2].events = POLLIN;
900
901 /* Loop forever. */
902 for (;;) {
903 handle_signals();
904
905 if (!XPending(dpy)) {
906 if (pollfifo && rp_glob_screen.bar_fifo_fd == -1)
907 pollfifo = 0;
908 else if (pollfifo)
909 pfd[2].fd = rp_glob_screen.bar_fifo_fd;
910
911 poll(pfd, pollfifo ? 3 : 2, -1);
912
913 if (pollfifo && (pfd[2].revents & (POLLERR|POLLNVAL))) {
914 warnx("error polling on FIFO");
915 pollfifo = 0;
916 continue;
917 }
918
919 if (pollfifo && (pfd[2].revents & (POLLHUP|POLLIN)))
920 bar_read_fifo();
921
922 if (pfd[1].revents & (POLLHUP|POLLIN))
923 receive_command();
924
925 if (!XPending(dpy))
926 continue;
927 }
928
929 XNextEvent(dpy, &rp_current_event);
930 delegate_event(&rp_current_event);
931 XSync(dpy, False);
932 }
933}