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