Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  *       Copyright 1988 by Evans & Sutherland Computer Corporation,
      3  *                          Salt Lake City, Utah
      4  *  Portions Copyright 1989 by the Massachusetts Institute of Technology
      5  *                        Cambridge, Massachusetts
      6  *
      7  * Copyright 1992 Claude Lecommandeur.
      8  */
      9 
     10 /***********************************************************************
     11  *
     12  * $XConsortium: menus.c,v 1.186 91/07/17 13:58:00 dave Exp $
     13  *
     14  * twm menu code
     15  *
     16  * 17-Nov-87 Thomas E. LaStrange                File created
     17  *
     18  * Do the necessary modification to be integrated in ctwm.
     19  * Can no longer be used for the standard twm.
     20  *
     21  * 22-April-92 Claude Lecommandeur.
     22  *
     23  *
     24  ***********************************************************************/
     25 
     26 #include "ctwm.h"
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <strings.h>
     32 
     33 #include "add_window.h"
     34 #include "colormaps.h"
     35 #include "drawing.h"
     36 #include "events.h"
     37 #include "functions.h"
     38 #include "functions_defs.h"
     39 #include "gram.tab.h"
     40 #include "iconmgr.h"
     41 #include "icons_builtin.h"
     42 #include "icons.h"
     43 #include "image.h"
     44 #include "list.h"
     45 #include "occupation.h"
     46 #include "otp.h"
     47 #include "screen.h"
     48 #ifdef SOUNDS
     49 #  include "sound.h"
     50 #endif
     51 #include "util.h"
     52 #include "vscreen.h"
     53 #include "win_iconify.h"
     54 #include "win_resize.h"
     55 #include "win_utils.h"
     56 #include "workspace_manager.h"
     57 
     58 MenuRoot *ActiveMenu = NULL;            /* the active menu */
     59 MenuItem *ActiveItem = NULL;            /* the active menu item */
     60 bool menuFromFrameOrWindowOrTitlebar = false;
     61 char *CurrentSelectedWorkspace;
     62 
     63 /* Should probably move, since nothing in this file uses anymore */
     64 int AlternateKeymap;
     65 bool AlternateContext;
     66 
     67 int MenuDepth = 0;              /* number of menus up */
     68 static struct {
     69 	int x;
     70 	int y;
     71 } MenuOrigins[MAXMENUDEPTH];
     72 static bool addingdefaults = false;
     73 
     74 
     75 
     76 static void Paint3DEntry(MenuRoot *mr, MenuItem *mi, bool exposure);
     77 static void PaintNormalEntry(MenuRoot *mr, MenuItem *mi, bool exposure);
     78 static void DestroyMenu(MenuRoot *menu);
     79 
     80 
     81 #define SHADOWWIDTH 5                   /* in pixels */
     82 #define ENTRY_SPACING 4
     83 
     84 
     85 /***********************************************************************
     86  *
     87  *  Procedure:
     88  *      AddFuncKey - add a function key to the list
     89  *
     90  *  Inputs:
     91  *      name    - the name of the key
     92  *      cont    - the context to look for the key press in
     93  *      nmods   - modifier keys that need to be pressed
     94  *      func    - the function to perform
     95  *      win_name- the window name (if any)
     96  *      action  - the action string associated with the function (if any)
     97  *
     98  ***********************************************************************
     99  */
    100 
    101 bool
    102 AddFuncKey(char *name, int cont, int nmods, int func,
    103            MenuRoot *menu, char *win_name, char *action)
    104 {
    105 	FuncKey *tmp;
    106 	KeySym keysym = NoSymbol;
    107 	KeyCode keycode = 0;
    108 
    109 	/*
    110 	 * Don't let a 0 keycode go through, since that means AnyKey to the
    111 	 * XGrabKey call in GrabKeys().  Conditionalize on dpy to handle
    112 	 * special cases where we don't have a server to talk to.
    113 	 */
    114 	keysym = XStringToKeysym(name);
    115 	if(dpy) {
    116 		keycode = XKeysymToKeycode(dpy, keysym);
    117 	}
    118 	if(keysym == NoSymbol || (dpy && keycode == 0)) {
    119 		fprintf(stderr, "ignore %s key binding (%s)\n", name,
    120 		        keysym == NoSymbol
    121 		        ? "key symbol not found"
    122 		        : "key code not found");
    123 		return false;
    124 	}
    125 
    126 	/* see if there already is a key defined for this context */
    127 	for(tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
    128 		if(tmp->keysym == keysym &&
    129 		                tmp->cont == cont &&
    130 		                tmp->mods == nmods) {
    131 			break;
    132 		}
    133 	}
    134 
    135 	if(tmp == NULL) {
    136 		tmp = malloc(sizeof(FuncKey));
    137 		tmp->next = Scr->FuncKeyRoot.next;
    138 		Scr->FuncKeyRoot.next = tmp;
    139 	}
    140 
    141 	tmp->name = name;
    142 	tmp->keysym = keysym;
    143 	tmp->keycode = keycode;
    144 	tmp->cont = cont;
    145 	tmp->mods = nmods;
    146 	tmp->func = func;
    147 	tmp->menu = menu;
    148 	tmp->win_name = win_name;
    149 	tmp->action = action;
    150 
    151 	return true;
    152 }
    153 
    154 /***********************************************************************
    155  *
    156  *  Procedure:
    157  *      AddFuncButton - add a function button to the list
    158  *
    159  *  Inputs:
    160  *      num     - the num of the button
    161  *      cont    - the context to look for the key press in
    162  *      nmods   - modifier keys that need to be pressed
    163  *      func    - the function to perform
    164  *      menu    - the menu (if any)
    165  *      item    - the menu item (if any)
    166  *
    167  ***********************************************************************
    168  */
    169 
    170 void
    171 AddFuncButton(int num, int cont, int nmods, int func,
    172               MenuRoot *menu, MenuItem *item)
    173 {
    174 	FuncButton *tmp;
    175 
    176 	/* Find existing def for this button/context/modifier if any */
    177 	for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
    178 		if((tmp->num == num) && (tmp->cont == cont) && (tmp->mods == nmods)) {
    179 			break;
    180 		}
    181 	}
    182 
    183 	/*
    184 	 * If it's already set, and we're addingdefault (i.e., called from
    185 	 * AddDefaultFuncButtons()), just return.  This lets us cram on
    186 	 * fallback mappings, without worrying about overriding user choices.
    187 	 */
    188 	if(tmp && addingdefaults) {
    189 		return;
    190 	}
    191 
    192 	/* No mapping yet; create a shell */
    193 	if(tmp == NULL) {
    194 		tmp = malloc(sizeof(FuncButton));
    195 		tmp->next = Scr->FuncButtonRoot.next;
    196 		Scr->FuncButtonRoot.next = tmp;
    197 	}
    198 
    199 	/* Set the new details */
    200 	tmp->num  = num;
    201 	tmp->cont = cont;
    202 	tmp->mods = nmods;
    203 	tmp->func = func;
    204 	tmp->menu = menu;
    205 	tmp->item = item;
    206 
    207 	return;
    208 }
    209 
    210 
    211 /*
    212  * AddDefaultFuncButtons - attach default bindings so that naive users
    213  * don't get messed up if they provide a minimal twmrc.
    214  *
    215  * This used to be in add_window.c, and maybe fits better in
    216  * decorations_init.c (only place it's called) now, but is currently here
    217  * so addingdefaults is in scope.
    218  *
    219  * XXX Probably better to adjust things so we can do that job _without_
    220  * the magic global var...
    221  */
    222 void
    223 AddDefaultFuncButtons(void)
    224 {
    225 	addingdefaults = true;
    226 
    227 #define SETDEF(btn, ctx, func) AddFuncButton(btn, ctx, 0, func, NULL, NULL)
    228 	SETDEF(Button1, C_TITLE,    F_MOVE);
    229 	SETDEF(Button1, C_ICON,     F_ICONIFY);
    230 	SETDEF(Button1, C_ICONMGR,  F_ICONIFY);
    231 
    232 	SETDEF(Button2, C_TITLE,    F_RAISELOWER);
    233 	SETDEF(Button2, C_ICON,     F_ICONIFY);
    234 	SETDEF(Button2, C_ICONMGR,  F_ICONIFY);
    235 #undef SETDEF
    236 
    237 	addingdefaults = false;
    238 }
    239 
    240 
    241 void
    242 PaintEntry(MenuRoot *mr, MenuItem *mi, bool exposure)
    243 {
    244 	if(Scr->use3Dmenus) {
    245 		Paint3DEntry(mr, mi, exposure);
    246 	}
    247 	else {
    248 		PaintNormalEntry(mr, mi, exposure);
    249 	}
    250 	if(mi->state) {
    251 		mr->lastactive = mi;
    252 	}
    253 }
    254 
    255 static void
    256 Paint3DEntry(MenuRoot *mr, MenuItem *mi, bool exposure)
    257 {
    258 	int y_offset;
    259 	int text_y;
    260 	GC gc;
    261 	XRectangle ink_rect, logical_rect;
    262 	XmbTextExtents(Scr->MenuFont.font_set, mi->item, mi->strlen,
    263 	               &ink_rect, &logical_rect);
    264 
    265 	y_offset = mi->item_num * Scr->EntryHeight + Scr->MenuShadowDepth;
    266 	text_y = y_offset + (Scr->EntryHeight - logical_rect.height) / 2
    267 	         - logical_rect.y;
    268 
    269 	if(mi->func != F_TITLE) {
    270 		int x, y;
    271 
    272 		gc = Scr->NormalGC;
    273 		if(mi->state) {
    274 			Draw3DBorder(mr->w, Scr->MenuShadowDepth, y_offset,
    275 			             mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
    276 			             mi->highlight, off, true, false);
    277 			FB(mi->highlight.fore, mi->highlight.back);
    278 			XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc,
    279 			                   mi->x + Scr->MenuShadowDepth, text_y, mi->item, mi->strlen);
    280 		}
    281 		else {
    282 			if(mi->user_colors || !exposure) {
    283 				XSetForeground(dpy, gc, mi->normal.back);
    284 				XFillRectangle(dpy, mr->w, gc,
    285 				               Scr->MenuShadowDepth, y_offset,
    286 				               mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight);
    287 				FB(mi->normal.fore, mi->normal.back);
    288 			}
    289 			else {
    290 				gc = Scr->MenuGC;
    291 			}
    292 			XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc,
    293 			                   mi->x + Scr->MenuShadowDepth, text_y,
    294 			                   mi->item, mi->strlen);
    295 			if(mi->separated) {
    296 				FB(Scr->MenuC.shadd, Scr->MenuC.shadc);
    297 				XDrawLine(dpy, mr->w, Scr->NormalGC,
    298 				          Scr->MenuShadowDepth,
    299 				          y_offset + Scr->EntryHeight - 2,
    300 				          mr->width - Scr->MenuShadowDepth,
    301 				          y_offset + Scr->EntryHeight - 2);
    302 				FB(Scr->MenuC.shadc, Scr->MenuC.shadd);
    303 				XDrawLine(dpy, mr->w, Scr->NormalGC,
    304 				          Scr->MenuShadowDepth,
    305 				          y_offset + Scr->EntryHeight - 1,
    306 				          mr->width - Scr->MenuShadowDepth,
    307 				          y_offset + Scr->EntryHeight - 1);
    308 			}
    309 		}
    310 
    311 		if(mi->func == F_MENU) {
    312 			/* create the pull right pixmap if needed */
    313 			if(Scr->pullPm == None) {
    314 				Scr->pullPm = Create3DMenuIcon(Scr->EntryHeight - ENTRY_SPACING, &Scr->pullW,
    315 				                               &Scr->pullH, Scr->MenuC);
    316 			}
    317 			x = mr->width - Scr->pullW - Scr->MenuShadowDepth - 2;
    318 			y = y_offset + ((Scr->EntryHeight - ENTRY_SPACING - Scr->pullH) / 2) + 2;
    319 			XCopyArea(dpy, Scr->pullPm, mr->w, gc, 0, 0, Scr->pullW, Scr->pullH, x, y);
    320 		}
    321 	}
    322 	else {
    323 		Draw3DBorder(mr->w, Scr->MenuShadowDepth, y_offset,
    324 		             mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
    325 		             mi->normal, off, true, false);
    326 		FB(mi->normal.fore, mi->normal.back);
    327 		XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
    328 		                   mi->x + 2, text_y, mi->item, mi->strlen);
    329 	}
    330 }
    331 
    332 
    333 static void
    334 PaintNormalEntry(MenuRoot *mr, MenuItem *mi, bool exposure)
    335 {
    336 	int y_offset;
    337 	int text_y;
    338 	GC gc;
    339 	XRectangle ink_rect, logical_rect;
    340 	XmbTextExtents(Scr->MenuFont.font_set, mi->item, mi->strlen,
    341 	               &ink_rect, &logical_rect);
    342 
    343 	y_offset = mi->item_num * Scr->EntryHeight;
    344 	text_y = y_offset + (Scr->EntryHeight - logical_rect.height) / 2
    345 	         - logical_rect.y;
    346 
    347 	if(mi->func != F_TITLE) {
    348 		int x, y;
    349 
    350 		if(mi->state) {
    351 			XSetForeground(dpy, Scr->NormalGC, mi->highlight.back);
    352 
    353 			XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
    354 			               mr->width, Scr->EntryHeight);
    355 			FB(mi->highlight.fore, mi->highlight.back);
    356 			XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
    357 			              mi->x, text_y, mi->item, mi->strlen);
    358 
    359 			gc = Scr->NormalGC;
    360 		}
    361 		else {
    362 			if(mi->user_colors || !exposure) {
    363 				XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
    364 
    365 				XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
    366 				               mr->width, Scr->EntryHeight);
    367 
    368 				FB(mi->normal.fore, mi->normal.back);
    369 				gc = Scr->NormalGC;
    370 			}
    371 			else {
    372 				gc = Scr->MenuGC;
    373 			}
    374 			XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, gc, mi->x,
    375 			              text_y, mi->item, mi->strlen);
    376 			if(mi->separated)
    377 				XDrawLine(dpy, mr->w, gc, 0, y_offset + Scr->EntryHeight - 1,
    378 				          mr->width, y_offset + Scr->EntryHeight - 1);
    379 		}
    380 
    381 		if(mi->func == F_MENU) {
    382 			/* create the pull right pixmap if needed */
    383 			if(Scr->pullPm == None) {
    384 				Scr->pullPm = CreateMenuIcon(Scr->MenuFont.height,
    385 				                             &Scr->pullW, &Scr->pullH);
    386 			}
    387 			x = mr->width - Scr->pullW - 5;
    388 			y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2);
    389 			XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0,
    390 			           Scr->pullW, Scr->pullH, x, y, 1);
    391 		}
    392 	}
    393 	else {
    394 		int y;
    395 
    396 		XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
    397 
    398 		/* fill the rectangle with the title background color */
    399 		XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
    400 		               mr->width, Scr->EntryHeight);
    401 
    402 		{
    403 			XSetForeground(dpy, Scr->NormalGC, mi->normal.fore);
    404 			/* now draw the dividing lines */
    405 			if(y_offset)
    406 				XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y_offset,
    407 				          mr->width, y_offset);
    408 			y = ((mi->item_num + 1) * Scr->EntryHeight) - 1;
    409 			XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y);
    410 		}
    411 
    412 		FB(mi->normal.fore, mi->normal.back);
    413 		/* finally render the title */
    414 		XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, mi->x,
    415 		              text_y, mi->item, mi->strlen);
    416 	}
    417 }
    418 
    419 void PaintMenu(MenuRoot *mr, XEvent *e)
    420 {
    421 	MenuItem *mi;
    422 
    423 	if(Scr->use3Dmenus) {
    424 		Draw3DBorder(mr->w, 0, 0, mr->width, mr->height,
    425 		             Scr->MenuShadowDepth, Scr->MenuC, off, false, false);
    426 	}
    427 	for(mi = mr->first; mi != NULL; mi = mi->next) {
    428 		int y_offset = mi->item_num * Scr->EntryHeight;
    429 
    430 		/* be smart about handling the expose, redraw only the entries
    431 		 * that we need to
    432 		 */
    433 		if(e->xexpose.y <= (y_offset + Scr->EntryHeight) &&
    434 		                (e->xexpose.y + e->xexpose.height) >= y_offset) {
    435 			PaintEntry(mr, mi, true);
    436 		}
    437 	}
    438 	XSync(dpy, 0);
    439 }
    440 
    441 
    442 void MakeWorkspacesMenu(void)
    443 {
    444 	static char **actions = NULL;
    445 	WorkSpace *wlist;
    446 	char **act;
    447 
    448 	if(! Scr->Workspaces) {
    449 		return;
    450 	}
    451 	AddToMenu(Scr->Workspaces, "TWM Workspaces", NULL, NULL, F_TITLE, NULL,
    452 	          NULL);
    453 	if(! actions) {
    454 		int count = 0;
    455 
    456 		for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL;
    457 		                wlist = wlist->next) {
    458 			count++;
    459 		}
    460 		count++;
    461 		actions = calloc(count, sizeof(char *));
    462 		act = actions;
    463 		for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL;
    464 		                wlist = wlist->next) {
    465 			asprintf(act, "WGOTO : %s", wlist->name);
    466 			act++;
    467 		}
    468 		*act = NULL;
    469 	}
    470 	act = actions;
    471 	for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL;
    472 	                wlist = wlist->next) {
    473 		AddToMenu(Scr->Workspaces, wlist->name, *act, Scr->Windows, F_MENU, NULL, NULL);
    474 		act++;
    475 	}
    476 	Scr->Workspaces->pinned = false;
    477 	MakeMenu(Scr->Workspaces);
    478 }
    479 
    480 static bool fromMenu;
    481 bool
    482 cur_fromMenu(void)
    483 {
    484 	return fromMenu;
    485 }
    486 
    487 void UpdateMenu(void)
    488 {
    489 	MenuItem *mi;
    490 	int i, x, y, x_root, y_root, entry;
    491 	bool done;
    492 	MenuItem *badItem = NULL;
    493 
    494 	fromMenu = true;
    495 
    496 	while(1) {
    497 		/* block until there is an event */
    498 		if(!menuFromFrameOrWindowOrTitlebar) {
    499 			XMaskEvent(dpy,
    500 			           ButtonPressMask | ButtonReleaseMask |
    501 			           KeyPressMask | KeyReleaseMask |
    502 			           EnterWindowMask | ExposureMask |
    503 			           VisibilityChangeMask | LeaveWindowMask |
    504 			           ButtonMotionMask, &Event);
    505 		}
    506 		if(Event.type == MotionNotify) {
    507 			/* discard any extra motion events before a release */
    508 			while(XCheckMaskEvent(dpy,
    509 			                      ButtonMotionMask | ButtonReleaseMask, &Event))
    510 				if(Event.type == ButtonRelease) {
    511 					break;
    512 				}
    513 		}
    514 
    515 		if(!DispatchEvent()) {
    516 			continue;
    517 		}
    518 
    519 		if((! ActiveMenu) || Cancel) {
    520 			menuFromFrameOrWindowOrTitlebar = false;
    521 			fromMenu = false;
    522 			return;
    523 		}
    524 
    525 		if(Event.type != MotionNotify) {
    526 			continue;
    527 		}
    528 
    529 		done = false;
    530 		XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild,
    531 		              &x_root, &y_root, &x, &y, &JunkMask);
    532 
    533 		/* if we haven't received the enter notify yet, wait */
    534 		if(!ActiveMenu->entered) {
    535 			continue;
    536 		}
    537 
    538 		XFindContext(dpy, ActiveMenu->w, ScreenContext, (XPointer *)&Scr);
    539 
    540 		if(x < 0 || y < 0 ||
    541 		                x >= ActiveMenu->width || y >= ActiveMenu->height) {
    542 			if(ActiveItem && ActiveItem->func != F_TITLE) {
    543 				ActiveItem->state = false;
    544 				PaintEntry(ActiveMenu, ActiveItem, false);
    545 			}
    546 			ActiveItem = NULL;
    547 			continue;
    548 		}
    549 
    550 		/* look for the entry that the mouse is in */
    551 		entry = y / Scr->EntryHeight;
    552 		for(i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) {
    553 			if(i == entry) {
    554 				break;
    555 			}
    556 		}
    557 
    558 		/* if there is an active item, we might have to turn it off */
    559 		if(ActiveItem) {
    560 			/* is the active item the one we are on ? */
    561 			if(ActiveItem->item_num == entry && ActiveItem->state) {
    562 				done = true;
    563 			}
    564 
    565 			/* if we weren't on the active entry, let's turn the old
    566 			 * active one off
    567 			 */
    568 			if(!done && ActiveItem->func != F_TITLE) {
    569 				ActiveItem->state = false;
    570 				PaintEntry(ActiveMenu, ActiveItem, false);
    571 			}
    572 		}
    573 
    574 		/* if we weren't on the active item, change the active item and turn
    575 		 * it on
    576 		 */
    577 		if(!done) {
    578 			ActiveItem = mi;
    579 			if(ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state) {
    580 				ActiveItem->state = true;
    581 				PaintEntry(ActiveMenu, ActiveItem, false);
    582 			}
    583 		}
    584 
    585 		/* now check to see if we were over the arrow of a pull right entry */
    586 		if(ActiveItem && ActiveItem->func == F_MENU &&
    587 		                ((ActiveMenu->width - x) < (ActiveMenu->width / 3))) {
    588 			MenuRoot *save = ActiveMenu;
    589 			int savex = MenuOrigins[MenuDepth - 1].x;
    590 			int savey = MenuOrigins[MenuDepth - 1].y;
    591 
    592 			if(MenuDepth < MAXMENUDEPTH) {
    593 				if(ActiveMenu == Scr->Workspaces) {
    594 					CurrentSelectedWorkspace = ActiveItem->item;
    595 				}
    596 				PopUpMenu(ActiveItem->sub,
    597 				          (savex + (((2 * ActiveMenu->width) / 3) - 1)),
    598 				          (savey + ActiveItem->item_num * Scr->EntryHeight)
    599 				          /*(savey + ActiveItem->item_num * Scr->EntryHeight +
    600 				           (Scr->EntryHeight >> 1))*/, False);
    601 				CurrentSelectedWorkspace = NULL;
    602 			}
    603 			else if(!badItem) {
    604 				XBell(dpy, 0);
    605 				badItem = ActiveItem;
    606 			}
    607 
    608 			/* if the menu did get popped up, unhighlight the active item */
    609 			if(save != ActiveMenu && ActiveItem->state) {
    610 				ActiveItem->state = false;
    611 				PaintEntry(save, ActiveItem, false);
    612 				ActiveItem = NULL;
    613 			}
    614 		}
    615 		if(badItem != ActiveItem) {
    616 			badItem = NULL;
    617 		}
    618 		XFlush(dpy);
    619 	}
    620 }
    621 
    622 
    623 /***********************************************************************
    624  *
    625  *  Procedure:
    626  *      NewMenuRoot - create a new menu root
    627  *
    628  *  Returned Value:
    629  *      (MenuRoot *)
    630  *
    631  *  Inputs:
    632  *      name    - the name of the menu root
    633  *
    634  ***********************************************************************
    635  */
    636 
    637 MenuRoot *NewMenuRoot(char *name)
    638 {
    639 	MenuRoot *tmp;
    640 
    641 #define UNUSED_PIXEL ((unsigned long) (~0))     /* more than 24 bits */
    642 
    643 	tmp = malloc(sizeof(MenuRoot));
    644 	tmp->highlight.fore = UNUSED_PIXEL;
    645 	tmp->highlight.back = UNUSED_PIXEL;
    646 	tmp->name = name;
    647 	tmp->prev = NULL;
    648 	tmp->first = NULL;
    649 	tmp->last = NULL;
    650 	tmp->defaultitem = NULL;
    651 	tmp->items = 0;
    652 	tmp->width = 0;
    653 	tmp->mapped = MRM_NEVER;
    654 	tmp->pull = false;
    655 	tmp->w = None;
    656 	tmp->shadow = None;
    657 	tmp->real_menu = false;
    658 
    659 	if(Scr->MenuList == NULL) {
    660 		Scr->MenuList = tmp;
    661 		Scr->MenuList->next = NULL;
    662 	}
    663 
    664 	if(Scr->LastMenu == NULL) {
    665 		Scr->LastMenu = tmp;
    666 		Scr->LastMenu->next = NULL;
    667 	}
    668 	else {
    669 		Scr->LastMenu->next = tmp;
    670 		Scr->LastMenu = tmp;
    671 		Scr->LastMenu->next = NULL;
    672 	}
    673 
    674 	if(strcmp(name, TWM_WINDOWS) == 0) {
    675 		Scr->Windows = tmp;
    676 	}
    677 
    678 	if(strcmp(name, TWM_ICONS) == 0) {
    679 		Scr->Icons = tmp;
    680 	}
    681 
    682 	if(strcmp(name, TWM_WORKSPACES) == 0) {
    683 		Scr->Workspaces = tmp;
    684 		if(!Scr->Windows) {
    685 			NewMenuRoot(TWM_WINDOWS);
    686 		}
    687 	}
    688 	if(strcmp(name, TWM_ALLWINDOWS) == 0) {
    689 		Scr->AllWindows = tmp;
    690 	}
    691 
    692 	/* Added by dl 2004 */
    693 	if(strcmp(name, TWM_ALLICONS) == 0) {
    694 		Scr->AllIcons = tmp;
    695 	}
    696 
    697 	/* Added by Dan Lilliehorn (dl (at) dl.nu) 2000-02-29       */
    698 	if(strcmp(name, TWM_KEYS) == 0) {
    699 		Scr->Keys = tmp;
    700 	}
    701 
    702 	if(strcmp(name, TWM_VISIBLE) == 0) {
    703 		Scr->Visible = tmp;
    704 	}
    705 
    706 	/* End addition */
    707 
    708 	return (tmp);
    709 }
    710 
    711 
    712 /***********************************************************************
    713  *
    714  *  Procedure:
    715  *      AddToMenu - add an item to a root menu
    716  *
    717  *  Returned Value:
    718  *      (MenuItem *)
    719  *
    720  *  Inputs:
    721  *      menu    - pointer to the root menu to add the item
    722  *      item    - the text to appear in the menu
    723  *      action  - the string to possibly execute
    724  *      sub     - the menu root if it is a pull-right entry
    725  *      func    - the numeric function
    726  *      fore    - foreground color string
    727  *      back    - background color string
    728  *
    729  ***********************************************************************
    730  */
    731 
    732 MenuItem *AddToMenu(MenuRoot *menu, char *item, char *action,
    733                     MenuRoot *sub, int func, char *fore, char *back)
    734 {
    735 	MenuItem *tmp;
    736 	int width;
    737 	char *itemname;
    738 	XRectangle ink_rect;
    739 	XRectangle logical_rect;
    740 
    741 #ifdef DEBUG_MENUS
    742 	fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n",
    743 	        item, action, sub, func);
    744 #endif
    745 
    746 	tmp = malloc(sizeof(MenuItem));
    747 	tmp->root = menu;
    748 
    749 	if(menu->first == NULL) {
    750 		menu->first = tmp;
    751 		tmp->prev = NULL;
    752 	}
    753 	else {
    754 		menu->last->next = tmp;
    755 		tmp->prev = menu->last;
    756 	}
    757 	menu->last = tmp;
    758 
    759 	if((menu == Scr->Workspaces) ||
    760 	                (menu == Scr->Windows) ||
    761 	                (menu == Scr->Icons) ||
    762 	                (menu == Scr->AllWindows) ||
    763 
    764 	                /* Added by dl 2004 */
    765 	                (menu == Scr->AllIcons) ||
    766 
    767 	                /* Added by Dan Lillehorn (dl (at) dl.nu) 2000-02-29 */
    768 	                (menu == Scr->Keys) ||
    769 	                (menu == Scr->Visible)) {
    770 
    771 		itemname = item;
    772 	}
    773 	else if(*item == '*') {
    774 		itemname = item + 1;
    775 		menu->defaultitem = tmp;
    776 	}
    777 	else {
    778 		itemname = item;
    779 	}
    780 
    781 	tmp->item = itemname;
    782 	tmp->strlen = strlen(itemname);
    783 	tmp->action = action;
    784 	tmp->next = NULL;
    785 	tmp->sub = NULL;
    786 	tmp->state = false;
    787 	tmp->func = func;
    788 	tmp->separated = false;
    789 
    790 	if(!Scr->HaveFonts) {
    791 		CreateFonts(Scr);
    792 	}
    793 
    794 	if(dpy) {
    795 		XmbTextExtents(Scr->MenuFont.font_set,
    796 		               itemname, tmp->strlen,
    797 		               &ink_rect, &logical_rect);
    798 		width = logical_rect.width;
    799 	}
    800 	else {
    801 		// Fake for non-dpy cases
    802 		width = 25;
    803 	}
    804 
    805 	if(width <= 0) {
    806 		width = 1;
    807 	}
    808 	if(width > menu->width) {
    809 		menu->width = width;
    810 	}
    811 
    812 	tmp->user_colors = false;
    813 	if(Scr->Monochrome == COLOR && fore != NULL) {
    814 		bool save;
    815 
    816 		save = Scr->FirstTime;
    817 		Scr->FirstTime = true;
    818 		GetColor(COLOR, &tmp->normal.fore, fore);
    819 		GetColor(COLOR, &tmp->normal.back, back);
    820 		if(Scr->use3Dmenus && !Scr->BeNiceToColormap) {
    821 			GetShadeColors(&tmp->normal);
    822 		}
    823 		Scr->FirstTime = save;
    824 		tmp->user_colors = true;
    825 	}
    826 	if(sub != NULL) {
    827 		tmp->sub = sub;
    828 		menu->pull = true;
    829 	}
    830 	tmp->item_num = menu->items++;
    831 
    832 	return (tmp);
    833 }
    834 
    835 
    836 void MakeMenus(void)
    837 {
    838 	MenuRoot *mr;
    839 
    840 	for(mr = Scr->MenuList; mr != NULL; mr = mr->next) {
    841 		if(mr->real_menu == false) {
    842 			continue;
    843 		}
    844 
    845 		mr->pinned = false;
    846 		MakeMenu(mr);
    847 	}
    848 }
    849 
    850 
    851 void MakeMenu(MenuRoot *mr)
    852 {
    853 	MenuItem *start, *tmp;
    854 	XColor f1, f2, f3;
    855 	XColor b1, b2, b3;
    856 	XColor save_fore, save_back;
    857 	int fred, fgreen, fblue;
    858 	int bred, bgreen, bblue;
    859 	int width, borderwidth;
    860 	unsigned long valuemask;
    861 	XSetWindowAttributes attributes;
    862 	Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
    863 	XRectangle ink_rect;
    864 	XRectangle logical_rect;
    865 
    866 	Scr->EntryHeight = Scr->MenuFont.height + 4;
    867 
    868 	/* lets first size the window accordingly */
    869 	if(mr->mapped == MRM_NEVER) {
    870 		int max_entry_height = 0;
    871 
    872 		if(mr->pull == true) {
    873 			mr->width += 16 + 10;
    874 		}
    875 		width = mr->width + 10;
    876 		for(MenuItem *cur = mr->first; cur != NULL; cur = cur->next) {
    877 			XmbTextExtents(Scr->MenuFont.font_set, cur->item, cur->strlen,
    878 			               &ink_rect, &logical_rect);
    879 			max_entry_height = MAX(max_entry_height, logical_rect.height);
    880 
    881 			if(cur->func != F_TITLE) {
    882 				cur->x = 5;
    883 			}
    884 			else {
    885 				cur->x = width - logical_rect.width;
    886 				cur->x /= 2;
    887 			}
    888 		}
    889 		Scr->EntryHeight = max_entry_height + ENTRY_SPACING;
    890 		mr->height = mr->items * Scr->EntryHeight;
    891 		mr->width += 10;
    892 		if(Scr->use3Dmenus) {
    893 			mr->width  += 2 * Scr->MenuShadowDepth;
    894 			mr->height += 2 * Scr->MenuShadowDepth;
    895 		}
    896 		if(Scr->Shadow && ! mr->pinned) {
    897 			/*
    898 			 * Make sure that you don't draw into the shadow window or else
    899 			 * the background bits there will get saved
    900 			 */
    901 			valuemask = (CWBackPixel | CWBorderPixel);
    902 			attributes.background_pixel = Scr->MenuShadowColor;
    903 			attributes.border_pixel = Scr->MenuShadowColor;
    904 			if(Scr->SaveUnder) {
    905 				valuemask |= CWSaveUnder;
    906 				attributes.save_under = True;
    907 			}
    908 			mr->shadow = XCreateWindow(dpy, Scr->Root, 0, 0,
    909 			                           mr->width,
    910 			                           mr->height,
    911 			                           0,
    912 			                           CopyFromParent,
    913 			                           CopyFromParent,
    914 			                           CopyFromParent,
    915 			                           valuemask, &attributes);
    916 		}
    917 
    918 		valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
    919 		attributes.background_pixel = Scr->MenuC.back;
    920 		attributes.border_pixel = Scr->MenuC.fore;
    921 		if(mr->pinned) {
    922 			attributes.event_mask = (ExposureMask | EnterWindowMask
    923 			                         | LeaveWindowMask | ButtonPressMask
    924 			                         | ButtonReleaseMask | PointerMotionMask
    925 			                         | ButtonMotionMask
    926 			                        );
    927 			attributes.cursor = Scr->MenuCursor;
    928 			valuemask |= CWCursor;
    929 		}
    930 		else {
    931 			attributes.event_mask = (ExposureMask | EnterWindowMask);
    932 		}
    933 
    934 		if(Scr->SaveUnder && ! mr->pinned) {
    935 			valuemask |= CWSaveUnder;
    936 			attributes.save_under = True;
    937 		}
    938 		if(Scr->BackingStore) {
    939 			valuemask |= CWBackingStore;
    940 			attributes.backing_store = Always;
    941 		}
    942 		borderwidth = Scr->use3Dmenus ? 0 : 1;
    943 		mr->w = XCreateWindow(dpy, Scr->Root, 0, 0, mr->width,
    944 		                      mr->height, borderwidth,
    945 		                      CopyFromParent, CopyFromParent,
    946 		                      CopyFromParent,
    947 		                      valuemask, &attributes);
    948 
    949 
    950 		XSaveContext(dpy, mr->w, MenuContext, (XPointer)mr);
    951 		XSaveContext(dpy, mr->w, ScreenContext, (XPointer)Scr);
    952 
    953 		mr->mapped = MRM_UNMAPPED;
    954 	}
    955 
    956 	if(Scr->use3Dmenus && (Scr->Monochrome == COLOR)
    957 	                && (mr->highlight.back == UNUSED_PIXEL)) {
    958 		XColor xcol;
    959 		char colname [32];
    960 		bool save;
    961 
    962 		xcol.pixel = Scr->MenuC.back;
    963 		XQueryColor(dpy, cmap, &xcol);
    964 		sprintf(colname, "#%04x%04x%04x",
    965 		        5 * ((int)xcol.red   / 6),
    966 		        5 * ((int)xcol.green / 6),
    967 		        5 * ((int)xcol.blue  / 6));
    968 		save = Scr->FirstTime;
    969 		Scr->FirstTime = true;
    970 		GetColor(Scr->Monochrome, &mr->highlight.back, colname);
    971 		Scr->FirstTime = save;
    972 	}
    973 
    974 	if(Scr->use3Dmenus && (Scr->Monochrome == COLOR)
    975 	                && (mr->highlight.fore == UNUSED_PIXEL)) {
    976 		XColor xcol;
    977 		char colname [32];
    978 		bool save;
    979 
    980 		xcol.pixel = Scr->MenuC.fore;
    981 		XQueryColor(dpy, cmap, &xcol);
    982 		sprintf(colname, "#%04x%04x%04x",
    983 		        5 * ((int)xcol.red   / 6),
    984 		        5 * ((int)xcol.green / 6),
    985 		        5 * ((int)xcol.blue  / 6));
    986 		save = Scr->FirstTime;
    987 		Scr->FirstTime = true;
    988 		GetColor(Scr->Monochrome, &mr->highlight.fore, colname);
    989 		Scr->FirstTime = save;
    990 	}
    991 	if(Scr->use3Dmenus && !Scr->BeNiceToColormap) {
    992 		GetShadeColors(&mr->highlight);
    993 	}
    994 
    995 	/* get the default colors into the menus */
    996 	for(tmp = mr->first; tmp != NULL; tmp = tmp->next) {
    997 		if(!tmp->user_colors) {
    998 			if(tmp->func != F_TITLE) {
    999 				tmp->normal.fore = Scr->MenuC.fore;
   1000 				tmp->normal.back = Scr->MenuC.back;
   1001 			}
   1002 			else {
   1003 				tmp->normal.fore = Scr->MenuTitleC.fore;
   1004 				tmp->normal.back = Scr->MenuTitleC.back;
   1005 			}
   1006 		}
   1007 
   1008 		if(mr->highlight.fore != UNUSED_PIXEL) {
   1009 			tmp->highlight.fore = mr->highlight.fore;
   1010 			tmp->highlight.back = mr->highlight.back;
   1011 		}
   1012 		else {
   1013 			tmp->highlight.fore = tmp->normal.back;
   1014 			tmp->highlight.back = tmp->normal.fore;
   1015 		}
   1016 		if(Scr->use3Dmenus && !Scr->BeNiceToColormap) {
   1017 			if(tmp->func != F_TITLE) {
   1018 				GetShadeColors(&tmp->highlight);
   1019 			}
   1020 			else {
   1021 				GetShadeColors(&tmp->normal);
   1022 			}
   1023 		}
   1024 	}
   1025 	mr->pmenu = NULL;
   1026 
   1027 	if(Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors) {
   1028 		return;
   1029 	}
   1030 
   1031 	// Do InterpolateMenuColors magic
   1032 	start = mr->first;
   1033 	while(1) {
   1034 		for(; start != NULL; start = start->next) {
   1035 			if(start->user_colors) {
   1036 				break;
   1037 			}
   1038 		}
   1039 		if(start == NULL) {
   1040 			break;
   1041 		}
   1042 
   1043 		MenuItem *end;
   1044 		for(end = start->next; end != NULL; end = end->next) {
   1045 			if(end->user_colors) {
   1046 				break;
   1047 			}
   1048 		}
   1049 		if(end == NULL) {
   1050 			break;
   1051 		}
   1052 
   1053 		/* we have a start and end to interpolate between */
   1054 		int num = end->item_num - start->item_num;
   1055 
   1056 		f1.pixel = start->normal.fore;
   1057 		XQueryColor(dpy, cmap, &f1);
   1058 		f2.pixel = end->normal.fore;
   1059 		XQueryColor(dpy, cmap, &f2);
   1060 
   1061 		b1.pixel = start->normal.back;
   1062 		XQueryColor(dpy, cmap, &b1);
   1063 		b2.pixel = end->normal.back;
   1064 		XQueryColor(dpy, cmap, &b2);
   1065 
   1066 		fred = ((int)f2.red - (int)f1.red) / num;
   1067 		fgreen = ((int)f2.green - (int)f1.green) / num;
   1068 		fblue = ((int)f2.blue - (int)f1.blue) / num;
   1069 
   1070 		bred = ((int)b2.red - (int)b1.red) / num;
   1071 		bgreen = ((int)b2.green - (int)b1.green) / num;
   1072 		bblue = ((int)b2.blue - (int)b1.blue) / num;
   1073 
   1074 		f3 = f1;
   1075 		f3.flags = DoRed | DoGreen | DoBlue;
   1076 
   1077 		b3 = b1;
   1078 		b3.flags = DoRed | DoGreen | DoBlue;
   1079 
   1080 		start->highlight.back = start->normal.fore;
   1081 		start->highlight.fore = start->normal.back;
   1082 		num -= 1;
   1083 		int i = 0;
   1084 		MenuItem *cur = start->next;
   1085 		// XXX Should be impossible to run out of cur's before num's,
   1086 		// unless the item_num's are wrong (which would break other
   1087 		// stuff), but add condition to quiet static analysis.
   1088 		for(; cur != NULL && i < num ; i++, cur = cur->next) {
   1089 			f3.red += fred;
   1090 			f3.green += fgreen;
   1091 			f3.blue += fblue;
   1092 			save_fore = f3;
   1093 
   1094 			b3.red += bred;
   1095 			b3.green += bgreen;
   1096 			b3.blue += bblue;
   1097 			save_back = b3;
   1098 
   1099 			XAllocColor(dpy, cmap, &f3);
   1100 			XAllocColor(dpy, cmap, &b3);
   1101 			cur->highlight.back = cur->normal.fore = f3.pixel;
   1102 			cur->highlight.fore = cur->normal.back = b3.pixel;
   1103 			cur->user_colors = true;
   1104 
   1105 			f3 = save_fore;
   1106 			b3 = save_back;
   1107 		}
   1108 		start = end;
   1109 		start->highlight.back = start->normal.fore;
   1110 		start->highlight.fore = start->normal.back;
   1111 	}
   1112 	return;
   1113 }
   1114 
   1115 
   1116 /***********************************************************************
   1117  *
   1118  *  Procedure:
   1119  *      PopUpMenu - pop up a pull down menu
   1120  *
   1121  *  Inputs:
   1122  *      menu    - the root pointer of the menu to pop up
   1123  *      x, y    - location of upper left of menu
   1124  *      center  - whether or not to center horizontally over position
   1125  *
   1126  ***********************************************************************
   1127  */
   1128 
   1129 bool
   1130 PopUpMenu(MenuRoot *menu, int x, int y, bool center)
   1131 {
   1132 	int WindowNameCount;
   1133 	TwmWindow **WindowNames;
   1134 	TwmWindow *tmp_win2, *tmp_win3;
   1135 	int i;
   1136 	bool clipped;
   1137 	if(!menu) {
   1138 		return false;
   1139 	}
   1140 
   1141 	InstallRootColormap();
   1142 
   1143 	if((menu == Scr->Windows) ||
   1144 	                (menu == Scr->Icons) ||
   1145 	                (menu == Scr->AllWindows) ||
   1146 	                /* Added by Dan 'dl' Lilliehorn 040607 */
   1147 	                (menu == Scr->AllIcons) ||
   1148 	                /* Added by Dan Lilliehorn (dl (at) dl.nu) 2000-02-29 */
   1149 	                (menu == Scr->Visible)) {
   1150 		TwmWindow *tmp_win;
   1151 		WorkSpace *ws;
   1152 		bool all, icons, visible_, allicons; /* visible, allicons:
   1153                                                   Added by dl */
   1154 		int func;
   1155 
   1156 		/* this is the twm windows menu,  let's go ahead and build it */
   1157 
   1158 		all = (menu == Scr->AllWindows);
   1159 		icons = (menu == Scr->Icons);
   1160 		visible_ = (menu == Scr->Visible);    /* Added by dl */
   1161 		allicons = (menu == Scr->AllIcons);
   1162 		DestroyMenu(menu);
   1163 
   1164 		menu->first = NULL;
   1165 		menu->last = NULL;
   1166 		menu->items = 0;
   1167 		menu->width = 0;
   1168 		menu->mapped = MRM_NEVER;
   1169 		menu->highlight.fore = UNUSED_PIXEL;
   1170 		menu->highlight.back = UNUSED_PIXEL;
   1171 		if(menu == Scr->Windows) {
   1172 			AddToMenu(menu, "TWM Windows", NULL, NULL, F_TITLE, NULL, NULL);
   1173 		}
   1174 		else if(menu == Scr->Icons) {
   1175 			AddToMenu(menu, "TWM Icons", NULL, NULL, F_TITLE, NULL, NULL);
   1176 		}
   1177 		else if(menu == Scr->Visible) { /* Added by dl 2000 */
   1178 			AddToMenu(menu, "TWM Visible", NULL, NULL, F_TITLE, NULL, NULL);
   1179 		}
   1180 		else if(menu == Scr->AllIcons) { /* Added by dl 2004 */
   1181 			AddToMenu(menu, "TWM All Icons", NULL, NULL, F_TITLE, NULL, NULL);
   1182 		}
   1183 		else {
   1184 			AddToMenu(menu, "TWM All Windows", NULL, NULL, F_TITLE, NULL, NULL);
   1185 		}
   1186 
   1187 		ws = NULL;
   1188 
   1189 		if(!(all || allicons)
   1190 		                && CurrentSelectedWorkspace && Scr->workSpaceManagerActive) {
   1191 			for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
   1192 				if(strcmp(ws->name, CurrentSelectedWorkspace) == 0) {
   1193 					break;
   1194 				}
   1195 			}
   1196 		}
   1197 		if(!Scr->currentvs) {
   1198 			return false;
   1199 		}
   1200 		if(!ws) {
   1201 			ws = Scr->currentvs->wsw->currentwspc;
   1202 		}
   1203 
   1204 		for(tmp_win = Scr->FirstWindow, WindowNameCount = 0;
   1205 		                tmp_win != NULL;
   1206 		                tmp_win = tmp_win->next) {
   1207 			if(tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
   1208 				continue;
   1209 			}
   1210 			if(Scr->ShortAllWindowsMenus && (tmp_win->iswspmgr || tmp_win->isiconmgr)) {
   1211 				continue;
   1212 			}
   1213 
   1214 			if(!(all || allicons) && !OCCUPY(tmp_win, ws)) {
   1215 				continue;
   1216 			}
   1217 			if(allicons && !tmp_win->isicon) {
   1218 				continue;
   1219 			}
   1220 			if(icons && !tmp_win->isicon) {
   1221 				continue;
   1222 			}
   1223 			if(visible_ && tmp_win->isicon) {
   1224 				continue;        /* added by dl */
   1225 			}
   1226 			WindowNameCount++;
   1227 		}
   1228 
   1229 		// Hack: always pretend there's at least one window, even if
   1230 		// there are none; that lets us skip special cases for empty
   1231 		// lists...
   1232 		if(WindowNameCount == 0) {
   1233 			WindowNameCount = 1;
   1234 		}
   1235 		WindowNames = calloc(WindowNameCount, sizeof(TwmWindow *));
   1236 
   1237 		WindowNameCount = 0;
   1238 		for(tmp_win = Scr->FirstWindow;
   1239 		                tmp_win != NULL;
   1240 		                tmp_win = tmp_win->next) {
   1241 			if(LookInList(Scr->IconMenuDontShow, tmp_win->name, &tmp_win->class)) {
   1242 				continue;
   1243 			}
   1244 
   1245 			if(tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
   1246 				continue;
   1247 			}
   1248 			if(Scr->ShortAllWindowsMenus &&
   1249 			                tmp_win == Scr->currentvs->wsw->twm_win) {
   1250 				continue;
   1251 			}
   1252 			if(Scr->ShortAllWindowsMenus && tmp_win->isiconmgr) {
   1253 				continue;
   1254 			}
   1255 
   1256 			if(!(all || allicons) && ! OCCUPY(tmp_win, ws)) {
   1257 				continue;
   1258 			}
   1259 			if(allicons && !tmp_win->isicon) {
   1260 				continue;
   1261 			}
   1262 			if(icons && !tmp_win->isicon) {
   1263 				continue;
   1264 			}
   1265 			if(visible_ && tmp_win->isicon) {
   1266 				continue;        /* added by dl */
   1267 			}
   1268 			tmp_win2 = tmp_win;
   1269 
   1270 			for(i = 0; i < WindowNameCount; i++) {
   1271 				int compresult;
   1272 				char *tmpname1, *tmpname2;
   1273 				tmpname1 = tmp_win2->name;
   1274 				tmpname2 = WindowNames[i]->name;
   1275 				if(Scr->CaseSensitive) {
   1276 					compresult = strcmp(tmpname1, tmpname2);
   1277 				}
   1278 				else {
   1279 					compresult = strcasecmp(tmpname1, tmpname2);
   1280 				}
   1281 				if(compresult < 0) {
   1282 					tmp_win3 = tmp_win2;
   1283 					tmp_win2 = WindowNames[i];
   1284 					WindowNames[i] = tmp_win3;
   1285 				}
   1286 			}
   1287 			WindowNames[WindowNameCount] = tmp_win2;
   1288 			WindowNameCount++;
   1289 		}
   1290 		func = (all || allicons || CurrentSelectedWorkspace) ? F_WINWARP :
   1291 		       F_POPUP;
   1292 		for(i = 0; i < WindowNameCount; i++) {
   1293 			char *tmpname;
   1294 			tmpname = WindowNames[i]->name;
   1295 			AddToMenu(menu, tmpname, (char *)WindowNames[i],
   1296 			          NULL, func, NULL, NULL);
   1297 		}
   1298 		free(WindowNames);
   1299 
   1300 		menu->pinned = false;
   1301 		MakeMenu(menu);
   1302 	}
   1303 
   1304 	/* Keys added by dl */
   1305 
   1306 	if(menu == Scr->Keys) {
   1307 		char *oldact = 0;
   1308 		int oldmod = 0;
   1309 
   1310 		DestroyMenu(menu);
   1311 
   1312 		menu->first = NULL;
   1313 		menu->last = NULL;
   1314 		menu->items = 0;
   1315 		menu->width = 0;
   1316 		menu->mapped = MRM_NEVER;
   1317 		menu->highlight.fore = UNUSED_PIXEL;
   1318 		menu->highlight.back = UNUSED_PIXEL;
   1319 
   1320 		AddToMenu(menu, "Twm Keys", NULL, NULL, F_TITLE, NULL, NULL);
   1321 
   1322 		for(const FuncKey *tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL;
   1323 		                tmpKey = tmpKey->next) {
   1324 			char *tmpStr;
   1325 
   1326 			if(tmpKey->func != F_EXEC) {
   1327 				continue;
   1328 			}
   1329 			if((tmpKey->action == oldact) && (tmpKey->mods == oldmod)) {
   1330 				continue;
   1331 			}
   1332 
   1333 			tmpStr = mk_twmkeys_entry(tmpKey);
   1334 			if(tmpStr == NULL) {
   1335 				tmpStr = strdup("(error)");
   1336 			}
   1337 
   1338 			AddToMenu(menu, tmpStr, tmpKey->action, NULL, tmpKey->func, NULL, NULL);
   1339 			oldact = tmpKey->action;
   1340 			oldmod = tmpKey->mods;
   1341 		}
   1342 		menu->pinned = false;
   1343 		MakeMenu(menu);
   1344 	}
   1345 	if(menu->w == None || menu->items == 0) {
   1346 		return false;
   1347 	}
   1348 
   1349 	/* Prevent recursively bringing up menus. */
   1350 	if((!menu->pinned) && (menu->mapped == MRM_MAPPED)) {
   1351 		return false;
   1352 	}
   1353 
   1354 	/*
   1355 	 * Dynamically set the parent;  this allows pull-ups to also be main
   1356 	 * menus, or to be brought up from more than one place.
   1357 	 */
   1358 	menu->prev = ActiveMenu;
   1359 
   1360 	if(menu->pinned) {
   1361 		ActiveMenu    = menu;
   1362 		menu->mapped  = MRM_MAPPED;
   1363 		menu->entered = true;
   1364 		MenuOrigins [MenuDepth].x = menu->x;
   1365 		MenuOrigins [MenuDepth].y = menu->y;
   1366 		MenuDepth++;
   1367 
   1368 		XRaiseWindow(dpy, menu->w);
   1369 		return true;
   1370 	}
   1371 
   1372 	XGrabPointer(dpy, Scr->Root, True,
   1373 	             ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
   1374 	             ButtonMotionMask | PointerMotionHintMask,
   1375 	             GrabModeAsync, GrabModeAsync,
   1376 	             Scr->Root,
   1377 	             Scr->MenuCursor, CurrentTime);
   1378 
   1379 	XGrabKeyboard(dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
   1380 
   1381 	ActiveMenu = menu;
   1382 	menu->mapped = MRM_MAPPED;
   1383 	menu->entered = false;
   1384 
   1385 	if(center) {
   1386 		x -= (menu->width / 2);
   1387 		y -= (Scr->EntryHeight / 2);    /* sticky menus would be nice here */
   1388 	}
   1389 
   1390 	/*
   1391 	* clip to screen
   1392 	*/
   1393 	clipped = ConstrainByLayout(Scr->Layout, -1, &x, menu->width, &y, menu->height);
   1394 	MenuOrigins[MenuDepth].x = x;
   1395 	MenuOrigins[MenuDepth].y = y;
   1396 	MenuDepth++;
   1397 
   1398 
   1399 	/*
   1400 	 * Position and display the menu, and its shadow if it has one.  We
   1401 	 * start by positioning and raising (above everything else on screen)
   1402 	 * the shadow.  Then position the menu itself, raise it up above
   1403 	 * that, and map it.  Then map the shadow; doing that after raising
   1404 	 * and mapping the menu avoids spending time drawing the bulk of the
   1405 	 * window which the menu covers up anyway.
   1406 	 */
   1407 	if(Scr->Shadow) {
   1408 		XMoveWindow(dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH);
   1409 		XRaiseWindow(dpy, menu->shadow);
   1410 	}
   1411 
   1412 	XMoveWindow(dpy, menu->w, x, y);
   1413 	XMapRaised(dpy, menu->w);
   1414 
   1415 	if(Scr->Shadow) {
   1416 		XMapWindow(dpy, menu->shadow);
   1417 	}
   1418 
   1419 	/* Move mouse pointer if we're supposed to */
   1420 	if(!Scr->NoWarpToMenuTitle && clipped && center) {
   1421 		const int xl = x + (menu->width      / 2);
   1422 		const int yt = y + (Scr->EntryHeight / 2);
   1423 		XWarpPointer(dpy, Scr->Root, Scr->Root, x, y,
   1424 		             menu->width, menu->height, xl, yt);
   1425 	}
   1426 
   1427 
   1428 	XSync(dpy, 0);
   1429 	return true;
   1430 }
   1431 
   1432 
   1433 /***********************************************************************
   1434  *
   1435  *  Procedure:
   1436  *      PopDownMenu - unhighlight the current menu selection and
   1437  *              take down the menus
   1438  *
   1439  ***********************************************************************
   1440  */
   1441 
   1442 void PopDownMenu(void)
   1443 {
   1444 	MenuRoot *tmp;
   1445 
   1446 	if(ActiveMenu == NULL) {
   1447 		return;
   1448 	}
   1449 
   1450 	if(ActiveItem) {
   1451 		ActiveItem->state = false;
   1452 		PaintEntry(ActiveMenu, ActiveItem, false);
   1453 	}
   1454 
   1455 	for(tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev) {
   1456 		if(! tmp->pinned) {
   1457 			HideMenu(tmp);
   1458 		}
   1459 		UninstallRootColormap();
   1460 	}
   1461 
   1462 	XFlush(dpy);
   1463 	ActiveMenu = NULL;
   1464 	ActiveItem = NULL;
   1465 	MenuDepth = 0;
   1466 	XUngrabKeyboard(dpy, CurrentTime);
   1467 	if(Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE
   1468 	                || Context == C_ICON) {
   1469 		menuFromFrameOrWindowOrTitlebar = true;
   1470 	}
   1471 
   1472 	return;
   1473 }
   1474 
   1475 
   1476 void HideMenu(MenuRoot *menu)
   1477 {
   1478 	if(!menu) {
   1479 		return;
   1480 	}
   1481 
   1482 	if(Scr->Shadow) {
   1483 		XUnmapWindow(dpy, menu->shadow);
   1484 	}
   1485 	XUnmapWindow(dpy, menu->w);
   1486 	menu->mapped = MRM_UNMAPPED;
   1487 }
   1488 
   1489 /***********************************************************************
   1490  *
   1491  *  Procedure:
   1492  *      FindMenuRoot - look for a menu root
   1493  *
   1494  *  Returned Value:
   1495  *      (MenuRoot *)  - a pointer to the menu root structure
   1496  *
   1497  *  Inputs:
   1498  *      name    - the name of the menu root
   1499  *
   1500  ***********************************************************************
   1501  */
   1502 
   1503 MenuRoot *FindMenuRoot(char *name)
   1504 {
   1505 	MenuRoot *tmp;
   1506 
   1507 	for(tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next) {
   1508 		if(strcmp(name, tmp->name) == 0) {
   1509 			return (tmp);
   1510 		}
   1511 	}
   1512 	return NULL;
   1513 }
   1514 
   1515 
   1516 
   1517 static void DestroyMenu(MenuRoot *menu)
   1518 {
   1519 	MenuItem *item;
   1520 
   1521 	if(menu->w) {
   1522 		XDeleteContext(dpy, menu->w, MenuContext);
   1523 		XDeleteContext(dpy, menu->w, ScreenContext);
   1524 		if(Scr->Shadow) {
   1525 			XDestroyWindow(dpy, menu->shadow);
   1526 		}
   1527 		XDestroyWindow(dpy, menu->w);
   1528 	}
   1529 
   1530 	for(item = menu->first; item;) {
   1531 		MenuItem *tmp = item;
   1532 		item = item->next;
   1533 		free(tmp);
   1534 	}
   1535 }
   1536 
   1537 
   1538 void MoveMenu(XEvent *eventp)
   1539 {
   1540 	int    XW, YW, newX, newY;
   1541 	bool   cont;
   1542 	bool   newev;
   1543 	unsigned long event_mask;
   1544 	XEvent ev;
   1545 
   1546 	if(! ActiveMenu) {
   1547 		return;
   1548 	}
   1549 	if(! ActiveMenu->pinned) {
   1550 		return;
   1551 	}
   1552 
   1553 	XW = eventp->xbutton.x_root - ActiveMenu->x;
   1554 	YW = eventp->xbutton.y_root - ActiveMenu->y;
   1555 	XGrabPointer(dpy, ActiveMenu->w, True,
   1556 	             ButtonPressMask  | ButtonReleaseMask | ButtonMotionMask,
   1557 	             GrabModeAsync, GrabModeAsync,
   1558 	             None, Scr->MoveCursor, CurrentTime);
   1559 
   1560 	newX = ActiveMenu->x;
   1561 	newY = ActiveMenu->y;
   1562 	cont = true;
   1563 	event_mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask |
   1564 	             ExposureMask;
   1565 	XMaskEvent(dpy, event_mask, &ev);
   1566 	while(cont) {
   1567 		ev.xbutton.x_root -= Scr->rootx;
   1568 		ev.xbutton.y_root -= Scr->rooty;
   1569 		switch(ev.xany.type) {
   1570 			case ButtonRelease :
   1571 				cont = false;
   1572 			case MotionNotify :
   1573 				if(!cont) {
   1574 					newev = false;
   1575 					while(XCheckMaskEvent(dpy, ButtonMotionMask | ButtonReleaseMask, &ev)) {
   1576 						newev = true;
   1577 						if(ev.type == ButtonRelease) {
   1578 							break;
   1579 						}
   1580 					}
   1581 					if(ev.type == ButtonRelease) {
   1582 						continue;
   1583 					}
   1584 					if(newev) {
   1585 						ev.xbutton.x_root -= Scr->rootx;
   1586 						ev.xbutton.y_root -= Scr->rooty;
   1587 					}
   1588 				}
   1589 				newX = ev.xbutton.x_root - XW;
   1590 				newY = ev.xbutton.y_root - YW;
   1591 				if(Scr->DontMoveOff) {
   1592 					ConstrainByBorders1(&newX, ActiveMenu->width,
   1593 					                    &newY, ActiveMenu->height);
   1594 				}
   1595 				XMoveWindow(dpy, ActiveMenu->w, newX, newY);
   1596 				XMaskEvent(dpy, event_mask, &ev);
   1597 				break;
   1598 			case ButtonPress :
   1599 				cont = false;
   1600 				newX = ActiveMenu->x;
   1601 				newY = ActiveMenu->y;
   1602 				break;
   1603 			case Expose:
   1604 			case NoExpose:
   1605 				Event = ev;
   1606 				DispatchEvent();
   1607 				XMaskEvent(dpy, event_mask, &ev);
   1608 				break;
   1609 		}
   1610 	}
   1611 	XUngrabPointer(dpy, CurrentTime);
   1612 	if(ev.xany.type == ButtonRelease) {
   1613 		ButtonPressed = -1;
   1614 	}
   1615 	/*XPutBackEvent (dpy, &ev);*/
   1616 	XMoveWindow(dpy, ActiveMenu->w, newX, newY);
   1617 	ActiveMenu->x = newX;
   1618 	ActiveMenu->y = newY;
   1619 	MenuOrigins [MenuDepth - 1].x = newX;
   1620 	MenuOrigins [MenuDepth - 1].y = newY;
   1621 
   1622 	return;
   1623 }
   1624 
   1625 
   1626 void WarpCursorToDefaultEntry(MenuRoot *menu)
   1627 {
   1628 	MenuItem    *item;
   1629 	Window       root;
   1630 	int          i, x, y, xl, yt;
   1631 	unsigned int w, h, bw, d;
   1632 
   1633 	for(i = 0, item = menu->first; item != menu->last; item = item->next) {
   1634 		if(item == menu->defaultitem) {
   1635 			break;
   1636 		}
   1637 		i++;
   1638 	}
   1639 	if(!XGetGeometry(dpy, menu->w, &root, &x, &y, &w, &h, &bw, &d)) {
   1640 		return;
   1641 	}
   1642 	xl = x + (menu->width / 2);
   1643 	yt = y + (i + 0.5) * Scr->EntryHeight;
   1644 
   1645 	XWarpPointer(dpy, Scr->Root, Scr->Root,
   1646 	             Event.xbutton.x_root, Event.xbutton.y_root,
   1647 	             menu->width, menu->height, xl, yt);
   1648 }
   1649 
   1650 
   1651 
   1652 /**
   1653  * Generate up a string representation of a keybinding->action.
   1654  * Internally used in generating TwmKeys menu.
   1655  */
   1656 char *
   1657 mk_twmkeys_entry(const FuncKey *key)
   1658 {
   1659 	char *ret;
   1660 	//         S+  C+  5(Mx+)  5(Ax+)
   1661 #define MSLEN (2 + 2 + 2 + 5 * 3 + 5 * 3 + 1 + 26)
   1662 	char modStr[MSLEN + 1];
   1663 	char *modStrCur = modStr;
   1664 
   1665 	// Init
   1666 	*modStrCur = '\0';
   1667 
   1668 	// Check and add prefixes for each modifier
   1669 #define DO(mask, str) do { \
   1670                 if(key->mods & mask##Mask) { \
   1671                         const int tslen = sizeof(str) - 1; \
   1672                         if((modStrCur - modStr + tslen) >= MSLEN) { \
   1673                                 fprintf(stderr, "BUG: No space to add '%s' " \
   1674                                                 "in %s()\n", str, __func__); \
   1675                                 return NULL; \
   1676                         } \
   1677                         strcpy(modStrCur, str); \
   1678                         modStrCur += tslen; \
   1679                 } \
   1680         } while(0)
   1681 
   1682 	// Mod1 is Meta (== Alt), so is special and comes first, apart and
   1683 	// differing from the other more generic ModX's.
   1684 	DO(Mod1, "M+");
   1685 
   1686 	// Shift/Ctrl are normal common bits.
   1687 	DO(Shift,   "S+");
   1688 	DO(Control, "C+");
   1689 
   1690 	// Other Mod's and Alt's are weirder, but possible.
   1691 	DO(Mod2, "M2+");
   1692 	DO(Mod3, "M3+");
   1693 	DO(Mod4, "M4+");
   1694 	DO(Mod5, "M5+");
   1695 
   1696 	DO(Alt1, "A1+");
   1697 	DO(Alt2, "A2+");
   1698 	DO(Alt3, "A3+");
   1699 	DO(Alt4, "A4+");
   1700 	DO(Alt5, "A5+");
   1701 
   1702 	// Overflows for test.  Watch out for colliding with X or our *Mask
   1703 	// defs.
   1704 	// +1 when combined with above, should be enough
   1705 #define Over1Mask (1<<30)
   1706 	DO(Over1, "a");
   1707 	// Way too big no matter what
   1708 #define OverAllMask (1<<31)
   1709 	DO(OverAll, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
   1710 
   1711 #undef OverAllMask
   1712 #undef Over1Mask
   1713 
   1714 #undef DO
   1715 
   1716 	asprintf(&ret, "[%s%s] %s", modStr, key->name, key->action);
   1717 	return ret;
   1718 }
   1719