menus.c revision 4a1eb5db
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 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 if (ptr) { 2175 fd = open(ptr, O_RDONLY); 2176 if (fd >= 0) 2177 { 2178 count = read(fd, buff, MAX_FILE_SIZE - 1); 2179 if (count > 0) 2180 XStoreBytes(dpy, buff, count); 2181 2182 close(fd); 2183 } 2184 else 2185 { 2186 fprintf (stderr, "%s: unable to open file \"%s\"\n", 2187 ProgramName, ptr); 2188 } 2189 free(ptr); 2190 } else { 2191 fprintf (stderr, "%s: error expanding filename\n", ProgramName); 2192 } 2193 break; 2194 2195 case F_REFRESH: 2196 { 2197 XSetWindowAttributes attributes; 2198 unsigned long valuemask; 2199 2200 valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder); 2201 attributes.background_pixel = Scr->Black; 2202 attributes.backing_store = NotUseful; 2203 attributes.save_under = False; 2204 w = XCreateWindow (dpy, Scr->Root, 0, 0, 2205 (unsigned int) Scr->MyDisplayWidth, 2206 (unsigned int) Scr->MyDisplayHeight, 2207 (unsigned int) 0, 2208 CopyFromParent, (unsigned int) CopyFromParent, 2209 (Visual *) CopyFromParent, valuemask, 2210 &attributes); 2211 XMapWindow (dpy, w); 2212 XDestroyWindow (dpy, w); 2213 XFlush (dpy); 2214 } 2215 break; 2216 2217 case F_WINREFRESH: 2218 if (DeferExecution(context, func, Scr->SelectCursor)) 2219 return TRUE; 2220 2221 if (context == C_ICON && tmp_win->icon_w) 2222 w = XCreateSimpleWindow(dpy, tmp_win->icon_w, 2223 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black); 2224 else 2225 w = XCreateSimpleWindow(dpy, tmp_win->frame, 2226 0, 0, 9999, 9999, 0, Scr->Black, Scr->Black); 2227 2228 XMapWindow(dpy, w); 2229 XDestroyWindow(dpy, w); 2230 XFlush(dpy); 2231 break; 2232 2233 case F_QUIT: 2234 Done(NULL, NULL); 2235 break; 2236 2237 case F_PRIORITY: 2238 if (HasSync) 2239 { 2240 if (DeferExecution (context, func, Scr->SelectCursor)) 2241 return TRUE; 2242 (void)XSyncSetPriority(dpy, tmp_win->w, atoi(action)); 2243 } 2244 break; 2245 case F_STARTWM: 2246 execlp("/bin/sh", "sh", "-c", action, (void *)NULL); 2247 fprintf (stderr, "%s: unable to start: %s\n", ProgramName, *Argv); 2248 break; 2249 2250 } 2251 2252 if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime); 2253 return do_next_action; 2254} 2255 2256 2257 2258/** 2259 * defer the execution of a function to the next button press if the context 2260 * is C_ROOT 2261 * 2262 * \param context the context in which the mouse button was pressed 2263 * \param func the function to defer 2264 * \param cursor cursor the cursor to display while waiting 2265 */ 2266static int 2267DeferExecution(int context, int func, Cursor cursor) 2268{ 2269 if (context == C_ROOT) 2270 { 2271 LastCursor = cursor; 2272 XGrabPointer(dpy, Scr->Root, True, 2273 ButtonPressMask | ButtonReleaseMask, 2274 GrabModeAsync, GrabModeAsync, 2275 Scr->Root, cursor, CurrentTime); 2276 2277 RootFunction = func; 2278 2279 return (TRUE); 2280 } 2281 2282 return (FALSE); 2283} 2284 2285 2286 2287/** 2288 *regrab the pointer with the LastCursor; 2289 */ 2290void 2291ReGrab(void) 2292{ 2293 XGrabPointer(dpy, Scr->Root, True, 2294 ButtonPressMask | ButtonReleaseMask, 2295 GrabModeAsync, GrabModeAsync, 2296 Scr->Root, LastCursor, CurrentTime); 2297} 2298 2299 2300 2301/** 2302 * checks each function in the list to see if it is one that needs 2303 * to be deferred. 2304 * 2305 * \param root the menu root to check 2306 */ 2307static Bool 2308NeedToDefer(MenuRoot *root) 2309{ 2310 MenuItem *mitem; 2311 2312 for (mitem = root->first; mitem != NULL; mitem = mitem->next) 2313 { 2314 switch (mitem->func) 2315 { 2316 case F_IDENTIFY: 2317 case F_RESIZE: 2318 case F_MOVE: 2319 case F_FORCEMOVE: 2320 case F_DEICONIFY: 2321 case F_ICONIFY: 2322 case F_RAISELOWER: 2323 case F_RAISE: 2324 case F_LOWER: 2325 case F_FOCUS: 2326 case F_DESTROY: 2327 case F_WINREFRESH: 2328 case F_ZOOM: 2329 case F_FULLZOOM: 2330 case F_HORIZOOM: 2331 case F_RIGHTZOOM: 2332 case F_LEFTZOOM: 2333 case F_TOPZOOM: 2334 case F_BOTTOMZOOM: 2335 case F_AUTORAISE: 2336 return TRUE; 2337 } 2338 } 2339 return FALSE; 2340} 2341 2342 2343 2344static void 2345Execute(const char *s) 2346{ 2347 /* FIXME: is all this stuff needed? There could be security problems here. */ 2348 static char buf[256]; 2349 char *ds = DisplayString (dpy); 2350 char *colon, *dot1; 2351 char oldDisplay[256]; 2352 char *doisplay; 2353 int restorevar = 0; 2354 2355 oldDisplay[0] = '\0'; 2356 doisplay=getenv("DISPLAY"); 2357 if (doisplay) 2358 strcpy (oldDisplay, doisplay); 2359 2360 /* 2361 * Build a display string using the current screen number, so that 2362 * X programs which get fired up from a menu come up on the screen 2363 * that they were invoked from, unless specifically overridden on 2364 * their command line. 2365 */ 2366 colon = strrchr (ds, ':'); 2367 if (colon) { /* if host[:]:dpy */ 2368 strcpy (buf, "DISPLAY="); 2369 strcat (buf, ds); 2370 colon = buf + 8 + (colon - ds); /* use version in buf */ 2371 dot1 = strchr (colon, '.'); /* first period after colon */ 2372 if (!dot1) dot1 = colon + strlen (colon); /* if not there, append */ 2373 (void) sprintf (dot1, ".%d", Scr->screen); 2374 putenv (buf); 2375 restorevar = 1; 2376 } 2377 2378 (void) system (s); 2379 2380 if (restorevar) { /* why bother? */ 2381 (void) snprintf (buf, sizeof(buf), "DISPLAY=%s", oldDisplay); 2382 putenv (buf); 2383 } 2384} 2385 2386 2387 2388/** 2389 * put input focus on the root window. 2390 */ 2391void 2392FocusOnRoot(void) 2393{ 2394 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2395 if (Scr->Focus != NULL) 2396 { 2397 SetBorder (Scr->Focus, False); 2398 if (Scr->Focus->hilite_w) XUnmapWindow (dpy, Scr->Focus->hilite_w); 2399 } 2400 InstallWindowColormaps(0, &Scr->TwmRoot); 2401 Scr->Focus = NULL; 2402 Scr->FocusRoot = TRUE; 2403} 2404 2405void 2406DeIconify(TwmWindow *tmp_win) 2407{ 2408 TwmWindow *t; 2409 2410 /* de-iconify the main window */ 2411 if (tmp_win->icon) 2412 { 2413 if (tmp_win->icon_on) 2414 Zoom(tmp_win->icon_w, tmp_win->frame); 2415 else if (tmp_win->group != (Window) 0) 2416 { 2417 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2418 { 2419 if (tmp_win->group == t->w && t->icon_on) 2420 { 2421 Zoom(t->icon_w, tmp_win->frame); 2422 break; 2423 } 2424 } 2425 } 2426 } 2427 2428 XMapWindow(dpy, tmp_win->w); 2429 tmp_win->mapped = TRUE; 2430 if (Scr->NoRaiseDeicon) 2431 XMapWindow(dpy, tmp_win->frame); 2432 else 2433 XMapRaised(dpy, tmp_win->frame); 2434 SetMapStateProp(tmp_win, NormalState); 2435 2436 if (tmp_win->icon_w) { 2437 XUnmapWindow(dpy, tmp_win->icon_w); 2438 IconDown (tmp_win); 2439 } 2440 if (tmp_win->list) 2441 XUnmapWindow(dpy, tmp_win->list->icon); 2442 if ((Scr->WarpCursor || 2443 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)) && 2444 tmp_win->icon) 2445 WarpToWindow (tmp_win); 2446 tmp_win->icon = FALSE; 2447 tmp_win->icon_on = FALSE; 2448 2449 2450 /* now de-iconify transients */ 2451 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2452 { 2453 if (t->transient && t->transientfor == tmp_win->w) 2454 { 2455 if (t->icon_on) 2456 Zoom(t->icon_w, t->frame); 2457 else 2458 Zoom(tmp_win->icon_w, t->frame); 2459 2460 XMapWindow(dpy, t->w); 2461 t->mapped = TRUE; 2462 if (Scr->NoRaiseDeicon) 2463 XMapWindow(dpy, t->frame); 2464 else 2465 XMapRaised(dpy, t->frame); 2466 SetMapStateProp(t, NormalState); 2467 2468 if (t->icon_w) { 2469 XUnmapWindow(dpy, t->icon_w); 2470 IconDown (t); 2471 } 2472 if (t->list) XUnmapWindow(dpy, t->list->icon); 2473 t->icon = FALSE; 2474 t->icon_on = FALSE; 2475 } 2476 } 2477 2478 XSync (dpy, 0); 2479} 2480 2481 2482 2483void 2484Iconify(TwmWindow *tmp_win, int def_x, int def_y) 2485{ 2486 TwmWindow *t; 2487 int iconify; 2488 XWindowAttributes winattrs; 2489 unsigned long eventMask; 2490 2491 iconify = ((!tmp_win->iconify_by_unmapping) || tmp_win->transient); 2492 if (iconify) 2493 { 2494 if (tmp_win->icon_w == (Window) 0) 2495 CreateIconWindow(tmp_win, def_x, def_y); 2496 else 2497 IconUp(tmp_win); 2498 XMapRaised(dpy, tmp_win->icon_w); 2499 } 2500 if (tmp_win->list) 2501 XMapWindow(dpy, tmp_win->list->icon); 2502 2503 XGetWindowAttributes(dpy, tmp_win->w, &winattrs); 2504 eventMask = winattrs.your_event_mask; 2505 2506 /* iconify transients first */ 2507 for (t = Scr->TwmRoot.next; t != NULL; t = t->next) 2508 { 2509 if (t->transient && t->transientfor == tmp_win->w) 2510 { 2511 if (iconify) 2512 { 2513 if (t->icon_on) 2514 Zoom(t->icon_w, tmp_win->icon_w); 2515 else 2516 Zoom(t->frame, tmp_win->icon_w); 2517 } 2518 2519 /* 2520 * Prevent the receipt of an UnmapNotify, since that would 2521 * cause a transition to the Withdrawn state. 2522 */ 2523 t->mapped = FALSE; 2524 XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask); 2525 XUnmapWindow(dpy, t->w); 2526 XSelectInput(dpy, t->w, eventMask); 2527 XUnmapWindow(dpy, t->frame); 2528 if (t->icon_w) 2529 XUnmapWindow(dpy, t->icon_w); 2530 SetMapStateProp(t, IconicState); 2531 SetBorder (t, False); 2532 if (t == Scr->Focus) 2533 { 2534 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2535 Scr->Focus = NULL; 2536 Scr->FocusRoot = TRUE; 2537 } 2538 if (t->list) XMapWindow(dpy, t->list->icon); 2539 t->icon = TRUE; 2540 t->icon_on = FALSE; 2541 } 2542 } 2543 2544 if (iconify) 2545 Zoom(tmp_win->frame, tmp_win->icon_w); 2546 2547 /* 2548 * Prevent the receipt of an UnmapNotify, since that would 2549 * cause a transition to the Withdrawn state. 2550 */ 2551 tmp_win->mapped = FALSE; 2552 XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask); 2553 XUnmapWindow(dpy, tmp_win->w); 2554 XSelectInput(dpy, tmp_win->w, eventMask); 2555 XUnmapWindow(dpy, tmp_win->frame); 2556 SetMapStateProp(tmp_win, IconicState); 2557 2558 SetBorder (tmp_win, False); 2559 if (tmp_win == Scr->Focus) 2560 { 2561 SetFocus ((TwmWindow *) NULL, LastTimestamp()); 2562 Scr->Focus = NULL; 2563 Scr->FocusRoot = TRUE; 2564 } 2565 tmp_win->icon = TRUE; 2566 if (iconify) 2567 tmp_win->icon_on = TRUE; 2568 else 2569 tmp_win->icon_on = FALSE; 2570 XSync (dpy, 0); 2571} 2572 2573 2574 2575static void 2576Identify (TwmWindow *t) 2577{ 2578 int i, n, twidth, width, height; 2579 int x, y; 2580 unsigned int wwidth, wheight, bw, depth; 2581 Window junk; 2582 int px, py, dummy; 2583 unsigned udummy; 2584 2585 n = 0; 2586 snprintf(Info[n++], INFO_SIZE, "Twm version: %s", Version); 2587 Info[n++][0] = '\0'; 2588 2589 if (t) { 2590 XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY, 2591 &wwidth, &wheight, &bw, &depth); 2592 (void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0, 2593 &x, &y, &junk); 2594 snprintf(Info[n++], INFO_SIZE, 2595 "Name = \"%s\"", t->full_name); 2596 snprintf(Info[n++], INFO_SIZE, 2597 "Class.res_name = \"%s\"", t->class.res_name); 2598 snprintf(Info[n++], INFO_SIZE, 2599 "Class.res_class = \"%s\"", t->class.res_class); 2600 Info[n++][0] = '\0'; 2601 snprintf(Info[n++], INFO_SIZE, 2602 "Geometry/root = %dx%d+%d+%d", wwidth, wheight, x, y); 2603 snprintf(Info[n++], INFO_SIZE, "Border width = %d", bw); 2604 snprintf(Info[n++], INFO_SIZE, "Depth = %d", depth); 2605 if (HasSync) 2606 { 2607 int priority; 2608 (void)XSyncGetPriority(dpy, t->w, &priority); 2609 snprintf(Info[n++], INFO_SIZE, "Priority = %d", priority); 2610 } 2611 } 2612 2613 Info[n++][0] = '\0'; 2614 snprintf(Info[n++], INFO_SIZE, "Click to dismiss...."); 2615 2616 /* figure out the width and height of the info window */ 2617 height = n * (Scr->DefaultFont.height+2); 2618 width = 1; 2619 for (i = 0; i < n; i++) 2620 { 2621 twidth = MyFont_TextWidth(&Scr->DefaultFont, Info[i], 2622 strlen(Info[i])); 2623 if (twidth > width) 2624 width = twidth; 2625 } 2626 if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow); 2627 2628 width += 10; /* some padding */ 2629 if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild, &px, &py, 2630 &dummy, &dummy, &udummy)) { 2631 px -= (width / 2); 2632 py -= (height / 3); 2633 if (px + width + BW2 >= Scr->MyDisplayWidth) 2634 px = Scr->MyDisplayWidth - width - BW2; 2635 if (py + height + BW2 >= Scr->MyDisplayHeight) 2636 py = Scr->MyDisplayHeight - height - BW2; 2637 if (px < 0) px = 0; 2638 if (py < 0) py = 0; 2639 } else { 2640 px = py = 0; 2641 } 2642 XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height); 2643 XMapRaised(dpy, Scr->InfoWindow); 2644 InfoLines = n; 2645} 2646 2647 2648void 2649SetMapStateProp(TwmWindow *tmp_win, int state) 2650{ 2651 unsigned long data[2]; /* "suggested" by ICCCM version 1 */ 2652 2653 data[0] = (unsigned long) state; 2654 data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None : 2655 tmp_win->icon_w); 2656 2657 XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32, 2658 PropModeReplace, (unsigned char *) data, 2); 2659} 2660 2661 2662 2663Bool 2664GetWMState (Window w, int *statep, Window *iwp) 2665{ 2666 Atom actual_type; 2667 int actual_format; 2668 unsigned long nitems, bytesafter; 2669 unsigned char *prop_return = NULL; 2670 Bool retval = False; 2671 2672 if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE, 2673 &actual_type, &actual_format, &nitems, &bytesafter, 2674 &prop_return) != Success || !prop_return) 2675 return False; 2676 2677 if (nitems <= 2) { /* "suggested" by ICCCM version 1 */ 2678 unsigned long *datap = (unsigned long *) prop_return; 2679 *statep = (int) datap[0]; 2680 *iwp = (Window) datap[1]; 2681 retval = True; 2682 } 2683 2684 XFree (prop_return); 2685 return retval; 2686} 2687 2688 2689void 2690WarpToScreen (int n, int inc) 2691{ 2692 Window dumwin; 2693 int x, y, dumint; 2694 unsigned int dummask; 2695 ScreenInfo *newscr = NULL; 2696 2697 while (!newscr) { 2698 /* wrap around */ 2699 if (n < 0) 2700 n = NumScreens - 1; 2701 else if (n >= NumScreens) 2702 n = 0; 2703 2704 newscr = ScreenList[n]; 2705 if (!newscr) { /* make sure screen is managed */ 2706 if (inc) { /* walk around the list */ 2707 n += inc; 2708 continue; 2709 } 2710 fprintf (stderr, "%s: unable to warp to unmanaged screen %d\n", 2711 ProgramName, n); 2712 Bell(XkbBI_MinorError,0,None); 2713 return; 2714 } 2715 } 2716 2717 if (Scr->screen == n) return; /* already on that screen */ 2718 2719 PreviousScreen = Scr->screen; 2720 XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y, 2721 &dumint, &dumint, &dummask); 2722 2723 XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y); 2724 return; 2725} 2726 2727 2728 2729 2730/** 2731 * rotate our internal copy of WM_COLORMAP_WINDOWS 2732 */ 2733static void 2734BumpWindowColormap (TwmWindow *tmp, int inc) 2735{ 2736 int i, j, previously_installed; 2737 ColormapWindow **cwins; 2738 2739 if (!tmp) return; 2740 2741 if (inc && tmp->cmaps.number_cwins > 0) { 2742 cwins = malloc(sizeof(ColormapWindow *) * tmp->cmaps.number_cwins); 2743 if (cwins) { 2744 if ((previously_installed = 2745 /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps && 2746 tmp->cmaps.number_cwins))) { 2747 for (i = tmp->cmaps.number_cwins; i-- > 0; ) 2748 tmp->cmaps.cwins[i]->colormap->state = 0; 2749 } 2750 2751 for (i = 0; i < tmp->cmaps.number_cwins; i++) { 2752 j = i - inc; 2753 if (j >= tmp->cmaps.number_cwins) 2754 j -= tmp->cmaps.number_cwins; 2755 else if (j < 0) 2756 j += tmp->cmaps.number_cwins; 2757 cwins[j] = tmp->cmaps.cwins[i]; 2758 } 2759 2760 free(tmp->cmaps.cwins); 2761 2762 tmp->cmaps.cwins = cwins; 2763 2764 if (tmp->cmaps.number_cwins > 1) 2765 bzero (tmp->cmaps.scoreboard, 2766 ColormapsScoreboardLength(&tmp->cmaps)); 2767 2768 if (previously_installed) 2769 InstallWindowColormaps(PropertyNotify, (TwmWindow *) NULL); 2770 } 2771 } else 2772 FetchWmColormapWindows (tmp); 2773} 2774 2775 2776static void 2777HideIconManager (void) 2778{ 2779 SetMapStateProp (Scr->iconmgr.twm_win, WithdrawnState); 2780 XUnmapWindow(dpy, Scr->iconmgr.twm_win->frame); 2781 if (Scr->iconmgr.twm_win->icon_w) 2782 XUnmapWindow (dpy, Scr->iconmgr.twm_win->icon_w); 2783 Scr->iconmgr.twm_win->mapped = FALSE; 2784 Scr->iconmgr.twm_win->icon = TRUE; 2785} 2786 2787 2788 2789void 2790SetBorder (TwmWindow *tmp, Bool onoroff) 2791{ 2792 if (tmp->highlight) { 2793 if (onoroff) { 2794 XSetWindowBorder (dpy, tmp->frame, tmp->border); 2795 if (tmp->title_w) 2796 XSetWindowBorder (dpy, tmp->title_w, tmp->border); 2797 } else { 2798 XSetWindowBorderPixmap (dpy, tmp->frame, tmp->gray); 2799 if (tmp->title_w) 2800 XSetWindowBorderPixmap (dpy, tmp->title_w, tmp->gray); 2801 } 2802 } 2803} 2804 2805 2806static void 2807DestroyMenu (MenuRoot *menu) 2808{ 2809 MenuItem *item; 2810 2811 if (menu->w) { 2812 XDeleteContext (dpy, menu->w, MenuContext); 2813 XDeleteContext (dpy, menu->w, ScreenContext); 2814 if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow); 2815 XDestroyWindow(dpy, menu->w); 2816 } 2817 2818 for (item = menu->first; item; ) { 2819 MenuItem *tmp = item; 2820 item = item->next; 2821 free (tmp); 2822 } 2823} 2824 2825 2826 2827/* 2828 * warping routines 2829 */ 2830 2831static void 2832WarpAlongRing (XButtonEvent *ev, Bool forward) 2833{ 2834 TwmWindow *r, *head; 2835 2836 if (Scr->RingLeader) 2837 head = Scr->RingLeader; 2838 else if (!(head = Scr->Ring)) 2839 return; 2840 2841 if (forward) { 2842 for (r = head->ring.next; r != head; r = r->ring.next) { 2843 if (!r || r->mapped) break; 2844 } 2845 } else { 2846 for (r = head->ring.prev; r != head; r = r->ring.prev) { 2847 if (!r || r->mapped) break; 2848 } 2849 } 2850 2851 if (r && r != head) { 2852 TwmWindow *p = Scr->RingLeader, *t; 2853 XPointer context_data; 2854 2855 Scr->RingLeader = r; 2856 WarpToWindow (r); 2857 2858 if (XFindContext (dpy, ev->window, TwmContext, &context_data) == 0) 2859 t = (TwmWindow *) context_data; 2860 else 2861 t = NULL; 2862 2863 if (p && p->mapped && p == t) { 2864 p->ring.cursor_valid = True; 2865 p->ring.curs_x = ev->x_root - t->frame_x; 2866 p->ring.curs_y = ev->y_root - t->frame_y; 2867 if (p->ring.curs_x < -p->frame_bw || 2868 p->ring.curs_x >= p->frame_width + p->frame_bw || 2869 p->ring.curs_y < -p->frame_bw || 2870 p->ring.curs_y >= p->frame_height + p->frame_bw) { 2871 /* somehow out of window */ 2872 p->ring.curs_x = p->frame_width / 2; 2873 p->ring.curs_y = p->frame_height / 2; 2874 } 2875 } 2876 } 2877} 2878 2879 2880 2881static void 2882WarpToWindow (TwmWindow *t) 2883{ 2884 int x, y; 2885 2886 if (t->auto_raise || !Scr->NoRaiseWarp) AutoRaiseWindow (t); 2887 if (t->ring.cursor_valid) { 2888 x = t->ring.curs_x; 2889 y = t->ring.curs_y; 2890 } else { 2891 x = t->frame_width / 2; 2892 y = t->frame_height / 2; 2893 } 2894 XWarpPointer (dpy, None, t->frame, 0, 0, 0, 0, x, y); 2895} 2896 2897 2898 2899 2900/* 2901 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all 2902 * client messages will have the following form: 2903 * 2904 * event type ClientMessage 2905 * message type _XA_WM_PROTOCOLS 2906 * window tmp->w 2907 * format 32 2908 * data[0] message atom 2909 * data[1] time stamp 2910 */ 2911static void 2912send_clientmessage (Window w, Atom a, Time timestamp) 2913{ 2914 XClientMessageEvent ev; 2915 2916 ev.type = ClientMessage; 2917 ev.window = w; 2918 ev.message_type = _XA_WM_PROTOCOLS; 2919 ev.format = 32; 2920 ev.data.l[0] = a; 2921 ev.data.l[1] = timestamp; 2922 XSendEvent (dpy, w, False, 0L, (XEvent *) &ev); 2923} 2924 2925void 2926SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp) 2927{ 2928 send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp); 2929} 2930 2931void 2932SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp) 2933{ 2934 send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp); 2935} 2936 2937void 2938SendTakeFocusMessage (TwmWindow *tmp, Time timestamp) 2939{ 2940 send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp); 2941} 2942