menus.c revision c2535118
1/*****************************************************************************/ 2/* 3 4Copyright 1989, 1998 The Open Group 5 6Permission to use, copy, modify, distribute, and sell this software and its 7documentation for any purpose is hereby granted without fee, provided that 8the above copyright notice appear in all copies and that both that 9copyright notice and this permission notice appear in supporting 10documentation. 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22Except as contained in this notice, the name of The Open Group shall not be 23used in advertising or otherwise to promote the sale, use or other dealings 24in this Software without prior written authorization from The Open Group. 25 26*/ 27/** Copyright 1988 by Evans & Sutherland Computer Corporation, **/ 28/** Salt Lake City, Utah **/ 29/** Cambridge, Massachusetts **/ 30/** **/ 31/** All Rights Reserved **/ 32/** **/ 33/** Permission to use, copy, modify, and distribute this software and **/ 34/** its documentation for any purpose and without fee is hereby **/ 35/** granted, provided that the above copyright notice appear in all **/ 36/** copies and that both that copyright notice and this permis- **/ 37/** sion notice appear in supporting documentation, and that the **/ 38/** name of Evans & Sutherland not be used in advertising **/ 39/** in publicity pertaining to distribution of the software without **/ 40/** specific, written prior permission. **/ 41/** **/ 42/** EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD **/ 43/** TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- **/ 44/** ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND **/ 45/** BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM- **/ 46/** AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA **/ 47/** OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER **/ 48/** TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE **/ 49/** OR PERFORMANCE OF THIS SOFTWARE. **/ 50/*****************************************************************************/ 51 52 53/*********************************************************************** 54 * 55 * twm menu code 56 * 57 * 17-Nov-87 Thomas E. LaStrange File created 58 * 59 ***********************************************************************/ 60 61#ifdef HAVE_CONFIG_H 62# include "config.h" 63#endif 64 65#include <stdio.h> 66#include <X11/Xos.h> 67#include "twm.h" 68#include "gc.h" 69#include "menus.h" 70#include "resize.h" 71#include "events.h" 72#include "util.h" 73#include "parse.h" 74#include "gram.h" 75#include "screen.h" 76#include "menus.h" 77#include "iconmgr.h" 78#include "add_window.h" 79#include "icons.h" 80#include "session.h" 81#include <X11/Xmu/CharSet.h> 82#include "version.h" 83#include <X11/extensions/sync.h> 84#include <X11/SM/SMlib.h> 85 86int RootFunction = 0; 87MenuRoot *ActiveMenu = NULL; /**< the active menu */ 88MenuItem *ActiveItem = NULL; /**< the active menu item */ 89int MoveFunction; /**< either F_MOVE or F_FORCEMOVE */ 90int WindowMoved = FALSE; 91int menuFromFrameOrWindowOrTitlebar = FALSE; 92 93int ConstMove = FALSE; /**< constrained move variables */ 94int ConstMoveDir; 95int ConstMoveX; 96int ConstMoveY; 97int ConstMoveXL; 98int ConstMoveXR; 99int ConstMoveYT; 100int ConstMoveYB; 101 102/* Globals used to keep track of whether the mouse has moved during 103 a resize function. */ 104int ResizeOrigX; 105int ResizeOrigY; 106 107int MenuDepth = 0; /**< number of menus up */ 108static struct { 109 int x; 110 int y; 111} MenuOrigins[MAXMENUDEPTH]; 112static Cursor LastCursor; 113 114static Bool belongs_to_twm_window ( TwmWindow *t, Window w ); 115static void Identify ( TwmWindow *t ); 116static void send_clientmessage ( Window w, Atom a, Time timestamp ); 117static void BumpWindowColormap ( TwmWindow *tmp, int inc ); 118static int DeferExecution ( int context, int func, Cursor cursor ); 119static Bool NeedToDefer ( MenuRoot *root ); 120static void DestroyMenu ( MenuRoot *menu ); 121static void MakeMenu ( MenuRoot *mr ); 122static void Execute ( const char *s ); 123static void HideIconManager ( void ); 124static void WarpAlongRing ( XButtonEvent *ev, Bool forward ); 125static int WarpThere ( TwmWindow * t ); 126static void WarpToWindow ( TwmWindow *t ); 127 128#define SHADOWWIDTH 5 /* in pixels */ 129 130 131 132 133/** 134 * initialize menu roots 135 */ 136void 137InitMenus(void) 138{ 139 int i, j, k; 140 FuncKey *key, *tmp; 141 142 for (i = 0; i < MAX_BUTTONS+1; i++) 143 for (j = 0; j < NUM_CONTEXTS; j++) 144 for (k = 0; k < MOD_SIZE; k++) 145 { 146 Scr->Mouse[i][j][k].func = 0; 147 Scr->Mouse[i][j][k].item = NULL; 148 } 149 150 Scr->DefaultFunction.func = 0; 151 Scr->WindowFunction.func = 0; 152 153 if (FirstScreen) 154 { 155 for (key = Scr->FuncKeyRoot.next; key != NULL;) 156 { 157 free(key->name); 158 tmp = key; 159 key = key->next; 160 free(tmp); 161 } 162 Scr->FuncKeyRoot.next = NULL; 163 } 164 165} 166 167 168 169/** 170 * add a function key to the list 171 * 172 * \param name the name of the key 173 * \param cont the context to look for the key press in 174 * \param mods modifier keys that need to be pressed 175 * \param func the function to perform 176 * \param win_name the window name (if any) 177 * \param action the action string associated with the function (if any) 178 */ 179Bool AddFuncKey (char *name, int cont, int mods, int func, char *win_name, 180 char *action) 181{ 182 FuncKey *tmp; 183 KeySym keysym; 184 KeyCode keycode; 185 186 /* 187 * Don't let a 0 keycode go through, since that means AnyKey to the 188 * XGrabKey call in GrabKeys(). 189 */ 190 if ((keysym = XStringToKeysym(name)) == NoSymbol || 191 (keycode = XKeysymToKeycode(dpy, keysym)) == 0) 192 { 193 return False; 194 } 195 196 /* see if there already is a key defined for this context */ 197 for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) 198 { 199 if (tmp->keysym == keysym && 200 tmp->cont == cont && 201 tmp->mods == mods) 202 break; 203 } 204 205 if (tmp == NULL) 206 { 207 tmp = malloc(sizeof(FuncKey)); 208 tmp->next = Scr->FuncKeyRoot.next; 209 Scr->FuncKeyRoot.next = tmp; 210 } 211 212 tmp->name = name; 213 tmp->keysym = keysym; 214 tmp->keycode = keycode; 215 tmp->cont = cont; 216 tmp->mods = mods; 217 tmp->func = func; 218 tmp->win_name = win_name; 219 tmp->action = action; 220 221 return True; 222} 223 224 225 226int CreateTitleButton (const char *name, int func, const char *action, 227 MenuRoot *menuroot, Bool rightside, Bool append) 228{ 229 TitleButton *tb = malloc (sizeof(TitleButton)); 230 231 if (!tb) { 232 fprintf (stderr, 233 "%s: unable to allocate %ld bytes for title button\n", 234 ProgramName, (unsigned long)sizeof(TitleButton)); 235 return 0; 236 } 237 238 tb->next = NULL; 239 tb->name = name; /* note that we are not copying */ 240 tb->bitmap = None; /* WARNING, values not set yet */ 241 tb->width = 0; /* see InitTitlebarButtons */ 242 tb->height = 0; /* ditto */ 243 tb->func = func; 244 tb->action = action; 245 tb->menuroot = menuroot; 246 tb->rightside = rightside; 247 if (rightside) { 248 Scr->TBInfo.nright++; 249 } else { 250 Scr->TBInfo.nleft++; 251 } 252 253 /* 254 * Cases for list: 255 * 256 * 1. empty list, prepend left put at head of list 257 * 2. append left, prepend right put in between left and right 258 * 3. append right put at tail of list 259 * 260 * Do not refer to widths and heights yet since buttons not created 261 * (since fonts not loaded and heights not known). 262 */ 263 if ((!Scr->TBInfo.head) || ((!append) && (!rightside))) { /* 1 */ 264 tb->next = Scr->TBInfo.head; 265 Scr->TBInfo.head = tb; 266 } else if (append && rightside) { /* 3 */ 267 register TitleButton *t; 268 for /* SUPPRESS 530 */ 269 (t = Scr->TBInfo.head; t->next; t = t->next); 270 t->next = tb; 271 tb->next = NULL; 272 } else { /* 2 */ 273 register TitleButton *t, *prev = NULL; 274 for (t = Scr->TBInfo.head; t && !t->rightside; t = t->next) { 275 prev = t; 276 } 277 if (prev) { 278 tb->next = prev->next; 279 prev->next = tb; 280 } else { 281 tb->next = Scr->TBInfo.head; 282 Scr->TBInfo.head = tb; 283 } 284 } 285 286 return 1; 287} 288 289 290 291/** 292 * Do all the necessary stuff to load in a titlebar button. If we can't find 293 * the button, then put in a question; if we can't find the question mark, 294 * something is wrong and we are probably going to be in trouble later on. 295 */ 296void InitTitlebarButtons (void) 297{ 298 TitleButton *tb; 299 int h; 300 301 /* 302 * initialize dimensions 303 */ 304 Scr->TBInfo.width = (Scr->TitleHeight - 305 2 * (Scr->FramePadding + Scr->ButtonIndent)); 306 Scr->TBInfo.pad = ((Scr->TitlePadding > 1) 307 ? ((Scr->TitlePadding + 1) / 2) : 1); 308 h = Scr->TBInfo.width - 2 * Scr->TBInfo.border; 309 310 /* 311 * add in some useful buttons and bindings so that novices can still 312 * use the system. 313 */ 314 if (!Scr->NoDefaults) { 315 /* insert extra buttons */ 316 if (!CreateTitleButton (TBPM_ICONIFY, F_ICONIFY, "", (MenuRoot *) NULL, 317 False, False)) { 318 fprintf (stderr, "%s: unable to add iconify button\n", 319 ProgramName); 320 } 321 if (!CreateTitleButton (TBPM_RESIZE, F_RESIZE, "", (MenuRoot *) NULL, 322 True, True)) { 323 fprintf (stderr, "%s: unable to add resize button\n", 324 ProgramName); 325 } 326 AddDefaultBindings (); 327 } 328 ComputeCommonTitleOffsets (); 329 330 /* 331 * load in images and do appropriate centering 332 */ 333 334 for (tb = Scr->TBInfo.head; tb; tb = tb->next) { 335 tb->bitmap = FindBitmap (tb->name, &tb->width, &tb->height); 336 if (!tb->bitmap) { 337 tb->bitmap = FindBitmap (TBPM_QUESTION, &tb->width, &tb->height); 338 if (!tb->bitmap) { /* cannot happen (see util.c) */ 339 fprintf (stderr, 340 "%s: unable to add titlebar button \"%s\"\n", 341 ProgramName, tb->name); 342 } 343 } 344 345 tb->dstx = (h - tb->width + 1) / 2; 346 if (tb->dstx < 0) { /* clip to minimize copying */ 347 tb->srcx = -(tb->dstx); 348 tb->width = h; 349 tb->dstx = 0; 350 } else { 351 tb->srcx = 0; 352 } 353 tb->dsty = (h - tb->height + 1) / 2; 354 if (tb->dsty < 0) { 355 tb->srcy = -(tb->dsty); 356 tb->height = h; 357 tb->dsty = 0; 358 } else { 359 tb->srcy = 0; 360 } 361 } 362} 363 364 365 366void 367PaintEntry(MenuRoot *mr, MenuItem *mi, int exposure) 368{ 369 int y_offset; 370 int text_y; 371 GC gc; 372 373#ifdef DEBUG_MENUS 374 fprintf(stderr, "Paint entry\n"); 375#endif 376 y_offset = mi->item_num * Scr->EntryHeight; 377 text_y = y_offset + Scr->MenuFont.y; 378 379 if (mi->func != F_TITLE) 380 { 381 int x, y; 382 383 if (mi->state) 384 { 385 XSetForeground(dpy, Scr->NormalGC, mi->hi_back); 386 387 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 388 mr->width, Scr->EntryHeight); 389 390 MyFont_ChangeGC(mi->hi_fore, mi->hi_back, &Scr->MenuFont); 391 392 MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, Scr->NormalGC, mi->x, 393 text_y, mi->item, mi->strlen); 394 395 gc = Scr->NormalGC; 396 } 397 else 398 { 399 if (mi->user_colors || !exposure) 400 { 401 XSetForeground(dpy, Scr->NormalGC, mi->back); 402 403 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 404 mr->width, Scr->EntryHeight); 405 406 MyFont_ChangeGC(mi->fore, mi->back, &Scr->MenuFont); 407 gc = Scr->NormalGC; 408 } 409 else 410 gc = Scr->MenuGC; 411 412 MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, gc, 413 mi->x, text_y, mi->item, mi->strlen); 414 415 } 416 417 if (mi->func == F_MENU) 418 { 419 /* create the pull right pixmap if needed */ 420 if (Scr->pullPm == None) 421 { 422 Scr->pullPm = CreateMenuIcon (Scr->MenuFont.height, 423 &Scr->pullW, &Scr->pullH); 424 } 425 x = mr->width - Scr->pullW - 5; 426 y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2); 427 XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0, 428 Scr->pullW, Scr->pullH, x, y, 1); 429 } 430 } 431 else 432 { 433 int y; 434 435 XSetForeground(dpy, Scr->NormalGC, mi->back); 436 437 /* fill the rectangle with the title background color */ 438 XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset, 439 mr->width, Scr->EntryHeight); 440 441 { 442 XSetForeground(dpy, Scr->NormalGC, mi->fore); 443 /* now draw the dividing lines */ 444 if (y_offset) 445 XDrawLine (dpy, mr->w, Scr->NormalGC, 0, y_offset, 446 mr->width, y_offset); 447 y = ((mi->item_num+1) * Scr->EntryHeight)-1; 448 XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y); 449 } 450 451 MyFont_ChangeGC(mi->fore, mi->back, &Scr->MenuFont); 452 /* finally render the title */ 453 MyFont_DrawString(dpy, mr->w, &Scr->MenuFont, Scr->NormalGC, mi->x, 454 text_y, mi->item, mi->strlen); 455 } 456} 457 458 459void 460PaintMenu(MenuRoot *mr, XEvent *e) 461{ 462 MenuItem *mi; 463 464 for (mi = mr->first; mi != NULL; mi = mi->next) 465 { 466 int y_offset = mi->item_num * Scr->EntryHeight; 467 468 /* be smart about handling the expose, redraw only the entries 469 * that we need to 470 */ 471 if (e->xexpose.y < (y_offset + Scr->EntryHeight) && 472 (e->xexpose.y + e->xexpose.height) > y_offset) 473 { 474 PaintEntry(mr, mi, True); 475 } 476 } 477 XSync(dpy, 0); 478} 479 480 481 482static Bool fromMenu; 483 484void 485UpdateMenu(void) 486{ 487 MenuItem *mi; 488 int i, x, y, x_root, y_root, entry; 489 int done; 490 MenuItem *badItem = NULL; 491 XPointer context_data; 492 493 fromMenu = TRUE; 494 495 while (TRUE) 496 { 497 /* block until there is an event */ 498 if (!menuFromFrameOrWindowOrTitlebar) { 499 XMaskEvent(dpy, 500 ButtonPressMask | ButtonReleaseMask | 501 EnterWindowMask | ExposureMask | 502 VisibilityChangeMask | LeaveWindowMask | 503 ButtonMotionMask, &Event); 504 } 505 if (Event.type == MotionNotify) { 506 /* discard any extra motion events before a release */ 507 while(XCheckMaskEvent(dpy, 508 ButtonMotionMask | ButtonReleaseMask, &Event)) 509 if (Event.type == ButtonRelease) 510 break; 511 } 512 513 if (!DispatchEvent ()) 514 continue; 515 516 if (Event.type == ButtonRelease || Cancel) { 517 menuFromFrameOrWindowOrTitlebar = FALSE; 518 fromMenu = FALSE; 519 return; 520 } 521 522 if (Event.type != MotionNotify) 523 continue; 524 525 if (!ActiveMenu) 526 continue; 527 528 done = FALSE; 529 XQueryPointer( dpy, ActiveMenu->w, &JunkRoot, &JunkChild, 530 &x_root, &y_root, &x, &y, &JunkMask); 531 532 /* if we haven't recieved the enter notify yet, wait */ 533 if (!ActiveMenu->entered) 534 continue; 535 536 if (XFindContext(dpy, ActiveMenu->w, ScreenContext, &context_data) == 0) 537 Scr = (struct ScreenInfo *) context_data; 538 539 if (x < 0 || y < 0 || 540 x >= ActiveMenu->width || y >= ActiveMenu->height) 541 { 542 if (ActiveItem && ActiveItem->func != F_TITLE) 543 { 544 ActiveItem->state = 0; 545 PaintEntry(ActiveMenu, ActiveItem, False); 546 } 547 ActiveItem = NULL; 548 continue; 549 } 550 551 /* look for the entry that the mouse is in */ 552 entry = y / Scr->EntryHeight; 553 for (i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi=mi->next) 554 { 555 if (i == entry) 556 break; 557 } 558 559 /* if there is an active item, we might have to turn it off */ 560 if (ActiveItem) 561 { 562 /* is the active item the one we are on ? */ 563 if (ActiveItem->item_num == entry && ActiveItem->state) 564 done = TRUE; 565 566 /* if we weren't on the active entry, let's turn the old 567 * active one off 568 */ 569 if (!done && ActiveItem->func != F_TITLE) 570 { 571 ActiveItem->state = 0; 572 PaintEntry(ActiveMenu, ActiveItem, False); 573 } 574 } 575 576 /* if we weren't on the active item, change the active item and turn 577 * it on 578 */ 579 if (!done) 580 { 581 ActiveItem = mi; 582 if (ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state) 583 { 584 ActiveItem->state = 1; 585 PaintEntry(ActiveMenu, ActiveItem, False); 586 } 587 } 588 589 /* now check to see if we were over the arrow of a pull right entry */ 590 if (ActiveItem && ActiveItem->func == F_MENU && 591 ((ActiveMenu->width - x) < (ActiveMenu->width >> 1))) 592 { 593 MenuRoot *save = ActiveMenu; 594 int savex = MenuOrigins[MenuDepth - 1].x; 595 int savey = MenuOrigins[MenuDepth - 1].y; 596 597 if (MenuDepth < MAXMENUDEPTH) { 598 PopUpMenu (ActiveItem->sub, 599 (savex + (ActiveMenu->width >> 1)), 600 (savey + ActiveItem->item_num * Scr->EntryHeight) 601 /*(savey + ActiveItem->item_num * Scr->EntryHeight + 602 (Scr->EntryHeight >> 1))*/, False); 603 } else if (!badItem) { 604 Bell(XkbBI_MinorError,0,None); 605 badItem = ActiveItem; 606 } 607 608 /* if the menu did get popped up, unhighlight the active item */ 609 if (save != ActiveMenu && ActiveItem->state) 610 { 611 ActiveItem->state = 0; 612 PaintEntry(save, ActiveItem, False); 613 ActiveItem = NULL; 614 } 615 } 616 if (badItem != ActiveItem) badItem = NULL; 617 XFlush(dpy); 618 } 619 620} 621 622 623 624/** 625 * create a new menu root 626 * 627 * \param name the name of the menu root 628 */ 629MenuRoot * 630NewMenuRoot(const char *name) 631{ 632 MenuRoot *tmp; 633 634#define UNUSED_PIXEL ((unsigned long) (~0)) /* more than 24 bits */ 635 636 tmp = malloc(sizeof(MenuRoot)); 637 tmp->hi_fore = UNUSED_PIXEL; 638 tmp->hi_back = UNUSED_PIXEL; 639 tmp->name = name; 640 tmp->prev = NULL; 641 tmp->first = NULL; 642 tmp->last = NULL; 643 tmp->items = 0; 644 tmp->width = 0; 645 tmp->mapped = NEVER_MAPPED; 646 tmp->pull = FALSE; 647 tmp->w = None; 648 tmp->shadow = None; 649 tmp->real_menu = FALSE; 650 651 if (Scr->MenuList == NULL) 652 { 653 Scr->MenuList = tmp; 654 Scr->MenuList->next = NULL; 655 } 656 657 if (Scr->LastMenu == NULL) 658 { 659 Scr->LastMenu = tmp; 660 Scr->LastMenu->next = NULL; 661 } 662 else 663 { 664 Scr->LastMenu->next = tmp; 665 Scr->LastMenu = tmp; 666 Scr->LastMenu->next = NULL; 667 } 668 669 if (strcmp(name, TWM_WINDOWS) == 0) 670 Scr->Windows = tmp; 671 672 return (tmp); 673} 674 675 676 677/** 678 * add an item to a root menu 679 * 680 * \param menu pointer to the root menu to add the item 681 * \param item the text to appear in the menu 682 * \param action the string to possibly execute 683 * \param sub the menu root if it is a pull-right entry 684 * \param func the numeric function 685 * \param fore foreground color string 686 * \param back background color string 687 */ 688MenuItem * 689AddToMenu(MenuRoot *menu, const char *item, const char *action, 690 MenuRoot *sub, int func, const char *fore, const char *back) 691{ 692 MenuItem *tmp; 693 int width; 694 695#ifdef DEBUG_MENUS 696 fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n", 697 item, action, sub, func); 698#endif 699 700 tmp = malloc(sizeof(MenuItem)); 701 tmp->root = menu; 702 703 if (menu->first == NULL) 704 { 705 menu->first = tmp; 706 tmp->prev = NULL; 707 } 708 else 709 { 710 menu->last->next = tmp; 711 tmp->prev = menu->last; 712 } 713 menu->last = tmp; 714 715 tmp->item = item; 716 tmp->strlen = strlen(item); 717 tmp->action = action; 718 tmp->next = NULL; 719 tmp->sub = NULL; 720 tmp->state = 0; 721 tmp->func = func; 722 723 if (!Scr->HaveFonts) CreateFonts(); 724 width = MyFont_TextWidth(&Scr->MenuFont, item, tmp->strlen); 725 if (width <= 0) 726 width = 1; 727 if (width > menu->width) 728 menu->width = width; 729 730 tmp->user_colors = FALSE; 731 if (Scr->Monochrome == COLOR && fore != NULL) 732 { 733 int save; 734 735 save = Scr->FirstTime; 736 Scr->FirstTime = TRUE; 737 GetColor(COLOR, &tmp->fore, fore); 738 GetColor(COLOR, &tmp->back, back); 739 Scr->FirstTime = save; 740 tmp->user_colors = TRUE; 741 } 742 if (sub != NULL) 743 { 744 tmp->sub = sub; 745 menu->pull = TRUE; 746 } 747 tmp->item_num = menu->items++; 748 749 return (tmp); 750} 751 752 753void 754MakeMenus(void) 755{ 756 MenuRoot *mr; 757 758 for (mr = Scr->MenuList; mr != NULL; mr = mr->next) 759 { 760 if (mr->real_menu == FALSE) 761 continue; 762 763 MakeMenu(mr); 764 } 765} 766 767 768static void 769MakeMenu(MenuRoot *mr) 770{ 771 MenuItem *start, *end, *cur, *tmp; 772 XColor f1, f2, f3; 773 XColor b1, b2, b3; 774 XColor save_fore, save_back; 775 int num, i; 776 int fred, fgreen, fblue; 777 int bred, bgreen, bblue; 778 int width; 779 unsigned long valuemask; 780 XSetWindowAttributes attributes; 781 Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c; 782 783 Scr->EntryHeight = Scr->MenuFont.height + 4; 784 785 /* lets first size the window accordingly */ 786 if (mr->mapped == NEVER_MAPPED) 787 { 788 if (mr->pull == TRUE) 789 { 790 mr->width += 16 + 10; 791 } 792 793 width = mr->width + 10; 794 795 for (cur = mr->first; cur != NULL; cur = cur->next) 796 { 797 if (cur->func != F_TITLE) 798 cur->x = 5; 799 else 800 { 801 cur->x = width - MyFont_TextWidth(&Scr->MenuFont, cur->item, 802 cur->strlen); 803 cur->x /= 2; 804 } 805 } 806 mr->height = mr->items * Scr->EntryHeight; 807 mr->width += 10; 808 809 if (Scr->Shadow) 810 { 811 /* 812 * Make sure that you don't draw into the shadow window or else 813 * the background bits there will get saved 814 */ 815 valuemask = (CWBackPixel | CWBorderPixel); 816 attributes.background_pixel = Scr->MenuShadowColor; 817 attributes.border_pixel = Scr->MenuShadowColor; 818 if (Scr->SaveUnder) { 819 valuemask |= CWSaveUnder; 820 attributes.save_under = True; 821 } 822 mr->shadow = XCreateWindow (dpy, Scr->Root, 0, 0, 823 (unsigned int) mr->width, 824 (unsigned int) mr->height, 825 (unsigned int)0, 826 CopyFromParent, 827 (unsigned int) CopyFromParent, 828 (Visual *) CopyFromParent, 829 valuemask, &attributes); 830 } 831 832 valuemask = (CWBackPixel | CWBorderPixel | CWEventMask); 833 attributes.background_pixel = Scr->MenuC.back; 834 attributes.border_pixel = Scr->MenuBorderColor; 835 attributes.event_mask = (ExposureMask | EnterWindowMask); 836 if (Scr->SaveUnder) { 837 valuemask |= CWSaveUnder; 838 attributes.save_under = True; 839 } 840 if (Scr->BackingStore) { 841 valuemask |= CWBackingStore; 842 attributes.backing_store = Always; 843 } 844 mr->w = XCreateWindow (dpy, Scr->Root, 0, 0, (unsigned int) mr->width, 845 (unsigned int) mr->height, 846 (unsigned int) Scr->MenuBorderWidth, 847 CopyFromParent, (unsigned int) CopyFromParent, 848 (Visual *) CopyFromParent, 849 valuemask, &attributes); 850 851 852 XSaveContext(dpy, mr->w, MenuContext, (caddr_t)mr); 853 XSaveContext(dpy, mr->w, ScreenContext, (caddr_t)Scr); 854 855 mr->mapped = UNMAPPED; 856 } 857 858 /* get the default colors into the menus */ 859 for (tmp = mr->first; tmp != NULL; tmp = tmp->next) 860 { 861 if (!tmp->user_colors) { 862 if (tmp->func != F_TITLE) { 863 tmp->fore = Scr->MenuC.fore; 864 tmp->back = Scr->MenuC.back; 865 } else { 866 tmp->fore = Scr->MenuTitleC.fore; 867 tmp->back = Scr->MenuTitleC.back; 868 } 869 } 870 871 if (mr->hi_fore != UNUSED_PIXEL) 872 { 873 tmp->hi_fore = mr->hi_fore; 874 tmp->hi_back = mr->hi_back; 875 } 876 else 877 { 878 tmp->hi_fore = tmp->back; 879 tmp->hi_back = tmp->fore; 880 } 881 } 882 883 if (Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors) 884 return; 885 886 start = mr->first; 887 while (TRUE) 888 { 889 for (; start != NULL; start = start->next) 890 { 891 if (start->user_colors) 892 break; 893 } 894 if (start == NULL) 895 break; 896 897 for (end = start->next; end != NULL; end = end->next) 898 { 899 if (end->user_colors) 900 break; 901 } 902 if (end == NULL) 903 break; 904 905 /* we have a start and end to interpolate between */ 906 num = end->item_num - start->item_num; 907 908 f1.pixel = start->fore; 909 XQueryColor(dpy, cmap, &f1); 910 f2.pixel = end->fore; 911 XQueryColor(dpy, cmap, &f2); 912 913 b1.pixel = start->back; 914 XQueryColor(dpy, cmap, &b1); 915 b2.pixel = end->back; 916 XQueryColor(dpy, cmap, &b2); 917 918 fred = ((int)f2.red - (int)f1.red) / num; 919 fgreen = ((int)f2.green - (int)f1.green) / num; 920 fblue = ((int)f2.blue - (int)f1.blue) / num; 921 922 bred = ((int)b2.red - (int)b1.red) / num; 923 bgreen = ((int)b2.green - (int)b1.green) / num; 924 bblue = ((int)b2.blue - (int)b1.blue) / num; 925 926 f3 = f1; 927 f3.flags = DoRed | DoGreen | DoBlue; 928 929 b3 = b1; 930 b3.flags = DoRed | DoGreen | DoBlue; 931 932 num -= 1; 933 for (i = 0, cur = start->next; i < num && cur; i++, cur = cur->next) 934 { 935 f3.red += fred; 936 f3.green += fgreen; 937 f3.blue += fblue; 938 save_fore = f3; 939 940 b3.red += bred; 941 b3.green += bgreen; 942 b3.blue += bblue; 943 save_back = b3; 944 945 XAllocColor(dpy, cmap, &f3); 946 XAllocColor(dpy, cmap, &b3); 947 cur->hi_back = cur->fore = f3.pixel; 948 cur->hi_fore = cur->back = b3.pixel; 949 cur->user_colors = True; 950 951 f3 = save_fore; 952 b3 = save_back; 953 } 954 start = end; 955 } 956} 957 958 959 960/** 961 * pop up a pull down menu. 962 * 963 * \param menu the root pointer of the menu to pop up 964 * \param x,y location of upper left of menu 965 * \param center whether or not to center horizontally over position 966 */ 967Bool 968PopUpMenu (MenuRoot *menu, int x, int y, Bool center) 969{ 970 int WindowNameCount; 971 TwmWindow **WindowNames; 972 TwmWindow *tmp_win2,*tmp_win3; 973 int i; 974 int (*compar)(const char *, const char *) = 975 (Scr->CaseSensitive ? strcmp : XmuCompareISOLatin1); 976 977 if (!menu) return False; 978 979 InstallRootColormap(); 980 981 if (menu == Scr->Windows) 982 { 983 TwmWindow *tmp_win; 984 985 /* this is the twm windows menu, let's go ahead and build it */ 986 987 DestroyMenu (menu); 988 989 menu->first = NULL; 990 menu->last = NULL; 991 menu->items = 0; 992 menu->width = 0; 993 menu->mapped = NEVER_MAPPED; 994 AddToMenu(menu, "TWM Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR); 995 996 for(tmp_win = Scr->TwmRoot.next , WindowNameCount=0; 997 tmp_win != NULL; 998 tmp_win = tmp_win->next) 999 WindowNameCount++; 1000 if (WindowNameCount != 0) 1001 { 1002 WindowNames = malloc(sizeof(TwmWindow *) * WindowNameCount); 1003 WindowNames[0] = Scr->TwmRoot.next; 1004 for(tmp_win = Scr->TwmRoot.next->next , WindowNameCount=1; 1005 tmp_win != NULL; 1006 tmp_win = tmp_win->next,WindowNameCount++) 1007 { 1008 tmp_win2 = tmp_win; 1009 for (i=0;i<WindowNameCount;i++) 1010 { 1011 if ((*compar)(tmp_win2->name,WindowNames[i]->name) < 0) 1012 { 1013 tmp_win3 = tmp_win2; 1014 tmp_win2 = WindowNames[i]; 1015 WindowNames[i] = tmp_win3; 1016 } 1017 } 1018 WindowNames[WindowNameCount] = tmp_win2; 1019 } 1020 for (i=0; i<WindowNameCount; i++) 1021 { 1022 AddToMenu(menu, WindowNames[i]->name, (char *)WindowNames[i], 1023 NULL, F_POPUP,NULL,NULL); 1024 } 1025 free(WindowNames); 1026 } 1027 1028 MakeMenu(menu); 1029 } 1030 1031 if (menu->w == None || menu->items == 0) return False; 1032 1033 /* Prevent recursively bringing up menus. */ 1034 if (menu->mapped == MAPPED) return False; 1035 1036 /* 1037 * Dynamically set the parent; this allows pull-ups to also be main 1038 * menus, or to be brought up from more than one place. 1039 */ 1040 menu->prev = ActiveMenu; 1041 1042 XGrabPointer(dpy, Scr->Root, True, 1043 ButtonPressMask | ButtonReleaseMask | 1044 ButtonMotionMask | PointerMotionHintMask, 1045 GrabModeAsync, GrabModeAsync, 1046 Scr->Root, Scr->MenuCursor, CurrentTime); 1047 1048 ActiveMenu = menu; 1049 menu->mapped = MAPPED; 1050 menu->entered = FALSE; 1051 1052 if (center) { 1053 x -= (menu->width / 2); 1054 y -= (Scr->EntryHeight / 2); /* sticky menus would be nice here */ 1055 } 1056 1057 /* 1058 * clip to screen 1059 */ 1060 if (x + menu->width > Scr->MyDisplayWidth) { 1061 x = Scr->MyDisplayWidth - menu->width; 1062 } 1063 if (x < 0) x = 0; 1064 if (y + menu->height > Scr->MyDisplayHeight) { 1065 y = Scr->MyDisplayHeight - menu->height; 1066 } 1067 if (y < 0) y = 0; 1068 1069 MenuOrigins[MenuDepth].x = x; 1070 MenuOrigins[MenuDepth].y = y; 1071 MenuDepth++; 1072 1073 XMoveWindow(dpy, menu->w, x, y); 1074 if (Scr->Shadow) { 1075 XMoveWindow (dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH); 1076 } 1077 if (Scr->Shadow) { 1078 XRaiseWindow (dpy, menu->shadow); 1079 } 1080 XMapRaised(dpy, menu->w); 1081 if (Scr->Shadow) { 1082 XMapWindow (dpy, menu->shadow); 1083 } 1084 XSync(dpy, 0); 1085 return True; 1086} 1087 1088 1089 1090/** 1091 * unhighlight the current menu selection and take down the menus 1092 */ 1093void 1094PopDownMenu(void) 1095{ 1096 MenuRoot *tmp; 1097 1098 if (ActiveMenu == NULL) 1099 return; 1100 1101 if (ActiveItem) 1102 { 1103 ActiveItem->state = 0; 1104 PaintEntry(ActiveMenu, ActiveItem, False); 1105 } 1106 1107 for (tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev) 1108 { 1109 if (Scr->Shadow) { 1110 XUnmapWindow (dpy, tmp->shadow); 1111 } 1112 XUnmapWindow(dpy, tmp->w); 1113 tmp->mapped = UNMAPPED; 1114 UninstallRootColormap(); 1115 } 1116 1117 XFlush(dpy); 1118 ActiveMenu = NULL; 1119 ActiveItem = NULL; 1120 MenuDepth = 0; 1121 if (Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE) 1122 menuFromFrameOrWindowOrTitlebar = TRUE; 1123} 1124 1125 1126 1127/** 1128 * look for a menu root 1129 * 1130 * \return a pointer to the menu root structure 1131 * 1132 * \param name the name of the menu root 1133 */ 1134MenuRoot * 1135FindMenuRoot(const char *name) 1136{ 1137 MenuRoot *tmp; 1138 1139 for (tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next) 1140 { 1141 if (strcmp(name, tmp->name) == 0) 1142 return (tmp); 1143 } 1144 return NULL; 1145} 1146 1147 1148 1149static Bool 1150belongs_to_twm_window (TwmWindow *t, Window w) 1151{ 1152 if (!t) return False; 1153 1154 if (w == t->frame || w == t->title_w || w == t->hilite_w || 1155 w == t->icon_w || w == t->icon_bm_w) return True; 1156 1157 if (t && t->titlebuttons) { 1158 register TBWindow *tbw; 1159 register int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright; 1160 for (tbw = t->titlebuttons; nb > 0; tbw++, nb--) { 1161 if (tbw->window == w) return True; 1162 } 1163 } 1164 return False; 1165} 1166 1167 1168 1169static void 1170resizeFromCenter(Window w, TwmWindow *tmp_win) 1171{ 1172 int lastx, lasty, bw2; 1173 XEvent event; 1174#if 0 1175 int namelen; 1176 int width, height; 1177 1178 namelen = strlen (tmp_win->name); 1179#endif 1180 bw2 = tmp_win->frame_bw * 2; 1181 AddingW = tmp_win->attr.width + bw2; 1182 AddingH = tmp_win->attr.height + tmp_win->title_height + bw2; 1183#if 0 1184 width = (SIZE_HINDENT + MyFont_TextWidth (&Scr->SizeFont, 1185 tmp_win->name, namelen)); 1186 height = Scr->SizeFont.height + SIZE_VINDENT * 2; 1187#endif 1188 XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY, 1189 (unsigned int *)&DragWidth, (unsigned int *)&DragHeight, 1190 &JunkBW, &JunkDepth); 1191 XWarpPointer(dpy, None, w, 1192 0, 0, 0, 0, DragWidth/2, DragHeight/2); 1193 XQueryPointer (dpy, Scr->Root, &JunkRoot, 1194 &JunkChild, &JunkX, &JunkY, 1195 &AddingX, &AddingY, &JunkMask); 1196#if 0 1197 Scr->SizeStringOffset = width + 1198 MyFont_TextWidth(&Scr->SizeFont, ": ", 2); 1199 XResizeWindow (dpy, Scr->SizeWindow, Scr->SizeStringOffset + 1200 Scr->SizeStringWidth, height); 1201 MyFont_DrawImageString (dpy, Scr->SizeWindow, &Scr->SizeFont, Scr->NormalGC, 1202 width, SIZE_VINDENT + Scr->SizeFont.ascent, 1203 ": ", 2); 1204#endif 1205 lastx = -10000; 1206 lasty = -10000; 1207#if 0 1208 MoveOutline(Scr->Root, 1209 origDragX - JunkBW, origDragY - JunkBW, 1210 DragWidth * JunkBW, DragHeight * JunkBW, 1211 tmp_win->frame_bw, 1212 tmp_win->title_height); 1213#endif 1214 MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight); 1215 while (TRUE) 1216 { 1217 XMaskEvent(dpy, 1218 ButtonPressMask | PointerMotionMask, &event); 1219 1220 if (event.type == MotionNotify) { 1221 /* discard any extra motion events before a release */ 1222 while(XCheckMaskEvent(dpy, 1223 ButtonMotionMask | ButtonPressMask, &event)) 1224 if (event.type == ButtonPress) 1225 break; 1226 } 1227 1228 if (event.type == ButtonPress) 1229 { 1230 MenuEndResize(tmp_win); 1231 XMoveResizeWindow(dpy, w, AddingX, AddingY, AddingW, AddingH); 1232 break; 1233 } 1234 1235/* if (!DispatchEvent ()) continue; */ 1236 1237 if (event.type != MotionNotify) { 1238 continue; 1239 } 1240 1241 /* 1242 * XXX - if we are going to do a loop, we ought to consider 1243 * using multiple GXxor lines so that we don't need to 1244 * grab the server. 1245 */ 1246 XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild, 1247 &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask); 1248 1249 if (lastx != AddingX || lasty != AddingY) 1250 { 1251 MenuDoResize(AddingX, AddingY, tmp_win); 1252 1253 lastx = AddingX; 1254 lasty = AddingY; 1255 } 1256 1257 } 1258} 1259 1260 1261 1262/** \fn ExecuteFunction 1263 * execute a twm root function. 1264 * 1265 * \param func the function to execute 1266 * \param action the menu action to execute 1267 * \param w the window to execute this function on 1268 * \param tmp_win the twm window structure 1269 * \param event the event that caused the function 1270 * \param context the context in which the button was pressed 1271 * \param pulldown flag indicating execution from pull down menu 1272 * 1273 * \return TRUE if should continue with remaining actions, 1274 * else FALSE to abort 1275 */ 1276 1277static int 1278WarpThere(TwmWindow *t) 1279{ 1280 if (Scr->WarpUnmapped || t->mapped) { 1281 if (!t->mapped) DeIconify (t); 1282 if (!Scr->NoRaiseWarp) XRaiseWindow (dpy, t->frame); 1283 WarpToWindow (t); 1284 return 1; 1285 } 1286 return 0; 1287} 1288 1289 1290int 1291ExecuteFunction(int func, const char *action, Window w, TwmWindow *tmp_win, 1292 XEvent *eventp, int context, int pulldown) 1293{ 1294 static Time last_time = 0; 1295 char tmp[200]; 1296 char *ptr; 1297 char buff[MAX_FILE_SIZE]; 1298 int count, fd; 1299 Window rootw; 1300 int origX, origY; 1301 int do_next_action = TRUE; 1302 int moving_icon = FALSE; 1303 Bool fromtitlebar = False; 1304 1305 RootFunction = 0; 1306 if (Cancel) 1307 return TRUE; /* XXX should this be FALSE? */ 1308 1309 switch (func) 1310 { 1311 case F_UPICONMGR: 1312 case F_LEFTICONMGR: 1313 case F_RIGHTICONMGR: 1314 case F_DOWNICONMGR: 1315 case F_FORWICONMGR: 1316 case F_BACKICONMGR: 1317 case F_NEXTICONMGR: 1318 case F_PREVICONMGR: 1319 case F_NOP: 1320 case F_TITLE: 1321 case F_DELTASTOP: 1322 case F_RAISELOWER: 1323 case F_WARPTOSCREEN: 1324 case F_WARPTO: 1325 case F_WARPRING: 1326 case F_WARPTOICONMGR: 1327 case F_WARPNEXT: 1328 case F_WARPPREV: 1329 case F_COLORMAP: 1330 break; 1331 default: 1332 XGrabPointer(dpy, Scr->Root, True, 1333 ButtonPressMask | ButtonReleaseMask, 1334 GrabModeAsync, GrabModeAsync, 1335 Scr->Root, Scr->WaitCursor, CurrentTime); 1336 break; 1337 } 1338 1339 switch (func) 1340 { 1341 case F_NOP: 1342 case F_TITLE: 1343 break; 1344 1345 case F_DELTASTOP: 1346 if (WindowMoved) do_next_action = FALSE; 1347 break; 1348 1349 case F_RESTART: 1350 { 1351 XSync (dpy, 0); 1352 Reborder (eventp->xbutton.time); 1353 XSync (dpy, 0); 1354 if (smcConn) 1355 SmcCloseConnection (smcConn, 0, NULL); 1356 execvp(*Argv, Argv); 1357 fprintf (stderr, "%s: unable to restart: %s\n", ProgramName, *Argv); 1358 break; 1359 } 1360 1361 case F_UPICONMGR: 1362 case F_DOWNICONMGR: 1363 case F_LEFTICONMGR: 1364 case F_RIGHTICONMGR: 1365 case F_FORWICONMGR: 1366 case F_BACKICONMGR: 1367 MoveIconManager(func); 1368 break; 1369 1370 case F_NEXTICONMGR: 1371 case F_PREVICONMGR: 1372 JumpIconManager(func); 1373 break; 1374 1375 case F_SHOWLIST: 1376 if (Scr->NoIconManagers) 1377 break; 1378 DeIconify(Scr->iconmgr.twm_win); 1379 XRaiseWindow(dpy, Scr->iconmgr.twm_win->frame); 1380 break; 1381 1382 case F_HIDELIST: 1383 if (Scr->NoIconManagers) 1384 break; 1385 HideIconManager (); 1386 break; 1387 1388 case F_SORTICONMGR: 1389 if (DeferExecution(context, func, Scr->SelectCursor)) 1390 return TRUE; 1391 1392 { 1393 int save_sort; 1394 1395 save_sort = Scr->SortIconMgr; 1396 Scr->SortIconMgr = TRUE; 1397 1398 if (context == C_ICONMGR) 1399 SortIconManager((IconMgr *) NULL); 1400 else if (tmp_win->iconmgr) 1401 SortIconManager(tmp_win->iconmgrp); 1402 else 1403 Bell(XkbBI_Info,0,tmp_win->w); 1404 1405 Scr->SortIconMgr = save_sort; 1406 } 1407 break; 1408 1409 case F_IDENTIFY: 1410 if (DeferExecution(context, func, Scr->SelectCursor)) 1411 return TRUE; 1412 1413 Identify(tmp_win); 1414 break; 1415 1416 case F_VERSION: 1417 Identify ((TwmWindow *) NULL); 1418 break; 1419 1420 case F_AUTORAISE: 1421 if (DeferExecution(context, func, Scr->SelectCursor)) 1422 return TRUE; 1423 1424 tmp_win->auto_raise = !tmp_win->auto_raise; 1425 if (tmp_win->auto_raise) ++(Scr->NumAutoRaises); 1426 else --(Scr->NumAutoRaises); 1427 break; 1428 1429 case F_BEEP: 1430 Bell(XkbBI_Info,0,None); 1431 break; 1432 1433 case F_POPUP: 1434 tmp_win = (TwmWindow *)action; 1435 if (Scr->WindowFunction.func != 0) 1436 { 1437 ExecuteFunction(Scr->WindowFunction.func, 1438 Scr->WindowFunction.item->action, 1439 w, tmp_win, eventp, C_FRAME, FALSE); 1440 } 1441 else 1442 { 1443 DeIconify(tmp_win); 1444 XRaiseWindow (dpy, tmp_win->frame); 1445 } 1446 break; 1447 1448 case F_RESIZE: 1449 EventHandler[EnterNotify] = HandleUnknown; 1450 EventHandler[LeaveNotify] = HandleUnknown; 1451 if (DeferExecution(context, func, Scr->MoveCursor)) 1452 return TRUE; 1453 1454 PopDownMenu(); 1455 1456 if (pulldown) 1457 XWarpPointer(dpy, None, Scr->Root, 1458 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root); 1459 1460 if (w != tmp_win->icon_w) { /* can't resize icons */ 1461 1462 if ((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE) 1463 && fromMenu) 1464 resizeFromCenter(w, tmp_win); 1465 else { 1466 /* 1467 * see if this is being done from the titlebar 1468 */ 1469 fromtitlebar = 1470 belongs_to_twm_window (tmp_win, eventp->xbutton.window); 1471 1472 /* Save pointer position so we can tell if it was moved or 1473 not during the resize. */ 1474 ResizeOrigX = eventp->xbutton.x_root; 1475 ResizeOrigY = eventp->xbutton.y_root; 1476 1477 StartResize (eventp, tmp_win, fromtitlebar); 1478 1479 do { 1480 XMaskEvent(dpy, 1481 ButtonPressMask | ButtonReleaseMask | 1482 EnterWindowMask | LeaveWindowMask | 1483 ButtonMotionMask, &Event); 1484 1485 if (fromtitlebar && Event.type == ButtonPress) { 1486 fromtitlebar = False; 1487 continue; 1488 } 1489 1490 if (Event.type == MotionNotify) { 1491 /* discard any extra motion events before a release */ 1492 while 1493 (XCheckMaskEvent 1494 (dpy, ButtonMotionMask | ButtonReleaseMask, &Event)) 1495 if (Event.type == ButtonRelease) 1496 break; 1497 } 1498 1499 if (!DispatchEvent ()) continue; 1500 1501 } while (!(Event.type == ButtonRelease || Cancel)); 1502 return TRUE; 1503 } 1504 } 1505 break; 1506 1507 1508 case F_ZOOM: 1509 case F_HORIZOOM: 1510 case F_FULLZOOM: 1511 case F_LEFTZOOM: 1512 case F_RIGHTZOOM: 1513 case F_TOPZOOM: 1514 case F_BOTTOMZOOM: 1515 if (DeferExecution(context, func, Scr->SelectCursor)) 1516 return TRUE; 1517 fullzoom(tmp_win, func); 1518 break; 1519 1520 case F_MOVE: 1521 case F_FORCEMOVE: 1522 if (DeferExecution(context, func, Scr->MoveCursor)) 1523 return TRUE; 1524 1525 PopDownMenu(); 1526 rootw = eventp->xbutton.root; 1527 MoveFunction = func; 1528 1529 if (pulldown) 1530 XWarpPointer(dpy, None, Scr->Root, 1531 0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root); 1532 1533 EventHandler[EnterNotify] = HandleUnknown; 1534 EventHandler[LeaveNotify] = HandleUnknown; 1535 1536 if (!Scr->NoGrabServer || !Scr->OpaqueMove) { 1537 XGrabServer(dpy); 1538 } 1539 XGrabPointer(dpy, eventp->xbutton.root, True, 1540 ButtonPressMask | ButtonReleaseMask | 1541 ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */ 1542 GrabModeAsync, GrabModeAsync, 1543 Scr->Root, Scr->MoveCursor, CurrentTime); 1544 1545 if (context == C_ICON && tmp_win->icon_w) 1546 { 1547 w = tmp_win->icon_w; 1548 DragX = eventp->xbutton.x; 1549 DragY = eventp->xbutton.y; 1550 moving_icon = TRUE; 1551 } 1552 1553 else if (w != tmp_win->icon_w) 1554 { 1555 XTranslateCoordinates(dpy, w, tmp_win->frame, 1556 eventp->xbutton.x, 1557 eventp->xbutton.y, 1558 &DragX, &DragY, &JunkChild); 1559 1560 w = tmp_win->frame; 1561 } 1562 1563 DragWindow = None; 1564 1565 XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY, 1566 (unsigned int *)&DragWidth, (unsigned int *)&DragHeight, &JunkBW, 1567 &JunkDepth); 1568 1569 origX = eventp->xbutton.x_root; 1570 origY = eventp->xbutton.y_root; 1571 CurrentDragX = origDragX; 1572 CurrentDragY = origDragY; 1573 1574 /* 1575 * only do the constrained move if timer is set; need to check it 1576 * in case of stupid or wicked fast servers 1577 */ 1578 if (ConstrainedMoveTime && 1579 (eventp->xbutton.time - last_time) < ConstrainedMoveTime) 1580 { 1581 int width, height; 1582 1583 ConstMove = TRUE; 1584 ConstMoveDir = MOVE_NONE; 1585 ConstMoveX = eventp->xbutton.x_root - DragX - JunkBW; 1586 ConstMoveY = eventp->xbutton.y_root - DragY - JunkBW; 1587 width = DragWidth + 2 * JunkBW; 1588 height = DragHeight + 2 * JunkBW; 1589 ConstMoveXL = ConstMoveX + width/3; 1590 ConstMoveXR = ConstMoveX + 2*(width/3); 1591 ConstMoveYT = ConstMoveY + height/3; 1592 ConstMoveYB = ConstMoveY + 2*(height/3); 1593 1594 XWarpPointer(dpy, None, w, 1595 0, 0, 0, 0, DragWidth/2, DragHeight/2); 1596 1597 XQueryPointer(dpy, w, &JunkRoot, &JunkChild, 1598 &JunkX, &JunkY, &DragX, &DragY, &JunkMask); 1599 } 1600 last_time = eventp->xbutton.time; 1601 1602 if (!Scr->OpaqueMove) 1603 { 1604 InstallRootColormap(); 1605 if (!Scr->MoveDelta) 1606 { 1607 /* 1608 * Draw initial outline. This was previously done the 1609 * first time though the outer loop by dropping out of 1610 * the XCheckMaskEvent inner loop down to one of the 1611 * MoveOutline's below. 1612 */ 1613 MoveOutline(rootw, 1614 origDragX - JunkBW, origDragY - JunkBW, 1615 DragWidth + 2 * JunkBW, DragHeight + 2 * JunkBW, 1616 tmp_win->frame_bw, 1617 moving_icon ? 0 : tmp_win->title_height); 1618 /* 1619 * This next line causes HandleReleaseNotify to call 1620 * XRaiseWindow(). This is solely to preserve the 1621 * previous behaviour that raises a window being moved 1622 * on button release even if you never actually moved 1623 * any distance (unless you move less than MoveDelta or 1624 * NoRaiseMove is set or OpaqueMove is set). 1625 */ 1626 DragWindow = w; 1627 } 1628 } 1629 1630 /* 1631 * see if this is being done from the titlebar 1632 */ 1633 fromtitlebar = belongs_to_twm_window (tmp_win, eventp->xbutton.window); 1634 1635 if (menuFromFrameOrWindowOrTitlebar) { 1636 /* warp the pointer to the middle of the window */ 1637 XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0, 1638 origDragX + DragWidth / 2, 1639 origDragY + DragHeight / 2); 1640 XFlush(dpy); 1641 } 1642 1643 while (TRUE) 1644 { 1645 long releaseEvent = menuFromFrameOrWindowOrTitlebar ? 1646 ButtonPress : ButtonRelease; 1647 long movementMask = menuFromFrameOrWindowOrTitlebar ? 1648 PointerMotionMask : ButtonMotionMask; 1649 1650 /* block until there is an interesting event */ 1651 XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | 1652 EnterWindowMask | LeaveWindowMask | 1653 ExposureMask | movementMask | 1654 VisibilityChangeMask, &Event); 1655 1656 /* throw away enter and leave events until release */ 1657 if (Event.xany.type == EnterNotify || 1658 Event.xany.type == LeaveNotify) continue; 1659 1660 if (Event.type == MotionNotify) { 1661 /* discard any extra motion events before a logical release */ 1662 while(XCheckMaskEvent(dpy, 1663 movementMask | releaseEvent, &Event)) 1664 if (Event.type == releaseEvent) 1665 break; 1666 } 1667 1668 /* test to see if we have a second button press to abort move */ 1669 if (!menuFromFrameOrWindowOrTitlebar && !MovedFromKeyPress) { 1670 if (Event.type == ButtonPress && DragWindow != None) { 1671 if (Scr->OpaqueMove) 1672 XMoveWindow (dpy, DragWindow, origDragX, origDragY); 1673 else 1674 MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0); 1675 DragWindow = None; 1676 } 1677 } 1678 if (fromtitlebar && Event.type == ButtonPress) { 1679 fromtitlebar = False; 1680 CurrentDragX = origX = Event.xbutton.x_root; 1681 CurrentDragY = origY = Event.xbutton.y_root; 1682 XTranslateCoordinates (dpy, rootw, tmp_win->frame, 1683 origX, origY, 1684 &DragX, &DragY, &JunkChild); 1685 continue; 1686 } 1687 1688 if (!DispatchEvent2 ()) continue; 1689 1690 if (Cancel) 1691 { 1692 WindowMoved = FALSE; 1693 if (!Scr->OpaqueMove) 1694 UninstallRootColormap(); 1695 return TRUE; /* XXX should this be FALSE? */ 1696 } 1697 if (Event.type == releaseEvent) 1698 { 1699 MoveOutline(rootw, 0, 0, 0, 0, 0, 0); 1700 if (moving_icon && 1701 ((CurrentDragX != origDragX || 1702 CurrentDragY != origDragY))) 1703 tmp_win->icon_moved = TRUE; 1704 if (!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar) 1705 XMoveWindow(dpy, DragWindow, 1706 Event.xbutton.x_root - DragWidth / 2, 1707 Event.xbutton.y_root - DragHeight / 2); 1708 break; 1709 } 1710 1711 /* something left to do only if the pointer moved */ 1712 if (Event.type != MotionNotify) 1713 continue; 1714 1715 XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild, 1716 &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), 1717 &JunkX, &JunkY, &JunkMask); 1718 1719 if (DragWindow == None && 1720 abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta && 1721 abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta) 1722 continue; 1723 1724 WindowMoved = TRUE; 1725 DragWindow = w; 1726 1727 if (!Scr->NoRaiseMove && Scr->OpaqueMove) /* can't restore... */ 1728 XRaiseWindow(dpy, DragWindow); 1729 1730 if (ConstMove) 1731 { 1732 switch (ConstMoveDir) 1733 { 1734 case MOVE_NONE: 1735 if (eventp->xmotion.x_root < ConstMoveXL || 1736 eventp->xmotion.x_root > ConstMoveXR) 1737 ConstMoveDir = MOVE_HORIZ; 1738 1739 if (eventp->xmotion.y_root < ConstMoveYT || 1740 eventp->xmotion.y_root > ConstMoveYB) 1741 ConstMoveDir = MOVE_VERT; 1742 1743 XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild, 1744 &JunkX, &JunkY, &DragX, &DragY, &JunkMask); 1745 break; 1746 1747 case MOVE_VERT: 1748 ConstMoveY = eventp->xmotion.y_root - DragY - JunkBW; 1749 break; 1750 1751 case MOVE_HORIZ: 1752 ConstMoveX= eventp->xmotion.x_root - DragX - JunkBW; 1753 break; 1754 } 1755 1756 if (ConstMoveDir != MOVE_NONE) 1757 { 1758 int xl, yt, xr, yb, w, h; 1759 1760 xl = ConstMoveX; 1761 yt = ConstMoveY; 1762 w = DragWidth + 2 * JunkBW; 1763 h = DragHeight + 2 * JunkBW; 1764 1765 if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) 1766 { 1767 xr = xl + w; 1768 yb = yt + h; 1769 1770 if (xl < 0) 1771 xl = 0; 1772 if (xr > Scr->MyDisplayWidth) 1773 xl = Scr->MyDisplayWidth - w; 1774 1775 if (yt < 0) 1776 yt = 0; 1777 if (yb > Scr->MyDisplayHeight) 1778 yt = Scr->MyDisplayHeight - h; 1779 } 1780 CurrentDragX = xl; 1781 CurrentDragY = yt; 1782 if (Scr->OpaqueMove) 1783 XMoveWindow(dpy, DragWindow, xl, yt); 1784 else 1785 MoveOutline(eventp->xmotion.root, xl, yt, w, h, 1786 tmp_win->frame_bw, 1787 moving_icon ? 0 : tmp_win->title_height); 1788 } 1789 } 1790 else if (DragWindow != None) 1791 { 1792 int xl, yt, xr, yb, w, h; 1793 if (!menuFromFrameOrWindowOrTitlebar) { 1794 xl = eventp->xmotion.x_root - DragX - JunkBW; 1795 yt = eventp->xmotion.y_root - DragY - JunkBW; 1796 } 1797 else { 1798 xl = eventp->xmotion.x_root - (DragWidth / 2); 1799 yt = eventp->xmotion.y_root - (DragHeight / 2); 1800 } 1801 w = DragWidth + 2 * JunkBW; 1802 h = DragHeight + 2 * JunkBW; 1803 1804 if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) 1805 { 1806 xr = xl + w; 1807 yb = yt + h; 1808 1809 if (xl < 0) 1810 xl = 0; 1811 if (xr > Scr->MyDisplayWidth) 1812 xl = Scr->MyDisplayWidth - w; 1813 1814 if (yt < 0) 1815 yt = 0; 1816 if (yb > Scr->MyDisplayHeight) 1817 yt = Scr->MyDisplayHeight - h; 1818 } 1819 1820 CurrentDragX = xl; 1821 CurrentDragY = yt; 1822 if (Scr->OpaqueMove) 1823 XMoveWindow(dpy, DragWindow, xl, yt); 1824 else 1825 MoveOutline(eventp->xmotion.root, xl, yt, w, h, 1826 tmp_win->frame_bw, 1827 moving_icon ? 0 : tmp_win->title_height); 1828 } 1829 1830 } 1831 MovedFromKeyPress = False; 1832 1833 1834 if (!Scr->OpaqueMove && DragWindow == None) 1835 UninstallRootColormap(); 1836 1837 break; 1838 1839 case F_FUNCTION: 1840 { 1841 MenuRoot *mroot; 1842 MenuItem *mitem; 1843 1844 if ((mroot = FindMenuRoot(action)) == NULL) 1845 { 1846 fprintf (stderr, "%s: couldn't find function \"%s\"\n", 1847 ProgramName, action); 1848 return TRUE; 1849 } 1850 1851 if (NeedToDefer(mroot) && DeferExecution(context, func, Scr->SelectCursor)) 1852 return TRUE; 1853 else 1854 { 1855 for (mitem = mroot->first; mitem != NULL; mitem = mitem->next) 1856 { 1857 if (!ExecuteFunction (mitem->func, mitem->action, w, 1858 tmp_win, eventp, context, pulldown)) 1859 break; 1860 } 1861 } 1862 } 1863 break; 1864 1865 case F_DEICONIFY: 1866 case F_ICONIFY: 1867 if (DeferExecution(context, func, Scr->SelectCursor)) 1868 return TRUE; 1869 1870 if (tmp_win->icon) 1871 { 1872 DeIconify(tmp_win); 1873 } 1874 else if (func == F_ICONIFY) 1875 { 1876 Iconify (tmp_win, eventp->xbutton.x_root - 5, 1877 eventp->xbutton.y_root - 5); 1878 } 1879 break; 1880 1881 case F_RAISELOWER: 1882 if (DeferExecution(context, func, Scr->SelectCursor)) 1883 return TRUE; 1884 1885 if (!WindowMoved) { 1886 XWindowChanges xwc; 1887 1888 xwc.stack_mode = Opposite; 1889 if (w != tmp_win->icon_w) 1890 w = tmp_win->frame; 1891 XConfigureWindow (dpy, w, CWStackMode, &xwc); 1892 } 1893 break; 1894 1895 case F_RAISE: 1896 if (DeferExecution(context, func, Scr->SelectCursor)) 1897 return TRUE; 1898 1899 /* check to make sure raise is not from the WindowFunction */ 1900 if (w == tmp_win->icon_w && Context != C_ROOT) 1901 XRaiseWindow(dpy, tmp_win->icon_w); 1902 else 1903 XRaiseWindow(dpy, tmp_win->frame); 1904 1905 break; 1906 1907 case F_LOWER: 1908 if (DeferExecution(context, func, Scr->SelectCursor)) 1909 return TRUE; 1910 1911 if (w == tmp_win->icon_w) 1912 XLowerWindow(dpy, tmp_win->icon_w); 1913 else 1914 XLowerWindow(dpy, tmp_win->frame); 1915 1916 break; 1917 1918 case F_FOCUS: 1919 if (DeferExecution(context, func, Scr->SelectCursor)) 1920 return TRUE; 1921 1922 if (tmp_win->icon == FALSE) 1923 { 1924 if (!Scr->FocusRoot && Scr->Focus == tmp_win) 1925 { 1926 FocusOnRoot(); 1927 } 1928 else 1929 { 1930 if (Scr->Focus != NULL) { 1931 SetBorder (Scr->Focus, False); 1932 if (Scr->Focus->hilite_w) 1933 XUnmapWindow (dpy, Scr->Focus->hilite_w); 1934 } 1935 1936 InstallWindowColormaps (0, tmp_win); 1937 if (tmp_win->hilite_w) XMapWindow (dpy, tmp_win->hilite_w); 1938 SetBorder (tmp_win, True); 1939 if (!tmp_win->wmhints || tmp_win->wmhints->input) 1940 SetFocus (tmp_win, eventp->xbutton.time); 1941 Scr->FocusRoot = FALSE; 1942 Scr->Focus = tmp_win; 1943 } 1944 } 1945 break; 1946 1947 case F_DESTROY: 1948 if (DeferExecution(context, func, Scr->DestroyCursor)) 1949 return TRUE; 1950 1951 if (tmp_win->iconmgr) 1952 Bell(XkbBI_MinorError,0,tmp_win->w); 1953 else 1954 XKillClient(dpy, tmp_win->w); 1955 break; 1956 1957 case F_DELETE: 1958 if (DeferExecution(context, func, Scr->DestroyCursor)) 1959 return TRUE; 1960 1961 if (tmp_win->iconmgr) /* don't send ourself a message */ 1962 HideIconManager (); 1963 else if (tmp_win->protocols & DoesWmDeleteWindow) 1964 SendDeleteWindowMessage (tmp_win, LastTimestamp()); 1965 else 1966 Bell(XkbBI_MinorError,0,tmp_win->w); 1967 break; 1968 1969 case F_SAVEYOURSELF: 1970 if (DeferExecution (context, func, Scr->SelectCursor)) 1971 return TRUE; 1972 1973 if (tmp_win->protocols & DoesWmSaveYourself) 1974 SendSaveYourselfMessage (tmp_win, LastTimestamp()); 1975 else 1976 Bell(XkbBI_MinorError,0,tmp_win->w); 1977 break; 1978 1979 case F_CIRCLEUP: 1980 XCirculateSubwindowsUp(dpy, Scr->Root); 1981 break; 1982 1983 case F_CIRCLEDOWN: 1984 XCirculateSubwindowsDown(dpy, Scr->Root); 1985 break; 1986 1987 case F_EXEC: 1988 PopDownMenu(); 1989 if (!Scr->NoGrabServer) { 1990 XUngrabServer (dpy); 1991 XSync (dpy, 0); 1992 } 1993 Execute(action); 1994 break; 1995 1996 case F_UNFOCUS: 1997 FocusOnRoot(); 1998 break; 1999 2000 case F_CUT: 2001 strcpy(tmp, action); 2002 strcat(tmp, "\n"); 2003 XStoreBytes(dpy, tmp, strlen(tmp)); 2004 break; 2005 2006 case F_CUTFILE: 2007 ptr = XFetchBytes(dpy, &count); 2008 if (ptr) { 2009 if (sscanf (ptr, "%s", tmp) == 1) { 2010 XFree (ptr); 2011 ptr = ExpandFilename(tmp); 2012 if (ptr) { 2013 fd = open (ptr, O_RDONLY); 2014 if (fd >= 0) { 2015 count = read (fd, buff, MAX_FILE_SIZE - 1); 2016 if (count > 0) XStoreBytes (dpy, buff, count); 2017 close(fd); 2018 } else { 2019 fprintf (stderr, 2020 "%s: unable to open cut file \"%s\"\n", 2021 ProgramName, tmp); 2022 } 2023 if (ptr != tmp) free (ptr); 2024 } 2025 } else { 2026 XFree(ptr); 2027 } 2028 } else { 2029 fprintf(stderr, "%s: cut buffer is empty\n", ProgramName); 2030 } 2031 break; 2032 2033 case F_WARPTOSCREEN: 2034 { 2035 if (strcmp (action, WARPSCREEN_NEXT) == 0) { 2036 WarpToScreen (Scr->screen + 1, 1); 2037 } else if (strcmp (action, WARPSCREEN_PREV) == 0) { 2038 WarpToScreen (Scr->screen - 1, -1); 2039 } else if (strcmp (action, WARPSCREEN_BACK) == 0) { 2040 WarpToScreen (PreviousScreen, 0); 2041 } else { 2042 WarpToScreen (atoi (action), 0); 2043 } 2044 } 2045 break; 2046 2047 case F_COLORMAP: 2048 { 2049 if (strcmp (action, COLORMAP_NEXT) == 0) { 2050 BumpWindowColormap (tmp_win, 1); 2051 } else if (strcmp (action, COLORMAP_PREV) == 0) { 2052 BumpWindowColormap (tmp_win, -1); 2053 } else { 2054 BumpWindowColormap (tmp_win, 0); 2055 } 2056 } 2057 break; 2058 2059 case F_WARPPREV: 2060 case F_WARPNEXT: 2061 { 2062 register TwmWindow *t; 2063 static TwmWindow *savedwarp = NULL; 2064 TwmWindow *of, *l, *n; 2065 int c=0; 2066 2067#define wseq(w) (func == F_WARPNEXT ? (w)->next : (w)->prev) 2068#define nwin(w) ((w) && (n=wseq(w)) != NULL && n != &Scr->TwmRoot ? n : l) 2069#define bwin(w) (!(w)||(w)->iconmgr||(w)==of||!(Scr->WarpUnmapped||(w)->mapped)) 2070 2071 of=(Scr->Focus ? Scr->Focus : &Scr->TwmRoot); 2072 2073 for(t=Scr->TwmRoot.next; t; t=t->next) if(!bwin(t)) break; 2074 if(!t) break; /* no windows we can use */ 2075 2076 if(func == F_WARPPREV) for(l=of; l->next; l=l->next) ; 2077 else l = Scr->TwmRoot.next; 2078 2079 for(t=of; bwin(t) && c < 2; t=nwin(t)) if(t == of) c++; 2080 2081 if(bwin(t) || c >= 2) Bell(XkbBI_MinorError,0,None); 2082 else { 2083 if(of && of == savedwarp) { 2084 Iconify(of, 0, 0); 2085 savedwarp = NULL; 2086 } 2087 if(!t->mapped) savedwarp = t; else savedwarp = NULL; 2088 WarpThere(t); 2089 } 2090 break; 2091 } 2092 2093 case F_WARPTO: 2094 { 2095 register TwmWindow *t; 2096 int len; 2097 2098 len = strlen(action); 2099 2100 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) { 2101 if (!strncmp(action, t->name, len)) 2102 if (WarpThere(t)) break; 2103 } 2104 if (!t) { 2105 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) { 2106 if (!strncmp(action, t->class.res_name, len)) 2107 if (WarpThere(t)) break; 2108 } 2109 if (!t) { 2110 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) { 2111 if (!strncmp(action, t->class.res_class, len)) 2112 if (WarpThere(t)) break; 2113 } 2114 } 2115 } 2116 2117 if (!t) 2118 Bell(XkbBI_MinorError,0,None); 2119 } 2120 break; 2121 2122 case F_WARPTOICONMGR: 2123 { 2124 TwmWindow *t; 2125 int len; 2126 Window raisewin = None, iconwin = None; 2127 2128 len = strlen(action); 2129 if (len == 0) { 2130 if (tmp_win && tmp_win->list) { 2131 raisewin = tmp_win->list->iconmgr->twm_win->frame; 2132 iconwin = tmp_win->list->icon; 2133 } else if (Scr->iconmgr.active) { 2134 raisewin = Scr->iconmgr.twm_win->frame; 2135 iconwin = Scr->iconmgr.active->w; 2136 } 2137 } else { 2138 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) { 2139 if (strncmp (action, t->icon_name, len) == 0) { 2140 if (t->list && t->list->iconmgr->twm_win->mapped) { 2141 raisewin = t->list->iconmgr->twm_win->frame; 2142 iconwin = t->list->icon; 2143 break; 2144 } 2145 } 2146 } 2147 } 2148 2149 if (raisewin) { 2150 XRaiseWindow (dpy, raisewin); 2151 XWarpPointer (dpy, None, iconwin, 0,0,0,0, 5, 5); 2152 } else { 2153 Bell(XkbBI_MinorError,0,None); 2154 } 2155 } 2156 break; 2157 2158 case F_WARPRING: 2159 switch (action[0]) { 2160 case 'n': 2161 WarpAlongRing (&eventp->xbutton, True); 2162 break; 2163 case 'p': 2164 WarpAlongRing (&eventp->xbutton, False); 2165 break; 2166 default: 2167 Bell(XkbBI_MinorError,0,None); 2168 break; 2169 } 2170 break; 2171 2172 case F_FILE: 2173 ptr = ExpandFilename(action); 2174 fd = open(ptr, O_RDONLY); 2175 if (fd >= 0) 2176 { 2177 count = read(fd, buff, MAX_FILE_SIZE - 1); 2178 if (count > 0) 2179 XStoreBytes(dpy, buff, count); 2180 2181 close(fd); 2182 } 2183 else 2184 { 2185 fprintf (stderr, "%s: unable to open file \"%s\"\n", 2186 ProgramName, ptr); 2187 } 2188 if (ptr != action) free(ptr); 2189 break; 2190 2191 case F_REFRESH: 2192 { 2193 XSetWindowAttributes attributes; 2194 unsigned long valuemask; 2195 2196 valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder); 2197 attributes.background_pixel = Scr->Black; 2198 attributes.backing_store = NotUseful; 2199 attributes.save_under = False; 2200 w = XCreateWindow (dpy, Scr->Root, 0, 0, 2201 (unsigned int) Scr->MyDisplayWidth, 2202 (unsigned int) Scr->MyDisplayHeight, 2203 (unsigned int) 0, 2204 CopyFromParent, (unsigned int) CopyFromParent, 2205 (Visual *) CopyFromParent, valuemask, 2206 &attributes); 2207 XMapWindow (dpy, w); 2208 XDestroyWindow (dpy, w); 2209 XFlush (dpy); 2210 } 2211 break; 2212 2213 case F_WINREFRESH: 2214 if (DeferExecution(context, func, Scr->SelectCursor)) 2215 return TRUE; 2216 2217 if (context == C_ICON && tmp_win->icon_w) 2218 w = XCreateSimpleWindow(dpy, tmp_win->icon_w, 2219 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black); 2220 else 2221 w = XCreateSimpleWindow(dpy, tmp_win->frame, 2222 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black); 2223 2224 XMapWindow(dpy, w); 2225 XDestroyWindow(dpy, w); 2226 XFlush(dpy); 2227 break; 2228 2229 case F_QUIT: 2230 Done(NULL, NULL); 2231 break; 2232 2233 case F_PRIORITY: 2234 if (HasSync) 2235 { 2236 if (DeferExecution (context, func, Scr->SelectCursor)) 2237 return TRUE; 2238 (void)XSyncSetPriority(dpy, tmp_win->w, atoi(action)); 2239 } 2240 break; 2241 case F_STARTWM: 2242 execlp("/bin/sh", "sh", "-c", action, (void *)NULL); 2243 fprintf (stderr, "%s: unable to start: %s\n", ProgramName, *Argv); 2244 break; 2245 2246 } 2247 2248 if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime); 2249 return do_next_action; 2250} 2251 2252 2253 2254/** 2255 * defer the execution of a function to the next button press if the context 2256 * is C_ROOT 2257 * 2258 * \param context the context in which the mouse button was pressed 2259 * \param func the function to defer 2260 * \param cursor cursor the cursor to display while waiting 2261 */ 2262static int 2263DeferExecution(int context, int func, Cursor cursor) 2264{ 2265 if (context == C_ROOT) 2266 { 2267 LastCursor = cursor; 2268 XGrabPointer(dpy, Scr->Root, True, 2269 ButtonPressMask | ButtonReleaseMask, 2270 GrabModeAsync, GrabModeAsync, 2271 Scr->Root, cursor, CurrentTime); 2272 2273 RootFunction = func; 2274 2275 return (TRUE); 2276 } 2277 2278 return (FALSE); 2279} 2280 2281 2282 2283/** 2284 *regrab the pointer with the LastCursor; 2285 */ 2286void 2287ReGrab(void) 2288{ 2289 XGrabPointer(dpy, Scr->Root, True, 2290 ButtonPressMask | ButtonReleaseMask, 2291 GrabModeAsync, GrabModeAsync, 2292 Scr->Root, LastCursor, CurrentTime); 2293} 2294 2295 2296 2297/** 2298 * checks each function in the list to see if it is one that needs 2299 * to be deferred. 2300 * 2301 * \param root the menu root to check 2302 */ 2303static Bool 2304NeedToDefer(MenuRoot *root) 2305{ 2306 MenuItem *mitem; 2307 2308 for (mitem = root->first; mitem != NULL; mitem = mitem->next) 2309 { 2310 switch (mitem->func) 2311 { 2312 case F_IDENTIFY: 2313 case F_RESIZE: 2314 case F_MOVE: 2315 case F_FORCEMOVE: 2316 case F_DEICONIFY: 2317 case F_ICONIFY: 2318 case F_RAISELOWER: 2319 case F_RAISE: 2320 case F_LOWER: 2321 case F_FOCUS: 2322 case F_DESTROY: 2323 case F_WINREFRESH: 2324 case F_ZOOM: 2325 case F_FULLZOOM: 2326 case F_HORIZOOM: 2327 case F_RIGHTZOOM: 2328 case F_LEFTZOOM: 2329 case F_TOPZOOM: 2330 case F_BOTTOMZOOM: 2331 case F_AUTORAISE: 2332 return TRUE; 2333 } 2334 } 2335 return FALSE; 2336} 2337 2338 2339 2340#if defined(sun) && defined(SVR4) 2341#include <sys/wait.h> 2342 2343/** 2344 * execute the string by /bin/sh 2345 * \param s the string containing the command 2346 */ 2347static int 2348System (const char *s) 2349{ 2350 int pid, status; 2351 if ((pid = fork ()) == 0) { 2352 (void) setpgrp(); 2353 execl ("/bin/sh", "sh", "-c", s, NULL); 2354 } else 2355 waitpid (pid, &status, 0); 2356 return status; 2357} 2358#define system(s) System(s) 2359 2360#endif 2361 2362static void 2363Execute(const char *s) 2364{ 2365 /* FIXME: is all this stuff needed? There could be security problems here. */ 2366 static char buf[256]; 2367 char *ds = DisplayString (dpy); 2368 char *colon, *dot1; 2369 char oldDisplay[256]; 2370 char *doisplay; 2371 int restorevar = 0; 2372 2373 oldDisplay[0] = '\0'; 2374 doisplay=getenv("DISPLAY"); 2375 if (doisplay) 2376 strcpy (oldDisplay, doisplay); 2377 2378 /* 2379 * Build a display string using the current screen number, so that 2380 * X programs which get fired up from a menu come up on the screen 2381 * that they were invoked from, unless specifically overridden on 2382 * their command line. 2383 */ 2384 colon = strrchr (ds, ':'); 2385 if (colon) { /* if host[:]:dpy */ 2386 strcpy (buf, "DISPLAY="); 2387 strcat (buf, ds); 2388 colon = buf + 8 + (colon - ds); /* use version in buf */ 2389 dot1 = strchr (colon, '.'); /* first period after colon */ 2390 if (!dot1) dot1 = colon + strlen (colon); /* if not there, append */ 2391 (void) sprintf (dot1, ".%d", Scr->screen); 2392 putenv (buf); 2393 restorevar = 1; 2394 } 2395 2396 (void) system (s); 2397 2398 if (restorevar) { /* why bother? */ 2399 (void) snprintf (buf, sizeof(buf), "DISPLAY=%s", oldDisplay); 2400 putenv (buf); 2401 } 2402} 2403 2404 2405 2406/** 2407 * put input focus on the root window. 2408 */ 2409void 2410FocusOnRoot(void) 2411{ 2412 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2413 if (Scr->Focus != NULL) 2414 { 2415 SetBorder (Scr->Focus, False); 2416 if (Scr->Focus->hilite_w) XUnmapWindow (dpy, Scr->Focus->hilite_w); 2417 } 2418 InstallWindowColormaps(0, &Scr->TwmRoot); 2419 Scr->Focus = NULL; 2420 Scr->FocusRoot = TRUE; 2421} 2422 2423void 2424DeIconify(TwmWindow *tmp_win) 2425{ 2426 TwmWindow *t; 2427 2428 /* de-iconify the main window */ 2429 if (tmp_win->icon) 2430 { 2431 if (tmp_win->icon_on) 2432 Zoom(tmp_win->icon_w, tmp_win->frame); 2433 else if (tmp_win->group != (Window) 0) 2434 { 2435 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2436 { 2437 if (tmp_win->group == t->w && t->icon_on) 2438 { 2439 Zoom(t->icon_w, tmp_win->frame); 2440 break; 2441 } 2442 } 2443 } 2444 } 2445 2446 XMapWindow(dpy, tmp_win->w); 2447 tmp_win->mapped = TRUE; 2448 if (Scr->NoRaiseDeicon) 2449 XMapWindow(dpy, tmp_win->frame); 2450 else 2451 XMapRaised(dpy, tmp_win->frame); 2452 SetMapStateProp(tmp_win, NormalState); 2453 2454 if (tmp_win->icon_w) { 2455 XUnmapWindow(dpy, tmp_win->icon_w); 2456 IconDown (tmp_win); 2457 } 2458 if (tmp_win->list) 2459 XUnmapWindow(dpy, tmp_win->list->icon); 2460 if ((Scr->WarpCursor || 2461 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)) && 2462 tmp_win->icon) 2463 WarpToWindow (tmp_win); 2464 tmp_win->icon = FALSE; 2465 tmp_win->icon_on = FALSE; 2466 2467 2468 /* now de-iconify transients */ 2469 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2470 { 2471 if (t->transient && t->transientfor == tmp_win->w) 2472 { 2473 if (t->icon_on) 2474 Zoom(t->icon_w, t->frame); 2475 else 2476 Zoom(tmp_win->icon_w, t->frame); 2477 2478 XMapWindow(dpy, t->w); 2479 t->mapped = TRUE; 2480 if (Scr->NoRaiseDeicon) 2481 XMapWindow(dpy, t->frame); 2482 else 2483 XMapRaised(dpy, t->frame); 2484 SetMapStateProp(t, NormalState); 2485 2486 if (t->icon_w) { 2487 XUnmapWindow(dpy, t->icon_w); 2488 IconDown (t); 2489 } 2490 if (t->list) XUnmapWindow(dpy, t->list->icon); 2491 t->icon = FALSE; 2492 t->icon_on = FALSE; 2493 } 2494 } 2495 2496 XSync (dpy, 0); 2497} 2498 2499 2500 2501void 2502Iconify(TwmWindow *tmp_win, int def_x, int def_y) 2503{ 2504 TwmWindow *t; 2505 int iconify; 2506 XWindowAttributes winattrs; 2507 unsigned long eventMask; 2508 2509 iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient); 2510 if (iconify) 2511 { 2512 if (tmp_win->icon_w == (Window) 0) 2513 CreateIconWindow(tmp_win, def_x, def_y); 2514 else 2515 IconUp(tmp_win); 2516 XMapRaised(dpy, tmp_win->icon_w); 2517 } 2518 if (tmp_win->list) 2519 XMapWindow(dpy, tmp_win->list->icon); 2520 2521 XGetWindowAttributes(dpy, tmp_win->w, &winattrs); 2522 eventMask = winattrs.your_event_mask; 2523 2524 /* iconify transients first */ 2525 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2526 { 2527 if (t->transient && t->transientfor == tmp_win->w) 2528 { 2529 if (iconify) 2530 { 2531 if (t->icon_on) 2532 Zoom(t->icon_w, tmp_win->icon_w); 2533 else 2534 Zoom(t->frame, tmp_win->icon_w); 2535 } 2536 2537 /* 2538 * Prevent the receipt of an UnmapNotify, since that would 2539 * cause a transition to the Withdrawn state. 2540 */ 2541 t->mapped = FALSE; 2542 XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask); 2543 XUnmapWindow(dpy, t->w); 2544 XSelectInput(dpy, t->w, eventMask); 2545 XUnmapWindow(dpy, t->frame); 2546 if (t->icon_w) 2547 XUnmapWindow(dpy, t->icon_w); 2548 SetMapStateProp(t, IconicState); 2549 SetBorder (t, False); 2550 if (t == Scr->Focus) 2551 { 2552 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2553 Scr->Focus = NULL; 2554 Scr->FocusRoot = TRUE; 2555 } 2556 if (t->list) XMapWindow(dpy, t->list->icon); 2557 t->icon = TRUE; 2558 t->icon_on = FALSE; 2559 } 2560 } 2561 2562 if (iconify) 2563 Zoom(tmp_win->frame, tmp_win->icon_w); 2564 2565 /* 2566 * Prevent the receipt of an UnmapNotify, since that would 2567 * cause a transition to the Withdrawn state. 2568 */ 2569 tmp_win->mapped = FALSE; 2570 XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask); 2571 XUnmapWindow(dpy, tmp_win->w); 2572 XSelectInput(dpy, tmp_win->w, eventMask); 2573 XUnmapWindow(dpy, tmp_win->frame); 2574 SetMapStateProp(tmp_win, IconicState); 2575 2576 SetBorder (tmp_win, False); 2577 if (tmp_win == Scr->Focus) 2578 { 2579 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2580 Scr->Focus = NULL; 2581 Scr->FocusRoot = TRUE; 2582 } 2583 tmp_win->icon = TRUE; 2584 if (iconify) 2585 tmp_win->icon_on = TRUE; 2586 else 2587 tmp_win->icon_on = FALSE; 2588 XSync (dpy, 0); 2589} 2590 2591 2592 2593static void 2594Identify (TwmWindow *t) 2595{ 2596 int i, n, twidth, width, height; 2597 int x, y; 2598 unsigned int wwidth, wheight, bw, depth; 2599 Window junk; 2600 int px, py, dummy; 2601 unsigned udummy; 2602 2603 n = 0; 2604 snprintf(Info[n++], INFO_SIZE, "Twm version: %s", Version); 2605 Info[n++][0] = '\0'; 2606 2607 if (t) { 2608 XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY, 2609 &wwidth, &wheight, &bw, &depth); 2610 (void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0, 2611 &x, &y, &junk); 2612 snprintf(Info[n++], INFO_SIZE, 2613 "Name = \"%s\"", t->full_name); 2614 snprintf(Info[n++], INFO_SIZE, 2615 "Class.res_name = \"%s\"", t->class.res_name); 2616 snprintf(Info[n++], INFO_SIZE, 2617 "Class.res_class = \"%s\"", t->class.res_class); 2618 Info[n++][0] = '\0'; 2619 snprintf(Info[n++], INFO_SIZE, 2620 "Geometry/root = %dx%d+%d+%d", wwidth, wheight, x, y); 2621 snprintf(Info[n++], INFO_SIZE, "Border width = %d", bw); 2622 snprintf(Info[n++], INFO_SIZE, "Depth = %d", depth); 2623 if (HasSync) 2624 { 2625 int priority; 2626 (void)XSyncGetPriority(dpy, t->w, &priority); 2627 snprintf(Info[n++], INFO_SIZE, "Priority = %d", priority); 2628 } 2629 } 2630 2631 Info[n++][0] = '\0'; 2632 snprintf(Info[n++], INFO_SIZE, "Click to dismiss...."); 2633 2634 /* figure out the width and height of the info window */ 2635 height = n * (Scr->DefaultFont.height+2); 2636 width = 1; 2637 for (i = 0; i < n; i++) 2638 { 2639 twidth = MyFont_TextWidth(&Scr->DefaultFont, Info[i], 2640 strlen(Info[i])); 2641 if (twidth > width) 2642 width = twidth; 2643 } 2644 if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow); 2645 2646 width += 10; /* some padding */ 2647 if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild, &px, &py, 2648 &dummy, &dummy, &udummy)) { 2649 px -= (width / 2); 2650 py -= (height / 3); 2651 if (px + width + BW2 >= Scr->MyDisplayWidth) 2652 px = Scr->MyDisplayWidth - width - BW2; 2653 if (py + height + BW2 >= Scr->MyDisplayHeight) 2654 py = Scr->MyDisplayHeight - height - BW2; 2655 if (px < 0) px = 0; 2656 if (py < 0) py = 0; 2657 } else { 2658 px = py = 0; 2659 } 2660 XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height); 2661 XMapRaised(dpy, Scr->InfoWindow); 2662 InfoLines = n; 2663} 2664 2665 2666void 2667SetMapStateProp(TwmWindow *tmp_win, int state) 2668{ 2669 unsigned long data[2]; /* "suggested" by ICCCM version 1 */ 2670 2671 data[0] = (unsigned long) state; 2672 data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None : 2673 tmp_win->icon_w); 2674 2675 XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32, 2676 PropModeReplace, (unsigned char *) data, 2); 2677} 2678 2679 2680 2681Bool 2682GetWMState (Window w, int *statep, Window *iwp) 2683{ 2684 Atom actual_type; 2685 int actual_format; 2686 unsigned long nitems, bytesafter; 2687 unsigned char *prop_return = NULL; 2688 Bool retval = False; 2689 2690 if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE, 2691 &actual_type, &actual_format, &nitems, &bytesafter, 2692 &prop_return) != Success || !prop_return) 2693 return False; 2694 2695 if (nitems <= 2) { /* "suggested" by ICCCM version 1 */ 2696 unsigned long *datap = (unsigned long *) prop_return; 2697 *statep = (int) datap[0]; 2698 *iwp = (Window) datap[1]; 2699 retval = True; 2700 } 2701 2702 XFree (prop_return); 2703 return retval; 2704} 2705 2706 2707void 2708WarpToScreen (int n, int inc) 2709{ 2710 Window dumwin; 2711 int x, y, dumint; 2712 unsigned int dummask; 2713 ScreenInfo *newscr = NULL; 2714 2715 while (!newscr) { 2716 /* wrap around */ 2717 if (n < 0) 2718 n = NumScreens - 1; 2719 else if (n >= NumScreens) 2720 n = 0; 2721 2722 newscr = ScreenList[n]; 2723 if (!newscr) { /* make sure screen is managed */ 2724 if (inc) { /* walk around the list */ 2725 n += inc; 2726 continue; 2727 } 2728 fprintf (stderr, "%s: unable to warp to unmanaged screen %d\n", 2729 ProgramName, n); 2730 Bell(XkbBI_MinorError,0,None); 2731 return; 2732 } 2733 } 2734 2735 if (Scr->screen == n) return; /* already on that screen */ 2736 2737 PreviousScreen = Scr->screen; 2738 XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y, 2739 &dumint, &dumint, &dummask); 2740 2741 XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y); 2742 return; 2743} 2744 2745 2746 2747 2748/** 2749 * rotate our internal copy of WM_COLORMAP_WINDOWS 2750 */ 2751static void 2752BumpWindowColormap (TwmWindow *tmp, int inc) 2753{ 2754 int i, j, previously_installed; 2755 ColormapWindow **cwins; 2756 2757 if (!tmp) return; 2758 2759 if (inc && tmp->cmaps.number_cwins > 0) { 2760 cwins = malloc(sizeof(ColormapWindow *) * tmp->cmaps.number_cwins); 2761 if (cwins) { 2762 if ((previously_installed = 2763 /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps && 2764 tmp->cmaps.number_cwins))) { 2765 for (i = tmp->cmaps.number_cwins; i-- > 0; ) 2766 tmp->cmaps.cwins[i]->colormap->state = 0; 2767 } 2768 2769 for (i = 0; i < tmp->cmaps.number_cwins; i++) { 2770 j = i - inc; 2771 if (j >= tmp->cmaps.number_cwins) 2772 j -= tmp->cmaps.number_cwins; 2773 else if (j < 0) 2774 j += tmp->cmaps.number_cwins; 2775 cwins[j] = tmp->cmaps.cwins[i]; 2776 } 2777 2778 free(tmp->cmaps.cwins); 2779 2780 tmp->cmaps.cwins = cwins; 2781 2782 if (tmp->cmaps.number_cwins > 1) 2783 bzero (tmp->cmaps.scoreboard, 2784 ColormapsScoreboardLength(&tmp->cmaps)); 2785 2786 if (previously_installed) 2787 InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL); 2788 } 2789 } else 2790 FetchWmColormapWindows (tmp); 2791} 2792 2793 2794static void 2795HideIconManager (void) 2796{ 2797 SetMapStateProp (Scr->iconmgr.twm_win, WithdrawnState); 2798 XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame); 2799 if (Scr->iconmgr.twm_win->icon_w) 2800 XUnmapWindow (dpy, Scr->iconmgr.twm_win->icon_w); 2801 Scr->iconmgr.twm_win->mapped = FALSE; 2802 Scr->iconmgr.twm_win->icon = TRUE; 2803} 2804 2805 2806 2807void 2808SetBorder (TwmWindow *tmp, Bool onoroff) 2809{ 2810 if (tmp->highlight) { 2811 if (onoroff) { 2812 XSetWindowBorder (dpy, tmp->frame, tmp->border); 2813 if (tmp->title_w) 2814 XSetWindowBorder (dpy, tmp->title_w, tmp->border); 2815 } else { 2816 XSetWindowBorderPixmap (dpy, tmp->frame, tmp->gray); 2817 if (tmp->title_w) 2818 XSetWindowBorderPixmap (dpy, tmp->title_w, tmp->gray); 2819 } 2820 } 2821} 2822 2823 2824static void 2825DestroyMenu (MenuRoot *menu) 2826{ 2827 MenuItem *item; 2828 2829 if (menu->w) { 2830 XDeleteContext (dpy, menu->w, MenuContext); 2831 XDeleteContext (dpy, menu->w, ScreenContext); 2832 if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow); 2833 XDestroyWindow(dpy, menu->w); 2834 } 2835 2836 for (item = menu->first; item; ) { 2837 MenuItem *tmp = item; 2838 item = item->next; 2839 free (tmp); 2840 } 2841} 2842 2843 2844 2845/* 2846 * warping routines 2847 */ 2848 2849static void 2850WarpAlongRing (XButtonEvent *ev, Bool forward) 2851{ 2852 TwmWindow *r, *head; 2853 2854 if (Scr->RingLeader) 2855 head = Scr->RingLeader; 2856 else if (!(head = Scr->Ring)) 2857 return; 2858 2859 if (forward) { 2860 for (r = head->ring.next; r != head; r = r->ring.next) { 2861 if (!r || r->mapped) break; 2862 } 2863 } else { 2864 for (r = head->ring.prev; r != head; r = r->ring.prev) { 2865 if (!r || r->mapped) break; 2866 } 2867 } 2868 2869 if (r && r != head) { 2870 TwmWindow *p = Scr->RingLeader, *t; 2871 XPointer context_data; 2872 2873 Scr->RingLeader = r; 2874 WarpToWindow (r); 2875 2876 if (XFindContext (dpy, ev->window, TwmContext, &context_data) == 0) 2877 t = (TwmWindow *) context_data; 2878 else 2879 t = NULL; 2880 2881 if (p && p->mapped && p == t) { 2882 p->ring.cursor_valid = True; 2883 p->ring.curs_x = ev->x_root - t->frame_x; 2884 p->ring.curs_y = ev->y_root - t->frame_y; 2885 if (p->ring.curs_x < -p->frame_bw || 2886 p->ring.curs_x >= p->frame_width + p->frame_bw || 2887 p->ring.curs_y < -p->frame_bw || 2888 p->ring.curs_y >= p->frame_height + p->frame_bw) { 2889 /* somehow out of window */ 2890 p->ring.curs_x = p->frame_width / 2; 2891 p->ring.curs_y = p->frame_height / 2; 2892 } 2893 } 2894 } 2895} 2896 2897 2898 2899static void 2900WarpToWindow (TwmWindow *t) 2901{ 2902 int x, y; 2903 2904 if (t->auto_raise || !Scr->NoRaiseWarp) AutoRaiseWindow (t); 2905 if (t->ring.cursor_valid) { 2906 x = t->ring.curs_x; 2907 y = t->ring.curs_y; 2908 } else { 2909 x = t->frame_width / 2; 2910 y = t->frame_height / 2; 2911 } 2912 XWarpPointer (dpy, None, t->frame, 0, 0, 0, 0, x, y); 2913} 2914 2915 2916 2917 2918/* 2919 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all 2920 * client messages will have the following form: 2921 * 2922 * event type ClientMessage 2923 * message type _XA_WM_PROTOCOLS 2924 * window tmp->w 2925 * format 32 2926 * data[0] message atom 2927 * data[1] time stamp 2928 */ 2929static void 2930send_clientmessage (Window w, Atom a, Time timestamp) 2931{ 2932 XClientMessageEvent ev; 2933 2934 ev.type = ClientMessage; 2935 ev.window = w; 2936 ev.message_type = _XA_WM_PROTOCOLS; 2937 ev.format = 32; 2938 ev.data.l[0] = a; 2939 ev.data.l[1] = timestamp; 2940 XSendEvent (dpy, w, False, 0L, (XEvent *) &ev); 2941} 2942 2943void 2944SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp) 2945{ 2946 send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp); 2947} 2948 2949void 2950SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp) 2951{ 2952 send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp); 2953} 2954 2955void 2956SendTakeFocusMessage (TwmWindow *tmp, Time timestamp) 2957{ 2958 send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp); 2959} 2960