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