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