event_handlers.c revision 0bbfda8a
1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * Copyright 1992 Claude Lecommandeur. 8 */ 9 10/*********************************************************************** 11 * 12 * $XConsortium: events.c,v 1.182 91/07/17 13:59:14 dave Exp $ 13 * 14 * twm event handling 15 * 16 * 17-Nov-87 Thomas E. LaStrange File created 17 * 18 * Do the necessary modification to be integrated in ctwm. 19 * Can no longer be used for the standard twm. 20 * 21 * 22-April-92 Claude Lecommandeur. 22 * 23 * 24 ***********************************************************************/ 25 26#include "ctwm.h" 27 28#include <stdio.h> 29#include <stdlib.h> 30#include <sys/time.h> 31 32#include <X11/Xatom.h> 33#include <X11/extensions/shape.h> 34 35#include "add_window.h" 36#include "animate.h" 37#include "clicktofocus.h" 38#include "colormaps.h" 39#include "ctwm_atoms.h" 40#include "events.h" 41#include "event_handlers.h" 42#include "event_internal.h" 43#include "event_names.h" 44#include "functions.h" 45#include "functions_defs.h" 46#include "gram.tab.h" 47#include "iconmgr.h" 48#include "icons.h" 49#include "image.h" 50#include "list.h" 51#include "occupation.h" 52#include "otp.h" 53#include "parse.h" 54#include "screen.h" 55#include "util.h" 56#include "vscreen.h" 57#include "win_decorations.h" 58#include "win_iconify.h" 59#include "win_ops.h" 60#include "win_regions.h" 61#include "win_resize.h" 62#include "win_utils.h" 63#include "workspace_manager.h" 64#include "workspace_utils.h" 65 66 67static void do_key_menu(MenuRoot *menu, /* menu to pop up */ 68 Window w); /* invoking window or None */ 69 70/* Only called from HandleFocusChange() */ 71static void HandleFocusIn(void); 72static void HandleFocusOut(void); 73 74/* 75 * This currently needs to live in the broader scope because of how it's 76 * used in deferred function handling. 77 */ 78static char *Action; 79 80static TwmWindow *ButtonWindow; /* button press window structure */ 81 82static void SendTakeFocusMessage(TwmWindow *tmp, Time timestamp); 83 84 85static unsigned int set_mask_ignore(unsigned int modifier) 86{ 87 modifier &= ~Scr->IgnoreModifier; 88 89 return modifier; 90} 91 92 93/*********************************************************************** 94 * 95 * Procedure: 96 * HandleColormapNotify - colormap notify event handler 97 * 98 * This procedure handles both a client changing its own colormap, and 99 * a client explicitly installing its colormap itself (only the window 100 * manager should do that, so we must set it correctly). 101 * 102 *********************************************************************** 103 */ 104 105void HandleColormapNotify(void) 106{ 107 XColormapEvent *cevent = (XColormapEvent *) &Event; 108 ColormapWindow *cwin, **cwins; 109 TwmColormap *cmap; 110 int lost, won, n, number_cwins; 111 112 /* if (! Tmp_win) return; */ 113 if(XFindContext(dpy, cevent->window, ColormapContext, 114 (XPointer *)&cwin) == XCNOENT) { 115 return; 116 } 117 cmap = cwin->colormap; 118 119 if(cevent->new) { 120 if(XFindContext(dpy, cevent->colormap, ColormapContext, 121 (XPointer *)&cwin->colormap) == XCNOENT) { 122 cwin->colormap = CreateTwmColormap(cevent->colormap); 123 } 124 else { 125 cwin->colormap->refcnt++; 126 } 127 128 cmap->refcnt--; 129 130 if(cevent->state == ColormapUninstalled) { 131 cmap->state &= ~CM_INSTALLED; 132 } 133 else { 134 cmap->state |= CM_INSTALLED; 135 } 136 137 if(cmap->state & CM_INSTALLABLE) { 138 InstallColormaps(ColormapNotify, NULL); 139 } 140 141 if(cmap->refcnt == 0) { 142 XDeleteContext(dpy, cmap->c, ColormapContext); 143 free(cmap); 144 } 145 146 return; 147 } 148 149 if(cevent->state == ColormapUninstalled && 150 (cmap->state & CM_INSTALLABLE)) { 151 if(!(cmap->state & CM_INSTALLED)) { 152 return; 153 } 154 cmap->state &= ~CM_INSTALLED; 155 156 if(!ColortableThrashing) { 157 ColortableThrashing = true; 158 XSync(dpy, 0); 159 } 160 161 if(cevent->serial >= Scr->cmapInfo.first_req) { 162 number_cwins = Scr->cmapInfo.cmaps->number_cwins; 163 164 /* 165 * Find out which colortables collided. 166 */ 167 168 cwins = Scr->cmapInfo.cmaps->cwins; 169 for(lost = won = -1, n = 0; 170 (lost == -1 || won == -1) && n < number_cwins; 171 n++) { 172 if(lost == -1 && cwins[n] == cwin) { 173 lost = n; /* This is the window which lost its colormap */ 174 continue; 175 } 176 177 if(won == -1 && 178 cwins[n]->colormap->install_req == cevent->serial) { 179 won = n; /* This is the window whose colormap caused */ 180 continue; /* the de-install of the previous colormap */ 181 } 182 } 183 184 /* 185 ** Cases are: 186 ** Both the request and the window were found: 187 ** One of the installs made honoring the WM_COLORMAP 188 ** property caused another of the colormaps to be 189 ** de-installed, just mark the scoreboard. 190 ** 191 ** Only the request was found: 192 ** One of the installs made honoring the WM_COLORMAP 193 ** property caused a window not in the WM_COLORMAP 194 ** list to lose its map. This happens when the map 195 ** it is losing is one which is trying to be installed, 196 ** but is getting getting de-installed by another map 197 ** in this case, we'll get a scoreable event later, 198 ** this one is meaningless. 199 ** 200 ** Neither the request nor the window was found: 201 ** Somebody called installcolormap, but it doesn't 202 ** affect the WM_COLORMAP windows. This case will 203 ** probably never occur. 204 ** 205 ** Only the window was found: 206 ** One of the WM_COLORMAP windows lost its colormap 207 ** but it wasn't one of the requests known. This is 208 ** probably because someone did an "InstallColormap". 209 ** The colormap policy is "enforced" by re-installing 210 ** the colormaps which are believed to be correct. 211 */ 212 213 if(won != -1) { 214 if(lost != -1) { 215 /* lower diagonal index calculation */ 216 if(lost > won) { 217 n = lost * (lost - 1) / 2 + won; 218 } 219 else { 220 n = won * (won - 1) / 2 + lost; 221 } 222 Scr->cmapInfo.cmaps->scoreboard[n] = 1; 223 } 224 else { 225 /* 226 ** One of the cwin installs caused one of the cwin 227 ** colormaps to be de-installed, so I'm sure to get an 228 ** UninstallNotify for the cwin I know about later. 229 ** I haven't got it yet, or the test of CM_INSTALLED 230 ** above would have failed. Turning the CM_INSTALLED 231 ** bit back on makes sure we get back here to score 232 ** the collision. 233 */ 234 cmap->state |= CM_INSTALLED; 235 } 236 } 237 else if(lost != -1) { 238 InstallColormaps(ColormapNotify, NULL); 239 } 240 else { 241 ColortableThrashing = false; /* Gross Hack for HP WABI. CL. */ 242 } 243 } 244 } 245 246 else if(cevent->state == ColormapUninstalled) { 247 cmap->state &= ~CM_INSTALLED; 248 } 249 250 else if(cevent->state == ColormapInstalled) { 251 cmap->state |= CM_INSTALLED; 252 } 253} 254 255 256/* 257 * LastFocusEvent -- skip over focus in/out events for this 258 * window. 259 */ 260 261static XEvent *LastFocusEvent(Window w, XEvent *first) 262{ 263 static XEvent current; 264 XEvent *last, new; 265 266 new = *first; 267 last = NULL; 268 269 do { 270 if((new.type == FocusIn || new.type == FocusOut) 271 && new.xfocus.mode == NotifyNormal 272 && (new.xfocus.detail == NotifyNonlinear 273 || new.xfocus.detail == NotifyPointer 274 || new.xfocus.detail == NotifyAncestor 275 || (new.xfocus.detail == NotifyNonlinearVirtual) 276 )) { 277 current = new; 278 last = ¤t; 279 } 280 281#ifdef TRACE_FOCUS 282 fprintf(stderr, "%s(): Focus%s 0x%x mode=%d, detail=%d\n", 283 __func__, new.xfocus.type == FocusIn ? "In" : "Out", 284 Tmp_win, new.xfocus.mode, new.xfocus.detail); 285#endif 286 287 } 288 while(XCheckWindowEvent(dpy, w, FocusChangeMask, &new)); 289 return last; 290} 291 292 293/* 294 * Focus change handlers. 295 * 296 * Depending on how events get called, these are sometimes redundant, as 297 * the Enter event handler does practically all of this anyway. But 298 * there are presumably ways we can wind up Focus'ing a window without 299 * Enter'ing it as well. 300 * 301 * It's also a little convoluted how these wind up getting called. With 302 * most events, we call a handler, then handle that event. However, with 303 * focus, we troll through our list of pending Focus-related events for 304 * the window and just handle the last one, since some could pile up 305 * fast. That means that, even if we get called for a FocusIn event, 306 * there might be a FocusOut later in the queue, and _that_'s the one we 307 * pick up and handle, and we discard the rest [for that window]. So, 308 * the event handling code calls a single entry point for both types, and 309 * then it figures out which backend handler to actually fire. 310 */ 311void 312HandleFocusChange(void) 313{ 314 XEvent *event; 315 316 /* If there's no event window, nothing to do */ 317 if(!Tmp_win) { 318 return; 319 } 320 321 /* 322 * Consume all the focus events for the window we're called about and 323 * grab the last one to process. 324 * 325 * XXX It should be guaranteed that the window in the X event in our 326 * global Event is the same as Tmp_win->w as the event dispatcher 327 * sets it so. Maybe we should do both checks on the same var for 328 * consistency though? 329 * 330 * It's not immediately clear how this can wind up returning nothing, 331 * but if it does, we don't have anything to do either. 332 */ 333 event = LastFocusEvent(Event.xany.window, &Event); 334 if(event == NULL) { 335 return; 336 } 337 338 /* 339 * Icon managers don't do anything with focus events on themselves, 340 * so just skip back if this is one. Done after LastFocusEvent() 341 * call for efficiency, so we don't fall into this func multiple 342 * times if multiple events are queued for it. 343 */ 344 if(Tmp_win->isiconmgr) { 345 return; 346 } 347 348#ifdef TRACE_FOCUS 349 fprintf(stderr, "HandleFocus%s(): 0x%x (0x%x, 0x%x), mode=%d, " 350 "detail=%d\n", 351 (event->type == FocusIn ? "In" : "Out"), 352 Tmp_win, Tmp_win->w, event->window, event->mode, 353 event->detail); 354#endif 355 356 /* And call actual handler */ 357 if(event->type == FocusIn) { 358 HandleFocusIn(); 359 } 360 else { 361 HandleFocusOut(); 362 } 363} 364 365 366static void 367HandleFocusIn(void) 368{ 369 if(! Tmp_win->wmhints->input) { 370 return; 371 } 372 if(Scr->Focus == Tmp_win) { 373 return; 374 } 375 376#ifdef EWMH 377 // Handle focus-dependent re-stacking of what we're moving out of. 378 if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) { 379 OtpUnfocusWindow(Scr->Focus); 380 // NULL's Scr->Focus 381 } 382#endif 383 384 if(Tmp_win->AutoSqueeze && Tmp_win->squeezed) { 385 AutoSqueeze(Tmp_win); 386 } 387 SetFocusVisualAttributes(Tmp_win, true); 388 389#ifdef EWMH 390 // Handle focus-dependent re-stacking of what we're moving in to. 391 if(Tmp_win && OtpIsFocusDependent(Tmp_win)) { 392 OtpFocusWindow(Tmp_win); 393 // Sets Scr->Focus 394 } 395#endif 396 397 // Redundant in EWMH case 398 Scr->Focus = Tmp_win; 399} 400 401 402static void 403HandleFocusOut(void) 404{ 405 if(Scr->Focus != Tmp_win) { 406 return; 407 } 408 if(Scr->SloppyFocus) { 409 return; 410 } 411 if(Tmp_win->AutoSqueeze && !Tmp_win->squeezed) { 412 AutoSqueeze(Tmp_win); 413 } 414 SetFocusVisualAttributes(Tmp_win, false); 415 416#ifdef EWMH 417 /* 418 * X-ref HandleFocusIn() comment. FocusOut is only leaving a window, 419 * not entering a new one, so there's only one we may need to 420 * restack. 421 */ 422 if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) { 423 OtpUnfocusWindow(Scr->Focus); 424 // NULL's Scr->Focus 425 } 426#endif 427 428 // Redundant in EWMH case 429 Scr->Focus = NULL; 430} 431 432 433 434/* 435 * Only sent if SubstructureNotifyMask is selected on the (root) window. 436 */ 437void HandleCirculateNotify() 438{ 439 VirtualScreen *vs; 440#ifdef DEBUG_EVENTS 441 fprintf(stderr, "HandleCirculateNotify\n"); 442 fprintf(stderr, "event=%x window=%x place=%d\n", 443 (unsigned)Event.xcirculate.event, 444 (unsigned)Event.xcirculate.window, 445 Event.xcirculate.place); 446#endif 447 448 for(vs = Scr->vScreenList; vs; vs = vs->next) { 449 if(Event.xcirculate.event == vs->window) { 450 TwmWindow *twm_win = GetTwmWindow(Event.xcirculate.window); 451 452 if(twm_win) { 453 WinType wt; 454 455 if(Event.xcirculate.window == twm_win->frame) { 456 wt = WinWin; 457 } 458 else if(twm_win->icon && 459 Event.xcirculate.window == twm_win->icon->w) { 460 wt = IconWin; 461 } 462 else { 463 return; 464 } 465 466 OtpHandleCirculateNotify(vs, 467 twm_win, wt, 468 Event.xcirculate.place); 469 } 470 } 471 } 472} 473 474/*********************************************************************** 475 * 476 * Procedure: 477 * HandleVisibilityNotify - visibility notify event handler 478 * 479 * This routine keeps track of visibility events so that colormap 480 * installation can keep the maximum number of useful colormaps 481 * installed at one time. 482 * 483 *********************************************************************** 484 */ 485 486void HandleVisibilityNotify(void) 487{ 488 XVisibilityEvent *vevent = (XVisibilityEvent *) &Event; 489 ColormapWindow *cwin; 490 TwmColormap *cmap; 491 492 if(XFindContext(dpy, vevent->window, ColormapContext, 493 (XPointer *)&cwin) == XCNOENT) { 494 return; 495 } 496 497 /* 498 * when Saber complains about retreiving an <int> from an <unsigned int> 499 * just type "touch vevent->state" and "cont" 500 */ 501 cmap = cwin->colormap; 502 if((cmap->state & CM_INSTALLABLE) && 503 vevent->state != cwin->visibility && 504 (vevent->state == VisibilityFullyObscured || 505 cwin->visibility == VisibilityFullyObscured) && 506 cmap->w == cwin->w) { 507 cwin->visibility = vevent->state; 508 InstallWindowColormaps(VisibilityNotify, NULL); 509 } 510 else { 511 cwin->visibility = vevent->state; 512 } 513} 514 515 516/*********************************************************************** 517 * 518 * Procedure: 519 * HandleKeyRelease - key release event handler 520 * 521 *********************************************************************** 522 */ 523 524void HandleKeyRelease(void) 525{ 526 if(Tmp_win == Scr->currentvs->wsw->twm_win) { 527 WMgrHandleKeyReleaseEvent(Scr->currentvs, &Event); 528 } 529} 530 531 532 533/* 534 * HandleKeyPress - key press event handler 535 * 536 * When a key is pressed, we may do various things with it. If we're in 537 * a menu, various keybindings move around in it, others get silently 538 * ignored. Else, we look through the various bindings set in the config 539 * file and invoke whatever should be. If none of that matches, and it 540 * seems like some window should have focus, pass the event down to that 541 * window. 542 */ 543void HandleKeyPress(void) 544{ 545 /* 546 * If the Info window (f.identify/f.version) is currently up, any key 547 * press will drop it away. 548 */ 549 if(Scr->InfoWindow.mapped) { 550 XUnmapWindow(dpy, Scr->InfoWindow.win); 551 Scr->InfoWindow.mapped = false; 552 } 553 554 555 /* 556 * If a menu is up, we interpret various keys as moving around or 557 * doing things in the menu. No other key bindings or usages are 558 * considered. 559 */ 560 if(ActiveMenu != NULL) { 561 MenuItem *item; 562 char *keynam; 563 KeySym keysym; 564 Window junkW; 565 566 item = NULL; 567 568 /* What key was pressed? */ 569 keysym = XLookupKeysym((XKeyEvent *) &Event, 0); 570 if(! keysym) { 571 return; 572 } 573 keynam = XKeysymToString(keysym); 574 if(! keynam) { 575 return; 576 } 577 578 579 /* 580 * Initial handling of the various keystrokes. Most keys are 581 * completely handled here; we only descend out into later for 582 * for Return/Right keys that do invoke-y stuff on menu entries. 583 */ 584 if(keysym == XK_Down || keysym == XK_space) { 585 /* 586 * Down or Spacebar moves us to the next entry in the menu, 587 * looping back around to the top when it falls off the 588 * bottom. 589 * 590 * Start with our X and (current+height)Y, then wrap around 591 * to the top (Y)/into the menu (X) as necessary. 592 */ 593 int xx = Event.xkey.x; 594 int yy = Event.xkey.y + Scr->EntryHeight; 595 int wx, wy; 596 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW); 597 if((wy < 0) || (wy > ActiveMenu->height)) { 598 yy -= (wy - (Scr->EntryHeight / 2) - 2); 599 } 600 if((wx < 0) || (wx > ActiveMenu->width)) { 601 xx -= (wx - (ActiveMenu->width / 2)); 602 } 603 604 /* 605 * Move the pointer there. We'll get a Motion notify from 606 * the X server as a result, which will fall into the loop in 607 * UpdateMenu() and handle re-highlighting etc. 608 */ 609 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y, 610 ActiveMenu->width, ActiveMenu->height, xx, yy); 611 return; 612 } 613 else if(keysym == XK_Up || keysym == XK_BackSpace) { 614 /* 615 * Up/Backspace move up an entry, with details similar in 616 * reverse to the above. 617 */ 618 int xx = Event.xkey.x; 619 int yy = Event.xkey.y - Scr->EntryHeight; 620 int wx, wy; 621 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW); 622 if((wy < 0) || (wy > ActiveMenu->height)) { 623 yy -= (wy - ActiveMenu->height + (Scr->EntryHeight / 2) + 2); 624 } 625 if((wx < 0) || (wx > ActiveMenu->width)) { 626 xx -= (wx - (ActiveMenu->width / 2)); 627 } 628 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y, 629 ActiveMenu->width, ActiveMenu->height, xx, yy); 630 return; 631 } 632 else if(keysym == XK_Right || keysym == XK_Return) { 633 /* 634 * Right/Return mean we're invoking some entry item, so we 635 * take note of where we are for activating at the end of 636 * this set of conditionals. 637 * 638 * Follow this down into the following if(item) block for 639 * details, particularly in the subtle differences between 640 * Right and Return on f.menu entries. 641 */ 642 item = ActiveItem; 643 } 644 else if(keysym == XK_Left || keysym == XK_Escape) { 645 /* 646 * Left/Escape back up to a higher menu level, or out totally 647 * from the top. 648 */ 649 MenuRoot *menu; 650 651 /* Leave pinned menus alone though */ 652 if(ActiveMenu->pinned) { 653 return; 654 } 655 656 /* Top-level? Clear out and leave menu mode totally. */ 657 if(!ActiveMenu->prev || MenuDepth == 1) { 658 PopDownMenu(); 659 XUngrabPointer(dpy, CurrentTime); 660 return; 661 } 662 663 /* 664 * We're in a sub level. Figure out various stuff for where 665 * we are and where we should be in the up-level, clear out 666 * the windows for this level, and warp us up there. 667 */ 668 int xx = Event.xkey.x; 669 int yy = Event.xkey.y; 670 int wx, wy; 671 menu = ActiveMenu->prev; 672 XTranslateCoordinates(dpy, Scr->Root, menu->w, xx, yy, &wx, &wy, &junkW); 673 xx -= (wx - (menu->width / 2)); 674 if(menu->lastactive) 675 yy -= (wy - menu->lastactive->item_num * Scr->EntryHeight - 676 (Scr->EntryHeight / 2) - 2); 677 else { 678 yy -= (wy - (Scr->EntryHeight / 2) - 2); 679 } 680 XUnmapWindow(dpy, ActiveMenu->w); 681 if(Scr->Shadow) { 682 XUnmapWindow(dpy, ActiveMenu->shadow); 683 } 684 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y, 685 menu->width, menu->height, xx, yy); 686 return; 687 } 688 else if(strlen(keynam) == 1) { 689 /* 690 * This would mean pressing a more normal (e.g., letter/num) 691 * key. These find the first entry starting with a matching 692 * character and jump to it. 693 */ 694 MenuItem *startitem; 695 int xx = Event.xkey.x; 696 int yy = Event.xkey.y; 697 int wx, wy; 698 699 startitem = ActiveItem ? ActiveItem : ActiveMenu->first; 700 item = startitem->next; 701 if(item == NULL) { 702 item = ActiveMenu->first; 703 } 704 unsigned int keymod = (Event.xkey.state & mods_used); 705 keymod = set_mask_ignore(keymod); 706 707 while(item != startitem) { 708 bool matched = false; 709 size_t offset = 0; 710 switch(item->item [0]) { 711 case '^' : 712 if((keymod & ControlMask) && 713 (keynam [0] == Tolower(item->item [1]))) { 714 matched = true; 715 } 716 break; 717 case '~' : 718 if((keymod & Mod1Mask) && 719 (keynam [0] == Tolower(item->item [1]))) { 720 matched = true; 721 } 722 break; 723 case ' ' : 724 offset = 1; 725 default : 726 if(((Scr->IgnoreCaseInMenuSelection) && 727 (keynam [0] == Tolower(item->item [offset]))) || 728 729 ((keymod & ShiftMask) && Isupper(item->item [offset]) && 730 (keynam [0] == Tolower(item->item [offset]))) || 731 732 (!(keymod & ShiftMask) && Islower(item->item [offset]) && 733 (keynam [0] == item->item [offset]))) { 734 matched = true; 735 } 736 break; 737 } 738 if(matched) { 739 break; 740 } 741 item = item->next; 742 if(item == NULL) { 743 item = ActiveMenu->first; 744 } 745 } 746 if(item == startitem) { 747 return; 748 } 749 wx = ActiveMenu->width / 2; 750 wy = (item->item_num * Scr->EntryHeight) + (Scr->EntryHeight / 2) + 2; 751 XTranslateCoordinates(dpy, ActiveMenu->w, Scr->Root, wx, wy, &xx, &yy, &junkW); 752 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y, 753 ActiveMenu->width, ActiveMenu->height, xx, yy); 754 return; 755 } 756 else { 757 /* Other keys get ignored */ 758 return; 759 } 760 761 762 /* 763 * So if we get here, the key pressed was a Right/Return on an 764 * entry to select it (chosen entry now in item). Every other 765 * case is have been completely handled in the block above and 766 * would have already returned. 767 * 768 * So item should always be the entry we just tried to invoke. 769 * I'm not sure how it could be empty, but if it is, we just hop 770 * ourselves out of the menu. Otherwise, we do whatever we want 771 * to do with the entry type we're on. 772 */ 773 if(item) { 774 switch(item->func) { 775 /* f.nop and f.title, we just silently let pass */ 776 case 0 : 777 case F_TITLE : 778 break; 779 780 /* If it's a f.menu, there's more magic to do */ 781 case F_MENU: { 782 /* 783 * Return is treated separately from Right. It 784 * "invokes" the menu item, which immediately calls 785 * whatever the default menu entry is (which may be 786 * nothing). 787 */ 788 if(!strcmp(keynam, "Return")) { 789 if(ActiveMenu == Scr->Workspaces) { 790 /* 791 * f.menu "TwmWorkspaces". The "invocation" 792 * of this jumps to the workspace in 793 * question, as if it were a default entry of 794 * f.gotoworkspace. 795 * 796 * XXX Grody magic. Maybe this should be 797 * unwound to a default entry... 798 */ 799 PopDownMenu(); 800 XUngrabPointer(dpy, CurrentTime); 801 GotoWorkSpaceByName(Scr->currentvs, item->action + 8); 802 } 803 else { 804 /* 805 * Calling the f.menu handler invokes the 806 * default action. We handle popping out of 807 * menus ourselves. 808 */ 809 ExecuteFunction(item->func, item->action, 810 ButtonWindow ? ButtonWindow->frame : None, 811 ButtonWindow, &Event, Context, false); 812 PopDownMenu(); 813 } 814 815 /* 816 * Whatever invocation Return does is done, so we 817 * are too. 818 */ 819 return; 820 } 821 822 /* 823 * Right arrow causes opening up a sub-f.menu. Open 824 * it up in the appropriate place, [re-]set 825 * highlights, and call do_key_menu() to do a lot of 826 * the internals of it. 827 */ 828 int xx = Event.xkey.x; 829 int yy = Event.xkey.y; 830 int wx, wy; 831 XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, 832 &wx, &wy, &junkW); 833 if(ActiveItem) { 834 ActiveItem->state = 0; 835 PaintEntry(ActiveMenu, ActiveItem, false); 836 ActiveItem = NULL; 837 } 838 xx -= (wx - ActiveMenu->width); 839 yy -= (wy - item->item_num * Scr->EntryHeight - (Scr->EntryHeight / 2) - 2); 840 Event.xkey.x_root = xx; 841 Event.xkey.y_root = yy; 842 XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y, 843 ActiveMenu->width, ActiveMenu->height, xx, yy); 844 if(ActiveMenu == Scr->Workspaces) { 845 CurrentSelectedWorkspace = item->item; 846 } 847 do_key_menu(item->sub, None); 848 CurrentSelectedWorkspace = NULL; 849 break; 850 } 851 852 /* 853 * Any other f.something. Pop down the menu (unless 854 * we're trying to pin it up), and invoke the function. 855 */ 856 default : 857 if(item->func != F_PIN) { 858 PopDownMenu(); 859 } 860 ExecuteFunction(item->func, item->action, 861 ButtonWindow ? ButtonWindow->frame : None, 862 ButtonWindow, &Event, Context, false); 863 } 864 865 /* Done whatever invocation of the entry we need */ 866 } 867 else { 868 /* Was no item; pop out of the menu */ 869 PopDownMenu(); 870 XUngrabPointer(dpy, CurrentTime); 871 } 872 873 /* 874 * We're done handling the keypress in a menu, so there's nothing 875 * else to do. 876 */ 877 return; 878 } 879 880 881 /* 882 * Not in a menu, so we loop through our various bindings. First, 883 * figure out what context we're in. This goes in a global var, 884 * presumably because stuff way down the chain of invoking some item 885 * may need to refer up to it. 886 */ 887 Context = C_NO_CONTEXT; 888 if(Event.xany.window == Scr->Root) { 889 if(AlternateContext) { 890 XUngrabPointer(dpy, CurrentTime); 891 XUngrabKeyboard(dpy, CurrentTime); 892 AlternateContext = false; 893 Context = C_ALTERNATE; 894 } 895 else if(AlternateKeymap && Event.xkey.subwindow) { 896 Tmp_win = GetTwmWindow(Event.xkey.subwindow); 897 if(Tmp_win) { 898 Event.xany.window = Tmp_win->w; 899 } 900 } 901 else { 902 Context = C_ROOT; 903 } 904 } 905 if(Tmp_win) { 906 if(0) { 907 /* Dummy to simplify constructions of else if's */ 908 } 909#ifdef EWMH_DESKTOP_ROOT 910 else if(Tmp_win->ewmhWindowType == wt_Desktop) { 911 fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_ROOT\n"); 912 Context = C_ROOT; 913 } 914#endif 915 else if(Event.xany.window == Tmp_win->title_w) { 916 Context = C_TITLE; 917 } 918 else if(Event.xany.window == Tmp_win->w) { 919 Context = C_WINDOW; 920 } 921 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) { 922 Context = C_ICON; 923 } 924 else if(Event.xany.window == Tmp_win->frame) { 925 Context = C_FRAME; 926 } 927 else if(Tmp_win->iconmanagerlist) { 928 if(Event.xany.window == Tmp_win->iconmanagerlist->w || 929 Event.xany.window == Tmp_win->iconmanagerlist->icon) { 930 Context = C_ICONMGR; 931 } 932 } 933 if(Tmp_win->iswspmgr) { 934 Context = C_WORKSPACE; 935 } 936 } 937 938 /* 939 * We've figured out the Context. Now see what modifiers we might 940 * have set... 941 */ 942 unsigned int modifier = (Event.xkey.state | AlternateKeymap) & mods_used; 943 modifier = set_mask_ignore(modifier); 944 if(AlternateKeymap) { 945 XUngrabPointer(dpy, CurrentTime); 946 XUngrabKeyboard(dpy, CurrentTime); 947 AlternateKeymap = 0; 948 } 949 950 951 /* 952 * Loop over our key bindings and do its thing if we find a matching 953 * one. 954 */ 955 for(FuncKey *key = Scr->FuncKeyRoot.next; key != NULL; key = key->next) { 956 /* 957 * Is this what we're trying to invoke? Gotta be the right key, 958 * and right modifier; those are easy. 959 * 960 * Context is tougher; that has to match what we're expecting as 961 * well, except in the case of C_NAME, which we always have to 962 * check to see if it'll match any windows. So if we have the 963 * right key and modifier, and it's a C_NAME context, it's a 964 * "maybe" match and we have to go through the checks. 965 */ 966 if(key->keycode != Event.xkey.keycode || 967 key->mods != modifier || 968 (key->cont != Context && key->cont != C_NAME)) { 969 /* Nope, not yet */ 970 continue; 971 } 972 973 /* 'k, it's a match (or potential match, in C_NAME case) */ 974 975 /* 976 * Weed out the functions that don't make sense to execute from a 977 * key press 978 * 979 * TODO: add keyboard moving/resizing of windows. 980 */ 981 if(key->func == F_MOVE || key->func == F_RESIZE) { 982 return; 983 } 984 985 if(key->cont != C_NAME) { 986 /* Normal context binding; do what it wants */ 987 if(key->func == F_MENU) { 988 /* 989 * f.menu doesn't call the f.menu handler; we directly 990 * do_key_menu() to pull it up. 991 * 992 * Note this is "we called f.menu from a keybinding", not 993 * "we hit f.menu inside a menu we had up"; that's above. 994 */ 995 ButtonWindow = Tmp_win; 996 do_key_menu(key->menu, (Window) None); 997 } 998 else { 999#ifdef EWMH_DESKTOP_ROOT 1000 if(Context == C_ROOT && Tmp_win != NULL) { 1001 Context = C_WINDOW; 1002 fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_WINDOW\n"); 1003 } 1004#endif /* EWMH */ 1005 ExecuteFunction(key->func, key->action, Event.xany.window, 1006 Tmp_win, &Event, Context, false); 1007 if(!AlternateKeymap && !AlternateContext) { 1008 XUngrabPointer(dpy, CurrentTime); 1009 } 1010 } 1011 return; 1012 } 1013 else { 1014 /* 1015 * By-name binding (i.e., quoted string for the context 1016 * argument in config; see the manual). Find windows 1017 * matching that name and invoke on them, if any. 1018 * 1019 * This is the 'maybe' case above; we don't know whether this 1020 * does something until we try it. If we don't get a match, 1021 * we loop back around and keep going through our functions 1022 * until we do. 1023 */ 1024 bool matched = false; 1025 const size_t len = strlen(key->win_name); 1026 1027 /* try and match the name first */ 1028 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL; 1029 Tmp_win = Tmp_win->next) { 1030 if(!strncmp(key->win_name, Tmp_win->name, len)) { 1031 matched = true; 1032 ExecuteFunction(key->func, key->action, Tmp_win->frame, 1033 Tmp_win, &Event, C_FRAME, false); 1034 if(!AlternateKeymap && !AlternateContext) { 1035 XUngrabPointer(dpy, CurrentTime); 1036 } 1037 } 1038 } 1039 1040 /* now try the res_name */ 1041 if(!matched) { 1042 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL; 1043 Tmp_win = Tmp_win->next) { 1044 if(!strncmp(key->win_name, Tmp_win->class.res_name, len)) { 1045 matched = true; 1046 ExecuteFunction(key->func, key->action, Tmp_win->frame, 1047 Tmp_win, &Event, C_FRAME, false); 1048 if(!AlternateKeymap && !AlternateContext) { 1049 XUngrabPointer(dpy, CurrentTime); 1050 } 1051 } 1052 } 1053 } 1054 1055 /* now try the res_class */ 1056 if(!matched) { 1057 for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL; 1058 Tmp_win = Tmp_win->next) { 1059 if(!strncmp(key->win_name, Tmp_win->class.res_class, len)) { 1060 matched = true; 1061 ExecuteFunction(key->func, key->action, Tmp_win->frame, 1062 Tmp_win, &Event, C_FRAME, false); 1063 if(!AlternateKeymap && !AlternateContext) { 1064 XUngrabPointer(dpy, CurrentTime); 1065 } 1066 } 1067 } 1068 } 1069 1070 /* 1071 * If we wound up invoking something, we're done, so return. 1072 * If we didn't, we fall through to the next loop through our 1073 * defined bindings. 1074 * 1075 * By-name bindings are unique in this; normal contexts 1076 * couldn't have multiple matches, so that side of things 1077 * finishes when it deals with its found match. But with 1078 * by-name we could have multiple bindings of a given 1079 * button/modifier with different names, so we have to go 1080 * back around to the next run through the for() loop. 1081 */ 1082 if(matched) { 1083 return; 1084 } 1085 } // regular context or by-name? 1086 } // Loop over all bindings 1087 1088 1089 /* 1090 * If we get here, no function key was bound to the key. Send it to 1091 * the client if it was in a window we know about. Mostly this 1092 * doesn't happen; clients with focus get their events more directly, 1093 * but special cases may cause this. 1094 */ 1095 if(Tmp_win) { 1096 if(Context == C_WORKSPACE) { 1097 WMgrHandleKeyPressEvent(Scr->currentvs, &Event); 1098 return; 1099 } 1100 if(Context == C_ICON || 1101 Context == C_FRAME || 1102 Context == C_TITLE || 1103 Context == C_ICONMGR) { 1104 Event.xkey.window = Tmp_win->w; 1105 XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event); 1106 } 1107 } 1108 1109 1110 /* And done */ 1111} 1112 1113 1114 1115/*********************************************************************** 1116 * 1117 * Procedure: 1118 * HandlePropertyNotify - property notify event handler 1119 * 1120 *********************************************************************** 1121 */ 1122 1123void HandlePropertyNotify(void) 1124{ 1125 Atom actual = None; 1126 int actual_format; 1127 unsigned long nitems, bytesafter; 1128 unsigned long valuemask; /* mask for create windows */ 1129 XSetWindowAttributes attributes; /* attributes for create windows */ 1130 Pixmap pm; 1131 Icon *icon; 1132 1133 1134 /* watch for standard colormap changes */ 1135 if(Event.xproperty.window == Scr->Root) { 1136 1137 if(Event.xproperty.atom == XA_WM_CURRENTWORKSPACE) { 1138 unsigned char *prop; 1139 switch(Event.xproperty.state) { 1140 case PropertyNewValue: 1141 if(XGetWindowProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE, 1142 0L, 200L, False, XA_STRING, &actual, &actual_format, 1143 &nitems, &bytesafter, &prop) == Success) { 1144 if(nitems == 0) { 1145 return; 1146 } 1147 GotoWorkSpaceByName(Scr->vScreenList, (char *)prop); 1148 XFree(prop); 1149 } 1150 return; 1151 1152 default: 1153 return; 1154 } 1155 } 1156 switch(Event.xproperty.state) { 1157 case PropertyNewValue: { 1158 XStandardColormap *maps = NULL; 1159 int nmaps; 1160 1161 if(XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps, 1162 Event.xproperty.atom)) { 1163 /* if got one, then replace any existing entry */ 1164 InsertRGBColormap(Event.xproperty.atom, maps, nmaps, true); 1165 } 1166 return; 1167 } 1168 1169 case PropertyDelete: 1170 RemoveRGBColormap(Event.xproperty.atom); 1171 return; 1172 } 1173 } 1174 1175 if(!Tmp_win) { 1176 return; /* unknown window */ 1177 } 1178 1179#define MAX_NAME_LEN 200L /* truncate to this many */ 1180 1181 switch(Event.xproperty.atom) { 1182 case XA_WM_NAME: { 1183 char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_NAME); 1184 if(prop == NULL) { 1185 // Clear 1186 FreeWMPropertyString(Tmp_win->names.wm_name); 1187 Tmp_win->names.wm_name = NULL; 1188 apply_window_name(Tmp_win); 1189 return; 1190 } 1191 1192 if(Tmp_win->names.wm_name != NULL 1193 && strcmp(Tmp_win->names.wm_name, prop) == 0) { 1194 /* No change, just free and skip out */ 1195 free(prop); 1196 return; 1197 } 1198 1199 /* It's changing, free the old and bring in the new */ 1200 FreeWMPropertyString(Tmp_win->names.wm_name); 1201 Tmp_win->names.wm_name = prop; 1202 1203 /* Kick the reset process */ 1204 apply_window_name(Tmp_win); 1205 1206 break; 1207 } 1208 1209 case XA_WM_ICON_NAME: { 1210 char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_ICON_NAME); 1211 if(prop == NULL) { 1212 // Clear 1213 FreeWMPropertyString(Tmp_win->names.wm_icon_name); 1214 Tmp_win->names.wm_icon_name = NULL; 1215 apply_window_icon_name(Tmp_win); 1216 return; 1217 } 1218 1219 /* No change? Nothing to do. */ 1220 if(Tmp_win->names.wm_icon_name != NULL 1221 && strcmp(Tmp_win->names.wm_icon_name, prop) == 0) { 1222 free(prop); 1223 return; 1224 } 1225 1226 /* It's changing, free the old and bring in the new */ 1227 FreeWMPropertyString(Tmp_win->names.wm_icon_name); 1228 Tmp_win->names.wm_icon_name = prop; 1229 1230 /* And show the new */ 1231 apply_window_icon_name(Tmp_win); 1232 1233 break; 1234 } 1235 1236 case XA_WM_HINTS: { 1237 { 1238 XWMHints *nhints = XGetWMHints(dpy, Event.xany.window); 1239 if(!nhints) { 1240 /* 1241 * I guess this means that window removed the 1242 * property completely. Just keep using what we 1243 * already have for it though; we gotta have 1244 * something, and whatever it last said is probably 1245 * more reasonable than getting completely new 1246 * synthetic hints anyway. 1247 */ 1248 break; 1249 } 1250 1251 XFree(Tmp_win->wmhints); 1252 Tmp_win->wmhints = munge_wmhints(Tmp_win, nhints); 1253 } 1254 1255 icon = Tmp_win->icon; 1256 1257 /* 1258 * If there already is an icon found in a way that has priority 1259 * over these hints, disable the flags and remove them from 1260 * consideration, now and in the future. 1261 */ 1262 if(Tmp_win->forced || 1263 (icon && icon->match == match_net_wm_icon)) { 1264 Tmp_win->wmhints->flags &= ~(IconWindowHint | IconPixmapHint | IconMaskHint); 1265 } 1266 1267 if(Tmp_win->wmhints->flags & IconWindowHint) { 1268 if(icon && icon->w) { 1269 int icon_x, icon_y; 1270 1271 /* 1272 * There's already an icon window. 1273 * Try to find out where it is; if we succeed, move the new 1274 * window to where the old one is. 1275 */ 1276 if(XGetGeometry(dpy, icon->w, &JunkRoot, &icon_x, 1277 &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth)) { 1278 /* 1279 * Move the new icon window to where the old one was. 1280 */ 1281 XMoveWindow(dpy, Tmp_win->wmhints->icon_window, icon_x, 1282 icon_y); 1283 } 1284 1285 /* 1286 * If the window is iconic, map the new icon window. 1287 */ 1288 if(Tmp_win->isicon) { 1289 XMapWindow(dpy, Tmp_win->wmhints->icon_window); 1290 } 1291 1292 /* 1293 * Now, if the old window isn't ours, unmap it, otherwise 1294 * just get rid of it completely. 1295 */ 1296 if(icon->w_not_ours) { 1297 if(icon->w != Tmp_win->wmhints->icon_window) { 1298 XUnmapWindow(dpy, icon->w); 1299 } 1300 } 1301 else { 1302 XDestroyWindow(dpy, icon->w); 1303 } 1304 1305 /* 1306 * The new icon window isn't our window, so note that fact 1307 * so that we don't treat it as ours. 1308 */ 1309 icon->w_not_ours = true; 1310 1311 /* 1312 * Now make the new window the icon window for this window, 1313 * and set it up to work as such (select for key presses 1314 * and button presses/releases, set up the contexts for it, 1315 * and define the cursor for it). 1316 */ 1317 icon->w = Tmp_win->wmhints->icon_window; 1318 XSelectInput(dpy, icon->w, 1319 KeyPressMask | ButtonPressMask | ButtonReleaseMask); 1320 XSaveContext(dpy, icon->w, TwmContext, (XPointer)Tmp_win); 1321 XSaveContext(dpy, icon->w, ScreenContext, (XPointer)Scr); 1322 XDefineCursor(dpy, icon->w, Scr->IconCursor); 1323 } 1324 } 1325 1326 if(icon && icon->w && 1327 (Tmp_win->wmhints->flags & IconPixmapHint)) { 1328 int x; 1329 unsigned int IconDepth; 1330 1331 if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_pixmap, &JunkRoot, 1332 &JunkX, &JunkY, (unsigned int *)&icon->width, 1333 (unsigned int *)&icon->height, &JunkBW, 1334 &IconDepth)) { 1335 return; 1336 } 1337 1338 pm = XCreatePixmap(dpy, Scr->Root, icon->width, 1339 icon->height, Scr->d_depth); 1340 1341 FB(icon->iconc.fore, icon->iconc.back); 1342 1343 if(IconDepth == Scr->d_depth) 1344 XCopyArea(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC, 1345 0, 0, icon->width, icon->height, 0, 0); 1346 else 1347 XCopyPlane(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC, 1348 0, 0, icon->width, icon->height, 0, 0, 1); 1349 1350 if(icon->image) { 1351 /* Release the existing Image: it may be a shared one (UnknownIcon) */ 1352 ReleaseIconImage(icon); 1353 /* conjure up a new Image */ 1354 Image *image = AllocImage(); 1355 image->pixmap = pm; 1356 image->width = icon->width; 1357 image->height = icon->height; 1358 icon->image = image; 1359 icon->match = match_icon_pixmap_hint; 1360 } 1361 1362 valuemask = CWBackPixmap; 1363 attributes.background_pixmap = pm; 1364 1365 if(icon->bm_w) { 1366 XDestroyWindow(dpy, icon->bm_w); 1367 } 1368 1369 x = GetIconOffset(icon); 1370 icon->bm_w = 1371 XCreateWindow(dpy, icon->w, x, 0, 1372 icon->width, 1373 icon->height, 1374 0, Scr->d_depth, 1375 CopyFromParent, Scr->d_visual, 1376 valuemask, &attributes); 1377 1378 if(!(Tmp_win->wmhints->flags & IconMaskHint)) { 1379 XRectangle rect; 1380 1381 rect.x = x; 1382 rect.y = 0; 1383 rect.width = icon->width; 1384 rect.height = icon->height; 1385 XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0, 1386 0, &rect, 1, ShapeUnion, 0); 1387 } 1388 XMapSubwindows(dpy, icon->w); 1389 RedoIconName(Tmp_win); 1390 } 1391 if(icon && icon->w && 1392 (Tmp_win->wmhints->flags & IconMaskHint) && 1393 icon->match == match_icon_pixmap_hint) { 1394 /* Only set the mask if the pixmap came from a WM_HINTS too, 1395 * for easier resource management. 1396 */ 1397 int x; 1398 Pixmap mask; 1399 GC gc; 1400 unsigned int IconWidth, IconHeight, IconDepth; 1401 1402 if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_mask, &JunkRoot, 1403 &JunkX, &JunkY, &IconWidth, &IconHeight, &JunkBW, 1404 &IconDepth)) { 1405 return; 1406 } 1407 if(IconDepth != 1) { 1408 return; 1409 } 1410 1411 mask = XCreatePixmap(dpy, Scr->Root, IconWidth, IconHeight, 1); 1412 if(!mask) { 1413 return; 1414 } 1415 gc = XCreateGC(dpy, mask, 0, NULL); 1416 if(!gc) { 1417 return; 1418 } 1419 XCopyArea(dpy, Tmp_win->wmhints->icon_mask, mask, gc, 1420 0, 0, IconWidth, IconHeight, 0, 0); 1421 XFreeGC(dpy, gc); 1422 x = GetIconOffset(icon); 1423 XShapeCombineMask(dpy, icon->bm_w, ShapeBounding, 0, 0, mask, 1424 ShapeSet); 1425 XShapeCombineMask(dpy, icon->w, ShapeBounding, x, 0, mask, 1426 ShapeSet); 1427 if(icon->image) { 1428 if(icon->image->mask) { 1429 XFreePixmap(dpy, icon->image->mask); 1430 } 1431 icon->image->mask = mask; 1432 RedoIconName(Tmp_win); 1433 } 1434 } 1435 if(Tmp_win->wmhints->flags & IconPixmapHint) { 1436 AutoPopupMaybe(Tmp_win); 1437 } 1438 1439 break; 1440 } 1441 1442 case XA_WM_NORMAL_HINTS: { 1443 GetWindowSizeHints(Tmp_win); 1444 break; 1445 } 1446 default: { 1447 if(Event.xproperty.atom == XA_WM_COLORMAP_WINDOWS) { 1448 FetchWmColormapWindows(Tmp_win); /* frees old data */ 1449 break; 1450 } 1451 else if(Event.xproperty.atom == XA_WM_PROTOCOLS) { 1452 FetchWmProtocols(Tmp_win); 1453 break; 1454 } 1455 else if(Event.xproperty.atom == XA_WM_OCCUPATION) { 1456 unsigned char *prop; 1457 if(XGetWindowProperty(dpy, Tmp_win->w, Event.xproperty.atom, 0L, MAX_NAME_LEN, 1458 False, 1459 XA_STRING, &actual, &actual_format, &nitems, 1460 &bytesafter, &prop) != Success || 1461 actual == None) { 1462 return; 1463 } 1464 ChangeOccupation(Tmp_win, GetMaskFromProperty(prop, nitems)); 1465 XFree(prop); 1466 } 1467 else if(Event.xproperty.atom == XA_CTWM_WM_NAME) { 1468 char *prop = GetWMPropertyString(Tmp_win->w, XA_CTWM_WM_NAME); 1469 if(prop == NULL) { 1470 // Clearing 1471 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name); 1472 Tmp_win->names.ctwm_wm_name = NULL; 1473 apply_window_name(Tmp_win); 1474 return; 1475 } 1476 1477 if(Tmp_win->names.ctwm_wm_name != NULL 1478 && strcmp(Tmp_win->names.ctwm_wm_name, 1479 prop) == 0) { 1480 /* No change, just free and skip out */ 1481 free(prop); 1482 return; 1483 } 1484 1485 /* It's changing, free the old and bring in the new */ 1486 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name); 1487 Tmp_win->names.ctwm_wm_name = prop; 1488 1489 /* Kick the reset process */ 1490 apply_window_name(Tmp_win); 1491 1492 return; 1493 } 1494 else if(Event.xproperty.atom == XA_CTWM_WM_ICON_NAME) { 1495 char *prop = GetWMPropertyString(Tmp_win->w, 1496 XA_CTWM_WM_ICON_NAME); 1497 if(prop == NULL) { 1498 // Clearing 1499 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name); 1500 Tmp_win->names.ctwm_wm_icon_name = NULL; 1501 apply_window_icon_name(Tmp_win); 1502 return; 1503 } 1504 1505 if(Tmp_win->names.ctwm_wm_icon_name != NULL 1506 && strcmp(Tmp_win->names.ctwm_wm_icon_name, 1507 prop) == 0) { 1508 /* No change, just free and skip out */ 1509 free(prop); 1510 return; 1511 } 1512 1513 /* It's changing, free the old and bring in the new */ 1514 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name); 1515 Tmp_win->names.ctwm_wm_icon_name = prop; 1516 1517 /* Kick the reset process */ 1518 apply_window_icon_name(Tmp_win); 1519 1520 break; 1521 } 1522#ifdef EWMH 1523 else if(EwmhHandlePropertyNotify(&Event.xproperty, Tmp_win)) { 1524 /* event handled */ 1525 } 1526#endif /* EWMH */ 1527 break; 1528 } 1529 } 1530} 1531 1532 1533/*********************************************************************** 1534 * 1535 * Procedure: 1536 * HandleClientMessage - client message event handler 1537 * 1538 *********************************************************************** 1539 */ 1540 1541void HandleClientMessage(void) 1542{ 1543 1544 if(Event.xclient.message_type == XA_WM_CHANGE_STATE) { 1545 if(Tmp_win != NULL) { 1546 if(Event.xclient.data.l[0] == IconicState && !Tmp_win->isicon) { 1547 XEvent button; 1548 XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild, 1549 &(button.xmotion.x_root), 1550 &(button.xmotion.y_root), 1551 &JunkX, &JunkY, &JunkMask); 1552 1553 ExecuteFunction(F_ICONIFY, NULL, Event.xany.window, 1554 Tmp_win, &button, FRAME, false); 1555 XUngrabPointer(dpy, CurrentTime); 1556 } 1557 } 1558 return; 1559 } 1560 1561#ifdef EWMH 1562 if(EwmhClientMessage(&Event.xclient)) { 1563 return; 1564 } 1565#endif 1566 1567 else if((Event.xclient.message_type == XA_WM_PROTOCOLS) && 1568 (Event.xclient.data.l[0] == XA_WM_END_OF_ANIMATION)) { 1569 if(Animating > 0) { 1570 Animating--; 1571 } 1572 else { 1573 fprintf(stderr, "!! end of unknown animation !!\n"); 1574 } 1575 } 1576} 1577 1578 1579/*********************************************************************** 1580 * 1581 * Procedure: 1582 * HandleExpose - expose event handler 1583 * 1584 *********************************************************************** 1585 */ 1586 1587static void flush_expose(Window w); 1588 1589void HandleExpose(void) 1590{ 1591 MenuRoot *tmp; 1592 VirtualScreen *vs; 1593 1594 if(XFindContext(dpy, Event.xany.window, MenuContext, (XPointer *)&tmp) == 0) { 1595 PaintMenu(tmp, &Event); 1596 return; 1597 } 1598 1599 if(Event.xexpose.count != 0) { 1600 return; 1601 } 1602 1603 if(Event.xany.window == Scr->InfoWindow.win && Scr->InfoWindow.mapped) { 1604 draw_info_window(); 1605 flush_expose(Event.xany.window); 1606 } 1607 else if(Tmp_win != NULL) { 1608 if(Scr->use3Dborders && (Event.xany.window == Tmp_win->frame)) { 1609 PaintBorders(Tmp_win, ((Tmp_win == Scr->Focus) ? true : false)); 1610 flush_expose(Event.xany.window); 1611 return; 1612 } 1613 else if(Event.xany.window == Tmp_win->title_w) { 1614 PaintTitle(Tmp_win); 1615 flush_expose(Event.xany.window); 1616 return; 1617 } 1618 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w) && 1619 ! Scr->NoIconTitlebar && 1620 ! LookInList(Scr->NoIconTitle, Tmp_win->name, &Tmp_win->class)) { 1621 PaintIcon(Tmp_win); 1622 flush_expose(Event.xany.window); 1623 return; 1624 } 1625 else if(Tmp_win->titlebuttons) { 1626 int i; 1627 TBWindow *tbw; 1628 Window w = Event.xany.window; 1629 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 1630 1631 /* 1632 * This looks an awful lot like a manual reimplementation of 1633 * PaintTitleButtons(). It's not quite though, it's just 1634 * looking up one button to paint it. And it would be a 1635 * little grody trying to shoehorn it in. 1636 */ 1637 for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) { 1638 if(w == tbw->window) { 1639 PaintTitleButton(Tmp_win, tbw); 1640 flush_expose(tbw->window); 1641 return; 1642 } 1643 } 1644 } 1645 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 1646 if(Tmp_win == vs->wsw->twm_win) { 1647 WMgrHandleExposeEvent(vs, &Event); 1648 flush_expose(Event.xany.window); 1649 return; 1650 } 1651 } 1652 if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) { 1653 /* 1654 * The occupyWindow has a bunch of sub-windows for each 1655 * button in it, and each of them wind up getting Expose 1656 * events kicked for them. The upshot is that we re-paint 1657 * the occupy window once for itself, and then once for each 1658 * button inside it. We'll always get one for the window 1659 * itself, so just paint it for that one, and ignore the rest 1660 * of the events. 1661 * 1662 * XXX Maybe a better solution is just to mask off Expose 1663 * events for the other windows... 1664 */ 1665 if(Event.xany.window == Scr->workSpaceMgr.occupyWindow->w) { 1666 PaintOccupyWindow(); 1667 flush_expose(Event.xany.window); 1668 } 1669 return; 1670 } 1671 else if(Tmp_win->iconmanagerlist) { 1672 WList *iconmanagerlist = Tmp_win->iconmanagerlist; 1673 1674 if(Event.xany.window == iconmanagerlist->w) { 1675 DrawIconManagerIconName(Tmp_win); 1676 flush_expose(Event.xany.window); 1677 return; 1678 } 1679 else if(Event.xany.window == iconmanagerlist->icon) { 1680 ShowIconifiedIcon(Tmp_win); 1681 flush_expose(Event.xany.window); 1682 return; 1683 } 1684 } 1685 } 1686} 1687 1688 1689static void remove_window_from_ring(TwmWindow *tmp) 1690{ 1691 TwmWindow *prev = tmp->ring.prev, *next = tmp->ring.next; 1692 1693 if(enter_win == tmp) { 1694 enter_flag = false; 1695 enter_win = NULL; 1696 } 1697 if(raise_win == Tmp_win) { 1698 raise_win = NULL; 1699 } 1700 if(leave_win == tmp) { 1701 leave_flag = false; 1702 leave_win = NULL; 1703 } 1704 if(lower_win == Tmp_win) { 1705 lower_win = NULL; 1706 } 1707 1708 /* 1709 * 1. Unlink window 1710 * 2. If window was only thing in ring, null out ring 1711 * 3. If window was ring leader, set to next (or null) 1712 */ 1713 if(prev) { 1714 prev->ring.next = next; 1715 } 1716 if(next) { 1717 next->ring.prev = prev; 1718 } 1719 if(Scr->Ring == tmp) { 1720 Scr->Ring = (next != tmp ? next : NULL); 1721 } 1722 1723 if(!Scr->Ring || Scr->RingLeader == tmp) { 1724 Scr->RingLeader = Scr->Ring; 1725 } 1726} 1727 1728 1729/*********************************************************************** 1730 * 1731 * Procedure: 1732 * HandleDestroyNotify - DestroyNotify event handler 1733 * 1734 *********************************************************************** 1735 */ 1736 1737void HandleDestroyNotify(void) 1738{ 1739 /* 1740 * Warning, this is also called by HandleUnmapNotify; if it ever needs to 1741 * look at the event, HandleUnmapNotify will have to mash the UnmapNotify 1742 * into a DestroyNotify. 1743 */ 1744 1745 if(Tmp_win == NULL) { 1746 return; 1747 } 1748 1749 RemoveWindowFromRegion(Tmp_win); 1750 1751 if(Tmp_win->icon != NULL) { 1752 OtpRemove(Tmp_win, IconWin); 1753 } 1754 OtpRemove(Tmp_win, WinWin); 1755 1756#ifdef EWMH 1757 /* Remove the old window from the EWMH client list */ 1758 EwmhDeleteClientWindow(Tmp_win); 1759 EwmhSet_NET_CLIENT_LIST_STACKING(); 1760#endif /* EWMH */ 1761 if(Tmp_win == Scr->Focus) { 1762 Scr->Focus = NULL; 1763 FocusOnRoot(); 1764 } 1765 if(Scr->SaveWorkspaceFocus) { 1766 struct WorkSpace *ws; 1767 for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) { 1768 if(ws->save_focus == Tmp_win) { 1769 ws->save_focus = NULL; 1770 } 1771 } 1772 } 1773 XDeleteContext(dpy, Tmp_win->w, TwmContext); 1774 XDeleteContext(dpy, Tmp_win->w, ScreenContext); 1775 XDeleteContext(dpy, Tmp_win->frame, TwmContext); 1776 XDeleteContext(dpy, Tmp_win->frame, ScreenContext); 1777 if(Tmp_win->icon && Tmp_win->icon->w) { 1778 XDeleteContext(dpy, Tmp_win->icon->w, TwmContext); 1779 XDeleteContext(dpy, Tmp_win->icon->w, ScreenContext); 1780 } 1781 if(Tmp_win->title_height) { 1782 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 1783 1784 XDeleteContext(dpy, Tmp_win->title_w, TwmContext); 1785 XDeleteContext(dpy, Tmp_win->title_w, ScreenContext); 1786 if(Tmp_win->hilite_wl) { 1787 XDeleteContext(dpy, Tmp_win->hilite_wl, TwmContext); 1788 XDeleteContext(dpy, Tmp_win->hilite_wl, ScreenContext); 1789 } 1790 if(Tmp_win->hilite_wr) { 1791 XDeleteContext(dpy, Tmp_win->hilite_wr, TwmContext); 1792 XDeleteContext(dpy, Tmp_win->hilite_wr, ScreenContext); 1793 } 1794 if(Tmp_win->lolite_wr) { 1795 XDeleteContext(dpy, Tmp_win->lolite_wr, TwmContext); 1796 XDeleteContext(dpy, Tmp_win->lolite_wr, ScreenContext); 1797 } 1798 if(Tmp_win->lolite_wl) { 1799 XDeleteContext(dpy, Tmp_win->lolite_wl, TwmContext); 1800 XDeleteContext(dpy, Tmp_win->lolite_wl, ScreenContext); 1801 } 1802 if(Tmp_win->titlebuttons) { 1803 int i; 1804 1805 for(i = 0; i < nb; i++) { 1806 XDeleteContext(dpy, Tmp_win->titlebuttons[i].window, 1807 TwmContext); 1808 XDeleteContext(dpy, Tmp_win->titlebuttons[i].window, 1809 ScreenContext); 1810 } 1811 } 1812 /* 1813 * The hilite_wl etc windows don't need to be XDestroyWindow()ed 1814 * since that will happen when the parent is destroyed (??) 1815 */ 1816 } 1817 1818 if(Scr->cmapInfo.cmaps == &Tmp_win->cmaps) { 1819 InstallColormaps(DestroyNotify, &Scr->RootColormaps); 1820 } 1821 1822 /* 1823 * TwmWindows contain the following pointers 1824 * 1825 * 1. (obsolete) 1826 * 2. name 1827 * 3. icon_name 1828 * 4. wmhints 1829 * 5. class.res_name 1830 * 6. class.res_class 1831 * 7. list 1832 * 8. iconmgrp 1833 * 9. cwins 1834 * 10. titlebuttons 1835 * 11. window ring 1836 * 12. squeeze_info (delete if squeeze_info_copied) 1837 * 13. HiliteImage 1838 * 14. iconslist 1839 */ 1840 WMapRemoveWindow(Tmp_win); 1841 if(Tmp_win->gray) { 1842 XFreePixmap(dpy, Tmp_win->gray); 1843 } 1844 1845 /* 1846 * According to the manual page, the following destroys all child windows 1847 * of the frame too, which is most of the windows we're concerned with, so 1848 * anything related to them must be done before here. 1849 * Icons are not child windows. 1850 */ 1851 XDestroyWindow(dpy, Tmp_win->frame); 1852 DeleteIconsList(Tmp_win); /* 14 */ 1853 if(Tmp_win->icon) { 1854 Icon *icon = Tmp_win->icon; 1855 if(icon->w && !icon->w_not_ours) { 1856 IconDown(Tmp_win); 1857 } 1858 DeleteIcon(icon); 1859 Tmp_win->icon = NULL; 1860 } 1861 Tmp_win->occupation = 0; 1862 RemoveIconManager(Tmp_win); /* 7 */ 1863 if(Scr->FirstWindow == Tmp_win) { 1864 Scr->FirstWindow = Tmp_win->next; 1865 } 1866 if(Tmp_win->prev != NULL) { 1867 Tmp_win->prev->next = Tmp_win->next; 1868 } 1869 if(Tmp_win->next != NULL) { 1870 Tmp_win->next->prev = Tmp_win->prev; 1871 } 1872 if(Tmp_win->auto_raise) { 1873 Scr->NumAutoRaises--; 1874 } 1875 if(Tmp_win->auto_lower) { 1876 Scr->NumAutoLowers--; 1877 } 1878 1879 FreeWMPropertyString(Tmp_win->names.ctwm_wm_name); // 2 1880 FreeWMPropertyString(Tmp_win->names.wm_name); // 2 1881 FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name); // 3 1882 FreeWMPropertyString(Tmp_win->names.wm_icon_name); // 3 1883#ifdef EWMH 1884 FreeWMPropertyString(Tmp_win->names.net_wm_name); // 2 1885 FreeWMPropertyString(Tmp_win->names.net_wm_icon_name); // 3 1886#endif 1887 1888 XFree(Tmp_win->wmhints); /* 4 */ 1889 if(Tmp_win->class.res_name && Tmp_win->class.res_name != NoName) { /* 5 */ 1890 XFree(Tmp_win->class.res_name); 1891 } 1892 if(Tmp_win->class.res_class && Tmp_win->class.res_class != NoName) { /* 6 */ 1893 XFree(Tmp_win->class.res_class); 1894 } 1895 free_cwins(Tmp_win); /* 9 */ 1896 if(Tmp_win->titlebuttons) { /* 10 */ 1897 free(Tmp_win->titlebuttons); 1898 Tmp_win->titlebuttons = NULL; 1899 } 1900 1901 remove_window_from_ring(Tmp_win); /* 11 */ 1902 if(Tmp_win->squeeze_info_copied) { /* 12 */ 1903 free(Tmp_win->squeeze_info); 1904 Tmp_win->squeeze_info = NULL; 1905 } 1906 DeleteHighlightWindows(Tmp_win); /* 13 */ 1907 1908 free(Tmp_win); 1909 Tmp_win = NULL; 1910 1911 if(Scr->ClickToFocus || Scr->SloppyFocus) { 1912 set_last_window(Scr->currentvs->wsw->currentwspc); 1913 } 1914} 1915 1916 1917void 1918HandleCreateNotify(void) 1919{ 1920#ifdef DEBUG_EVENTS 1921 fprintf(stderr, "CreateNotify w = 0x%x\n", 1922 (unsigned)Event.xcreatewindow.window); 1923 fflush(stderr); 1924 XBell(dpy, 0); 1925 XSync(dpy, 0); 1926#endif 1927} 1928 1929 1930/*********************************************************************** 1931 * 1932 * Procedure: 1933 * HandleMapRequest - MapRequest event handler 1934 * 1935 *********************************************************************** 1936 */ 1937 1938void HandleMapRequest(void) 1939{ 1940 int zoom_save; 1941 1942 Event.xany.window = Event.xmaprequest.window; 1943 Tmp_win = GetTwmWindow(Event.xany.window); 1944 1945 /* If the window has never been mapped before ... */ 1946 if(Tmp_win == NULL) { 1947 /* Add decorations. */ 1948 VirtualScreen *vs = Scr->currentvs; 1949 1950 Tmp_win = AddWindow(Event.xany.window, 1951 AWT_NORMAL, 1952 NULL, 1953 vs); 1954 if(Tmp_win == NULL) { 1955 return; 1956 } 1957#ifdef EWMH 1958 /* add the new window to the EWMH client list */ 1959 EwmhAddClientWindow(Tmp_win); 1960 EwmhSet_NET_CLIENT_LIST_STACKING(); 1961 1962 /* Tell it whatever we think of it */ 1963 EwmhSet_NET_WM_STATE(Tmp_win, EWMH_STATE_ALL); 1964#endif /* EWMH */ 1965 } 1966 else { 1967 /* 1968 * If the window has been unmapped by the client, it won't be listed 1969 * in the icon manager. Add it again, if requested. 1970 */ 1971 if(Tmp_win->iconmanagerlist == NULL) { 1972 AddIconManager(Tmp_win); 1973 } 1974 } 1975 1976 if(Tmp_win->isiconmgr) { 1977 return; 1978 } 1979 if(Tmp_win->squeezed) { 1980 return; 1981 } 1982 1983 if(Scr->WindowMask) { 1984 XRaiseWindow(dpy, Scr->WindowMask); 1985 } 1986 1987 /* If it's not merely iconified, and we have hints, use them. */ 1988 if(! Tmp_win->isicon) { 1989 int state; 1990 Window icon; 1991 1992 state = NormalState; 1993 /* use WM_STATE if enabled */ 1994 if(!(RestartPreviousState && GetWMState(Tmp_win->w, &state, &icon) && 1995 (state == NormalState || state == IconicState || state == InactiveState))) { 1996 if(Tmp_win->wmhints->flags & StateHint) { 1997 state = Tmp_win->wmhints->initial_state; 1998 } 1999 } 2000 switch(state) { 2001 case DontCareState: 2002 case NormalState: 2003 case ZoomState: 2004 if(Tmp_win->StartSqueezed) { 2005 Squeeze(Tmp_win); 2006 } 2007 else { 2008 XMapWindow(dpy, Tmp_win->w); 2009 } 2010 XMapWindow(dpy, Tmp_win->frame); 2011 SetMapStateProp(Tmp_win, NormalState); 2012 SetRaiseWindow(Tmp_win); 2013 Tmp_win->mapped = true; 2014 if(Scr->ClickToFocus && Tmp_win->wmhints->input) { 2015 SetFocus(Tmp_win, CurrentTime); 2016 } 2017 /* kai */ 2018 if(Scr->AutoFocusToTransients && 2019 Tmp_win->istransient && 2020 Tmp_win->wmhints->input) { 2021 SetFocus(Tmp_win, CurrentTime); 2022 } 2023 break; 2024 2025 case InactiveState: 2026 if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) && 2027 HandlingEvents && /* to avoid warping during startup */ 2028 LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) { 2029 if(!Scr->NoRaiseDeicon) { 2030 OtpRaise(Tmp_win, WinWin); 2031 } 2032 AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win); 2033 } 2034 Tmp_win->mapped = true; 2035 if(Tmp_win->UnmapByMovingFarAway) { 2036 XMoveWindow(dpy, Tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1); 2037 XMapWindow(dpy, Tmp_win->w); 2038 XMapWindow(dpy, Tmp_win->frame); 2039 } 2040 if(Tmp_win->StartSqueezed) { 2041 Squeeze(Tmp_win); 2042 } 2043 break; 2044 2045 case IconicState: 2046 zoom_save = Scr->DoZoom; 2047 Scr->DoZoom = false; 2048 Iconify(Tmp_win, -100, -100); 2049 Scr->DoZoom = zoom_save; 2050 break; 2051 } 2052 } 2053 /* If no hints, or currently an icon, just "deiconify" */ 2054 else { 2055 if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) && 2056 LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) { 2057 AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win); 2058 } 2059 if(1/*OCCUPY (Tmp_win, Scr->workSpaceMgr.activeWSPC)*/) { 2060 if(Tmp_win->StartSqueezed) { 2061 Squeeze(Tmp_win); 2062 } 2063 DeIconify(Tmp_win); 2064 SetRaiseWindow(Tmp_win); 2065 } 2066 else { 2067 Tmp_win->mapped = true; 2068 } 2069 } 2070 if(Tmp_win->mapped) { 2071 WMapMapWindow(Tmp_win); 2072 } 2073 MaybeAnimate = true; 2074} 2075 2076 2077/*********************************************************************** 2078 * 2079 * Procedure: 2080 * HandleMapNotify - MapNotify event handler 2081 * 2082 *********************************************************************** 2083 */ 2084 2085void HandleMapNotify(void) 2086{ 2087 if(Tmp_win == NULL) { 2088 return; 2089 } 2090 2091 /* 2092 * Need to do the grab to avoid race condition of having server send 2093 * MapNotify to client before the frame gets mapped; this is bad because 2094 * the client would think that the window has a chance of being viewable 2095 * when it really isn't. 2096 */ 2097 2098 XGrabServer(dpy); 2099 if(Tmp_win->icon && Tmp_win->icon->w) { 2100 XUnmapWindow(dpy, Tmp_win->icon->w); 2101 } 2102 if(Tmp_win->title_w) { 2103 XMapSubwindows(dpy, Tmp_win->title_w); 2104 } 2105 XMapSubwindows(dpy, Tmp_win->frame); 2106 if(Scr->Focus != Tmp_win && Tmp_win->hilite_wl) { 2107 XUnmapWindow(dpy, Tmp_win->hilite_wl); 2108 } 2109 if(Scr->Focus != Tmp_win && Tmp_win->hilite_wr) { 2110 XUnmapWindow(dpy, Tmp_win->hilite_wr); 2111 } 2112 if(Scr->Focus == Tmp_win && Tmp_win->lolite_wl) { 2113 XUnmapWindow(dpy, Tmp_win->lolite_wl); 2114 } 2115 if(Scr->Focus == Tmp_win && Tmp_win->lolite_wr) { 2116 XUnmapWindow(dpy, Tmp_win->lolite_wr); 2117 } 2118 2119 XMapWindow(dpy, Tmp_win->frame); 2120 XUngrabServer(dpy); 2121 XFlush(dpy); 2122 Tmp_win->mapped = true; 2123 Tmp_win->isicon = false; 2124 Tmp_win->icon_on = false; 2125} 2126 2127 2128/*********************************************************************** 2129 * 2130 * Procedure: 2131 * HandleUnmapNotify - UnmapNotify event handler 2132 * 2133 *********************************************************************** 2134 */ 2135 2136void HandleUnmapNotify(void) 2137{ 2138 int dstx, dsty; 2139 Window dumwin; 2140 2141 /* 2142 * The July 27, 1988 ICCCM spec states that a client wishing to switch 2143 * to WithdrawnState should send a synthetic UnmapNotify with the 2144 * event field set to (pseudo-)root, in case the window is already 2145 * unmapped (which is the case for twm for IconicState). Unfortunately, 2146 * we looked for the TwmContext using that field, so try the window 2147 * field also. 2148 */ 2149 if(Tmp_win == NULL) { 2150 Event.xany.window = Event.xunmap.window; 2151 Tmp_win = GetTwmWindow(Event.xany.window); 2152 } 2153 2154 if(Tmp_win == NULL || Event.xunmap.window == Tmp_win->frame || 2155 (Tmp_win->icon && Event.xunmap.window == Tmp_win->icon->w) || 2156 (!Tmp_win->mapped && !Tmp_win->isicon)) { 2157 return; 2158 } 2159 /* 2160 if (Tmp_win == NULL || (!Tmp_win->mapped && !Tmp_win->isicon)) 2161 return; 2162 */ 2163 /* 2164 * The program may have unmapped the client window, from either 2165 * NormalState or IconicState. Handle the transition to WithdrawnState. 2166 * 2167 * We need to reparent the window back to the root (so that twm exiting 2168 * won't cause it to get mapped) and then throw away all state (pretend 2169 * that we've received a DestroyNotify). 2170 */ 2171 /* Is it the correct behaviour ??? 2172 XDeleteProperty (dpy, Tmp_win->w, XA_WM_OCCUPATION); 2173 */ 2174#ifdef EWMH 2175 EwmhUnmapNotify(Tmp_win); 2176#endif /* EWMH */ 2177 XGrabServer(dpy); 2178 if(XTranslateCoordinates(dpy, Event.xunmap.window, Tmp_win->attr.root, 2179 0, 0, &dstx, &dsty, &dumwin)) { 2180 XEvent ev; 2181 Bool reparented = XCheckTypedWindowEvent(dpy, Event.xunmap.window, 2182 ReparentNotify, &ev); 2183 SetMapStateProp(Tmp_win, WithdrawnState); 2184 if(reparented) { 2185 if(Tmp_win->old_bw) XSetWindowBorderWidth(dpy, 2186 Event.xunmap.window, 2187 Tmp_win->old_bw); 2188 if(Tmp_win->wmhints->flags & IconWindowHint) { 2189 XUnmapWindow(dpy, Tmp_win->wmhints->icon_window); 2190 } 2191 } 2192 else { 2193 XReparentWindow(dpy, Event.xunmap.window, Tmp_win->attr.root, 2194 dstx, dsty); 2195 RestoreWithdrawnLocation(Tmp_win); 2196 } 2197 XRemoveFromSaveSet(dpy, Event.xunmap.window); 2198 XSelectInput(dpy, Event.xunmap.window, NoEventMask); 2199 HandleDestroyNotify(); /* do not need to mash event before */ 2200 } /* else window no longer exists and we'll get a destroy notify */ 2201 XUngrabServer(dpy); 2202 XFlush(dpy); 2203} 2204 2205 2206/*********************************************************************** 2207 * 2208 * Procedure: 2209 * HandleMotionNotify - MotionNotify event handler 2210 * 2211 *********************************************************************** 2212 */ 2213 2214void HandleMotionNotify(void) 2215{ 2216 if(ResizeWindow != (Window) 0) { 2217 XQueryPointer(dpy, Event.xany.window, 2218 &(Event.xmotion.root), &JunkChild, 2219 &(Event.xmotion.x_root), &(Event.xmotion.y_root), 2220 &(Event.xmotion.x), &(Event.xmotion.y), 2221 &JunkMask); 2222 2223 FixRootEvent(&Event); 2224 /* Set WindowMoved appropriately so that f.deltastop will 2225 work with resize as well as move. */ 2226 if(abs(Event.xmotion.x - ResizeOrigX) >= Scr->MoveDelta 2227 || abs(Event.xmotion.y - ResizeOrigY) >= Scr->MoveDelta) { 2228 WindowMoved = true; 2229 } 2230 2231 Tmp_win = GetTwmWindow(ResizeWindow); 2232 if(Tmp_win && Tmp_win->winbox) { 2233 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window, 2234 Event.xmotion.x_root, Event.xmotion.y_root, 2235 &(Event.xmotion.x_root), &(Event.xmotion.y_root), &JunkChild); 2236 } 2237 DoResize(Event.xmotion.x_root, Event.xmotion.y_root, Tmp_win); 2238 } 2239 else if(Scr->BorderCursors && Tmp_win && Event.xany.window == Tmp_win->frame) { 2240 SetBorderCursor(Tmp_win, Event.xmotion.x, Event.xmotion.y); 2241 } 2242} 2243 2244 2245/*********************************************************************** 2246 * 2247 * Procedure: 2248 * HandleButtonRelease - ButtonRelease event handler 2249 * 2250 *********************************************************************** 2251 */ 2252void HandleButtonRelease(void) 2253{ 2254 int xl, yt, w, h; 2255 unsigned mask; 2256 2257 if(Scr->InfoWindow.mapped) { /* delete info box on 2nd button release */ 2258 if(Context == C_IDENTIFY) { 2259 XUnmapWindow(dpy, Scr->InfoWindow.win); 2260 Scr->InfoWindow.mapped = false; 2261 Context = C_NO_CONTEXT; 2262 } 2263 } 2264 2265 if(DragWindow != None) { 2266 MoveOutline(Scr->XineramaRoot, 0, 0, 0, 0, 0, 0); 2267 2268 Tmp_win = GetTwmWindow(DragWindow); 2269 if(Tmp_win->winbox) { 2270 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window, 2271 Event.xbutton.x_root, Event.xbutton.y_root, 2272 &(Event.xbutton.x_root), &(Event.xbutton.y_root), &JunkChild); 2273 } 2274 if(DragWindow == Tmp_win->frame) { 2275 xl = Event.xbutton.x_root - DragX - Tmp_win->frame_bw; 2276 yt = Event.xbutton.y_root - DragY - Tmp_win->frame_bw; 2277 w = DragWidth + 2 * Tmp_win->frame_bw; 2278 h = DragHeight + 2 * Tmp_win->frame_bw; 2279 } 2280 else { 2281 xl = Event.xbutton.x_root - DragX - DragBW; 2282 yt = Event.xbutton.y_root - DragY - DragBW; 2283 w = DragWidth + 2 * DragBW; 2284 h = DragHeight + 2 * DragBW; 2285 } 2286 2287 if(ConstMove) { 2288 if(ConstMoveDir == MOVE_HORIZ) { 2289 yt = ConstMoveY; 2290 } 2291 2292 if(ConstMoveDir == MOVE_VERT) { 2293 xl = ConstMoveX; 2294 } 2295 2296 if(ConstMoveDir == MOVE_NONE) { 2297 yt = ConstMoveY; 2298 xl = ConstMoveX; 2299 } 2300 } 2301 2302 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 2303 TryToGrid(Tmp_win, &xl, &yt); 2304 } 2305 if(MoveFunction == F_MOVEPUSH && 2306 Scr->OpaqueMove && 2307 DragWindow == Tmp_win->frame) { 2308 TryToPush(Tmp_win, xl, yt); 2309 } 2310 if(MoveFunction == F_MOVEPACK || 2311 (MoveFunction == F_MOVEPUSH && 2312 DragWindow == Tmp_win->frame)) { 2313 TryToPack(Tmp_win, &xl, &yt); 2314 } 2315 if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) { 2316 ConstrainByBorders(Tmp_win, &xl, w, &yt, h); 2317 } 2318 2319 CurrentDragX = xl; 2320 CurrentDragY = yt; 2321 /* 2322 * sometimes getScreenOf() replies with the wrong window when moving 2323 * y to a negative number. Need to figure out why... [XXX] 2324 * It seems to be because the first XTranslateCoordinates() doesn't 2325 * translate the coordinates to be relative to XineramaRoot. 2326 * That in turn is probably because a window is visible in a ws or 2327 * vs where it shouldn't (inconsistent with its occupation). 2328 * A problem of this kind was fixed with f.adoptwindow but remains 2329 * with f.hypermove. 2330 * As a result, the window remains in the original vs/ws and 2331 * is irretrievably moved out of view. [Rhialto] 2332 */ 2333 if(xl < 0 || yt < 0 || xl > Scr->rootw || yt > Scr->rooth) { 2334 int odestx, odesty; 2335 int destx, desty; 2336 Window cr; 2337 VirtualScreen *newvs; 2338 2339 XTranslateCoordinates(dpy, Tmp_win->vs->window, 2340 Scr->XineramaRoot, xl, yt, &odestx, &odesty, &cr); 2341 2342 newvs = findIfVScreenOf(odestx, odesty); 2343 2344 if(newvs && newvs->wsw && newvs->wsw->currentwspc) { 2345 XTranslateCoordinates(dpy, Scr->XineramaRoot, 2346 newvs->window, odestx, odesty, 2347 &destx, &desty, &cr); 2348 AddToWorkSpace(newvs->wsw->currentwspc->name, Tmp_win); 2349 RemoveFromWorkSpace(Tmp_win->vs->wsw->currentwspc->name, Tmp_win); 2350 xl = destx; 2351 yt = desty; 2352 } 2353 } 2354 if(DragWindow == Tmp_win->frame) { 2355 SetupWindow(Tmp_win, xl, yt, 2356 Tmp_win->frame_width, Tmp_win->frame_height, -1); 2357 } 2358 else { 2359 XMoveWindow(dpy, DragWindow, xl, yt); 2360 if(DragWindow == Tmp_win->icon->w) { 2361 Tmp_win->icon->w_x = xl; 2362 Tmp_win->icon->w_y = yt; 2363 } 2364 } 2365 2366 if(!Scr->NoRaiseMove) { /* && !Scr->OpaqueMove) opaque already did */ 2367 if(DragWindow == Tmp_win->frame) { 2368 OtpRaise(Tmp_win, WinWin); 2369 } 2370 else if(Tmp_win->icon && DragWindow == Tmp_win->icon->w) { 2371 OtpRaise(Tmp_win, IconWin); 2372 } 2373 else { 2374 fprintf(stderr, "ERROR -- events.c:2815\n"); 2375 } 2376 } 2377 2378 if(!Scr->OpaqueMove) { 2379 UninstallRootColormap(); 2380 } 2381 else { 2382 XSync(dpy, 0); 2383 } 2384 2385 if(Scr->NumAutoRaises) { 2386 enter_flag = true; 2387 enter_win = NULL; 2388 raise_win = ((DragWindow == Tmp_win->frame && !Scr->NoRaiseMove) 2389 ? Tmp_win : NULL); 2390 } 2391 2392 /* CCC equivalent code for auto lower not needed? */ 2393 2394#if 0 2395 if(Scr->NumAutoLowers) { 2396 leave_flag = true; 2397 leave_win = NULL; 2398 lower_win = ((DragWindow == Tmp_win->frame) 2399 ? Tmp_win : NULL); 2400 } 2401#endif 2402 2403 DragWindow = (Window) 0; 2404 ConstMove = false; 2405 } 2406 2407 if(ResizeWindow != (Window) 0) { 2408 EndResize(); 2409 } 2410 2411 if(ActiveMenu != NULL && RootFunction == 0) { 2412 if(ActiveItem) { 2413 int func = ActiveItem->func; 2414 Action = ActiveItem->action; 2415 switch(func) { 2416 case F_TITLE: 2417 if(Scr->StayUpMenus) { 2418 ButtonPressed = -1; 2419 if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) { 2420 WarpCursorToDefaultEntry(ActiveMenu); 2421 } 2422 return; 2423 } 2424 break; 2425 case F_MOVE: 2426 case F_FORCEMOVE: 2427 case F_DESTROY: 2428 case F_DELETE: 2429 case F_DELETEORDESTROY: 2430 ButtonPressed = -1; 2431 break; 2432 case F_CIRCLEUP: 2433 case F_CIRCLEDOWN: 2434 case F_REFRESH: 2435 case F_WARPTOSCREEN: 2436 PopDownMenu(); 2437 break; 2438 default: 2439 break; 2440 } 2441 if(func != F_PIN && func != F_MENU) { 2442 PopDownMenu(); 2443 } 2444 ExecuteFunction(func, Action, 2445 ButtonWindow ? ButtonWindow->frame : None, 2446 ButtonWindow, &Event, Context, true); 2447 Context = C_NO_CONTEXT; 2448 ButtonWindow = NULL; 2449 2450 /* if we are not executing a defered command, then take down the 2451 * menu 2452 */ 2453 if(ActiveMenu) { 2454 PopDownMenu(); 2455 } 2456 } 2457 else if(Scr->StayUpMenus && !ActiveMenu->entered) { 2458 ButtonPressed = -1; 2459 if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) { 2460 WarpCursorToDefaultEntry(ActiveMenu); 2461 } 2462 return; 2463 } 2464 else { 2465 PopDownMenu(); 2466 } 2467 } 2468 2469 mask = (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask); 2470 switch(Event.xbutton.button) { 2471 case Button1: 2472 mask &= ~Button1Mask; 2473 break; 2474 case Button2: 2475 mask &= ~Button2Mask; 2476 break; 2477 case Button3: 2478 mask &= ~Button3Mask; 2479 break; 2480 case Button4: 2481 mask &= ~Button4Mask; 2482 break; 2483 case Button5: 2484 mask &= ~Button5Mask; 2485 break; 2486 } 2487 2488 if(RootFunction != 0 || 2489 ResizeWindow != None || 2490 DragWindow != None) { 2491 ButtonPressed = -1; 2492 } 2493 2494 if(AlternateKeymap || AlternateContext) { 2495 ButtonPressed = -1; 2496 return; 2497 } 2498 2499 if(RootFunction == 0 && 2500 (Event.xbutton.state & mask) == 0 && 2501 DragWindow == None && 2502 ResizeWindow == None) { 2503 XUngrabPointer(dpy, CurrentTime); 2504 XUngrabServer(dpy); 2505 XFlush(dpy); 2506 EventHandler[EnterNotify] = HandleEnterNotify; 2507 EventHandler[LeaveNotify] = HandleLeaveNotify; 2508 ButtonPressed = -1; 2509 if(DownIconManager) { 2510 DownIconManager->down = false; 2511 if(Scr->Highlight) { 2512 DrawIconManagerBorder(DownIconManager, false); 2513 } 2514 DownIconManager = NULL; 2515 } 2516 Cancel = false; 2517 } 2518} 2519 2520 2521/* 2522 * Pop up a submenu as a result of moving the mouse right on its entry. 2523 */ 2524static void do_menu(MenuRoot *menu, /* menu to pop up */ 2525 Window w) /* invoking window or None */ 2526{ 2527 int x = Event.xbutton.x_root; 2528 int y = Event.xbutton.y_root; 2529 bool center; 2530 2531 if(!Scr->NoGrabServer) { 2532 XGrabServer(dpy); 2533 } 2534 if(w) { 2535 int h = Scr->TBInfo.width - Scr->TBInfo.border; 2536 Window child; 2537 2538 XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child); 2539 center = false; 2540 } 2541 else { 2542 center = true; 2543 } 2544 if(PopUpMenu(menu, x, y, center)) { 2545 UpdateMenu(); 2546 } 2547 else { 2548 XBell(dpy, 0); 2549 } 2550} 2551 2552 2553/* 2554 * Pop up a submenu as a result of hitting the Right arrow key while on 2555 * its entry. We should try folding these two together a bit more. 2556 */ 2557static void do_key_menu(MenuRoot *menu, /* menu to pop up */ 2558 Window w) /* invoking window or None */ 2559{ 2560 int x = Event.xkey.x_root; 2561 int y = Event.xkey.y_root; 2562 bool center; 2563 2564 /* I don't think this is necessary. 2565 if (!Scr->NoGrabServer) XGrabServer(dpy); 2566 */ 2567 if(w) { 2568 int h = Scr->TBInfo.width - Scr->TBInfo.border; 2569 Window child; 2570 2571 XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child); 2572 center = false; 2573 } 2574 else { 2575 center = true; 2576 } 2577 if(PopUpMenu(menu, x, y, center)) { 2578 /* 2579 * Note: UpdateMenu() has the internal re-capture of the event 2580 * loop to handle in-menu stuff, so this won't actually return 2581 * until we somehow exit out of that [sub]menu. 2582 */ 2583 UpdateMenu(); 2584 } 2585 else { 2586 XBell(dpy, 0); 2587 } 2588 2589} 2590 2591 2592/*********************************************************************** 2593 * 2594 * Procedure: 2595 * HandleButtonPress - ButtonPress event handler 2596 * 2597 *********************************************************************** 2598 */ 2599void HandleButtonPress(void) 2600{ 2601 unsigned int modifier; 2602 Cursor cur; 2603 MenuRoot *mr; 2604 FuncButton *tmp = 0; 2605 int func = 0; 2606 Window w; 2607 2608 2609 /* pop down the menu, if any */ 2610 2611 if(XFindContext(dpy, Event.xbutton.window, MenuContext, 2612 (XPointer *) &mr) != XCSUCCESS) { 2613 mr = NULL; 2614 } 2615 if(ActiveMenu && (! ActiveMenu->pinned) && 2616 (Event.xbutton.subwindow != ActiveMenu->w)) { 2617 PopDownMenu(); 2618 return; 2619 } 2620 if((ActiveMenu != NULL) && (RootFunction != 0) && (mr != ActiveMenu)) { 2621 PopDownMenu(); 2622 } 2623 2624 XSync(dpy, 0); 2625 /* XXX - remove? */ 2626 2627 /* want menus if we have info box */ 2628 if(ButtonPressed != -1 && !Scr->InfoWindow.mapped) { 2629 /* we got another butt press in addition to one still held 2630 * down, we need to cancel the operation we were doing 2631 */ 2632 Cancel = true; 2633 CurrentDragX = origDragX; 2634 CurrentDragY = origDragY; 2635 if(!menuFromFrameOrWindowOrTitlebar) { 2636 if(Scr->OpaqueMove && DragWindow != None) { 2637 XMoveWindow(dpy, DragWindow, origDragX, origDragY); 2638 } 2639 else { 2640 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 2641 } 2642 } 2643 XUnmapWindow(dpy, Scr->SizeWindow); 2644 if(!Scr->OpaqueMove) { 2645 UninstallRootColormap(); 2646 } 2647 ResizeWindow = None; 2648 DragWindow = None; 2649 cur = LeftButt; 2650 if(Event.xbutton.button == Button2) { 2651 cur = MiddleButt; 2652 } 2653 else if(Event.xbutton.button >= Button3) { 2654 cur = RightButt; 2655 } 2656 2657 XGrabPointer(dpy, Scr->Root, True, 2658 ButtonReleaseMask | ButtonPressMask, 2659 GrabModeAsync, GrabModeAsync, 2660 Scr->Root, cur, CurrentTime); 2661 2662 return; 2663 } 2664 else { 2665 ButtonPressed = Event.xbutton.button; 2666 } 2667 2668 if((ActiveMenu != NULL) && (ActiveMenu->pinned)) { 2669 if(Event.xbutton.window == ActiveMenu->w) { 2670 modifier = (Event.xbutton.state & mods_used); 2671 modifier = set_mask_ignore(modifier); 2672 if((ActiveItem && (ActiveItem->func == F_TITLE)) || (modifier == Mod1Mask)) { 2673 MoveMenu(&Event); 2674 /*ButtonPressed = -1;*/ 2675 } 2676 } 2677 Context = C_ROOT; 2678 return; 2679 } 2680 2681 if(ResizeWindow != None || 2682 DragWindow != None || 2683 ActiveMenu != NULL) { 2684 return; 2685 } 2686 2687 /* check the title bar buttons */ 2688 if(Tmp_win && Tmp_win->title_height && Tmp_win->titlebuttons) { 2689 int i; 2690 TBWindow *tbw; 2691 TitleButtonFunc *tbf; 2692 int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 2693 2694 modifier = Event.xbutton.state & mods_used; 2695 modifier = set_mask_ignore(modifier); 2696 2697 for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) { 2698 if(Event.xany.window == tbw->window) { 2699 for(tbf = tbw->info->funs; tbf; tbf = tbf->next) { 2700 if(tbf->num == ButtonPressed 2701 && tbf->mods == modifier) { 2702 switch(tbf->func) { 2703 /* 2704 * Opening up a menu doesn't use the f.menu 2705 * handler, we use our do_menu(); x-ref 2706 * comments in the handler for details. 2707 */ 2708 case F_MENU : 2709 Context = C_TITLE; 2710 ButtonWindow = Tmp_win; 2711 do_menu(tbf->menuroot, tbw->window); 2712 break; 2713 2714 default : 2715 ExecuteFunction(tbf->func, tbf->action, 2716 Event.xany.window, Tmp_win, 2717 &Event, C_TITLE, false); 2718 } 2719 return; 2720 } 2721 } 2722 } 2723 } 2724 } 2725 2726 Context = C_NO_CONTEXT; 2727 2728 if(Event.xany.window == Scr->InfoWindow.win) { 2729 Context = C_IDENTIFY; 2730 } 2731 2732 if(Event.xany.window == Scr->Root) { 2733 if(AlternateContext) { 2734 XUngrabPointer(dpy, CurrentTime); 2735 XUngrabKeyboard(dpy, CurrentTime); 2736 AlternateContext = false; 2737 Context = C_ALTERNATE; 2738 } 2739 else if(AlternateKeymap && Event.xbutton.subwindow) { 2740 int dx, dy; 2741 Window child; 2742 2743 w = Event.xbutton.subwindow; 2744 Tmp_win = GetTwmWindow(w); 2745 if(Tmp_win) { 2746 Event.xany.window = Tmp_win->frame; 2747 XTranslateCoordinates(dpy, Scr->Root, Tmp_win->frame, 2748 Event.xbutton.x, Event.xbutton.y, &dx, &dy, &child); 2749 Event.xbutton.x = dx; 2750 Event.xbutton.x = dy; 2751 Event.xbutton.subwindow = child; 2752 } 2753 } 2754 else { 2755 Context = C_ROOT; 2756 } 2757 } 2758 if(Tmp_win) { 2759 if(Tmp_win->iconmanagerlist && (RootFunction != 0) && 2760 ((Event.xany.window == Tmp_win->iconmanagerlist->icon) || 2761 (Event.xany.window == Tmp_win->iconmanagerlist->w))) { 2762 int x, y; 2763 2764 Tmp_win = Tmp_win->iconmanagerlist->iconmgr->twm_win; 2765 XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w, 2766 Event.xbutton.x, Event.xbutton.y, 2767 &x, &y, &JunkChild); 2768 2769 Event.xbutton.x = x - Tmp_win->frame_bw3D; 2770 Event.xbutton.y = y - Tmp_win->title_height - Tmp_win->frame_bw3D; 2771 Event.xany.window = Tmp_win->w; 2772 Context = C_WINDOW; 2773 } 2774#ifdef EWMH_DESKTOP_ROOT 2775 else if(Tmp_win->ewmhWindowType == wt_Desktop) { 2776 fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_ROOT\n"); 2777 Context = C_ROOT; 2778 } 2779#endif 2780 else if(Event.xany.window == Tmp_win->title_w) { 2781 if(Scr->ClickToFocus && Tmp_win->wmhints->input) { 2782 SetFocus(Tmp_win, CurrentTime); 2783 } 2784 Context = C_TITLE; 2785 } 2786 else if(Event.xany.window == Tmp_win->w) { 2787 if(Scr->ClickToFocus || Scr->RaiseOnClick) { 2788 if(Scr->ClickToFocus && Tmp_win->wmhints->input) { 2789 SetFocus(Tmp_win, CurrentTime); 2790 } 2791 if(Scr->RaiseOnClick) { 2792 OtpRaise(Tmp_win, WinWin); 2793 WMapRaise(Tmp_win); 2794 } 2795 XSync(dpy, 0); 2796 XAllowEvents(dpy, ReplayPointer, CurrentTime); 2797 XSync(dpy, 0); 2798 ButtonPressed = -1; 2799 return; 2800 } 2801 else { 2802 printf("ERROR! ERROR! ERROR! YOU SHOULD NOT BE HERE!!!\n"); 2803 Context = C_WINDOW; 2804 } 2805 } 2806 else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) { 2807 Context = C_ICON; 2808 } 2809 else if(Event.xany.window == Tmp_win->frame) { 2810 Window chwin; 2811 2812 /* since we now place a button grab on the frame instead 2813 * of the window, (see GrabButtons() in add_window.c), we 2814 * need to figure out where the pointer exactly is before 2815 * assigning Context. If the pointer is on the application 2816 * window we will change the event structure to look as if 2817 * it came from the application window. 2818 */ 2819 if(Event.xbutton.subwindow == Tmp_win->w) { 2820 XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w, 2821 Event.xbutton.x, Event.xbutton.y, 2822 &Event.xbutton.x, &Event.xbutton.y, 2823 &chwin); 2824 Event.xbutton.window = Tmp_win->w; 2825 2826 if(Tmp_win->iswinbox && chwin) { 2827 int x, y; 2828 XTranslateCoordinates(dpy, Tmp_win->w, chwin, 2829 Event.xbutton.x, Event.xbutton.y, 2830 &x, &y, &chwin); 2831 if(chwin && (Tmp_win = GetTwmWindow(chwin))) { 2832 Event.xany.window = chwin; 2833 Event.xbutton.x = x; 2834 Event.xbutton.y = y; 2835 } 2836 } 2837 Context = C_WINDOW; 2838 } 2839 else if(Event.xbutton.subwindow 2840 && (Event.xbutton.subwindow == Tmp_win->title_w)) { 2841 Context = C_TITLE; 2842 } 2843 else { 2844 Context = C_FRAME; 2845 } 2846 if(Scr->ClickToFocus && Tmp_win->wmhints->input) { 2847 SetFocus(Tmp_win, CurrentTime); 2848 } 2849 } 2850 else if(Tmp_win->iswspmgr || 2851 (Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) { 2852 /*Context = C_WINDOW; probably a typo */ 2853 Context = C_WORKSPACE; 2854 } 2855 else if(Tmp_win->iconmanagerlist) { 2856 if((Event.xany.window == Tmp_win->iconmanagerlist->icon) || 2857 (Event.xany.window == Tmp_win->iconmanagerlist->w)) { 2858 Tmp_win->iconmanagerlist->down = true; 2859 if(Scr->Highlight) { 2860 DrawIconManagerBorder(Tmp_win->iconmanagerlist, false); 2861 } 2862 DownIconManager = Tmp_win->iconmanagerlist; 2863 Context = C_ICONMGR; 2864 } 2865 } 2866 } 2867 2868 /* this section of code checks to see if we were in the middle of 2869 * a command executed from a menu 2870 */ 2871 if(RootFunction != 0) { 2872 if(Event.xany.window == Scr->Root) { 2873 Window win; 2874 int x, y; 2875 2876 /* if the window was the Root, we don't know for sure it 2877 * it was the root. We must check to see if it happened to be 2878 * inside of a client that was getting button press events. 2879 */ 2880 XTranslateCoordinates(dpy, Scr->Root, Scr->Root, 2881 Event.xbutton.x, 2882 Event.xbutton.y, 2883 &x, &y, &Event.xany.window); 2884 2885 if(Event.xany.window != 0 && 2886 (Tmp_win = GetTwmWindow(Event.xany.window))) { 2887 if(Tmp_win->iswinbox) { 2888 XTranslateCoordinates(dpy, Scr->Root, Event.xany.window, 2889 x, y, &x, &y, &win); 2890 XTranslateCoordinates(dpy, Event.xany.window, win, 2891 x, y, &x, &y, &win); 2892 if(win != 0) { 2893 Event.xany.window = win; 2894 } 2895 } 2896 } 2897 if(Event.xany.window == 0 || 2898 !(Tmp_win = GetTwmWindow(Event.xany.window))) { 2899 RootFunction = 0; 2900 XBell(dpy, 0); 2901 return; 2902 } 2903 XTranslateCoordinates(dpy, Scr->Root, Event.xany.window, 2904 Event.xbutton.x, 2905 Event.xbutton.y, 2906 &x, &y, &JunkChild); 2907 2908 Event.xbutton.x = x; 2909 Event.xbutton.y = y; 2910 Context = C_WINDOW; 2911 } 2912 else if(mr != NULL) { 2913 RootFunction = 0; 2914 XBell(dpy, 0); 2915 return; 2916 } 2917 2918 /* make sure we are not trying to move an identify window */ 2919 if(Event.xany.window != Scr->InfoWindow.win) { 2920 /* 2921 * X-ref comment at top of file about Action; this is where 2922 * we need to use its broader lifespan. 2923 */ 2924 ExecuteFunction(RootFunction, Action, Event.xany.window, 2925 Tmp_win, &Event, Context, false); 2926 } 2927 2928 RootFunction = 0; 2929 return; 2930 } 2931 2932 ButtonWindow = Tmp_win; 2933 2934 /* if we get to here, we have to execute a function or pop up a 2935 * menu 2936 */ 2937 modifier = (Event.xbutton.state | AlternateKeymap) & mods_used; 2938 modifier = set_mask_ignore(modifier); 2939 if(AlternateKeymap) { 2940 XUngrabPointer(dpy, CurrentTime); 2941 XUngrabKeyboard(dpy, CurrentTime); 2942 AlternateKeymap = 0; 2943 } 2944 if((Context == C_NO_CONTEXT) || (Context == C_IDENTIFY)) { 2945 return; 2946 } 2947 2948 RootFunction = 0; 2949 2950 /* see if there already is a key defined for this context */ 2951 for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) { 2952 if((tmp->num == Event.xbutton.button) && 2953 (tmp->cont == Context) && (tmp->mods == modifier)) { 2954 break; 2955 } 2956 } 2957 if(tmp) { 2958 func = tmp->func; 2959 switch(func) { 2960 /* 2961 * f.menu isn't invoked, it's handle magically. Other funcs 2962 * we just invoke. X-ref the f.menu handler for details. 2963 */ 2964 case F_MENU : 2965 do_menu(tmp->menu, (Window) None); 2966 break; 2967 2968 default : 2969 if(func != 0) { 2970 Action = tmp->item ? tmp->item->action : NULL; 2971#ifdef EWMH_DESKTOP_ROOT 2972 if(Context == C_ROOT && Tmp_win != NULL) { 2973 Context = C_WINDOW; 2974 fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_WINDOW\n"); 2975 } 2976#endif /* EWMH */ 2977 ExecuteFunction(func, 2978 Action, Event.xany.window, Tmp_win, &Event, Context, false); 2979 } 2980 } 2981 } 2982 else { 2983 if(Tmp_win == Scr->currentvs->wsw->twm_win) { 2984 WMgrHandleButtonEvent(Scr->currentvs, &Event); 2985 return; 2986 } 2987 } 2988 if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) { 2989 OccupyHandleButtonEvent(&Event); 2990 } 2991 else if(func == 0 && Scr->DefaultFunction.func != 0) { 2992 if(Scr->DefaultFunction.func == F_MENU) { 2993 do_menu(Scr->DefaultFunction.menu, (Window) None); 2994 } 2995 else { 2996 Action = Scr->DefaultFunction.item ? 2997 Scr->DefaultFunction.item->action : NULL; 2998 ExecuteFunction(Scr->DefaultFunction.func, Action, 2999 Event.xany.window, Tmp_win, &Event, Context, false); 3000 } 3001 } 3002} 3003 3004 3005/*********************************************************************** 3006 * 3007 * Procedure: 3008 * HENQueueScanner - EnterNotify event q scanner 3009 * 3010 * Looks at the queued events and determines if any matching 3011 * LeaveNotify events or EnterEvents deriving from the 3012 * termination of a grab are behind this event to allow 3013 * skipping of unnecessary processing. 3014 * 3015 *********************************************************************** 3016 */ 3017 3018typedef struct HENScanArgs { 3019 Window w; /* Window we are currently entering */ 3020 Bool leaves; /* Any LeaveNotifies found for this window */ 3021 Bool inferior; /* Was NotifyInferior the mode for LeaveNotify */ 3022 Bool enters; /* Any EnterNotify events with NotifyUngrab */ 3023} HENScanArgs; 3024 3025/* ARGSUSED*/ 3026static Bool HENQueueScanner(Display *display, XEvent *ev, char *_args) 3027{ 3028 HENScanArgs *args = (void *)_args; 3029 3030 if(ev->type == LeaveNotify) { 3031 if(ev->xcrossing.window == args->w && 3032 ev->xcrossing.mode == NotifyNormal) { 3033 args->leaves = True; 3034 /* 3035 * Only the last event found matters for the Inferior field. 3036 */ 3037 args->inferior = 3038 (ev->xcrossing.detail == NotifyInferior); 3039 } 3040 } 3041 else if(ev->type == EnterNotify) { 3042 if(ev->xcrossing.mode == NotifyUngrab) { 3043 args->enters = True; 3044 } 3045 } 3046 3047 return (False); 3048} 3049 3050 3051/*********************************************************************** 3052 * 3053 * Procedure: 3054 * HandleEnterNotify - EnterNotify event handler 3055 * 3056 *********************************************************************** 3057 */ 3058 3059void HandleEnterNotify(void) 3060{ 3061 MenuRoot *mr, *tmp; 3062 XEnterWindowEvent *ewp = &Event.xcrossing; 3063 HENScanArgs scanArgs; 3064 XEvent dummy; 3065 VirtualScreen *vs; 3066 3067 /* 3068 * if we aren't in the middle of menu processing 3069 */ 3070 if(!ActiveMenu) { 3071 /* 3072 * We're not interested in pseudo Enter/Leave events generated 3073 * from grab initiations. 3074 */ 3075 if(ewp->mode == NotifyGrab) { 3076 return; 3077 } 3078 3079 /* 3080 * Scan for Leave and Enter Notify events to see if we can avoid some 3081 * unnecessary processing. 3082 */ 3083 scanArgs.w = ewp->window; 3084 scanArgs.leaves = scanArgs.enters = False; 3085 XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs); 3086 3087 /* 3088 * if entering root window, restore twm default colormap so that 3089 * titlebars are legible 3090 */ 3091 if(ewp->window == Scr->Root) { 3092 Window forus_ret; 3093 int focus_rev; 3094 3095 if(!scanArgs.leaves && !scanArgs.enters) { 3096 InstallColormaps(EnterNotify, &Scr->RootColormaps); 3097 } 3098 if(! Scr->FocusRoot) { 3099 return; 3100 } 3101 XGetInputFocus(dpy, &forus_ret, &focus_rev); 3102 if((forus_ret != PointerRoot) && (forus_ret != None)) { 3103 SetFocus(NULL, Event.xcrossing.time); 3104 } 3105 return; 3106 } 3107 for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) { 3108 if(ewp->window == vs->window) { 3109 Scr->Root = vs->window; 3110 Scr->rootx = Scr->crootx + vs->x; 3111 Scr->rooty = Scr->crooty + vs->y; 3112 Scr->rootw = vs->w; 3113 Scr->rooth = vs->h; 3114 Scr->currentvs = vs; 3115#if 0 3116 fprintf(stderr, "entering new vs : 0x%x, 0x%x, %d, %d, %d, %d\n", 3117 vs, Scr->Root, vs->x, vs->y, vs->w, vs->h); 3118#endif 3119 return; 3120 } 3121 } 3122 3123 /* Handle RaiseDelay, if any..... 3124 */ 3125 if(RaiseDelay > 0) { 3126 if(Tmp_win && Tmp_win->auto_raise && 3127 (!Tmp_win->iconmanagerlist || 3128 Tmp_win->iconmanagerlist->w != ewp->window)) { 3129 ColormapWindow *cwin; 3130 static struct timeval tout, timeout = {0, 12500}; 3131 3132 if(XFindContext(dpy, Tmp_win->w, ColormapContext, 3133 (XPointer *)&cwin) == XCNOENT) { 3134 cwin = NULL; 3135 } 3136 3137 if((ewp->detail != NotifyInferior 3138 || Tmp_win->frame == ewp->window) 3139 && (!cwin || cwin->visibility != VisibilityUnobscured)) { 3140 int x, y, px, py, d, i; 3141 Window w; 3142 3143 XQueryPointer(dpy, Scr->Root, &w, &w, &px, &py, 3144 &d, &d, (unsigned int *)&d); 3145 3146 /* The granularity of RaiseDelay is about 25 ms. 3147 * The timeout variable is set to 12.5 ms since we 3148 * pass this way twice each time a twm window is 3149 * entered. 3150 */ 3151 for(i = 25; i < RaiseDelay; i += 25) { 3152 tout = timeout; 3153 select(0, NULL, NULL, NULL, &tout); 3154 /* Did we leave this window already? */ 3155 scanArgs.w = ewp->window; 3156 scanArgs.leaves = scanArgs.enters = False; 3157 XCheckIfEvent(dpy, &dummy, HENQueueScanner, 3158 (void *) &scanArgs); 3159 if(scanArgs.leaves && !scanArgs.inferior) { 3160 return; 3161 } 3162 3163 XQueryPointer(dpy, Scr->Root, &w, &w, &x, &y, 3164 &d, &d, (unsigned int *)&d); 3165 3166 /* Has the pointer moved? If so reset the loop cnt. 3167 * We want the pointer to be still for RaiseDelay 3168 * milliseconds before terminating the loop 3169 */ 3170 if(x != px || y != py) { 3171 i = 0; 3172 px = x; 3173 py = y; 3174 } 3175 } 3176 } 3177 } 3178 3179 /* 3180 * Scan for Leave and Enter Notify events to see if we can avoid some 3181 * unnecessary processing. 3182 */ 3183 scanArgs.w = ewp->window; 3184 scanArgs.leaves = scanArgs.enters = False; 3185 XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs); 3186 3187 /* 3188 * if entering root window, restore twm default colormap so that 3189 * titlebars are legible 3190 */ 3191 if(ewp->window == Scr->Root) { 3192 if(!scanArgs.leaves && !scanArgs.enters) { 3193 InstallColormaps(EnterNotify, &Scr->RootColormaps); 3194 } 3195 return; 3196 } 3197 } 3198 /* End of RaiseDelay modification. */ 3199 3200 /* 3201 * if we have an event for a specific one of our windows 3202 */ 3203 if(Tmp_win) { 3204 /* 3205 * If currently in PointerRoot mode (indicated by FocusRoot), then 3206 * focus on this window 3207 */ 3208 if(Scr->FocusRoot && (!scanArgs.leaves || scanArgs.inferior)) { 3209 bool accinput; 3210 3211 if(Scr->ShrinkIconTitles && 3212 Tmp_win->icon && 3213 ewp->window == Tmp_win->icon->w && 3214 ewp->detail != NotifyInferior) { 3215 if(Scr->AutoRaiseIcons) { 3216 OtpRaise(Tmp_win, IconWin); 3217 } 3218 ExpandIconTitle(Tmp_win); 3219 return; 3220 } 3221 3222 if(Tmp_win->iconmanagerlist) { 3223 CurrentIconManagerEntry(Tmp_win->iconmanagerlist); 3224 } 3225 3226 accinput = Tmp_win->mapped && Tmp_win->wmhints->input; 3227 if(Tmp_win->iconmanagerlist && 3228 ewp->window == Tmp_win->iconmanagerlist->w && 3229 !accinput && 3230 Tmp_win->iconmanagerlist->iconmgr && 3231 Tmp_win->iconmanagerlist->iconmgr->twm_win) { 3232 SetFocus(Tmp_win->iconmanagerlist->iconmgr->twm_win, 3233 CurrentTime); 3234 return; 3235 } 3236 3237 if(Tmp_win->mapped) { 3238 /* 3239 * unhighlight old focus window 3240 */ 3241 3242 /* 3243 * If entering the frame or the icon manager, then do 3244 * "window activation things": 3245 * 3246 * 1. <highlighting is not done here any more> 3247 * 2. install frame colormap 3248 * 3. <frame and highlight border not set here> 3249 * 4. focus on client window to forward typing 3250 * 4a. same as 4 but for icon mgr w/with NoTitleFocus 3251 * 5. send WM_TAKE_FOCUS if requested 3252 */ 3253 if(Scr->BorderCursors && ewp->window == Tmp_win->frame) { 3254 SetBorderCursor(Tmp_win, ewp->x, ewp->y); 3255 } 3256 if(ewp->window == Tmp_win->frame || 3257 (Scr->IconManagerFocus && 3258 Tmp_win->iconmanagerlist && 3259 ewp->window == Tmp_win->iconmanagerlist->w)) { 3260 3261 if(!scanArgs.leaves && !scanArgs.enters) { 3262 InstallColormaps(EnterNotify, /* 2 */ 3263 &Scr->RootColormaps); 3264 } 3265 3266 /* 3267 * Event is in the frame or the icon mgr: 3268 * 3269 * "4" -- TitleFocus is set: windows should get 3270 * focus as long as they accept input. 3271 * 3272 * "4a" - If TitleFocus is not set, windows should get 3273 * the focus if the event was in the icon mgr 3274 * (as long as they accept input). 3275 * 3276 */ 3277 3278 /* If the window takes input... */ 3279 if(Tmp_win->wmhints->input) { 3280 3281 /* if 4 or 4a, focus on the window */ 3282 if(Scr->TitleFocus || 3283 (Tmp_win->iconmanagerlist && 3284 (Tmp_win->iconmanagerlist->w == ewp->window))) { 3285 SetFocus(Tmp_win, ewp->time); 3286 } 3287 } 3288 3289 if(Scr->TitleFocus && 3290 (Tmp_win->protocols & DoesWmTakeFocus)) { /* 5 */ 3291 3292 /* for both locally or globally active */ 3293 SendTakeFocusMessage(Tmp_win, ewp->time); 3294 } 3295 else if(!Scr->TitleFocus 3296 && Tmp_win->wmhints->input 3297 && Event.xcrossing.focus) { 3298 SynthesiseFocusIn(Tmp_win->w); 3299 } 3300 3301 } 3302 else if(ewp->window == Tmp_win->w) { 3303 /* 3304 * If we are entering the application window, install 3305 * its colormap(s). 3306 */ 3307 if(Scr->BorderCursors) { 3308 SetBorderCursor(Tmp_win, -1000, -1000); 3309 } 3310 if(!scanArgs.leaves || scanArgs.inferior) { 3311 InstallWindowColormaps(EnterNotify, Tmp_win); 3312 } 3313 3314 if(Event.xcrossing.focus) { 3315 SynthesiseFocusIn(Tmp_win->w); 3316 } 3317 3318 /* must deal with WM_TAKE_FOCUS clients now, if 3319 we're not in TitleFocus mode */ 3320 3321 if(!(Scr->TitleFocus) && 3322 (Tmp_win->protocols & DoesWmTakeFocus)) { 3323 3324 /* locally active clients need help from WM 3325 to get the input focus */ 3326 3327 if(Tmp_win->wmhints->input) { 3328 SetFocus(Tmp_win, ewp->time); 3329 } 3330 3331 /* for both locally & globally active clnts */ 3332 3333 SendTakeFocusMessage(Tmp_win, ewp->time); 3334 } 3335 } 3336 } /* end if Tmp_win->mapped */ 3337 if(ewp->window == Tmp_win->wmhints->icon_window && 3338 (!scanArgs.leaves || scanArgs.inferior)) { 3339 InstallWindowColormaps(EnterNotify, Tmp_win); 3340 } 3341 } /* end if FocusRoot */ 3342 else if(Scr->BorderCursors && (ewp->window == Tmp_win->w)) { 3343 SetBorderCursor(Tmp_win, -1000, -1000); 3344 } 3345 /* 3346 * If this window is to be autoraised, mark it so 3347 */ 3348 if(Tmp_win->auto_raise) { 3349 enter_win = Tmp_win; 3350 if(enter_flag == false) { 3351 AutoRaiseWindow(Tmp_win); 3352 } 3353 } 3354 else if(enter_flag && raise_win == Tmp_win) { 3355 enter_win = Tmp_win; 3356 } 3357 /* 3358 * set ring leader 3359 */ 3360 if(Tmp_win->ring.next && (!enter_flag || raise_win == enter_win)) { 3361 Scr->RingLeader = Tmp_win; 3362 } 3363 XSync(dpy, 0); 3364 return; 3365 } /* end if Tmp_win */ 3366 } /* end if !ActiveMenu */ 3367 3368 /* 3369 * Find the menu that we are dealing with now; punt if unknown 3370 */ 3371 if(XFindContext(dpy, ewp->window, MenuContext, (XPointer *)&mr) != XCSUCCESS) { 3372 return; 3373 } 3374 3375 if(! ActiveMenu && mr->pinned && (RootFunction == 0)) { 3376 PopUpMenu(mr, 0, 0, false); 3377 Context = C_ROOT; 3378 UpdateMenu(); 3379 return; 3380 } 3381 mr->entered = true; 3382 if(RootFunction == 0) { 3383 for(tmp = ActiveMenu; tmp; tmp = tmp->prev) { 3384 if(tmp == mr) { 3385 break; 3386 } 3387 } 3388 if(! tmp) { 3389 return; 3390 } 3391 3392 for(tmp = ActiveMenu; tmp != mr; tmp = tmp->prev) { 3393 if(tmp->pinned) { 3394 break; 3395 } 3396 HideMenu(tmp); 3397 MenuDepth--; 3398 } 3399 UninstallRootColormap(); 3400 3401 if(ActiveItem) { 3402 ActiveItem->state = 0; 3403 PaintEntry(ActiveMenu, ActiveItem, false); 3404 } 3405 ActiveItem = NULL; 3406 ActiveMenu = mr; 3407 if(1/*Scr->StayUpMenus*/) { 3408 int i, x, y, x_root, y_root, entry; 3409 MenuItem *mi; 3410 3411 XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild, &x_root, &y_root, 3412 &x, &y, &JunkMask); 3413 if((x > 0) && (y > 0) && (x < ActiveMenu->width) && (y < ActiveMenu->height)) { 3414 entry = y / Scr->EntryHeight; 3415 for(i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) { 3416 if(i == entry) { 3417 break; 3418 } 3419 } 3420 if(mi) { 3421 ActiveItem = mi; 3422 ActiveItem->state = 1; 3423 PaintEntry(ActiveMenu, ActiveItem, false); 3424 } 3425 } 3426 } 3427 if(ActiveMenu->pinned) { 3428 XUngrabPointer(dpy, CurrentTime); 3429 } 3430 } 3431 return; 3432} 3433 3434 3435/*********************************************************************** 3436 * 3437 * Procedure: 3438 * HLNQueueScanner - LeaveNotify event q scanner 3439 * 3440 * Looks at the queued events and determines if any 3441 * EnterNotify events are behind this event to allow 3442 * skipping of unnecessary processing. 3443 * 3444 *********************************************************************** 3445 */ 3446 3447typedef struct HLNScanArgs { 3448 Window w; /* The window getting the LeaveNotify */ 3449 Bool enters; /* Any EnterNotify event at all */ 3450 Bool matches; /* Any matching EnterNotify events */ 3451} HLNScanArgs; 3452 3453/* ARGSUSED*/ 3454static Bool HLNQueueScanner(Display *display, XEvent *ev, char *_args) 3455{ 3456 HLNScanArgs *args = (void *)_args; 3457 3458 if(ev->type == EnterNotify && ev->xcrossing.mode != NotifyGrab) { 3459 args->enters = True; 3460 if(ev->xcrossing.window == args->w) { 3461 args->matches = True; 3462 } 3463 } 3464 3465 return (False); 3466} 3467 3468 3469/*********************************************************************** 3470 * 3471 * Procedure: 3472 * HandleLeaveNotify - LeaveNotify event handler 3473 * 3474 *********************************************************************** 3475 */ 3476 3477void HandleLeaveNotify(void) 3478{ 3479 bool inicon; 3480 3481 if(ActiveMenu && ActiveMenu->pinned 3482 && (Event.xcrossing.window == ActiveMenu->w)) { 3483 PopDownMenu(); 3484 } 3485 3486 if(Tmp_win == NULL) { 3487 /* No window to be Leave'ing, so nothing much to do... */ 3488 return; 3489 } 3490 3491 /* 3492 * We're not interested in pseudo Enter/Leave events generated 3493 * from grab initiations and terminations. 3494 */ 3495 if(Event.xcrossing.mode != NotifyNormal) { 3496 return; 3497 } 3498 3499 if(Scr->ShrinkIconTitles && 3500 Tmp_win->icon && 3501 Event.xcrossing.window == Tmp_win->icon->w && 3502 Event.xcrossing.detail != NotifyInferior) { 3503 ShrinkIconTitle(Tmp_win); 3504 return; 3505 } 3506 3507 // Are we Leave'ing the icon manager entry for the Tmp_win in 3508 // question, or some other part of the window itself? 3509 inicon = (Tmp_win->iconmanagerlist && 3510 Tmp_win->iconmanagerlist->w == Event.xcrossing.window); 3511 3512 if(Scr->RingLeader && Scr->RingLeader == Tmp_win && 3513 (Event.xcrossing.detail != NotifyInferior && 3514 Event.xcrossing.window != Tmp_win->w)) { 3515#ifdef DEBUG 3516 fprintf(stderr, 3517 "HandleLeaveNotify: Event.xcrossing.window %x != Tmp_win->w %x\n", 3518 Event.xcrossing.window, Tmp_win->w); 3519#endif 3520 if(!inicon) { 3521 if(Event.xcrossing.window != Tmp_win->frame /*was: Tmp_win->mapped*/) { 3522 Tmp_win->ring.cursor_valid = false; 3523#ifdef DEBUG 3524 fprintf(stderr, "HandleLeaveNotify: cursor_valid = false\n"); 3525#endif 3526 } 3527 else { /* Event.xcrossing.window == Tmp_win->frame */ 3528 Tmp_win->ring.cursor_valid = true; 3529 Tmp_win->ring.curs_x = (Event.xcrossing.x_root - 3530 Tmp_win->frame_x); 3531 Tmp_win->ring.curs_y = (Event.xcrossing.y_root - 3532 Tmp_win->frame_y); 3533#ifdef DEBUG 3534 fprintf(stderr, 3535 "HandleLeaveNotify: cursor_valid = true; x = %d (%d-%d), y = %d (%d-%d)\n", 3536 Tmp_win->ring.curs_x, Event.xcrossing.x_root, Tmp_win->frame_x, 3537 Tmp_win->ring.curs_y, Event.xcrossing.y_root, Tmp_win->frame_y); 3538#endif 3539 } 3540 } 3541 Scr->RingLeader = NULL; 3542 } 3543 3544 3545 /* 3546 * Are we moving focus based on the leave? There are 2 steps to 3547 * this: 3548 * - Scr->FocusRoot is our "are we automatically changing focus 3549 * based on the leave" flag. This gets unset when ClickToFocus 3550 * is config'd or a window is f.focus'd. 3551 * - Then we check the detail for the focus leaving. We're only 3552 * getting here for normal entry/exits. Most cases outside of 3553 * the icon manager peek ahead in the event queue for any 3554 * Enter's, and don't do anything if there are; if there were, 3555 * we'd be moving the focus when we get them, so no point doing 3556 * it twice. So the remainder here assumes there aren't any 3557 * Enters waiting. 3558 * 3559 * See 3560 * <https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#Normal_EntryExit_Events> 3561 * for details of the cases. So, when do we want to un-set the 3562 * focus? Let's look at each doc'd value... 3563 * - NotifyAncestor means we're leaving a subwindow for its 3564 * parent. That means the root, so, yep, we want to yield 3565 * focus up to it. 3566 * - NotifyNonLinear means we're leaving a window for something 3567 * that isn't a parent or child. So we should probably yield 3568 * focus in this case too. 3569 * - NotifyInferior means we're leaving a window for a 3570 * subwindow of it. From the WM perspective, that means 3571 * we're leaving focus where it was. 3572 * - NotifyVirtual means we're in the middle of an ascending 3573 * sequence. Nothing to do; the Ancestor handling already 3574 * did the job. 3575 * - NotifyNonLinearVirtual is another "in the middle" case, so 3576 * we skip handling there too; the endpoints will do 3577 * whatever's necessary. 3578 */ 3579 if(Scr->FocusRoot 3580 && Event.xcrossing.detail != NotifyInferior 3581 && Event.xcrossing.detail != NotifyVirtual 3582 && Event.xcrossing.detail != NotifyNonlinearVirtual 3583 ) { 3584 HLNScanArgs scanArgs; 3585 XEvent dummy; 3586 3587 /* 3588 * Scan for EnterNotify events to see if we can avoid some 3589 * unnecessary processing. 3590 */ 3591 scanArgs.w = Event.xcrossing.window; 3592 scanArgs.enters = scanArgs.matches = False; 3593 XCheckIfEvent(dpy, &dummy, HLNQueueScanner, 3594 (char *) &scanArgs); 3595 3596 if((inicon && Scr->IconManagerFocus) 3597 || (Event.xcrossing.window == Tmp_win->frame 3598 && !scanArgs.matches) 3599 ) { 3600 // Defocusing window because we moved out of its entry in an 3601 // icon manager, or because we moved out of its frame. 3602 3603 // Nothing to do if we were in the icon manager, and the 3604 // window's either unmapped or doesn't accept input. XXX Is 3605 // the inicon flag needed here? If it's not mapped, we 3606 // presumably couldn't have gotten a Leave on its frame 3607 // anyway, and if it's not accepting input, we probably don't 3608 // need to focus out anyway? Left conditional because this 3609 // matches historical behavior prior to some rework here, but 3610 // revisit. 3611 if(inicon && (!Tmp_win->mapped || !Tmp_win->wmhints->input)) { 3612 return; 3613 } 3614 3615 // Shift away focus 3616 if(Scr->TitleFocus || Tmp_win->protocols & DoesWmTakeFocus) { 3617 SetFocus(NULL, Event.xcrossing.time); 3618 } 3619 3620 // If we're in the icon manager, we need to take a FocusOut 3621 // event for the window, since it wouldn't have gotten one. 3622 // If we're in the frame, we fake one anyway as historical 3623 // code says "pretend there was a focus out as sometimes we 3624 // don't get one". 3625 if(Event.xcrossing.focus) { 3626 SynthesiseFocusOut(Tmp_win->w); 3627 } 3628 } 3629 else if(Event.xcrossing.window == Tmp_win->w && !scanArgs.enters) { 3630 // Flipping colormaps around because we moved out of the 3631 // window. 3632 InstallColormaps(LeaveNotify, &Scr->RootColormaps); 3633 } 3634 } 3635 3636 /* Autolower modification. */ 3637 if(Tmp_win->auto_lower) { 3638 leave_win = Tmp_win; 3639 if(leave_flag == false) { 3640 AutoLowerWindow(Tmp_win); 3641 } 3642 } 3643 else if(leave_flag && lower_win == Tmp_win) { 3644 leave_win = Tmp_win; 3645 } 3646 3647 XSync(dpy, 0); 3648 return; 3649} 3650 3651 3652/*********************************************************************** 3653 * 3654 * Procedure: 3655 * HandleConfigureRequest - ConfigureRequest event handler 3656 * 3657 *********************************************************************** 3658 */ 3659 3660void HandleConfigureRequest(void) 3661{ 3662 XWindowChanges xwc; 3663 unsigned long xwcm; 3664 int x, y, width, height, bw; 3665 int gravx, gravy; 3666 XConfigureRequestEvent *cre = &Event.xconfigurerequest; 3667 bool sendEvent; 3668 3669#ifdef DEBUG_EVENTS 3670 fprintf(stderr, "ConfigureRequest\n"); 3671 if(cre->value_mask & CWX) { 3672 fprintf(stderr, " x = %d\n", cre->x); 3673 } 3674 if(cre->value_mask & CWY) { 3675 fprintf(stderr, " y = %d\n", cre->y); 3676 } 3677 if(cre->value_mask & CWWidth) { 3678 fprintf(stderr, " width = %d\n", cre->width); 3679 } 3680 if(cre->value_mask & CWHeight) { 3681 fprintf(stderr, " height = %d\n", cre->height); 3682 } 3683 if(cre->value_mask & CWSibling) { 3684 fprintf(stderr, " above = 0x%x\n", (unsigned)cre->above); 3685 } 3686 if(cre->value_mask & CWStackMode) { 3687 fprintf(stderr, " stack = %d\n", cre->detail); 3688 } 3689#endif 3690 3691 /* 3692 * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will 3693 * be wrong 3694 */ 3695 Event.xany.window = cre->window; /* mash parent field */ 3696 Tmp_win = GetTwmWindow(cre->window); 3697 3698 /* 3699 * According to the July 27, 1988 ICCCM draft, we should ignore size and 3700 * position fields in the WM_NORMAL_HINTS property when we map a window. 3701 * Instead, we'll read the current geometry. Therefore, we should respond 3702 * to configuration requests for windows which have never been mapped. 3703 */ 3704 if(!Tmp_win || (Tmp_win->icon && (Tmp_win->icon->w == cre->window))) { 3705 xwcm = cre->value_mask & 3706 (CWX | CWY | CWWidth | CWHeight | CWBorderWidth); 3707 xwc.x = cre->x; 3708 xwc.y = cre->y; 3709 xwc.width = cre->width; 3710 xwc.height = cre->height; 3711 xwc.border_width = cre->border_width; 3712 XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc); 3713 return; 3714 } 3715 3716 sendEvent = false; 3717 if((cre->value_mask & CWStackMode) && Tmp_win->stackmode) { 3718 TwmWindow *otherwin; 3719 3720 if(cre->value_mask & CWSibling) { 3721 otherwin = GetTwmWindow(cre->above); 3722 if(otherwin) { 3723 OtpForcePlacement(Tmp_win, cre->detail, otherwin); 3724 } 3725 else { 3726 fprintf(stderr, "XConfigureRequest: unkown otherwin\n"); 3727 } 3728 } 3729 else { 3730 switch(cre->detail) { 3731 case TopIf: 3732 case Above: 3733 OtpRaise(Tmp_win, WinWin); 3734 break; 3735 case BottomIf: 3736 case Below: 3737 OtpLower(Tmp_win, WinWin); 3738 break; 3739 case Opposite: 3740 OtpRaiseLower(Tmp_win, WinWin); 3741 break; 3742 default: 3743 ; 3744 } 3745 } 3746 sendEvent = true; 3747 } 3748 3749 3750 /* Don't modify frame_XXX fields before calling SetupWindow! */ 3751 x = Tmp_win->frame_x; 3752 y = Tmp_win->frame_y; 3753 width = Tmp_win->frame_width; 3754 height = Tmp_win->frame_height; 3755 bw = Tmp_win->frame_bw; 3756 3757 /* 3758 * Section 4.1.5 of the ICCCM states that the (x,y) coordinates in the 3759 * configure request are for the upper-left outer corner of the window. 3760 * This means that we need to adjust for the additional title height as 3761 * well as for any border width changes that we decide to allow. The 3762 * current window gravity is to be used in computing the adjustments, just 3763 * as when initially locating the window. Note that if we do decide to 3764 * allow border width changes, we will need to send the synthetic 3765 * ConfigureNotify event. 3766 */ 3767 GetGravityOffsets(Tmp_win, &gravx, &gravy); 3768 3769 if(cre->value_mask & CWBorderWidth) { 3770 int bwdelta = cre->border_width - Tmp_win->old_bw; /* posit growth */ 3771 if(bwdelta && Scr->ClientBorderWidth) { /* if change allowed */ 3772 x += gravx * bwdelta; /* change default values only */ 3773 y += gravy * bwdelta; /* ditto */ 3774 bw = cre->border_width; 3775 if(Tmp_win->title_height) { 3776 height += bwdelta; 3777 } 3778 x += (gravx < 0) ? bwdelta : -bwdelta; 3779 y += (gravy < 0) ? bwdelta : -bwdelta; 3780 } 3781 Tmp_win->old_bw = cre->border_width; /* for restoring */ 3782 } 3783 3784 if((cre->value_mask & CWX)) { /* override even if border change */ 3785 x = cre->x - bw; 3786 x -= ((gravx < 0) ? 0 : Tmp_win->frame_bw3D); 3787 } 3788 if((cre->value_mask & CWY)) { 3789 y = cre->y - ((gravy < 0) ? 0 : Tmp_win->title_height) - bw; 3790 y -= ((gravy < 0) ? 0 : Tmp_win->frame_bw3D); 3791 } 3792 3793 if(cre->value_mask & CWWidth) { 3794 width = cre->width + 2 * Tmp_win->frame_bw3D; 3795 } 3796 if(cre->value_mask & CWHeight) { 3797 height = cre->height + Tmp_win->title_height + 2 * Tmp_win->frame_bw3D; 3798 } 3799 3800 if(width != Tmp_win->frame_width || height != Tmp_win->frame_height) { 3801 unzoom(Tmp_win); 3802 } 3803 3804 /* Workaround for Java 1.4 bug that freezes the application whenever 3805 * a new window is displayed. (When UsePPosition is on and either 3806 * UseThreeDBorders or BorderWidth 0 is set.) 3807 */ 3808 if(!bw) { 3809 sendEvent = true; 3810 } 3811 3812 /* 3813 * SetupWindow (x,y) are the location of the upper-left outer corner and 3814 * are passed directly to XMoveResizeWindow (frame). The (width,height) 3815 * are the inner size of the frame. The inner width is the same as the 3816 * requested client window width; the inner height is the same as the 3817 * requested client window height plus any title bar slop. 3818 */ 3819#ifdef DEBUG_EVENTS 3820 fprintf(stderr, "SetupFrame(x=%d, y=%d, width=%d, height=%d, bw=%d)\n", 3821 x, y, width, height, bw); 3822#endif 3823 SetupFrame(Tmp_win, x, y, width, height, bw, sendEvent); 3824} 3825 3826 3827/*********************************************************************** 3828 * 3829 * Procedure: 3830 * HandleShapeNotify - shape notification event handler 3831 * 3832 *********************************************************************** 3833 */ 3834void 3835HandleShapeNotify(void) 3836{ 3837 XShapeEvent *sev = (XShapeEvent *) &Event; 3838 3839 if(Tmp_win == NULL) { 3840 return; 3841 } 3842 if(sev->kind != ShapeBounding) { 3843 return; 3844 } 3845 if(!Tmp_win->wShaped && sev->shaped) { 3846 XShapeCombineMask(dpy, Tmp_win->frame, ShapeClip, 0, 0, None, 3847 ShapeSet); 3848 } 3849 Tmp_win->wShaped = sev->shaped; 3850 SetFrameShape(Tmp_win); 3851} 3852 3853/*********************************************************************** 3854 * 3855 * Procedure: 3856 * HandleSelectionClear - selection lost event handler 3857 * 3858 *********************************************************************** 3859 */ 3860#ifdef EWMH 3861void 3862HandleSelectionClear(void) 3863{ 3864 XSelectionClearEvent *sev = (XSelectionClearEvent *) &Event; 3865 3866 if(sev->window == Scr->icccm_Window) { 3867 EwmhSelectionClear(sev); 3868 } 3869} 3870#endif 3871 3872 3873/*********************************************************************** 3874 * 3875 * Procedure: 3876 * HandleUnknown - unknown event handler 3877 * 3878 *********************************************************************** 3879 */ 3880 3881void HandleUnknown(void) 3882{ 3883#ifdef DEBUG_EVENTS 3884 fprintf(stderr, "HandleUnknown: Event.type = %d\n", Event.type); 3885#endif 3886} 3887 3888 3889static void flush_expose(Window w) 3890{ 3891 XEvent dummy; 3892 3893 while(XCheckTypedWindowEvent(dpy, w, Expose, &dummy)) { 3894 /* nada */; 3895 } 3896} 3897 3898 3899/* Util func used a few times above */ 3900static void 3901SendTakeFocusMessage(TwmWindow *tmp, Time timestamp) 3902{ 3903 send_clientmessage(tmp->w, XA_WM_TAKE_FOCUS, timestamp); 3904} 3905