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