A tiling window manager
at master 933 lines 22 kB view raw
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}