menus.c revision 9cd34f4b
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 2340static void 2341Execute(const char *s) 2342{ 2343 /* FIXME: is all this stuff needed? There could be security problems here. */ 2344 static char buf[256]; 2345 char *ds = DisplayString (dpy); 2346 char *colon, *dot1; 2347 char oldDisplay[256]; 2348 char *doisplay; 2349 int restorevar = 0; 2350 2351 oldDisplay[0] = '\0'; 2352 doisplay=getenv("DISPLAY"); 2353 if (doisplay) 2354 strcpy (oldDisplay, doisplay); 2355 2356 /* 2357 * Build a display string using the current screen number, so that 2358 * X programs which get fired up from a menu come up on the screen 2359 * that they were invoked from, unless specifically overridden on 2360 * their command line. 2361 */ 2362 colon = strrchr (ds, ':'); 2363 if (colon) { /* if host[:]:dpy */ 2364 strcpy (buf, "DISPLAY="); 2365 strcat (buf, ds); 2366 colon = buf + 8 + (colon - ds); /* use version in buf */ 2367 dot1 = strchr (colon, '.'); /* first period after colon */ 2368 if (!dot1) dot1 = colon + strlen (colon); /* if not there, append */ 2369 (void) sprintf (dot1, ".%d", Scr->screen); 2370 putenv (buf); 2371 restorevar = 1; 2372 } 2373 2374 (void) system (s); 2375 2376 if (restorevar) { /* why bother? */ 2377 (void) snprintf (buf, sizeof(buf), "DISPLAY=%s", oldDisplay); 2378 putenv (buf); 2379 } 2380} 2381 2382 2383 2384/** 2385 * put input focus on the root window. 2386 */ 2387void 2388FocusOnRoot(void) 2389{ 2390 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2391 if (Scr->Focus != NULL) 2392 { 2393 SetBorder (Scr->Focus, False); 2394 if (Scr->Focus->hilite_w) XUnmapWindow (dpy, Scr->Focus->hilite_w); 2395 } 2396 InstallWindowColormaps(0, &Scr->TwmRoot); 2397 Scr->Focus = NULL; 2398 Scr->FocusRoot = TRUE; 2399} 2400 2401void 2402DeIconify(TwmWindow *tmp_win) 2403{ 2404 TwmWindow *t; 2405 2406 /* de-iconify the main window */ 2407 if (tmp_win->icon) 2408 { 2409 if (tmp_win->icon_on) 2410 Zoom(tmp_win->icon_w, tmp_win->frame); 2411 else if (tmp_win->group != (Window) 0) 2412 { 2413 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2414 { 2415 if (tmp_win->group == t->w && t->icon_on) 2416 { 2417 Zoom(t->icon_w, tmp_win->frame); 2418 break; 2419 } 2420 } 2421 } 2422 } 2423 2424 XMapWindow(dpy, tmp_win->w); 2425 tmp_win->mapped = TRUE; 2426 if (Scr->NoRaiseDeicon) 2427 XMapWindow(dpy, tmp_win->frame); 2428 else 2429 XMapRaised(dpy, tmp_win->frame); 2430 SetMapStateProp(tmp_win, NormalState); 2431 2432 if (tmp_win->icon_w) { 2433 XUnmapWindow(dpy, tmp_win->icon_w); 2434 IconDown (tmp_win); 2435 } 2436 if (tmp_win->list) 2437 XUnmapWindow(dpy, tmp_win->list->icon); 2438 if ((Scr->WarpCursor || 2439 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)) && 2440 tmp_win->icon) 2441 WarpToWindow (tmp_win); 2442 tmp_win->icon = FALSE; 2443 tmp_win->icon_on = FALSE; 2444 2445 2446 /* now de-iconify transients */ 2447 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2448 { 2449 if (t->transient && t->transientfor == tmp_win->w) 2450 { 2451 if (t->icon_on) 2452 Zoom(t->icon_w, t->frame); 2453 else 2454 Zoom(tmp_win->icon_w, t->frame); 2455 2456 XMapWindow(dpy, t->w); 2457 t->mapped = TRUE; 2458 if (Scr->NoRaiseDeicon) 2459 XMapWindow(dpy, t->frame); 2460 else 2461 XMapRaised(dpy, t->frame); 2462 SetMapStateProp(t, NormalState); 2463 2464 if (t->icon_w) { 2465 XUnmapWindow(dpy, t->icon_w); 2466 IconDown (t); 2467 } 2468 if (t->list) XUnmapWindow(dpy, t->list->icon); 2469 t->icon = FALSE; 2470 t->icon_on = FALSE; 2471 } 2472 } 2473 2474 XSync (dpy, 0); 2475} 2476 2477 2478 2479void 2480Iconify(TwmWindow *tmp_win, int def_x, int def_y) 2481{ 2482 TwmWindow *t; 2483 int iconify; 2484 XWindowAttributes winattrs; 2485 unsigned long eventMask; 2486 2487 iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient); 2488 if (iconify) 2489 { 2490 if (tmp_win->icon_w == (Window) 0) 2491 CreateIconWindow(tmp_win, def_x, def_y); 2492 else 2493 IconUp(tmp_win); 2494 XMapRaised(dpy, tmp_win->icon_w); 2495 } 2496 if (tmp_win->list) 2497 XMapWindow(dpy, tmp_win->list->icon); 2498 2499 XGetWindowAttributes(dpy, tmp_win->w, &winattrs); 2500 eventMask = winattrs.your_event_mask; 2501 2502 /* iconify transients first */ 2503 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2504 { 2505 if (t->transient && t->transientfor == tmp_win->w) 2506 { 2507 if (iconify) 2508 { 2509 if (t->icon_on) 2510 Zoom(t->icon_w, tmp_win->icon_w); 2511 else 2512 Zoom(t->frame, tmp_win->icon_w); 2513 } 2514 2515 /* 2516 * Prevent the receipt of an UnmapNotify, since that would 2517 * cause a transition to the Withdrawn state. 2518 */ 2519 t->mapped = FALSE; 2520 XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask); 2521 XUnmapWindow(dpy, t->w); 2522 XSelectInput(dpy, t->w, eventMask); 2523 XUnmapWindow(dpy, t->frame); 2524 if (t->icon_w) 2525 XUnmapWindow(dpy, t->icon_w); 2526 SetMapStateProp(t, IconicState); 2527 SetBorder (t, False); 2528 if (t == Scr->Focus) 2529 { 2530 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2531 Scr->Focus = NULL; 2532 Scr->FocusRoot = TRUE; 2533 } 2534 if (t->list) XMapWindow(dpy, t->list->icon); 2535 t->icon = TRUE; 2536 t->icon_on = FALSE; 2537 } 2538 } 2539 2540 if (iconify) 2541 Zoom(tmp_win->frame, tmp_win->icon_w); 2542 2543 /* 2544 * Prevent the receipt of an UnmapNotify, since that would 2545 * cause a transition to the Withdrawn state. 2546 */ 2547 tmp_win->mapped = FALSE; 2548 XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask); 2549 XUnmapWindow(dpy, tmp_win->w); 2550 XSelectInput(dpy, tmp_win->w, eventMask); 2551 XUnmapWindow(dpy, tmp_win->frame); 2552 SetMapStateProp(tmp_win, IconicState); 2553 2554 SetBorder (tmp_win, False); 2555 if (tmp_win == Scr->Focus) 2556 { 2557 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2558 Scr->Focus = NULL; 2559 Scr->FocusRoot = TRUE; 2560 } 2561 tmp_win->icon = TRUE; 2562 if (iconify) 2563 tmp_win->icon_on = TRUE; 2564 else 2565 tmp_win->icon_on = FALSE; 2566 XSync (dpy, 0); 2567} 2568 2569 2570 2571static void 2572Identify (TwmWindow *t) 2573{ 2574 int i, n, twidth, width, height; 2575 int x, y; 2576 unsigned int wwidth, wheight, bw, depth; 2577 Window junk; 2578 int px, py, dummy; 2579 unsigned udummy; 2580 2581 n = 0; 2582 snprintf(Info[n++], INFO_SIZE, "Twm version: %s", Version); 2583 Info[n++][0] = '\0'; 2584 2585 if (t) { 2586 XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY, 2587 &wwidth, &wheight, &bw, &depth); 2588 (void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0, 2589 &x, &y, &junk); 2590 snprintf(Info[n++], INFO_SIZE, 2591 "Name = \"%s\"", t->full_name); 2592 snprintf(Info[n++], INFO_SIZE, 2593 "Class.res_name = \"%s\"", t->class.res_name); 2594 snprintf(Info[n++], INFO_SIZE, 2595 "Class.res_class = \"%s\"", t->class.res_class); 2596 Info[n++][0] = '\0'; 2597 snprintf(Info[n++], INFO_SIZE, 2598 "Geometry/root = %dx%d+%d+%d", wwidth, wheight, x, y); 2599 snprintf(Info[n++], INFO_SIZE, "Border width = %d", bw); 2600 snprintf(Info[n++], INFO_SIZE, "Depth = %d", depth); 2601 if (HasSync) 2602 { 2603 int priority; 2604 (void)XSyncGetPriority(dpy, t->w, &priority); 2605 snprintf(Info[n++], INFO_SIZE, "Priority = %d", priority); 2606 } 2607 } 2608 2609 Info[n++][0] = '\0'; 2610 snprintf(Info[n++], INFO_SIZE, "Click to dismiss...."); 2611 2612 /* figure out the width and height of the info window */ 2613 height = n * (Scr->DefaultFont.height+2); 2614 width = 1; 2615 for (i = 0; i < n; i++) 2616 { 2617 twidth = MyFont_TextWidth(&Scr->DefaultFont, Info[i], 2618 strlen(Info[i])); 2619 if (twidth > width) 2620 width = twidth; 2621 } 2622 if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow); 2623 2624 width += 10; /* some padding */ 2625 if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild, &px, &py, 2626 &dummy, &dummy, &udummy)) { 2627 px -= (width / 2); 2628 py -= (height / 3); 2629 if (px + width + BW2 >= Scr->MyDisplayWidth) 2630 px = Scr->MyDisplayWidth - width - BW2; 2631 if (py + height + BW2 >= Scr->MyDisplayHeight) 2632 py = Scr->MyDisplayHeight - height - BW2; 2633 if (px < 0) px = 0; 2634 if (py < 0) py = 0; 2635 } else { 2636 px = py = 0; 2637 } 2638 XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height); 2639 XMapRaised(dpy, Scr->InfoWindow); 2640 InfoLines = n; 2641} 2642 2643 2644void 2645SetMapStateProp(TwmWindow *tmp_win, int state) 2646{ 2647 unsigned long data[2]; /* "suggested" by ICCCM version 1 */ 2648 2649 data[0] = (unsigned long) state; 2650 data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None : 2651 tmp_win->icon_w); 2652 2653 XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32, 2654 PropModeReplace, (unsigned char *) data, 2); 2655} 2656 2657 2658 2659Bool 2660GetWMState (Window w, int *statep, Window *iwp) 2661{ 2662 Atom actual_type; 2663 int actual_format; 2664 unsigned long nitems, bytesafter; 2665 unsigned char *prop_return = NULL; 2666 Bool retval = False; 2667 2668 if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE, 2669 &actual_type, &actual_format, &nitems, &bytesafter, 2670 &prop_return) != Success || !prop_return) 2671 return False; 2672 2673 if (nitems <= 2) { /* "suggested" by ICCCM version 1 */ 2674 unsigned long *datap = (unsigned long *) prop_return; 2675 *statep = (int) datap[0]; 2676 *iwp = (Window) datap[1]; 2677 retval = True; 2678 } 2679 2680 XFree (prop_return); 2681 return retval; 2682} 2683 2684 2685void 2686WarpToScreen (int n, int inc) 2687{ 2688 Window dumwin; 2689 int x, y, dumint; 2690 unsigned int dummask; 2691 ScreenInfo *newscr = NULL; 2692 2693 while (!newscr) { 2694 /* wrap around */ 2695 if (n < 0) 2696 n = NumScreens - 1; 2697 else if (n >= NumScreens) 2698 n = 0; 2699 2700 newscr = ScreenList[n]; 2701 if (!newscr) { /* make sure screen is managed */ 2702 if (inc) { /* walk around the list */ 2703 n += inc; 2704 continue; 2705 } 2706 fprintf (stderr, "%s: unable to warp to unmanaged screen %d\n", 2707 ProgramName, n); 2708 Bell(XkbBI_MinorError,0,None); 2709 return; 2710 } 2711 } 2712 2713 if (Scr->screen == n) return; /* already on that screen */ 2714 2715 PreviousScreen = Scr->screen; 2716 XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y, 2717 &dumint, &dumint, &dummask); 2718 2719 XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y); 2720 return; 2721} 2722 2723 2724 2725 2726/** 2727 * rotate our internal copy of WM_COLORMAP_WINDOWS 2728 */ 2729static void 2730BumpWindowColormap (TwmWindow *tmp, int inc) 2731{ 2732 int i, j, previously_installed; 2733 ColormapWindow **cwins; 2734 2735 if (!tmp) return; 2736 2737 if (inc && tmp->cmaps.number_cwins > 0) { 2738 cwins = malloc(sizeof(ColormapWindow *) * tmp->cmaps.number_cwins); 2739 if (cwins) { 2740 if ((previously_installed = 2741 /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps && 2742 tmp->cmaps.number_cwins))) { 2743 for (i = tmp->cmaps.number_cwins; i-- > 0; ) 2744 tmp->cmaps.cwins[i]->colormap->state = 0; 2745 } 2746 2747 for (i = 0; i < tmp->cmaps.number_cwins; i++) { 2748 j = i - inc; 2749 if (j >= tmp->cmaps.number_cwins) 2750 j -= tmp->cmaps.number_cwins; 2751 else if (j < 0) 2752 j += tmp->cmaps.number_cwins; 2753 cwins[j] = tmp->cmaps.cwins[i]; 2754 } 2755 2756 free(tmp->cmaps.cwins); 2757 2758 tmp->cmaps.cwins = cwins; 2759 2760 if (tmp->cmaps.number_cwins > 1) 2761 bzero (tmp->cmaps.scoreboard, 2762 ColormapsScoreboardLength(&tmp->cmaps)); 2763 2764 if (previously_installed) 2765 InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL); 2766 } 2767 } else 2768 FetchWmColormapWindows (tmp); 2769} 2770 2771 2772static void 2773HideIconManager (void) 2774{ 2775 SetMapStateProp (Scr->iconmgr.twm_win, WithdrawnState); 2776 XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame); 2777 if (Scr->iconmgr.twm_win->icon_w) 2778 XUnmapWindow (dpy, Scr->iconmgr.twm_win->icon_w); 2779 Scr->iconmgr.twm_win->mapped = FALSE; 2780 Scr->iconmgr.twm_win->icon = TRUE; 2781} 2782 2783 2784 2785void 2786SetBorder (TwmWindow *tmp, Bool onoroff) 2787{ 2788 if (tmp->highlight) { 2789 if (onoroff) { 2790 XSetWindowBorder (dpy, tmp->frame, tmp->border); 2791 if (tmp->title_w) 2792 XSetWindowBorder (dpy, tmp->title_w, tmp->border); 2793 } else { 2794 XSetWindowBorderPixmap (dpy, tmp->frame, tmp->gray); 2795 if (tmp->title_w) 2796 XSetWindowBorderPixmap (dpy, tmp->title_w, tmp->gray); 2797 } 2798 } 2799} 2800 2801 2802static void 2803DestroyMenu (MenuRoot *menu) 2804{ 2805 MenuItem *item; 2806 2807 if (menu->w) { 2808 XDeleteContext (dpy, menu->w, MenuContext); 2809 XDeleteContext (dpy, menu->w, ScreenContext); 2810 if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow); 2811 XDestroyWindow(dpy, menu->w); 2812 } 2813 2814 for (item = menu->first; item; ) { 2815 MenuItem *tmp = item; 2816 item = item->next; 2817 free (tmp); 2818 } 2819} 2820 2821 2822 2823/* 2824 * warping routines 2825 */ 2826 2827static void 2828WarpAlongRing (XButtonEvent *ev, Bool forward) 2829{ 2830 TwmWindow *r, *head; 2831 2832 if (Scr->RingLeader) 2833 head = Scr->RingLeader; 2834 else if (!(head = Scr->Ring)) 2835 return; 2836 2837 if (forward) { 2838 for (r = head->ring.next; r != head; r = r->ring.next) { 2839 if (!r || r->mapped) break; 2840 } 2841 } else { 2842 for (r = head->ring.prev; r != head; r = r->ring.prev) { 2843 if (!r || r->mapped) break; 2844 } 2845 } 2846 2847 if (r && r != head) { 2848 TwmWindow *p = Scr->RingLeader, *t; 2849 XPointer context_data; 2850 2851 Scr->RingLeader = r; 2852 WarpToWindow (r); 2853 2854 if (XFindContext (dpy, ev->window, TwmContext, &context_data) == 0) 2855 t = (TwmWindow *) context_data; 2856 else 2857 t = NULL; 2858 2859 if (p && p->mapped && p == t) { 2860 p->ring.cursor_valid = True; 2861 p->ring.curs_x = ev->x_root - t->frame_x; 2862 p->ring.curs_y = ev->y_root - t->frame_y; 2863 if (p->ring.curs_x < -p->frame_bw || 2864 p->ring.curs_x >= p->frame_width + p->frame_bw || 2865 p->ring.curs_y < -p->frame_bw || 2866 p->ring.curs_y >= p->frame_height + p->frame_bw) { 2867 /* somehow out of window */ 2868 p->ring.curs_x = p->frame_width / 2; 2869 p->ring.curs_y = p->frame_height / 2; 2870 } 2871 } 2872 } 2873} 2874 2875 2876 2877static void 2878WarpToWindow (TwmWindow *t) 2879{ 2880 int x, y; 2881 2882 if (t->auto_raise || !Scr->NoRaiseWarp) AutoRaiseWindow (t); 2883 if (t->ring.cursor_valid) { 2884 x = t->ring.curs_x; 2885 y = t->ring.curs_y; 2886 } else { 2887 x = t->frame_width / 2; 2888 y = t->frame_height / 2; 2889 } 2890 XWarpPointer (dpy, None, t->frame, 0, 0, 0, 0, x, y); 2891} 2892 2893 2894 2895 2896/* 2897 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all 2898 * client messages will have the following form: 2899 * 2900 * event type ClientMessage 2901 * message type _XA_WM_PROTOCOLS 2902 * window tmp->w 2903 * format 32 2904 * data[0] message atom 2905 * data[1] time stamp 2906 */ 2907static void 2908send_clientmessage (Window w, Atom a, Time timestamp) 2909{ 2910 XClientMessageEvent ev; 2911 2912 ev.type = ClientMessage; 2913 ev.window = w; 2914 ev.message_type = _XA_WM_PROTOCOLS; 2915 ev.format = 32; 2916 ev.data.l[0] = a; 2917 ev.data.l[1] = timestamp; 2918 XSendEvent (dpy, w, False, 0L, (XEvent *) &ev); 2919} 2920 2921void 2922SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp) 2923{ 2924 send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp); 2925} 2926 2927void 2928SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp) 2929{ 2930 send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp); 2931} 2932 2933void 2934SendTakeFocusMessage (TwmWindow *tmp, Time timestamp) 2935{ 2936 send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp); 2937} 2938