menus.c revision 645f5050
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/**                           All Rights Reserved                           **/
8/**                                                                         **/
9/**    Permission to use, copy, modify, and distribute this software and    **/
10/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
11/**    granted, provided that the above copyright notice appear  in  all    **/
12/**    copies and that both  that  copyright  notice  and  this  permis-    **/
13/**    sion  notice appear in supporting  documentation,  and  that  the    **/
14/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
15/**    in publicity pertaining to distribution of the  software  without    **/
16/**    specific, written prior permission.                                  **/
17/**                                                                         **/
18/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
19/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
20/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
21/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
22/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
23/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
24/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
25/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
26/*****************************************************************************/
27/*
28 *  [ ctwm ]
29 *
30 *  Copyright 1992 Claude Lecommandeur.
31 *
32 * Permission to use, copy, modify  and distribute this software  [ctwm] and
33 * its documentation for any purpose is hereby granted without fee, provided
34 * that the above  copyright notice appear  in all copies and that both that
35 * copyright notice and this permission notice appear in supporting documen-
36 * tation, and that the name of  Claude Lecommandeur not be used in adverti-
37 * sing or  publicity  pertaining to  distribution of  the software  without
38 * specific, written prior permission. Claude Lecommandeur make no represen-
39 * tations  about the suitability  of this software  for any purpose.  It is
40 * provided "as is" without express or implied warranty.
41 *
42 * Claude Lecommandeur DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
43 * INCLUDING ALL  IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS.  IN NO
44 * EVENT SHALL  Claude Lecommandeur  BE LIABLE FOR ANY SPECIAL,  INDIRECT OR
45 * CONSEQUENTIAL  DAMAGES OR ANY  DAMAGES WHATSOEVER  RESULTING FROM LOSS OF
46 * USE, DATA  OR PROFITS,  WHETHER IN AN ACTION  OF CONTRACT,  NEGLIGENCE OR
47 * OTHER  TORTIOUS ACTION,  ARISING OUT OF OR IN  CONNECTION WITH THE USE OR
48 * PERFORMANCE OF THIS SOFTWARE.
49 *
50 * Author:  Claude Lecommandeur [ lecom@sic.epfl.ch ][ April 1992 ]
51 */
52
53
54/***********************************************************************
55 *
56 * $XConsortium: menus.c,v 1.186 91/07/17 13:58:00 dave Exp $
57 *
58 * twm menu code
59 *
60 * 17-Nov-87 Thomas E. LaStrange		File created
61 *
62 * Do the necessary modification to be integrated in ctwm.
63 * Can no longer be used for the standard twm.
64 *
65 * 22-April-92 Claude Lecommandeur.
66 *
67 *
68 ***********************************************************************/
69
70#if defined(USE_SIGNALS) && defined(__sgi)
71#  define _BSD_SIGNALS
72#endif
73
74#include <stdio.h>
75#include <signal.h>
76
77#ifdef VMS
78#include <stdlib.h>
79#include <string.h>
80#include <unixio.h>
81#include <file.h>
82#include <decw$include/Xos.h>
83#include <decw$include/Xatom.h>
84#else
85#include <X11/Xos.h>
86#include <X11/Xatom.h>
87#endif
88#include "twm.h"
89#include "ctwm.h"
90#include "gc.h"
91#include "menus.h"
92#include "resize.h"
93#include "events.h"
94#include "list.h"
95#include "util.h"
96#include "parse.h"
97#include "screen.h"
98#include "icons.h"
99#include "add_window.h"
100#include "windowbox.h"
101#include "workmgr.h"
102#include "cursor.h"
103#include "gnomewindefs.h"
104#ifdef SOUNDS
105#  include "sound.h"
106#endif
107#ifdef VMS
108#  include <X11Xmu/CharSet.h>
109#  include <decw$bitmaps/menu12.xbm>
110#  include <X11SM/SMlib.h>
111#  include "vms_cmd_services.h"
112#  include <lib$routines.h>
113#else
114#  include <X11/Xmu/CharSet.h>
115#  include <X11/SM/SMlib.h>
116#endif
117#include "version.h"
118
119#if defined(MACH) || defined(__MACH__) || defined(sony_news) || defined(NeXT)
120#define lrand48 random
121#endif
122#if defined(VMS) || defined(__DARWIN__)
123#define lrand48 rand
124#endif
125
126#ifndef VMS
127#define MAX(x,y) ((x)>(y)?(x):(y))
128#define MIN(x,y) ((x)<(y)?(x):(y))
129#endif
130#define ABS(x) ((x)<0?-(x):(x))
131
132int RootFunction = 0;
133MenuRoot *ActiveMenu = NULL;		/* the active menu */
134MenuItem *ActiveItem = NULL;		/* the active menu item */
135int MoveFunction;			/* either F_MOVE or F_FORCEMOVE */
136int WindowMoved = FALSE;
137int menuFromFrameOrWindowOrTitlebar = FALSE;
138char *CurrentSelectedWorkspace;
139int AlternateKeymap;
140Bool AlternateContext;
141
142extern char *captivename;
143
144int ConstMove = FALSE;		/* constrained move variables */
145int ConstMoveDir;
146int ConstMoveX;
147int ConstMoveY;
148int ConstMoveXL;
149int ConstMoveXR;
150int ConstMoveYT;
151int ConstMoveYB;
152
153/* Globals used to keep track of whether the mouse has moved during
154   a resize function. */
155int ResizeOrigX;
156int ResizeOrigY;
157
158int MenuDepth = 0;		/* number of menus up */
159static struct {
160    int x;
161    int y;
162} MenuOrigins[MAXMENUDEPTH];
163static Cursor LastCursor;
164static Bool addingdefaults = False;
165
166void jump (TwmWindow *tmp_win, int  direction, char *action);
167void waitamoment (float timeout);
168
169extern char *Action;
170extern int Context;
171extern TwmWindow *ButtonWindow, *Tmp_win;
172extern XEvent ButtonEvent;
173extern char *InitFile;
174extern int ConstrainedMoveTime;
175static void Identify (TwmWindow *t);
176
177#define SHADOWWIDTH 5			/* in pixels */
178
179#ifdef GNOME
180  extern Atom _XA_WIN_STATE;
181#endif /* GNOME */
182
183
184
185/***********************************************************************
186 *
187 *  Procedure:
188 *	InitMenus - initialize menu roots
189 *
190 ***********************************************************************
191 */
192
193void InitMenus(void)
194{
195    Scr->DefaultFunction.func = 0;
196    Scr->WindowFunction.func  = 0;
197    Scr->ChangeWorkspaceFunction.func  = 0;
198    Scr->DeIconifyFunction.func  = 0;
199    Scr->IconifyFunction.func  = 0;
200
201    Scr->FuncKeyRoot.next = NULL;
202    Scr->FuncButtonRoot.next = NULL;
203}
204
205
206
207/***********************************************************************
208 *
209 *  Procedure:
210 *	AddFuncKey - add a function key to the list
211 *
212 *  Inputs:
213 *	name	- the name of the key
214 *	cont	- the context to look for the key press in
215 *	mods	- modifier keys that need to be pressed
216 *	func	- the function to perform
217 *	win_name- the window name (if any)
218 *	action	- the action string associated with the function (if any)
219 *
220 ***********************************************************************
221 */
222
223Bool AddFuncKey (char *name, int cont, int mods, int func,
224		 MenuRoot *menu, char *win_name, char *action)
225{
226    FuncKey *tmp;
227    KeySym keysym;
228    KeyCode keycode;
229
230    /*
231     * Don't let a 0 keycode go through, since that means AnyKey to the
232     * XGrabKey call in GrabKeys().
233     */
234    if ((keysym = XStringToKeysym(name)) == NoSymbol ||
235	(keycode = XKeysymToKeycode(dpy, keysym)) == 0)
236    {
237	return False;
238    }
239
240    /* see if there already is a key defined for this context */
241    for (tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next)
242    {
243	if (tmp->keysym == keysym &&
244	    tmp->cont == cont &&
245	    tmp->mods == mods)
246	    break;
247    }
248    if (tmp && addingdefaults) return (True);
249
250    if (tmp == NULL)
251    {
252	tmp = (FuncKey *) malloc(sizeof(FuncKey));
253	tmp->next = Scr->FuncKeyRoot.next;
254	Scr->FuncKeyRoot.next = tmp;
255    }
256
257    tmp->name = name;
258    tmp->keysym = keysym;
259    tmp->keycode = keycode;
260    tmp->cont = cont;
261    tmp->mods = mods;
262    tmp->func = func;
263    tmp->menu = menu;
264    tmp->win_name = win_name;
265    tmp->action = action;
266
267    return True;
268}
269
270/***********************************************************************
271 *
272 *  Procedure:
273 *	AddFuncButton - add a function button to the list
274 *
275 *  Inputs:
276 *	num	- the num of the button
277 *	cont	- the context to look for the key press in
278 *	mods	- modifier keys that need to be pressed
279 *	func	- the function to perform
280 *	menu    - the menu (if any)
281 *	item	- the menu item (if any)
282 *
283 ***********************************************************************
284 */
285
286Bool AddFuncButton (int num, int cont, int mods, int func,
287		    MenuRoot *menu, MenuItem *item)
288{
289    FuncButton *tmp;
290
291    /* see if there already is a key defined for this context */
292    for (tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
293	if ((tmp->num == num) && (tmp->cont == cont) && (tmp->mods == mods))
294	    break;
295    }
296    if (tmp && addingdefaults) return (True);
297
298    if (tmp == NULL) {
299	tmp = (FuncButton*) malloc (sizeof (FuncButton));
300	tmp->next = Scr->FuncButtonRoot.next;
301	Scr->FuncButtonRoot.next = tmp;
302    }
303
304    tmp->num  = num;
305    tmp->cont = cont;
306    tmp->mods = mods;
307    tmp->func = func;
308    tmp->menu = menu;
309    tmp->item = item;
310
311    return True;
312}
313
314
315
316static TitleButton *cur_tb = NULL;
317
318void ModifyCurrentTB(int button, int mods, int func, char *action,
319		     MenuRoot *menuroot)
320{
321    TitleButtonFunc *tbf;
322
323    if (!cur_tb) {
324        fprintf (stderr, "%s: can't find titlebutton\n", ProgramName);
325	return;
326    }
327    for (tbf = cur_tb->funs; tbf; tbf = tbf->next) {
328	if (tbf->num == button && tbf->mods == mods)
329	    break;
330    }
331    if (!tbf) {
332	tbf = (TitleButtonFunc *)malloc(sizeof(TitleButtonFunc));
333	if (!tbf) {
334	    fprintf (stderr, "%s: out of memory\n", ProgramName);
335	    return;
336	}
337	tbf->next = cur_tb->funs;
338	cur_tb->funs = tbf;
339    }
340    tbf->num = button;
341    tbf->mods = mods;
342    tbf->func = func;
343    tbf->action = action;
344    tbf->menuroot = menuroot;
345}
346
347int CreateTitleButton (char *name, int func, char *action, MenuRoot *menuroot,
348		       Bool rightside, Bool append)
349{
350    int button;
351    cur_tb = (TitleButton *) malloc (sizeof(TitleButton));
352
353    if (!cur_tb) {
354	fprintf (stderr,
355		 "%s:  unable to allocate %lu bytes for title button\n",
356		 ProgramName, (unsigned long) sizeof(TitleButton));
357	return 0;
358    }
359
360    cur_tb->next = NULL;
361    cur_tb->name = name;                      /* note that we are not copying */
362    cur_tb->image = None;                     /* WARNING, values not set yet */
363    cur_tb->width = 0;                        /* see InitTitlebarButtons */
364    cur_tb->height = 0;                       /* ditto */
365    cur_tb->rightside = rightside;
366    cur_tb->funs = NULL;
367    if (rightside) {
368	Scr->TBInfo.nright++;
369    } else {
370	Scr->TBInfo.nleft++;
371    }
372
373    for(button = 0; button < MAX_BUTTONS; button++){
374	ModifyCurrentTB(button + 1, 0, func, action, menuroot);
375    }
376
377    /*
378     * Cases for list:
379     *
380     *     1.  empty list, prepend left       put at head of list
381     *     2.  append left, prepend right     put in between left and right
382     *     3.  append right                   put at tail of list
383     *
384     * Do not refer to widths and heights yet since buttons not created
385     * (since fonts not loaded and heights not known).
386     */
387    if ((!Scr->TBInfo.head) || ((!append) && (!rightside))) {	/* 1 */
388	cur_tb->next = Scr->TBInfo.head;
389	Scr->TBInfo.head = cur_tb;
390    } else if (append && rightside) {	/* 3 */
391	register TitleButton *t;
392	for /* SUPPRESS 530 */
393	  (t = Scr->TBInfo.head; t->next; t = t->next);
394	t->next = cur_tb;
395	cur_tb->next = NULL;
396    } else {				/* 2 */
397	register TitleButton *t, *prev = NULL;
398	for (t = Scr->TBInfo.head; t && !t->rightside; t = t->next) {
399	    prev = t;
400	}
401	if (prev) {
402	    cur_tb->next = prev->next;
403	    prev->next = cur_tb;
404	} else {
405	    cur_tb->next = Scr->TBInfo.head;
406	    Scr->TBInfo.head = cur_tb;
407	}
408    }
409
410    return 1;
411}
412
413
414
415/*
416 * InitTitlebarButtons - Do all the necessary stuff to load in a titlebar
417 * button.  If we can't find the button, then put in a question; if we can't
418 * find the question mark, something is wrong and we are probably going to be
419 * in trouble later on.
420 */
421void InitTitlebarButtons (void)
422{
423    TitleButton *tb;
424    int h;
425
426    /*
427     * initialize dimensions
428     */
429    Scr->TBInfo.width = (Scr->TitleHeight -
430			 2 * (Scr->FramePadding + Scr->ButtonIndent));
431    if (Scr->use3Dtitles)
432	Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
433		       ? ((Scr->TitlePadding + 1) / 2) : 0);
434    else
435	Scr->TBInfo.pad = ((Scr->TitlePadding > 1)
436		       ? ((Scr->TitlePadding + 1) / 2) : 1);
437    h = Scr->TBInfo.width - 2 * Scr->TBInfo.border;
438
439    /*
440     * add in some useful buttons and bindings so that novices can still
441     * use the system.
442     */
443    if (!Scr->NoDefaults) {
444	/* insert extra buttons */
445	if (Scr->use3Dtitles) {
446	    if (!CreateTitleButton (TBPM_3DDOT, F_ICONIFY, "", (MenuRoot *) NULL,
447				False, False)) {
448	        fprintf (stderr, "%s:  unable to add iconify button\n", ProgramName);
449	    }
450	    if (!CreateTitleButton (TBPM_3DRESIZE, F_RESIZE, "", (MenuRoot *) NULL,
451				True, True)) {
452	        fprintf (stderr, "%s:  unable to add resize button\n", ProgramName);
453	    }
454	}
455	else {
456	    if (!CreateTitleButton (TBPM_ICONIFY, F_ICONIFY, "", (MenuRoot *) NULL,
457				False, False)) {
458	        fprintf (stderr, "%s:  unable to add iconify button\n", ProgramName);
459	    }
460	    if (!CreateTitleButton (TBPM_RESIZE, F_RESIZE, "", (MenuRoot *) NULL,
461				True, True)) {
462	        fprintf (stderr, "%s:  unable to add resize button\n", ProgramName);
463	    }
464	}
465	addingdefaults = True;
466	AddDefaultBindings ();
467	addingdefaults = False;
468    }
469    ComputeCommonTitleOffsets ();
470
471    /*
472     * load in images and do appropriate centering
473     */
474
475    for (tb = Scr->TBInfo.head; tb; tb = tb->next) {
476	tb->image = GetImage (tb->name, Scr->TitleC);
477	if (!tb->image) {
478	    tb->image = GetImage (TBPM_QUESTION, Scr->TitleC);
479	    if (!tb->image) {		/* cannot happen (see util.c) */
480		fprintf (stderr, "%s:  unable to add titlebar button \"%s\"\n",
481			 ProgramName, tb->name);
482	    }
483	}
484	tb->width  = tb->image->width;
485	tb->height = tb->image->height;
486	tb->dstx = (h - tb->width + 1) / 2;
487	if (tb->dstx < 0) {		/* clip to minimize copying */
488	    tb->srcx = -(tb->dstx);
489	    tb->width = h;
490	    tb->dstx = 0;
491	} else {
492	    tb->srcx = 0;
493	}
494	tb->dsty = (h - tb->height + 1) / 2;
495	if (tb->dsty < 0) {
496	    tb->srcy = -(tb->dsty);
497	    tb->height = h;
498	    tb->dsty = 0;
499	} else {
500	    tb->srcy = 0;
501	}
502    }
503}
504
505
506void PaintEntry(MenuRoot *mr, MenuItem *mi, int exposure)
507{
508    if (Scr->use3Dmenus)
509	Paint3DEntry (mr, mi, exposure);
510    else
511	PaintNormalEntry (mr, mi, exposure);
512    if (mi->state) mr->lastactive = mi;
513}
514
515void Paint3DEntry(MenuRoot *mr, MenuItem *mi, int exposure)
516{
517    int y_offset;
518    int text_y;
519    GC gc;
520
521    y_offset = mi->item_num * Scr->EntryHeight + Scr->MenuShadowDepth;
522    text_y = y_offset + Scr->MenuFont.y + 2;
523
524    if (mi->func != F_TITLE) {
525	int x, y;
526
527	gc = Scr->NormalGC;
528	if (mi->state) {
529	    Draw3DBorder (mr->w, Scr->MenuShadowDepth, y_offset,
530			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
531			mi->highlight, off, True, False);
532	    FB(mi->highlight.fore, mi->highlight.back);
533	    XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, gc,
534		mi->x + Scr->MenuShadowDepth, text_y, mi->item, mi->strlen);
535	}
536	else {
537	    if (mi->user_colors || !exposure) {
538		XSetForeground (dpy, gc, mi->normal.back);
539		XFillRectangle (dpy, mr->w, gc,
540			Scr->MenuShadowDepth, y_offset,
541			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight);
542		FB (mi->normal.fore, mi->normal.back);
543	    }
544	    else {
545		gc = Scr->MenuGC;
546	    }
547	    XmbDrawImageString (dpy, mr->w, Scr->MenuFont.font_set, gc,
548				mi->x + Scr->MenuShadowDepth, text_y,
549				mi->item, mi->strlen);
550	    if (mi->separated) {
551		FB (Scr->MenuC.shadd, Scr->MenuC.shadc);
552		XDrawLine (dpy, mr->w, Scr->NormalGC,
553				Scr->MenuShadowDepth,
554				y_offset + Scr->EntryHeight - 2,
555				mr->width - Scr->MenuShadowDepth,
556				y_offset + Scr->EntryHeight - 2);
557		FB (Scr->MenuC.shadc, Scr->MenuC.shadd);
558		XDrawLine (dpy, mr->w, Scr->NormalGC,
559				Scr->MenuShadowDepth,
560				y_offset + Scr->EntryHeight - 1,
561				mr->width - Scr->MenuShadowDepth,
562				y_offset + Scr->EntryHeight - 1);
563	    }
564	}
565
566	if (mi->func == F_MENU) {
567	    /* create the pull right pixmap if needed */
568	    if (Scr->pullPm == None)
569	    {
570		Scr->pullPm = Create3DMenuIcon (Scr->MenuFont.height, &Scr->pullW,
571				&Scr->pullH, Scr->MenuC);
572	    }
573	    x = mr->width - Scr->pullW - Scr->MenuShadowDepth - 2;
574	    y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2) + 2;
575	    XCopyArea (dpy, Scr->pullPm, mr->w, gc, 0, 0, Scr->pullW, Scr->pullH, x, y);
576	}
577    }
578    else
579    {
580	Draw3DBorder (mr->w, Scr->MenuShadowDepth, y_offset,
581			mr->width - 2 * Scr->MenuShadowDepth, Scr->EntryHeight, 1,
582			mi->normal, off, True, False);
583	FB (mi->normal.fore, mi->normal.back);
584	XmbDrawImageString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
585			   mi->x + 2, text_y, mi->item, mi->strlen);
586    }
587}
588
589
590
591void PaintNormalEntry(MenuRoot *mr, MenuItem *mi, int exposure)
592{
593    int y_offset;
594    int text_y;
595    GC gc;
596
597    y_offset = mi->item_num * Scr->EntryHeight;
598    text_y = y_offset + Scr->MenuFont.y;
599
600    if (mi->func != F_TITLE)
601    {
602	int x, y;
603
604	if (mi->state)
605	{
606	    XSetForeground(dpy, Scr->NormalGC, mi->highlight.back);
607
608	    XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
609		mr->width, Scr->EntryHeight);
610	    FB(mi->highlight.fore, mi->highlight.back);
611	    XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC,
612			  mi->x, text_y, mi->item, mi->strlen);
613
614	    gc = Scr->NormalGC;
615	}
616	else
617	{
618	    if (mi->user_colors || !exposure)
619	    {
620		XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
621
622		XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
623		    mr->width, Scr->EntryHeight);
624
625		FB(mi->normal.fore, mi->normal.back);
626		gc = Scr->NormalGC;
627	    }
628	    else {
629		gc = Scr->MenuGC;
630	    }
631	    XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, gc, mi->x,
632			  text_y, mi->item, mi->strlen);
633	    if (mi->separated)
634		XDrawLine (dpy, mr->w, gc, 0, y_offset + Scr->EntryHeight - 1,
635				mr->width, y_offset + Scr->EntryHeight - 1);
636	}
637
638	if (mi->func == F_MENU)
639	{
640	    /* create the pull right pixmap if needed */
641	    if (Scr->pullPm == None)
642	    {
643		Scr->pullPm = CreateMenuIcon (Scr->MenuFont.height,
644					     &Scr->pullW, &Scr->pullH);
645	    }
646	    x = mr->width - Scr->pullW - 5;
647	    y = y_offset + ((Scr->MenuFont.height - Scr->pullH) / 2);
648	    XCopyPlane(dpy, Scr->pullPm, mr->w, gc, 0, 0,
649		Scr->pullW, Scr->pullH, x, y, 1);
650	}
651    }
652    else
653    {
654	int y;
655
656	XSetForeground(dpy, Scr->NormalGC, mi->normal.back);
657
658	/* fill the rectangle with the title background color */
659	XFillRectangle(dpy, mr->w, Scr->NormalGC, 0, y_offset,
660	    mr->width, Scr->EntryHeight);
661
662	{
663	    XSetForeground(dpy, Scr->NormalGC, mi->normal.fore);
664	    /* now draw the dividing lines */
665	    if (y_offset)
666	      XDrawLine (dpy, mr->w, Scr->NormalGC, 0, y_offset,
667			 mr->width, y_offset);
668	    y = ((mi->item_num+1) * Scr->EntryHeight)-1;
669	    XDrawLine(dpy, mr->w, Scr->NormalGC, 0, y, mr->width, y);
670	}
671
672	FB(mi->normal.fore, mi->normal.back);
673	/* finally render the title */
674	XmbDrawString(dpy, mr->w, Scr->MenuFont.font_set, Scr->NormalGC, mi->x,
675		      text_y, mi->item, mi->strlen);
676    }
677}
678
679void PaintMenu(MenuRoot *mr, XEvent *e)
680{
681    MenuItem *mi;
682
683    if (Scr->use3Dmenus) {
684	Draw3DBorder (mr->w, 0, 0, mr->width, mr->height,
685		Scr->MenuShadowDepth, Scr->MenuC, off, False, False);
686    }
687    for (mi = mr->first; mi != NULL; mi = mi->next)
688    {
689	int y_offset = mi->item_num * Scr->EntryHeight;
690
691	/* be smart about handling the expose, redraw only the entries
692	 * that we need to
693	 */
694	if (e->xexpose.y <= (y_offset + Scr->EntryHeight) &&
695	    (e->xexpose.y + e->xexpose.height) >= y_offset)
696	{
697	    PaintEntry(mr, mi, True);
698	}
699    }
700    XSync(dpy, 0);
701}
702
703
704
705void MakeWorkspacesMenu (void)
706{
707    static char **actions = NULL;
708    WorkSpace *wlist;
709    char **act;
710
711    if (! Scr->Workspaces) return;
712    AddToMenu (Scr->Workspaces, "TWM Workspaces", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
713    if (! actions) {
714	int count = 0;
715
716        for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
717            count++;
718        }
719	count++;
720	actions = (char**) malloc (count * sizeof (char*));
721	act = actions;
722        for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
723	    *act = (char*) malloc (strlen ("WGOTO : ") + strlen (wlist->name) + 1);
724	    sprintf (*act, "WGOTO : %s", wlist->name);
725	    act++;
726	}
727	*act = NULL;
728    }
729    act = actions;
730    for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
731        AddToMenu (Scr->Workspaces, wlist->name, *act, Scr->Windows, F_MENU, NULL, NULL);
732	act++;
733    }
734    Scr->Workspaces->pinned = False;
735    MakeMenu (Scr->Workspaces);
736}
737
738static Bool fromMenu;
739
740int UpdateMenu(void)
741{
742    MenuItem *mi;
743    int i, x, y, x_root, y_root, entry;
744    int done;
745    MenuItem *badItem = NULL;
746
747    fromMenu = TRUE;
748
749    while (TRUE)
750    {
751	/* block until there is an event */
752        if (!menuFromFrameOrWindowOrTitlebar) {
753	  XMaskEvent(dpy,
754		     ButtonPressMask | ButtonReleaseMask |
755		     KeyPressMask | KeyReleaseMask |
756		     EnterWindowMask | ExposureMask |
757		     VisibilityChangeMask | LeaveWindowMask |
758		     ButtonMotionMask, &Event);
759	}
760	if (Event.type == MotionNotify) {
761	    /* discard any extra motion events before a release */
762	    while(XCheckMaskEvent(dpy,
763		ButtonMotionMask | ButtonReleaseMask, &Event))
764		if (Event.type == ButtonRelease)
765		    break;
766	}
767
768	if (!DispatchEvent ())
769	    continue;
770
771	if ((! ActiveMenu) || Cancel) {
772	  menuFromFrameOrWindowOrTitlebar = FALSE;
773	  fromMenu = FALSE;
774	  return (0);
775	}
776
777	if (Event.type != MotionNotify)
778	    continue;
779
780	done = FALSE;
781	XQueryPointer( dpy, ActiveMenu->w, &JunkRoot, &JunkChild,
782	    &x_root, &y_root, &x, &y, &JunkMask);
783
784	/* if we haven't recieved the enter notify yet, wait */
785	if (ActiveMenu && !ActiveMenu->entered)
786	    continue;
787
788	XFindContext(dpy, ActiveMenu->w, ScreenContext, (XPointer *)&Scr);
789
790	if (x < 0 || y < 0 ||
791	    x >= ActiveMenu->width || y >= ActiveMenu->height)
792	{
793	    if (ActiveItem && ActiveItem->func != F_TITLE)
794	    {
795		ActiveItem->state = 0;
796		PaintEntry(ActiveMenu, ActiveItem, False);
797	    }
798	    ActiveItem = NULL;
799	    continue;
800	}
801
802	/* look for the entry that the mouse is in */
803	entry = y / Scr->EntryHeight;
804	for (i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi=mi->next)
805	{
806	    if (i == entry)
807		break;
808	}
809
810	/* if there is an active item, we might have to turn it off */
811	if (ActiveItem)
812	{
813	    /* is the active item the one we are on ? */
814	    if (ActiveItem->item_num == entry && ActiveItem->state)
815		done = TRUE;
816
817	    /* if we weren't on the active entry, let's turn the old
818	     * active one off
819	     */
820	    if (!done && ActiveItem->func != F_TITLE)
821	    {
822		ActiveItem->state = 0;
823		PaintEntry(ActiveMenu, ActiveItem, False);
824	    }
825	}
826
827	/* if we weren't on the active item, change the active item and turn
828	 * it on
829	 */
830	if (!done)
831	{
832	    ActiveItem = mi;
833	    if (ActiveItem && ActiveItem->func != F_TITLE && !ActiveItem->state)
834	    {
835		ActiveItem->state = 1;
836		PaintEntry(ActiveMenu, ActiveItem, False);
837	    }
838	}
839
840	/* now check to see if we were over the arrow of a pull right entry */
841	if (ActiveItem && ActiveItem->func == F_MENU &&
842	   ((ActiveMenu->width - x) < (ActiveMenu->width / 3)))
843	{
844	    MenuRoot *save = ActiveMenu;
845	    int savex = MenuOrigins[MenuDepth - 1].x;
846	    int savey = MenuOrigins[MenuDepth - 1].y;
847
848	    if (MenuDepth < MAXMENUDEPTH) {
849		if (ActiveMenu == Scr->Workspaces)
850		    CurrentSelectedWorkspace = ActiveItem->item;
851		PopUpMenu (ActiveItem->sub,
852			   (savex + (((2 * ActiveMenu->width) / 3) - 1)),
853			   (savey + ActiveItem->item_num * Scr->EntryHeight)
854			   /*(savey + ActiveItem->item_num * Scr->EntryHeight +
855			    (Scr->EntryHeight >> 1))*/, False);
856		CurrentSelectedWorkspace = NULL;
857	    } else if (!badItem) {
858		XBell (dpy, 0);
859		badItem = ActiveItem;
860	    }
861
862	    /* if the menu did get popped up, unhighlight the active item */
863	    if (save != ActiveMenu && ActiveItem->state)
864	    {
865		ActiveItem->state = 0;
866		PaintEntry(save, ActiveItem, False);
867		ActiveItem = NULL;
868	    }
869	}
870	if (badItem != ActiveItem) badItem = NULL;
871	XFlush(dpy);
872    }
873}
874
875
876
877/***********************************************************************
878 *
879 *  Procedure:
880 *	NewMenuRoot - create a new menu root
881 *
882 *  Returned Value:
883 *	(MenuRoot *)
884 *
885 *  Inputs:
886 *	name	- the name of the menu root
887 *
888 ***********************************************************************
889 */
890
891MenuRoot *NewMenuRoot(char *name)
892{
893    MenuRoot *tmp;
894
895#define UNUSED_PIXEL ((unsigned long) (~0))	/* more than 24 bits */
896
897    tmp = (MenuRoot *) malloc(sizeof(MenuRoot));
898    tmp->highlight.fore = UNUSED_PIXEL;
899    tmp->highlight.back = UNUSED_PIXEL;
900    tmp->name = name;
901    tmp->prev = NULL;
902    tmp->first = NULL;
903    tmp->last = NULL;
904    tmp->defaultitem = NULL;
905    tmp->items = 0;
906    tmp->width = 0;
907    tmp->mapped = NEVER_MAPPED;
908    tmp->pull = FALSE;
909    tmp->w = None;
910    tmp->shadow = None;
911    tmp->real_menu = FALSE;
912
913    if (Scr->MenuList == NULL)
914    {
915	Scr->MenuList = tmp;
916	Scr->MenuList->next = NULL;
917    }
918
919    if (Scr->LastMenu == NULL)
920    {
921	Scr->LastMenu = tmp;
922	Scr->LastMenu->next = NULL;
923    }
924    else
925    {
926	Scr->LastMenu->next = tmp;
927	Scr->LastMenu = tmp;
928	Scr->LastMenu->next = NULL;
929    }
930
931    if (strcmp(name, TWM_WINDOWS) == 0)
932	Scr->Windows = tmp;
933
934    if (strcmp(name, TWM_ICONS) == 0)
935	Scr->Icons = tmp;
936
937    if (strcmp(name, TWM_WORKSPACES) == 0) {
938	Scr->Workspaces = tmp;
939	if (!Scr->Windows) NewMenuRoot (TWM_WINDOWS);
940    }
941    if (strcmp(name, TWM_ALLWINDOWS) == 0)
942	Scr->AllWindows = tmp;
943
944    /* Added by dl 2004 */
945    if (strcmp(name, TWM_ALLICONS) == 0)
946    Scr->AllIcons = tmp;
947
948    /* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29       */
949    if (strcmp(name, TWM_KEYS) == 0)
950	Scr->Keys = tmp;
951
952    if (strcmp(name, TWM_VISIBLE) == 0)
953      Scr->Visible = tmp;
954
955    /* End addition */
956
957    return (tmp);
958}
959
960
961
962/***********************************************************************
963 *
964 *  Procedure:
965 *	AddToMenu - add an item to a root menu
966 *
967 *  Returned Value:
968 *	(MenuItem *)
969 *
970 *  Inputs:
971 *	menu	- pointer to the root menu to add the item
972 *	item	- the text to appear in the menu
973 *	action	- the string to possibly execute
974 *	sub	- the menu root if it is a pull-right entry
975 *	func	- the numeric function
976 *	fore	- foreground color string
977 *	back	- background color string
978 *
979 ***********************************************************************
980 */
981
982MenuItem *AddToMenu(MenuRoot *menu, char *item, char *action,
983		    MenuRoot *sub, int func, char *fore, char *back)
984{
985    MenuItem *tmp;
986    int width;
987    char *itemname;
988    XRectangle ink_rect;
989    XRectangle logical_rect;
990
991#ifdef DEBUG_MENUS
992    fprintf(stderr, "adding menu item=\"%s\", action=%s, sub=%d, f=%d\n",
993	item, action, sub, func);
994#endif
995
996    tmp = (MenuItem *) malloc(sizeof(MenuItem));
997    tmp->root = menu;
998
999    if (menu->first == NULL)
1000    {
1001	menu->first = tmp;
1002	tmp->prev = NULL;
1003    }
1004    else
1005    {
1006	menu->last->next = tmp;
1007	tmp->prev = menu->last;
1008    }
1009    menu->last = tmp;
1010
1011    if ((menu == Scr->Workspaces) ||
1012	(menu == Scr->Windows) ||
1013	(menu == Scr->Icons) ||
1014	(menu == Scr->AllWindows) ||
1015
1016	/* Added by dl 2004 */
1017	(menu == Scr->AllIcons) ||
1018
1019	/* Added by Dan Lillehorn (dl@dl.nu) 2000-02-29 */
1020	(menu == Scr->Keys) ||
1021	(menu == Scr->Visible)) {
1022
1023	itemname = item;
1024    } else
1025    if (*item == '*') {
1026	itemname = item + 1;
1027	menu->defaultitem = tmp;
1028    }
1029    else {
1030	itemname = item;
1031    }
1032
1033    tmp->item = itemname;
1034    tmp->strlen = strlen(itemname);
1035    tmp->action = action;
1036    tmp->next = NULL;
1037    tmp->sub = NULL;
1038    tmp->state = 0;
1039    tmp->func = func;
1040    tmp->separated = 0;
1041
1042    if (!Scr->HaveFonts) CreateFonts();
1043
1044    XmbTextExtents(Scr->MenuFont.font_set,
1045		   itemname, tmp->strlen,
1046		   &ink_rect, &logical_rect);
1047    width = logical_rect.width;
1048
1049    if (width <= 0)
1050	width = 1;
1051    if (width > menu->width)
1052	menu->width = width;
1053
1054    tmp->user_colors = FALSE;
1055    if (Scr->Monochrome == COLOR && fore != NULL)
1056    {
1057	int save;
1058
1059	save = Scr->FirstTime;
1060	Scr->FirstTime = TRUE;
1061	GetColor(COLOR, &tmp->normal.fore, fore);
1062	GetColor(COLOR, &tmp->normal.back, back);
1063	if (Scr->use3Dmenus && !Scr->BeNiceToColormap) GetShadeColors (&tmp->normal);
1064	Scr->FirstTime = save;
1065	tmp->user_colors = TRUE;
1066    }
1067    if (sub != NULL)
1068    {
1069	tmp->sub = sub;
1070	menu->pull = TRUE;
1071    }
1072    tmp->item_num = menu->items++;
1073
1074    return (tmp);
1075}
1076
1077
1078void MakeMenus(void)
1079{
1080    MenuRoot *mr;
1081
1082    for (mr = Scr->MenuList; mr != NULL; mr = mr->next)
1083    {
1084	if (mr->real_menu == FALSE)
1085	    continue;
1086
1087	mr->pinned = False;
1088	MakeMenu(mr);
1089    }
1090}
1091
1092
1093
1094int MakeMenu(MenuRoot *mr)
1095{
1096    MenuItem *start, *end, *cur, *tmp;
1097    XColor f1, f2, f3;
1098    XColor b1, b2, b3;
1099    XColor save_fore, save_back;
1100    int num, i;
1101    int fred, fgreen, fblue;
1102    int bred, bgreen, bblue;
1103    int width, borderwidth;
1104    unsigned long valuemask;
1105    XSetWindowAttributes attributes;
1106    Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
1107    XRectangle ink_rect;
1108    XRectangle logical_rect;
1109
1110    Scr->EntryHeight = Scr->MenuFont.height + 4;
1111
1112    /* lets first size the window accordingly */
1113    if (mr->mapped == NEVER_MAPPED)
1114    {
1115	if (mr->pull == TRUE) {
1116	    mr->width += 16 + 10;
1117	}
1118	width = mr->width + 10;
1119	for (cur = mr->first; cur != NULL; cur = cur->next) {
1120	    if (cur->func != F_TITLE)
1121		cur->x = 5;
1122	    else {
1123		XmbTextExtents(Scr->MenuFont.font_set, cur->item, cur->strlen,
1124			       &ink_rect, &logical_rect);
1125		cur->x = width - logical_rect.width;
1126		cur->x /= 2;
1127	    }
1128	}
1129	mr->height = mr->items * Scr->EntryHeight;
1130	mr->width += 10;
1131	if (Scr->use3Dmenus) {
1132	    mr->width  += 2 * Scr->MenuShadowDepth;
1133	    mr->height += 2 * Scr->MenuShadowDepth;
1134	}
1135	if (Scr->Shadow && ! mr->pinned)
1136	{
1137	    /*
1138	     * Make sure that you don't draw into the shadow window or else
1139	     * the background bits there will get saved
1140	     */
1141	    valuemask = (CWBackPixel | CWBorderPixel);
1142	    attributes.background_pixel = Scr->MenuShadowColor;
1143	    attributes.border_pixel = Scr->MenuShadowColor;
1144	    if (Scr->SaveUnder) {
1145		valuemask |= CWSaveUnder;
1146		attributes.save_under = True;
1147	    }
1148	    mr->shadow = XCreateWindow (dpy, Scr->Root, 0, 0,
1149					(unsigned int) mr->width,
1150					(unsigned int) mr->height,
1151					(unsigned int)0,
1152					CopyFromParent,
1153					(unsigned int) CopyFromParent,
1154					(Visual *) CopyFromParent,
1155					valuemask, &attributes);
1156	}
1157
1158	valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
1159	attributes.background_pixel = Scr->MenuC.back;
1160	attributes.border_pixel = Scr->MenuC.fore;
1161	if (mr->pinned) {
1162	    attributes.event_mask = (ExposureMask | EnterWindowMask
1163				| LeaveWindowMask | ButtonPressMask
1164				| ButtonReleaseMask | PointerMotionMask
1165				| ButtonMotionMask
1166				);
1167	    attributes.cursor = Scr->MenuCursor;
1168	    valuemask |= CWCursor;
1169	}
1170	else
1171	    attributes.event_mask = (ExposureMask | EnterWindowMask);
1172
1173	if (Scr->SaveUnder && ! mr->pinned) {
1174	    valuemask |= CWSaveUnder;
1175	    attributes.save_under = True;
1176	}
1177	if (Scr->BackingStore) {
1178	    valuemask |= CWBackingStore;
1179	    attributes.backing_store = Always;
1180	}
1181	borderwidth = Scr->use3Dmenus ? 0 : 1;
1182	mr->w = XCreateWindow (dpy, Scr->Root, 0, 0, (unsigned int) mr->width,
1183			       (unsigned int) mr->height, (unsigned int) borderwidth,
1184			       CopyFromParent, (unsigned int) CopyFromParent,
1185			       (Visual *) CopyFromParent,
1186			       valuemask, &attributes);
1187
1188
1189	XSaveContext(dpy, mr->w, MenuContext, (XPointer)mr);
1190	XSaveContext(dpy, mr->w, ScreenContext, (XPointer)Scr);
1191
1192	mr->mapped = UNMAPPED;
1193    }
1194
1195    if (Scr->use3Dmenus && (Scr->Monochrome == COLOR) &&  (mr->highlight.back == UNUSED_PIXEL)) {
1196	XColor xcol;
1197	char colname [32];
1198	short save;
1199
1200	xcol.pixel = Scr->MenuC.back;
1201	XQueryColor (dpy, cmap, &xcol);
1202	sprintf (colname, "#%04x%04x%04x",
1203		5 * ((int)xcol.red   / 6),
1204		5 * ((int)xcol.green / 6),
1205		5 * ((int)xcol.blue  / 6));
1206	save = Scr->FirstTime;
1207	Scr->FirstTime = True;
1208	GetColor (Scr->Monochrome, &mr->highlight.back, colname);
1209	Scr->FirstTime = save;
1210    }
1211
1212    if (Scr->use3Dmenus && (Scr->Monochrome == COLOR) && (mr->highlight.fore == UNUSED_PIXEL)) {
1213	XColor xcol;
1214	char colname [32];
1215	short save;
1216
1217	xcol.pixel = Scr->MenuC.fore;
1218	XQueryColor (dpy, cmap, &xcol);
1219	sprintf (colname, "#%04x%04x%04x",
1220		5 * ((int)xcol.red   / 6),
1221		5 * ((int)xcol.green / 6),
1222		5 * ((int)xcol.blue  / 6));
1223	save = Scr->FirstTime;
1224	Scr->FirstTime = True;
1225	GetColor (Scr->Monochrome, &mr->highlight.fore, colname);
1226	Scr->FirstTime = save;
1227    }
1228    if (Scr->use3Dmenus && !Scr->BeNiceToColormap) GetShadeColors (&mr->highlight);
1229
1230    /* get the default colors into the menus */
1231    for (tmp = mr->first; tmp != NULL; tmp = tmp->next)
1232    {
1233	if (!tmp->user_colors) {
1234	    if (tmp->func != F_TITLE) {
1235		tmp->normal.fore = Scr->MenuC.fore;
1236		tmp->normal.back = Scr->MenuC.back;
1237	    } else {
1238		tmp->normal.fore = Scr->MenuTitleC.fore;
1239		tmp->normal.back = Scr->MenuTitleC.back;
1240	    }
1241	}
1242
1243	if (mr->highlight.fore != UNUSED_PIXEL)
1244	{
1245	    tmp->highlight.fore = mr->highlight.fore;
1246	    tmp->highlight.back = mr->highlight.back;
1247	}
1248	else
1249	{
1250	    tmp->highlight.fore = tmp->normal.back;
1251	    tmp->highlight.back = tmp->normal.fore;
1252	}
1253	if (Scr->use3Dmenus && !Scr->BeNiceToColormap) {
1254	    if (tmp->func != F_TITLE)
1255		GetShadeColors (&tmp->highlight);
1256	    else
1257		GetShadeColors (&tmp->normal);
1258	}
1259    }
1260    mr->pmenu = NULL;
1261
1262    if (Scr->Monochrome == MONOCHROME || !Scr->InterpolateMenuColors)
1263	return 0;
1264
1265    start = mr->first;
1266    while (TRUE)
1267    {
1268	for (; start != NULL; start = start->next)
1269	{
1270	    if (start->user_colors)
1271		break;
1272	}
1273	if (start == NULL)
1274	    break;
1275
1276	for (end = start->next; end != NULL; end = end->next)
1277	{
1278	    if (end->user_colors)
1279		break;
1280	}
1281	if (end == NULL)
1282	    break;
1283
1284	/* we have a start and end to interpolate between */
1285	num = end->item_num - start->item_num;
1286
1287	f1.pixel = start->normal.fore;
1288	XQueryColor(dpy, cmap, &f1);
1289	f2.pixel = end->normal.fore;
1290	XQueryColor(dpy, cmap, &f2);
1291
1292	b1.pixel = start->normal.back;
1293	XQueryColor(dpy, cmap, &b1);
1294	b2.pixel = end->normal.back;
1295	XQueryColor(dpy, cmap, &b2);
1296
1297	fred = ((int)f2.red - (int)f1.red) / num;
1298	fgreen = ((int)f2.green - (int)f1.green) / num;
1299	fblue = ((int)f2.blue - (int)f1.blue) / num;
1300
1301	bred = ((int)b2.red - (int)b1.red) / num;
1302	bgreen = ((int)b2.green - (int)b1.green) / num;
1303	bblue = ((int)b2.blue - (int)b1.blue) / num;
1304
1305	f3 = f1;
1306	f3.flags = DoRed | DoGreen | DoBlue;
1307
1308	b3 = b1;
1309	b3.flags = DoRed | DoGreen | DoBlue;
1310
1311	start->highlight.back = start->normal.fore;
1312	start->highlight.fore = start->normal.back;
1313	num -= 1;
1314	for (i = 0, cur = start->next; i < num; i++, cur = cur->next)
1315	{
1316	    f3.red += fred;
1317	    f3.green += fgreen;
1318	    f3.blue += fblue;
1319	    save_fore = f3;
1320
1321	    b3.red += bred;
1322	    b3.green += bgreen;
1323	    b3.blue += bblue;
1324	    save_back = b3;
1325
1326	    XAllocColor(dpy, cmap, &f3);
1327	    XAllocColor(dpy, cmap, &b3);
1328	    cur->highlight.back = cur->normal.fore = f3.pixel;
1329	    cur->highlight.fore = cur->normal.back = b3.pixel;
1330	    cur->user_colors = True;
1331
1332	    f3 = save_fore;
1333	    b3 = save_back;
1334	}
1335	start = end;
1336	start->highlight.back = start->normal.fore;
1337	start->highlight.fore = start->normal.back;
1338    }
1339    return 1;
1340}
1341
1342
1343
1344/***********************************************************************
1345 *
1346 *  Procedure:
1347 *	PopUpMenu - pop up a pull down menu
1348 *
1349 *  Inputs:
1350 *	menu	- the root pointer of the menu to pop up
1351 *	x, y	- location of upper left of menu
1352 *      center	- whether or not to center horizontally over position
1353 *
1354 ***********************************************************************
1355 */
1356
1357Bool PopUpMenu (MenuRoot *menu, int x, int y, Bool center)
1358{
1359    int WindowNameCount;
1360    TwmWindow **WindowNames;
1361    TwmWindow *tmp_win2,*tmp_win3;
1362    int i;
1363    int xl, yt;
1364    Bool clipped;
1365#ifdef CLAUDE
1366    char tmpname3 [256], tmpname4 [256];
1367    int hasmoz = 0;
1368#endif
1369    if (!menu) return False;
1370
1371    InstallRootColormap();
1372
1373    if ((menu == Scr->Windows) ||
1374	(menu == Scr->Icons) ||
1375	(menu == Scr->AllWindows) ||
1376	/* Added by Dan 'dl' Lilliehorn 040607 */
1377	(menu == Scr->AllIcons) ||
1378	/* Added by Dan Lilliehorn (dl@dl.nu) 2000-02-29 */
1379	(menu == Scr->Visible))
1380    {
1381	TwmWindow *tmp_win;
1382	WorkSpace *ws;
1383	Boolean all, icons, visible_, allicons; /* visible, allicons:
1384						  Added by dl */
1385	int func;
1386
1387	/* this is the twm windows menu,  let's go ahead and build it */
1388
1389	all = (menu == Scr->AllWindows);
1390	icons = (menu == Scr->Icons);
1391	visible_ = (menu == Scr->Visible);    /* Added by dl */
1392	allicons = (menu == Scr->AllIcons);
1393	DestroyMenu (menu);
1394
1395	menu->first = NULL;
1396	menu->last = NULL;
1397	menu->items = 0;
1398	menu->width = 0;
1399	menu->mapped = NEVER_MAPPED;
1400	menu->highlight.fore = UNUSED_PIXEL;
1401	menu->highlight.back = UNUSED_PIXEL;
1402	if (menu == Scr->Windows)
1403  	    AddToMenu(menu, "TWM Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR);
1404	else
1405	if (menu == Scr->Icons)
1406  	    AddToMenu(menu, "TWM Icons", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1407	else
1408	if (menu == Scr->Visible) /* Added by dl 2000 */
1409	    AddToMenu(menu, "TWM Visible", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1410	else
1411	if (menu == Scr->AllIcons) /* Added by dl 2004 */
1412	    AddToMenu(menu, "TWM All Icons", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1413	else
1414  	    AddToMenu(menu, "TWM All Windows", NULLSTR, NULL, F_TITLE,NULLSTR,NULLSTR);
1415
1416	ws = NULL;
1417
1418	if (! (all || allicons)
1419	    && CurrentSelectedWorkspace && Scr->workSpaceManagerActive) {
1420	    for (ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
1421		if (strcmp (ws->name, CurrentSelectedWorkspace) == 0) break;
1422	    }
1423	}
1424	if (!Scr->currentvs) return False;
1425	if (!ws) ws = Scr->currentvs->wsw->currentwspc;
1426
1427        for (tmp_win = Scr->FirstWindow, WindowNameCount = 0;
1428             tmp_win != NULL;
1429             tmp_win = tmp_win->next) {
1430	  if (tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) continue;
1431	  if (Scr->ShortAllWindowsMenus && (tmp_win->wspmgr || tmp_win->iconmgr)) continue;
1432
1433	  if (!(all || allicons) && !OCCUPY (tmp_win, ws)) continue;
1434	  if (allicons && !tmp_win->isicon) continue;
1435	  if (icons && !tmp_win->isicon) continue;
1436	  if (visible_ && tmp_win->isicon) continue;  /* added by dl */
1437	  WindowNameCount++;
1438	}
1439        WindowNames = (TwmWindow **)malloc(sizeof(TwmWindow *)*WindowNameCount);
1440	WindowNameCount = 0;
1441        for (tmp_win = Scr->FirstWindow;
1442             tmp_win != NULL;
1443             tmp_win = tmp_win->next)
1444        {
1445	    if (LookInList (Scr->IconMenuDontShow, tmp_win->full_name, &tmp_win->class)) continue;
1446
1447	    if (tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) continue;
1448	    if (Scr->ShortAllWindowsMenus &&
1449		tmp_win == Scr->currentvs->wsw->twm_win) continue;
1450	    if (Scr->ShortAllWindowsMenus && tmp_win->iconmgr) continue;
1451
1452	    if (!(all || allicons)&& ! OCCUPY (tmp_win, ws)) continue;
1453	    if (allicons && !tmp_win->isicon) continue;
1454	    if (icons && !tmp_win->isicon) continue;
1455	    if (visible_ && tmp_win->isicon) continue;  /* added by dl */
1456            tmp_win2 = tmp_win;
1457
1458            for (i = 0; i < WindowNameCount; i++) {
1459		int compresult;
1460		char *tmpname1, *tmpname2;
1461		tmpname1 = tmp_win2->name;
1462		tmpname2 = WindowNames[i]->name;
1463#ifdef CLAUDE
1464		if (strlen (tmpname1) == 1) tmpname1 = " No title";
1465		if (strlen (tmpname2) == 1) tmpname2 = " No title";
1466
1467                if (!strncasecmp (tmp_win2->class.res_class, "navigator", 9) ||
1468		    !strncasecmp (tmp_win2->class.res_class, "mozilla",   7)) {
1469		  tmpname3 [0] = ' '; tmpname3 [1] = '\0';
1470		  strcat (tmpname3, tmpname1);
1471		} else {
1472		  strcpy (tmpname3, tmpname1);
1473		}
1474                if (!strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) ||
1475		    !strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7)) {
1476		  tmpname4 [0] = ' '; tmpname4 [1] = '\0';
1477		  strcat (tmpname4, tmpname2);
1478		} else {
1479		  strcpy (tmpname4, tmpname2);
1480		}
1481		tmpname1 = tmpname3;
1482		tmpname2 = tmpname4;
1483#endif
1484		if (Scr->CaseSensitive)
1485		    compresult = strcmp(tmpname1,tmpname2);
1486		else
1487		    compresult = XmuCompareISOLatin1(tmpname1,tmpname2);
1488                if (compresult < 0) {
1489                    tmp_win3 = tmp_win2;
1490                    tmp_win2 = WindowNames[i];
1491                    WindowNames[i] = tmp_win3;
1492                }
1493            }
1494            WindowNames[WindowNameCount] = tmp_win2;
1495	    WindowNameCount++;
1496        }
1497	func = (all || allicons || CurrentSelectedWorkspace) ? F_WINWARP :
1498	      F_POPUP;
1499        for (i = 0; i < WindowNameCount; i++)
1500        {
1501	    char *tmpname;
1502	    tmpname = WindowNames[i]->name;
1503#ifdef CLAUDE
1504	    if (!strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) ||
1505		!strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7) ||
1506		!strncasecmp (WindowNames[i]->class.res_class, "netscape",  8) ||
1507		!strncasecmp (WindowNames[i]->class.res_class, "konqueror", 9)) {
1508	      hasmoz = 1;
1509	    }
1510	    if (hasmoz && strncasecmp (WindowNames[i]->class.res_class, "navigator", 9) &&
1511		          strncasecmp (WindowNames[i]->class.res_class, "mozilla",   7) &&
1512		          strncasecmp (WindowNames[i]->class.res_class, "netscape",  8) &&
1513		          strncasecmp (WindowNames[i]->class.res_class, "konqueror", 9)) {
1514	      menu->last->separated = 1;
1515	      hasmoz = 0;
1516	    }
1517#endif
1518            AddToMenu(menu, tmpname, (char *)WindowNames[i],
1519                      NULL, func,NULL,NULL);
1520        }
1521        free(WindowNames);
1522
1523	menu->pinned = False;
1524	MakeMenu(menu);
1525    }
1526
1527    /* Keys added by dl */
1528
1529    if (menu == Scr->Keys) {
1530	FuncKey *tmpKey;
1531	char *tmpStr, *tmpStr2;
1532	char modStr[5];
1533	char *oldact = 0;
1534	int oldmod = 0;
1535	int tmpLen;
1536
1537	DestroyMenu (menu);
1538
1539	menu->first = NULL;
1540	menu->last = NULL;
1541	menu->items = 0;
1542	menu->width = 0;
1543	menu->mapped = NEVER_MAPPED;
1544	menu->highlight.fore = UNUSED_PIXEL;
1545	menu->highlight.back = UNUSED_PIXEL;
1546
1547	AddToMenu(menu, "Twm Keys", NULLSTR, NULL, F_TITLE, NULLSTR, NULLSTR);
1548
1549	for (tmpKey = Scr->FuncKeyRoot.next; tmpKey != NULL;  tmpKey = tmpKey->next) {
1550	    if (tmpKey->func != F_EXEC) continue;
1551	    if ((tmpKey->action == oldact) && (tmpKey->mods == oldmod)) continue;
1552	    strcpy (modStr, "");
1553	    switch (tmpKey->mods) {
1554	        case  1: strcpy (modStr, "S");     break;
1555	        case  4: strcpy (modStr, "C");     break;
1556		case  5: strcpy (modStr, "S + C"); break;
1557		case  8: strcpy (modStr, "M");     break;
1558		case  9: strcpy (modStr, "S + M"); break;
1559		case 12: strcpy (modStr, "C + M"); break;
1560		default: break;
1561	    }
1562	    tmpLen = (strlen (tmpKey->name) + strlen (modStr) + 5);
1563	    tmpStr = malloc (sizeof(char) * tmpLen);
1564	    sprintf (tmpStr,"[%s + %s]", tmpKey->name, modStr);
1565	    tmpStr2 = malloc (sizeof(char) * (strlen (tmpKey->action) + tmpLen + 2));
1566	    sprintf (tmpStr2, "%s %s", tmpStr, tmpKey->action);
1567
1568	    AddToMenu (menu, tmpStr2, tmpKey->action, NULL, tmpKey->func, NULLSTR, NULLSTR);
1569	    oldact = tmpKey->action;
1570	    oldmod = tmpKey->mods;
1571	}
1572	menu->pinned = False;
1573	MakeMenu(menu);
1574    }
1575    if (menu->w == None || menu->items == 0) return False;
1576
1577    /* Prevent recursively bringing up menus. */
1578    if ((!menu->pinned) && (menu->mapped == MAPPED)) return False;
1579
1580    /*
1581     * Dynamically set the parent;  this allows pull-ups to also be main
1582     * menus, or to be brought up from more than one place.
1583     */
1584    menu->prev = ActiveMenu;
1585
1586    if (menu->pinned) {
1587	ActiveMenu    = menu;
1588	menu->mapped  = MAPPED;
1589	menu->entered = TRUE;
1590	MenuOrigins [MenuDepth].x = menu->x;
1591	MenuOrigins [MenuDepth].y = menu->y;
1592	MenuDepth++;
1593
1594	XRaiseWindow (dpy, menu->w);
1595	return (True);
1596    }
1597
1598    XGrabPointer(dpy, Scr->Root, True,
1599	ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
1600	ButtonMotionMask | PointerMotionHintMask,
1601	GrabModeAsync, GrabModeAsync,
1602	Scr->Root,
1603	Scr->MenuCursor, CurrentTime);
1604
1605    XGrabKeyboard (dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
1606
1607    ActiveMenu = menu;
1608    menu->mapped = MAPPED;
1609    menu->entered = FALSE;
1610
1611    if (center) {
1612	x -= (menu->width / 2);
1613	y -= (Scr->EntryHeight / 2);	/* sticky menus would be nice here */
1614    }
1615
1616    /*
1617    * clip to screen
1618    */
1619    clipped = FALSE;
1620    if (x + menu->width > Scr->rootw) {
1621	x = Scr->rootw - menu->width;
1622	clipped = TRUE;
1623    }
1624    if (x < 0) {
1625	x = 0;
1626	clipped = TRUE;
1627    }
1628    if (y + menu->height > Scr->rooth) {
1629	y = Scr->rooth - menu->height;
1630	clipped = TRUE;
1631    }
1632    if (y < 0) {
1633	y = 0;
1634	clipped = TRUE;
1635    }
1636    MenuOrigins[MenuDepth].x = x;
1637    MenuOrigins[MenuDepth].y = y;
1638    MenuDepth++;
1639
1640    if (Scr->Root != Scr->CaptiveRoot) {
1641      XReparentWindow (dpy, menu->shadow, Scr->Root, x, y);
1642      XReparentWindow (dpy, menu->w, Scr->Root, x, y);
1643    } else
1644      XMoveWindow (dpy, menu->w, x, y);
1645    if (Scr->Shadow) {
1646	XMoveWindow  (dpy, menu->shadow, x + SHADOWWIDTH, y + SHADOWWIDTH);
1647	XRaiseWindow (dpy, menu->shadow);
1648    }
1649    XMapRaised(dpy, menu->w);
1650if (!Scr->NoWarpToMenuTitle && clipped && center) {
1651	xl = x + (menu->width      / 2);
1652	yt = y + (Scr->EntryHeight / 2);
1653	XWarpPointer (dpy, Scr->Root, Scr->Root, x, y, menu->width, menu->height, xl, yt);
1654    }
1655    if (Scr->Shadow) XMapWindow (dpy, menu->shadow);
1656    XSync(dpy, 0);
1657    return True;
1658}
1659
1660
1661
1662/***********************************************************************
1663 *
1664 *  Procedure:
1665 *	PopDownMenu - unhighlight the current menu selection and
1666 *		take down the menus
1667 *
1668 ***********************************************************************
1669 */
1670
1671int PopDownMenu(void)
1672{
1673    MenuRoot *tmp;
1674
1675    if (ActiveMenu == NULL)
1676	return (1);
1677
1678    if (ActiveItem)
1679    {
1680	ActiveItem->state = 0;
1681	PaintEntry(ActiveMenu, ActiveItem, False);
1682    }
1683
1684    for (tmp = ActiveMenu; tmp != NULL; tmp = tmp->prev)
1685    {
1686	if (! tmp->pinned) HideMenu (tmp);
1687	UninstallRootColormap();
1688    }
1689
1690    XFlush(dpy);
1691    ActiveMenu = NULL;
1692    ActiveItem = NULL;
1693    MenuDepth = 0;
1694    XUngrabKeyboard (dpy, CurrentTime);
1695    if (Context == C_WINDOW || Context == C_FRAME || Context == C_TITLE || Context == C_ICON)
1696      menuFromFrameOrWindowOrTitlebar = TRUE;
1697
1698    return 1;
1699}
1700
1701
1702
1703Bool HideMenu (MenuRoot *menu)
1704{
1705    if (!menu) return False;
1706
1707    if (Scr->Shadow) {
1708	XUnmapWindow (dpy, menu->shadow);
1709    }
1710    XUnmapWindow (dpy, menu->w);
1711    menu->mapped = UNMAPPED;
1712
1713    return True;
1714}
1715
1716/***********************************************************************
1717 *
1718 *  Procedure:
1719 *	FindMenuRoot - look for a menu root
1720 *
1721 *  Returned Value:
1722 *	(MenuRoot *)  - a pointer to the menu root structure
1723 *
1724 *  Inputs:
1725 *	name	- the name of the menu root
1726 *
1727 ***********************************************************************
1728 */
1729
1730MenuRoot *FindMenuRoot(char *name)
1731{
1732    MenuRoot *tmp;
1733
1734    for (tmp = Scr->MenuList; tmp != NULL; tmp = tmp->next)
1735    {
1736	if (strcmp(name, tmp->name) == 0)
1737	    return (tmp);
1738    }
1739    return NULL;
1740}
1741
1742
1743
1744static Bool belongs_to_twm_window (register TwmWindow *t, register Window w)
1745{
1746    if (!t) return False;
1747
1748    if (w == t->frame || w == t->title_w || w == t->hilite_wl || w == t->hilite_wr ||
1749	(t->icon && (w == t->icon->w || w == t->icon->bm_w))) return True;
1750
1751    if (t && t->titlebuttons) {
1752	register TBWindow *tbw;
1753	register int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1754	for (tbw = t->titlebuttons; nb > 0; tbw++, nb--) {
1755	    if (tbw->window == w) return True;
1756	}
1757    }
1758    return False;
1759}
1760
1761
1762
1763
1764/***********************************************************************
1765 *
1766 *  Procedure:
1767 *	resizeFromCenter -
1768 *
1769 ***********************************************************************
1770 */
1771
1772void resizeFromCenter(Window w, TwmWindow *tmp_win)
1773{
1774  int lastx, lasty, bw2;
1775  int namelen;
1776  XRectangle inc_rect;
1777  XRectangle logical_rect;
1778
1779  namelen = strlen (tmp_win->name);
1780  bw2 = tmp_win->frame_bw * 2;
1781  AddingW = tmp_win->attr.width + bw2 + 2 * tmp_win->frame_bw3D;
1782  AddingH = tmp_win->attr.height + tmp_win->title_height + bw2 + 2 * tmp_win->frame_bw3D;
1783
1784  XmbTextExtents(Scr->SizeFont.font_set, tmp_win->name, namelen,
1785		 &inc_rect, &logical_rect);
1786
1787  XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
1788	       &DragWidth, &DragHeight,
1789	       &JunkBW, &JunkDepth);
1790
1791  XWarpPointer(dpy, None, w,
1792	       0, 0, 0, 0, DragWidth/2, DragHeight/2);
1793  XQueryPointer (dpy, Scr->Root, &JunkRoot,
1794		 &JunkChild, &JunkX, &JunkY,
1795		 &AddingX, &AddingY, &JunkMask);
1796
1797  lastx = -10000;
1798  lasty = -10000;
1799
1800  MenuStartResize(tmp_win, origDragX, origDragY, DragWidth, DragHeight);
1801  while (TRUE)
1802    {
1803      XMaskEvent(dpy,
1804		 ButtonPressMask | PointerMotionMask | ExposureMask, &Event);
1805
1806      if (Event.type == MotionNotify) {
1807	/* discard any extra motion events before a release */
1808	while(XCheckMaskEvent(dpy,
1809			      ButtonMotionMask | ButtonPressMask, &Event))
1810	  if (Event.type == ButtonPress)
1811	    break;
1812      }
1813
1814      if (Event.type == ButtonPress)
1815	{
1816	  MenuEndResize(tmp_win);
1817	  XMoveResizeWindow(dpy, w, AddingX, AddingY, AddingW, AddingH);
1818	  break;
1819	}
1820
1821      if (Event.type != MotionNotify) {
1822	DispatchEvent2 ();
1823	continue;
1824      }
1825
1826      /*
1827       * XXX - if we are going to do a loop, we ought to consider
1828       * using multiple GXxor lines so that we don't need to
1829       * grab the server.
1830       */
1831      XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
1832		    &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask);
1833
1834      if (lastx != AddingX || lasty != AddingY)
1835	{
1836	  MenuDoResize(AddingX, AddingY, tmp_win);
1837
1838	  lastx = AddingX;
1839	  lasty = AddingY;
1840	}
1841
1842    }
1843}
1844
1845
1846
1847/***********************************************************************
1848 *
1849 *  Procedure:
1850 *	ExecuteFunction - execute a twm root function
1851 *
1852 *  Inputs:
1853 *	func	- the function to execute
1854 *	action	- the menu action to execute
1855 *	w	- the window to execute this function on
1856 *	tmp_win	- the twm window structure
1857 *	event	- the event that caused the function
1858 *	context - the context in which the button was pressed
1859 *	pulldown- flag indicating execution from pull down menu
1860 *
1861 *  Returns:
1862 *	TRUE if should continue with remaining actions else FALSE to abort
1863 *
1864 ***********************************************************************
1865 */
1866
1867int ExecuteFunction(int func, void *action, Window w, TwmWindow *tmp_win,
1868		    XEvent *eventp, int context, int pulldown)
1869{
1870    static Time last_time = 0;
1871    char tmp[200];
1872    char *ptr;
1873    char buff[MAX_FILE_SIZE];
1874    int count, fd;
1875    Window rootw;
1876    int origX, origY;
1877    int do_next_action = TRUE;
1878    int moving_icon = FALSE;
1879    Bool fromtitlebar = False;
1880    Bool from3dborder = False;
1881    TwmWindow *t;
1882
1883    RootFunction = 0;
1884    if (Cancel)
1885	return TRUE;			/* XXX should this be FALSE? */
1886
1887    switch (func)
1888    {
1889    case F_UPICONMGR:
1890    case F_LEFTICONMGR:
1891    case F_RIGHTICONMGR:
1892    case F_DOWNICONMGR:
1893    case F_FORWICONMGR:
1894    case F_BACKICONMGR:
1895    case F_NEXTICONMGR:
1896    case F_PREVICONMGR:
1897    case F_NOP:
1898    case F_TITLE:
1899    case F_DELTASTOP:
1900    case F_RAISELOWER:
1901    case F_WARPTOSCREEN:
1902    case F_WARPTO:
1903    case F_WARPRING:
1904    case F_WARPTOICONMGR:
1905    case F_COLORMAP:
1906    case F_ALTKEYMAP:
1907    case F_ALTCONTEXT:
1908	break;
1909
1910    default:
1911        XGrabPointer(dpy, Scr->Root, True,
1912            ButtonPressMask | ButtonReleaseMask,
1913            GrabModeAsync, GrabModeAsync,
1914            Scr->Root, Scr->WaitCursor, CurrentTime);
1915	break;
1916    }
1917
1918    switch (func)
1919    {
1920#ifdef SOUNDS
1921    case F_TOGGLESOUND:
1922	toggle_sound();
1923	break;
1924    case F_REREADSOUNDS:
1925	reread_sounds();
1926	break;
1927#endif
1928    case F_NOP:
1929    case F_TITLE:
1930	break;
1931
1932    case F_DELTASTOP:
1933	if (WindowMoved) do_next_action = FALSE;
1934	break;
1935
1936    case F_RESTART: {
1937	DoRestart(eventp->xbutton.time);
1938	break;
1939    }
1940    case F_UPICONMGR:
1941    case F_DOWNICONMGR:
1942    case F_LEFTICONMGR:
1943    case F_RIGHTICONMGR:
1944    case F_FORWICONMGR:
1945    case F_BACKICONMGR:
1946	MoveIconManager(func);
1947        break;
1948
1949    case F_FORWMAPICONMGR:
1950    case F_BACKMAPICONMGR:
1951	MoveMappedIconManager(func);
1952	break;
1953
1954    case F_NEXTICONMGR:
1955    case F_PREVICONMGR:
1956	JumpIconManager(func);
1957        break;
1958
1959    case F_SHOWLIST:
1960	if (Scr->NoIconManagers) break;
1961	ShowIconManager ();
1962	break;
1963
1964    case F_STARTANIMATION :
1965	StartAnimation ();
1966	break;
1967
1968    case F_STOPANIMATION :
1969	StopAnimation ();
1970	break;
1971
1972    case F_SPEEDUPANIMATION :
1973	ModifyAnimationSpeed (1);
1974	break;
1975
1976    case F_SLOWDOWNANIMATION :
1977	ModifyAnimationSpeed (-1);
1978	break;
1979
1980    case F_HIDELIST:
1981	if (Scr->NoIconManagers) break;
1982	HideIconManager ();
1983	break;
1984
1985    case F_SHOWWORKMGR:
1986	if (! Scr->workSpaceManagerActive) break;
1987	DeIconify (Scr->currentvs->wsw->twm_win);
1988	RaiseWindow(Scr->currentvs->wsw->twm_win);
1989	break;
1990
1991    case F_HIDEWORKMGR:
1992	if (! Scr->workSpaceManagerActive) break;
1993	Iconify (Scr->currentvs->wsw->twm_win, eventp->xbutton.x_root - 5,
1994		     eventp->xbutton.y_root - 5);
1995	break;
1996
1997    case F_TOGGLEWORKMGR:
1998	if (! Scr->workSpaceManagerActive) break;
1999	if (Scr->currentvs->wsw->twm_win->mapped)
2000	    Iconify (Scr->currentvs->wsw->twm_win, eventp->xbutton.x_root - 5,
2001		     eventp->xbutton.y_root - 5);
2002	else {
2003	    DeIconify (Scr->currentvs->wsw->twm_win);
2004	    RaiseWindow(Scr->currentvs->wsw->twm_win);
2005	}
2006	break;
2007
2008    case F_TOGGLESTATE :
2009	WMapToggleState (Scr->currentvs);
2010	break;
2011
2012    case F_SETBUTTONSTATE :
2013	WMapSetButtonsState (Scr->currentvs);
2014	break;
2015
2016    case F_SETMAPSTATE :
2017	WMapSetMapState (Scr->currentvs);
2018	break;
2019
2020    case F_PIN :
2021	if (! ActiveMenu) break;
2022	if (ActiveMenu->pinned) {
2023	    XUnmapWindow (dpy, ActiveMenu->w);
2024	    ActiveMenu->mapped = UNMAPPED;
2025	}
2026	else {
2027	    XWindowAttributes attr;
2028	    MenuRoot *menu;
2029
2030	    if (ActiveMenu->pmenu == NULL) {
2031		menu  = (MenuRoot*) malloc (sizeof (struct MenuRoot));
2032		*menu = *ActiveMenu;
2033		menu->pinned = True;
2034		menu->mapped = NEVER_MAPPED;
2035		menu->width -= 10;
2036		if (menu->pull) menu->width -= 16 + 10;
2037		MakeMenu (menu);
2038		ActiveMenu->pmenu = menu;
2039	    }
2040	    else menu = ActiveMenu->pmenu;
2041	    if (menu->mapped == MAPPED) break;
2042	    XGetWindowAttributes (dpy, ActiveMenu->w, &attr);
2043	    menu->x = attr.x;
2044	    menu->y = attr.y;
2045	    XMoveWindow (dpy, menu->w, menu->x, menu->y);
2046	    XMapRaised  (dpy, menu->w);
2047	    menu->mapped = MAPPED;
2048	}
2049	PopDownMenu();
2050	break;
2051
2052    case F_MOVEMENU:
2053	break;
2054
2055    case F_FITTOCONTENT :
2056	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2057	if (!tmp_win->iswinbox) {
2058	    XBell (dpy, 0);
2059	    break;
2060	}
2061	fittocontent (tmp_win);
2062	break;
2063
2064    case F_VANISH:
2065	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2066
2067	WMgrRemoveFromCurrentWorkSpace (Scr->currentvs, tmp_win);
2068	break;
2069
2070    case F_WARPHERE:
2071	WMgrAddToCurrentWorkSpaceAndWarp (Scr->currentvs, action);
2072	break;
2073
2074    case F_ADDTOWORKSPACE:
2075	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2076	AddToWorkSpace (action, tmp_win);
2077	break;
2078
2079    case F_REMOVEFROMWORKSPACE:
2080	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2081	RemoveFromWorkSpace (action, tmp_win);
2082	break;
2083
2084    case F_TOGGLEOCCUPATION:
2085	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2086	ToggleOccupation (action, tmp_win);
2087	break;
2088
2089    case F_MOVETONEXTWORKSPACE:
2090		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2091		MoveToNextWorkSpace(Scr->currentvs,tmp_win);
2092		break;
2093
2094    case F_MOVETOPREVWORKSPACE:
2095		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2096		MoveToPrevWorkSpace(Scr->currentvs,tmp_win);
2097		break;
2098
2099    case F_MOVETONEXTWORKSPACEANDFOLLOW:
2100		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2101		MoveToNextWorkSpaceAndFollow(Scr->currentvs,tmp_win);
2102		break;
2103
2104    case F_MOVETOPREVWORKSPACEANDFOLLOW:
2105		if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2106		MoveToPrevWorkSpaceAndFollow(Scr->currentvs,tmp_win);
2107		break;
2108
2109    case F_SORTICONMGR:
2110	if (DeferExecution(context, func, Scr->SelectCursor))
2111	    return TRUE;
2112
2113	{
2114	    int save_sort;
2115
2116	    save_sort = Scr->SortIconMgr;
2117	    Scr->SortIconMgr = TRUE;
2118
2119	    if (context == C_ICONMGR)
2120		SortIconManager((IconMgr *) NULL);
2121	    else if (tmp_win->iconmgr)
2122		SortIconManager(tmp_win->iconmgrp);
2123	    else
2124		XBell(dpy, 0);
2125
2126	    Scr->SortIconMgr = save_sort;
2127	}
2128	break;
2129
2130    case F_ALTKEYMAP: {
2131	int alt, stat_;
2132
2133	if (! action) return TRUE;
2134	stat_ = sscanf (action, "%d", &alt);
2135	if (stat_ != 1) return TRUE;
2136	if ((alt < 1) || (alt > 5)) return TRUE;
2137	AlternateKeymap = Alt1Mask << (alt - 1);
2138	XGrabPointer (dpy, Scr->Root, True, ButtonPressMask | ButtonReleaseMask,
2139			GrabModeAsync, GrabModeAsync,
2140			Scr->Root, Scr->AlterCursor, CurrentTime);
2141	XGrabKeyboard (dpy, Scr->Root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
2142	return TRUE;
2143    }
2144
2145    case F_ALTCONTEXT: {
2146	AlternateContext = True;
2147	XGrabPointer (dpy, Scr->Root, False, ButtonPressMask | ButtonReleaseMask,
2148			GrabModeAsync, GrabModeAsync,
2149			Scr->Root, Scr->AlterCursor, CurrentTime);
2150	XGrabKeyboard (dpy, Scr->Root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
2151	return TRUE;
2152    }
2153    case F_IDENTIFY:
2154	if (DeferExecution(context, func, Scr->SelectCursor))
2155	    return TRUE;
2156
2157	Identify(tmp_win);
2158	break;
2159
2160    case F_INITSIZE: {
2161	int grav, x, y;
2162	unsigned int width, height, swidth, sheight;
2163
2164	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2165	grav = ((tmp_win->hints.flags & PWinGravity)
2166		      ? tmp_win->hints.win_gravity : NorthWestGravity);
2167
2168	if (!(tmp_win->hints.flags & USSize) && !(tmp_win->hints.flags & PSize)) break;
2169
2170	width  = tmp_win->hints.width  + 2 * tmp_win->frame_bw3D;
2171	height  = tmp_win->hints.height + 2 * tmp_win->frame_bw3D + tmp_win->title_height;
2172	ConstrainSize (tmp_win, &width, &height);
2173
2174	x  = tmp_win->frame_x;
2175	y  = tmp_win->frame_y;
2176	swidth = tmp_win->frame_width;
2177	sheight = tmp_win->frame_height;
2178	switch (grav) {
2179	    case ForgetGravity :
2180	    case StaticGravity :
2181	    case NorthWestGravity :
2182	    case NorthGravity :
2183	    case WestGravity :
2184	    case CenterGravity :
2185		break;
2186
2187	    case NorthEastGravity :
2188	    case EastGravity :
2189		x += swidth - width;
2190		break;
2191
2192	    case SouthWestGravity :
2193	    case SouthGravity :
2194		y += sheight - height;
2195		break;
2196
2197	    case SouthEastGravity :
2198		x += swidth - width;
2199		y += sheight - height;
2200		break;
2201	}
2202	SetupWindow (tmp_win, x, y, width, height, -1);
2203	break;
2204    }
2205
2206    case F_MOVERESIZE: {
2207	int x, y, mask;
2208	unsigned int width, height;
2209	int px = 20, py = 30;
2210
2211	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
2212	mask = XParseGeometry (action, &x, &y, &width, &height);
2213	if (!(mask &  WidthValue)) width = tmp_win->frame_width;
2214	else width += 2 * tmp_win->frame_bw3D;
2215	if (!(mask & HeightValue)) height = tmp_win->frame_height;
2216	else height += 2 * tmp_win->frame_bw3D + tmp_win->title_height;
2217	ConstrainSize (tmp_win, &width, &height);
2218	if (mask & XValue) {
2219	    if (mask & XNegative) x += Scr->rootw  - width;
2220	} else x = tmp_win->frame_x;
2221	if (mask & YValue) {
2222	    if (mask & YNegative) y += Scr->rooth - height;
2223	} else y = tmp_win->frame_y;
2224
2225	{
2226	    int		 junkX, junkY;
2227	    unsigned int junkK;
2228	    Window	 junkW;
2229	    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
2230	}
2231	px -= tmp_win->frame_x; if (px > width) px = width / 2;
2232	py -= tmp_win->frame_y; if (py > height) px = height / 2;
2233	SetupWindow (tmp_win, x, y, width, height, -1);
2234	XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, x + px, y + py);
2235	break;
2236    }
2237
2238    case F_VERSION:
2239	Identify ((TwmWindow *) NULL);
2240	break;
2241
2242    case F_AUTORAISE:
2243	if (DeferExecution(context, func, Scr->SelectCursor))
2244	    return TRUE;
2245
2246	tmp_win->auto_raise = !tmp_win->auto_raise;
2247	if (tmp_win->auto_raise) ++(Scr->NumAutoRaises);
2248	else --(Scr->NumAutoRaises);
2249	break;
2250
2251    case F_AUTOLOWER:
2252	if (DeferExecution(context, func, Scr->SelectCursor))
2253	    return TRUE;
2254
2255	tmp_win->auto_lower = !tmp_win->auto_lower;
2256	if (tmp_win->auto_lower) ++(Scr->NumAutoLowers);
2257	else --(Scr->NumAutoLowers);
2258	break;
2259
2260    case F_BEEP:
2261	XBell(dpy, 0);
2262	break;
2263
2264    case F_POPUP:
2265	tmp_win = (TwmWindow *)action;
2266	if (! tmp_win) break;
2267	if (Scr->WindowFunction.func != 0)
2268	{
2269	   ExecuteFunction(Scr->WindowFunction.func,
2270			   Scr->WindowFunction.item->action,
2271			   w, tmp_win, eventp, C_FRAME, FALSE);
2272	}
2273	else
2274	{
2275	    DeIconify(tmp_win);
2276	    RaiseWindow (tmp_win);
2277	}
2278	break;
2279
2280    case F_WINWARP:
2281	tmp_win = (TwmWindow *)action;
2282
2283	if (! tmp_win) break;
2284	if (Scr->WarpUnmapped || tmp_win->mapped) {
2285	    if (!tmp_win->mapped) DeIconify (tmp_win);
2286	    WarpToWindow (tmp_win, Scr->RaiseOnWarp);
2287	}
2288	break;
2289
2290    case F_RESIZE:
2291	EventHandler[EnterNotify] = HandleUnknown;
2292	EventHandler[LeaveNotify] = HandleUnknown;
2293	if (DeferExecution(context, func, Scr->MoveCursor))
2294	    return TRUE;
2295
2296	PopDownMenu();
2297	if (tmp_win->squeezed) {
2298	    XBell (dpy, 0);
2299	    break;
2300	}
2301	if (tmp_win->OpaqueResize) {
2302	    /*
2303	     * OpaqueResize defaults to a thousand.  Assume that any number
2304	     * >= 1000 is "infinity" and don't bother calculating.
2305	     */
2306	    if (Scr->OpaqueResizeThreshold >= 1000)
2307		Scr->OpaqueResize = TRUE;
2308	    else {
2309		/*
2310		 * scrsz will hold the number of pixels in your resolution,
2311		 * which can get big.  [signed] int may not cut it.
2312		 */
2313		unsigned long winsz, scrsz;
2314		winsz = tmp_win->frame_width * tmp_win->frame_height;
2315		scrsz = Scr->rootw  * Scr->rooth;
2316		if (winsz > (scrsz * (Scr->OpaqueResizeThreshold / 100.0)))
2317		    Scr->OpaqueResize = FALSE;
2318		else
2319		    Scr->OpaqueResize = TRUE;
2320	    }
2321	}
2322	else
2323	    Scr->OpaqueResize = FALSE;
2324
2325	if (pulldown)
2326	    XWarpPointer(dpy, None, Scr->Root,
2327		0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
2328
2329	if (!tmp_win->icon || (w != tmp_win->icon->w)) {	/* can't resize icons */
2330
2331/*	  fromMenu = False;  ????? */
2332	  if ((Context == C_FRAME || Context == C_WINDOW || Context == C_TITLE)
2333	      && fromMenu)
2334	    resizeFromCenter(w, tmp_win);
2335	  else {
2336	    /*
2337	     * see if this is being done from the titlebar
2338	     */
2339	    from3dborder = (eventp->xbutton.window == tmp_win->frame);
2340	    fromtitlebar = !from3dborder &&
2341	      belongs_to_twm_window (tmp_win, eventp->xbutton.window);
2342
2343	    /* Save pointer position so we can tell if it was moved or
2344	       not during the resize. */
2345	    ResizeOrigX = eventp->xbutton.x_root;
2346	    ResizeOrigY = eventp->xbutton.y_root;
2347
2348	    StartResize (eventp, tmp_win, fromtitlebar, from3dborder);
2349
2350	    do {
2351		XMaskEvent(dpy,
2352			   ButtonPressMask | ButtonReleaseMask |
2353			   EnterWindowMask | LeaveWindowMask |
2354			   ButtonMotionMask | VisibilityChangeMask | ExposureMask, &Event);
2355
2356		if (fromtitlebar && Event.type == ButtonPress) {
2357		    fromtitlebar = False;
2358		    continue;
2359		}
2360
2361	    	if (Event.type == MotionNotify) {
2362		  /* discard any extra motion events before a release */
2363		  while
2364		    (XCheckMaskEvent
2365		     (dpy, ButtonMotionMask | ButtonReleaseMask, &Event))
2366		      if (Event.type == ButtonRelease)
2367			break;
2368		}
2369
2370	      if (!DispatchEvent2 ()) continue;
2371
2372	    } while (!(Event.type == ButtonRelease || Cancel));
2373	    return TRUE;
2374	  }
2375	}
2376	break;
2377
2378
2379    case F_ZOOM:
2380    case F_HORIZOOM:
2381    case F_FULLZOOM:
2382    case F_LEFTZOOM:
2383    case F_RIGHTZOOM:
2384    case F_TOPZOOM:
2385    case F_BOTTOMZOOM:
2386	if (DeferExecution(context, func, Scr->SelectCursor))
2387	    return TRUE;
2388	if (tmp_win->squeezed) {
2389	    XBell(dpy, 0);
2390	    break;
2391	}
2392	fullzoom(tmp_win, func);
2393	break;
2394
2395    case F_PACK:
2396	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2397	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2398	packwindow (tmp_win, action);
2399	break;
2400
2401    case F_FILL:
2402	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2403	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2404	fillwindow (tmp_win, action);
2405	break;
2406
2407    case F_JUMPLEFT:
2408	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2409	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2410	jump (tmp_win, J_LEFT, action);
2411	break;
2412    case F_JUMPRIGHT:
2413	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2414	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2415	jump (tmp_win, J_RIGHT, action);
2416	break;
2417    case F_JUMPDOWN:
2418	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2419	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2420	jump (tmp_win, J_BOTTOM, action);
2421	break;
2422    case F_JUMPUP:
2423	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2424	if (tmp_win->squeezed) { XBell(dpy, 0); break; }
2425	jump (tmp_win, J_TOP, action);
2426	break;
2427
2428    case F_SAVEGEOMETRY:
2429	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2430	savegeometry (tmp_win);
2431	break;
2432
2433    case F_RESTOREGEOMETRY:
2434	if (DeferExecution(context, func, Scr->SelectCursor)) return TRUE;
2435	restoregeometry (tmp_win);
2436	break;
2437
2438    case F_HYPERMOVE: {
2439	Bool	cont = True;
2440	Window	root = RootWindow (dpy, Scr->screen);
2441	Cursor	cursor;
2442	CaptiveCTWM cctwm0, cctwm;
2443
2444	if (DeferExecution(context, func, Scr->MoveCursor)) return TRUE;
2445
2446	if (tmp_win->iswinbox || tmp_win->wspmgr) {
2447	    XBell (dpy, 0);
2448	    break;
2449	}
2450	cctwm0 = GetCaptiveCTWMUnderPointer ();
2451	cursor = MakeStringCursor (cctwm0.name);
2452	free (cctwm0.name);
2453	if (DeferExecution (context, func, Scr->MoveCursor)) return TRUE;
2454
2455	XGrabPointer (dpy, root, True,
2456		ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
2457		GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime);
2458	while (cont) {
2459	    XMaskEvent (dpy, ButtonPressMask | ButtonMotionMask |
2460				ButtonReleaseMask, &Event);
2461	    switch (Event.xany.type) {
2462		case ButtonPress :
2463		    cont = False;
2464		    break;
2465
2466		case ButtonRelease :
2467		    cont = False;
2468		    cctwm = GetCaptiveCTWMUnderPointer ();
2469		    free (cctwm.name);
2470		    if (cctwm.root == Scr->Root) break;
2471		    SetNoRedirect (tmp_win->w);
2472		    XUngrabButton (dpy, AnyButton, AnyModifier, tmp_win->w);
2473		    XReparentWindow (dpy, tmp_win->w, cctwm.root, 0, 0);
2474		    XMapWindow (dpy, tmp_win->w);
2475		    break;
2476
2477		case MotionNotify :
2478		    cctwm = GetCaptiveCTWMUnderPointer ();
2479		    if (cctwm.root != cctwm0.root) {
2480			XFreeCursor (dpy, cursor);
2481			cursor = MakeStringCursor (cctwm.name);
2482			cctwm0 = cctwm;
2483			XChangeActivePointerGrab (dpy,
2484				ButtonPressMask | ButtonMotionMask | ButtonReleaseMask,
2485				cursor, CurrentTime);
2486		    }
2487		    free (cctwm.name);
2488		    break;
2489	    }
2490	}
2491	ButtonPressed = -1;
2492	XUngrabPointer (dpy, CurrentTime);
2493	XFreeCursor (dpy, cursor);
2494	break;
2495    }
2496
2497    case F_MOVE:
2498    case F_FORCEMOVE:
2499    case F_MOVEPACK:
2500    case F_MOVEPUSH: {
2501        Window grabwin, dragroot;
2502
2503	if (DeferExecution(context, func, Scr->MoveCursor))
2504	    return TRUE;
2505
2506	PopDownMenu();
2507	if (tmp_win->OpaqueMove) {
2508	    int sw, ss;
2509	    float sf;
2510
2511	    sw = tmp_win->frame_width * tmp_win->frame_height;
2512	    ss = Scr->rootw  * Scr->rooth;
2513	    sf = Scr->OpaqueMoveThreshold / 100.0;
2514	    if (sw > (ss * sf))
2515		Scr->OpaqueMove = FALSE;
2516	    else
2517		Scr->OpaqueMove = TRUE;
2518	}
2519	else
2520	    Scr->OpaqueMove = FALSE;
2521
2522	dragroot = Scr->XineramaRoot;
2523
2524	if (tmp_win->winbox) {
2525	    XTranslateCoordinates (dpy, dragroot, tmp_win->winbox->window,
2526		eventp->xbutton.x_root, eventp->xbutton.y_root,
2527		&(eventp->xbutton.x_root), &(eventp->xbutton.y_root), &JunkChild);
2528	}
2529	rootw = eventp->xbutton.root;
2530	MoveFunction = func;
2531
2532	if (pulldown)
2533	    XWarpPointer(dpy, None, Scr->Root,
2534		0, 0, 0, 0, eventp->xbutton.x_root, eventp->xbutton.y_root);
2535
2536	EventHandler[EnterNotify] = HandleUnknown;
2537	EventHandler[LeaveNotify] = HandleUnknown;
2538
2539	if (!Scr->NoGrabServer || !Scr->OpaqueMove) {
2540	    XGrabServer(dpy);
2541	}
2542
2543	Scr->SizeStringOffset = SIZE_HINDENT;
2544	XResizeWindow (dpy, Scr->SizeWindow,
2545		   Scr->SizeStringWidth + SIZE_HINDENT * 2,
2546		   Scr->SizeFont.height + SIZE_VINDENT * 2);
2547	XMapRaised (dpy, Scr->SizeWindow);
2548
2549	grabwin = Scr->XineramaRoot;
2550	if (tmp_win->winbox) grabwin = tmp_win->winbox->window;
2551	XGrabPointer(dpy, grabwin, True,
2552	    ButtonPressMask | ButtonReleaseMask |
2553	    ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
2554	    GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor, CurrentTime);
2555
2556	if (context == C_ICON && tmp_win->icon && tmp_win->icon->w)
2557	{
2558	    w = tmp_win->icon->w;
2559	    DragX = eventp->xbutton.x;
2560	    DragY = eventp->xbutton.y;
2561	    moving_icon = TRUE;
2562	    if (tmp_win->OpaqueMove) Scr->OpaqueMove = TRUE;
2563	}
2564
2565	else if (! tmp_win->icon || w != tmp_win->icon->w)
2566	{
2567	    XTranslateCoordinates(dpy, w, tmp_win->frame,
2568		eventp->xbutton.x,
2569		eventp->xbutton.y,
2570		&DragX, &DragY, &JunkChild);
2571
2572	    w = tmp_win->frame;
2573	}
2574
2575	DragWindow = None;
2576
2577	/* Get x/y relative to parent window, i.e. the virtual screen, Root.
2578	 * XMoveWindow() moves are relative to this.
2579	 * MoveOutline()s however are drawn from the XineramaRoot since they
2580	 * may cross virtual screens.
2581	 */
2582	XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY,
2583	    &DragWidth, &DragHeight, &DragBW,
2584	    &JunkDepth);
2585
2586	JunkBW = DragBW;
2587	origX = eventp->xbutton.x_root;
2588	origY = eventp->xbutton.y_root;
2589	CurrentDragX = origDragX;
2590	CurrentDragY = origDragY;
2591
2592	/*
2593	 * only do the constrained move if timer is set; need to check it
2594	 * in case of stupid or wicked fast servers
2595	 */
2596	if (ConstrainedMoveTime &&
2597	    (eventp->xbutton.time - last_time) < ConstrainedMoveTime)
2598	{
2599	    int width, height;
2600
2601	    ConstMove = TRUE;
2602	    ConstMoveDir = MOVE_NONE;
2603	    ConstMoveX = eventp->xbutton.x_root - DragX - JunkBW;
2604	    ConstMoveY = eventp->xbutton.y_root - DragY - JunkBW;
2605	    width = DragWidth + 2 * JunkBW;
2606	    height = DragHeight + 2 * JunkBW;
2607	    ConstMoveXL = ConstMoveX + width/3;
2608	    ConstMoveXR = ConstMoveX + 2*(width/3);
2609	    ConstMoveYT = ConstMoveY + height/3;
2610	    ConstMoveYB = ConstMoveY + 2*(height/3);
2611
2612	    XWarpPointer(dpy, None, w,
2613		0, 0, 0, 0, DragWidth/2, DragHeight/2);
2614
2615	    XQueryPointer(dpy, w, &JunkRoot, &JunkChild,
2616		&JunkX, &JunkY, &DragX, &DragY, &JunkMask);
2617	}
2618	last_time = eventp->xbutton.time;
2619
2620	if (!Scr->OpaqueMove)
2621	{
2622	    InstallRootColormap();
2623	    if (!Scr->MoveDelta)
2624	    {
2625		/*
2626		 * Draw initial outline.  This was previously done the
2627		 * first time though the outer loop by dropping out of
2628		 * the XCheckMaskEvent inner loop down to one of the
2629		 * MoveOutline's below.
2630		 */
2631		MoveOutline(dragroot,
2632		    origDragX - JunkBW + Scr->currentvs->x,
2633		    origDragY - JunkBW + Scr->currentvs->y,
2634		    DragWidth + 2 * JunkBW, DragHeight + 2 * JunkBW,
2635		    tmp_win->frame_bw,
2636		    moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2637		/*
2638		 * This next line causes HandleReleaseNotify to call
2639		 * XRaiseWindow().  This is solely to preserve the
2640		 * previous behaviour that raises a window being moved
2641		 * on button release even if you never actually moved
2642		 * any distance (unless you move less than MoveDelta or
2643		 * NoRaiseMove is set or OpaqueMove is set).
2644		 */
2645		DragWindow = w;
2646	    }
2647	}
2648
2649	/*
2650	 * see if this is being done from the titlebar
2651	 */
2652	fromtitlebar = belongs_to_twm_window (tmp_win, eventp->xbutton.window);
2653
2654	if (menuFromFrameOrWindowOrTitlebar) {
2655	  /* warp the pointer to the middle of the window */
2656	  XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
2657		       origDragX + DragWidth / 2,
2658		       origDragY + DragHeight / 2);
2659	  XFlush(dpy);
2660	}
2661
2662	DisplayPosition (tmp_win, CurrentDragX, CurrentDragY);
2663	while (TRUE)
2664	{
2665	    long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
2666	                          ButtonPress : ButtonRelease;
2667	    long movementMask = menuFromFrameOrWindowOrTitlebar ?
2668	                          PointerMotionMask : ButtonMotionMask;
2669
2670	    /* block until there is an interesting event */
2671	    XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
2672				    EnterWindowMask | LeaveWindowMask |
2673				    ExposureMask | movementMask |
2674				    VisibilityChangeMask, &Event);
2675
2676	    /* throw away enter and leave events until release */
2677	    if (Event.xany.type == EnterNotify ||
2678		Event.xany.type == LeaveNotify) continue;
2679
2680	    if (Event.type == MotionNotify) {
2681		/* discard any extra motion events before a logical release */
2682		while(XCheckMaskEvent(dpy,
2683		    movementMask | releaseEvent, &Event))
2684		  if (Event.type == releaseEvent) {
2685		    break;
2686		  }
2687	    }
2688
2689	    /* test to see if we have a second button press to abort move */
2690	    if (!menuFromFrameOrWindowOrTitlebar)
2691	      if (Event.type == ButtonPress && DragWindow != None) {
2692		Cursor cur;
2693		if (Scr->OpaqueMove) {
2694		  XMoveWindow (dpy, DragWindow, origDragX, origDragY);
2695		} else {
2696		  MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
2697		}
2698		DragWindow = None;
2699
2700		XUnmapWindow (dpy, Scr->SizeWindow);
2701		cur = LeftButt;
2702		if (Event.xbutton.button == Button2)
2703		    cur = MiddleButt;
2704		else if (Event.xbutton.button >= Button3)
2705		    cur = RightButt;
2706
2707		XGrabPointer (dpy, Scr->Root, True,
2708		    ButtonReleaseMask | ButtonPressMask,
2709		    GrabModeAsync, GrabModeAsync,
2710		    Scr->Root, cur, CurrentTime);
2711		return TRUE;
2712	      }
2713
2714	    if (fromtitlebar && Event.type == ButtonPress) {
2715		fromtitlebar = False;
2716		CurrentDragX = origX = Event.xbutton.x_root;
2717		CurrentDragY = origY = Event.xbutton.y_root;
2718		XTranslateCoordinates (dpy, rootw, tmp_win->frame,
2719				       origX, origY,
2720				       &DragX, &DragY, &JunkChild);
2721		continue;
2722	    }
2723
2724	    if (!DispatchEvent2 ()) continue;
2725
2726	    if (Cancel)
2727	    {
2728		WindowMoved = FALSE;
2729		if (!Scr->OpaqueMove)
2730		    UninstallRootColormap();
2731		return TRUE;	/* XXX should this be FALSE? */
2732	    }
2733	    if (Event.type == releaseEvent)
2734	    {
2735		MoveOutline(dragroot, 0, 0, 0, 0, 0, 0);
2736		if (moving_icon &&
2737		    ((CurrentDragX != origDragX ||
2738		      CurrentDragY != origDragY)))
2739		    tmp_win->icon_moved = TRUE;
2740		if (!Scr->OpaqueMove && menuFromFrameOrWindowOrTitlebar) {
2741		    int xl = Event.xbutton.x_root - (DragWidth  / 2),
2742		        yt = Event.xbutton.y_root - (DragHeight / 2);
2743		    if (!moving_icon &&
2744		       (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2745			TryToPack (tmp_win, &xl, &yt);
2746		    XMoveWindow(dpy, DragWindow, xl, yt);
2747		}
2748		if (menuFromFrameOrWindowOrTitlebar) DragWindow = None;
2749		break;
2750	    }
2751
2752	    /* something left to do only if the pointer moved */
2753	    if (Event.type != MotionNotify)
2754		continue;
2755
2756	    XQueryPointer(dpy, rootw, &(eventp->xmotion.root), &JunkChild,
2757		&(eventp->xmotion.x_root), &(eventp->xmotion.y_root),
2758		&JunkX, &JunkY, &JunkMask);
2759
2760	    FixRootEvent (eventp);
2761	    if (tmp_win->winbox) {
2762		XTranslateCoordinates (dpy, dragroot, tmp_win->winbox->window,
2763		    eventp->xmotion.x_root, eventp->xmotion.y_root,
2764		    &(eventp->xmotion.x_root), &(eventp->xmotion.y_root), &JunkChild);
2765	    }
2766	    if (DragWindow == None &&
2767		abs(eventp->xmotion.x_root - origX) < Scr->MoveDelta &&
2768	        abs(eventp->xmotion.y_root - origY) < Scr->MoveDelta)
2769		continue;
2770
2771	    DragWindow = w;
2772
2773	    if (!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved)
2774	      RaiseFrame(DragWindow);
2775
2776	    WindowMoved = TRUE;
2777
2778	    if (ConstMove)
2779	    {
2780		switch (ConstMoveDir)
2781		{
2782		    case MOVE_NONE:
2783			if (eventp->xmotion.x_root < ConstMoveXL ||
2784			    eventp->xmotion.x_root > ConstMoveXR)
2785			    ConstMoveDir = MOVE_HORIZ;
2786
2787			if (eventp->xmotion.y_root < ConstMoveYT ||
2788			    eventp->xmotion.y_root > ConstMoveYB)
2789			    ConstMoveDir = MOVE_VERT;
2790
2791			XQueryPointer(dpy, DragWindow, &JunkRoot, &JunkChild,
2792			    &JunkX, &JunkY, &DragX, &DragY, &JunkMask);
2793			break;
2794
2795		    case MOVE_VERT:
2796			ConstMoveY = eventp->xmotion.y_root - DragY - JunkBW;
2797			break;
2798
2799		    case MOVE_HORIZ:
2800			ConstMoveX= eventp->xmotion.x_root - DragX - JunkBW;
2801			break;
2802		}
2803
2804		if (ConstMoveDir != MOVE_NONE)
2805		{
2806		    int xl, yt, width, height;
2807
2808		    xl = ConstMoveX;
2809		    yt = ConstMoveY;
2810		    width = DragWidth + 2 * JunkBW;
2811		    height = DragHeight + 2 * JunkBW;
2812
2813		    if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2814		        TryToGrid (tmp_win, &xl, &yt);
2815		    if (!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove)
2816			TryToPush (tmp_win, xl, yt, 0);
2817
2818		    if (!moving_icon &&
2819			(MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2820			TryToPack (tmp_win, &xl, &yt);
2821
2822		    if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2823		    {
2824                        ConstrainByBorders (tmp_win, &xl, width, &yt, height);
2825		    }
2826		    CurrentDragX = xl;
2827		    CurrentDragY = yt;
2828		    if (Scr->OpaqueMove) {
2829		      if (MoveFunction == F_MOVEPUSH && !moving_icon) {
2830			SetupWindow (tmp_win, xl, yt,
2831				     tmp_win->frame_width, tmp_win->frame_height, -1);
2832		      } else {
2833			XMoveWindow(dpy, DragWindow, xl, yt);
2834		      }
2835			WMapSetupWindow (tmp_win, xl, yt, -1, -1);
2836		    }
2837		    else {
2838			MoveOutline(dragroot, xl + Scr->currentvs->x,
2839			    yt + Scr->currentvs->y, width, height,
2840			    tmp_win->frame_bw,
2841			    moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2842		    }
2843		}
2844	    }
2845	    else if (DragWindow != None)
2846	    {
2847		int xroot, yroot;
2848		int xl, yt, width, height;
2849
2850
2851		/*
2852		 * this is split out for virtual screens.  In that case, it's
2853		 * possible to drag windows from one workspace to another, and
2854		 * as such, these need to be adjusted to the root, rather
2855		 * than this virtual screen...
2856		 */
2857		xroot = eventp->xmotion.x_root;
2858		yroot = eventp->xmotion.y_root;
2859
2860		if (!menuFromFrameOrWindowOrTitlebar) {
2861		  xl = xroot - DragX - JunkBW;
2862		  yt = yroot - DragY - JunkBW;
2863		}
2864		else {
2865		  xl = xroot - (DragWidth / 2);
2866		  yt = yroot - (DragHeight / 2);
2867		}
2868		width = DragWidth + 2 * JunkBW;
2869		height = DragHeight + 2 * JunkBW;
2870
2871		if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2872		    TryToGrid (tmp_win, &xl, &yt);
2873		if (!moving_icon && MoveFunction == F_MOVEPUSH && Scr->OpaqueMove)
2874		    TryToPush (tmp_win, xl, yt, 0);
2875
2876		if (!moving_icon &&
2877		    (MoveFunction == F_MOVEPACK || MoveFunction == F_MOVEPUSH))
2878		    TryToPack (tmp_win, &xl, &yt);
2879
2880		if (Scr->DontMoveOff && MoveFunction != F_FORCEMOVE)
2881		{
2882                    ConstrainByBorders (tmp_win, &xl, width, &yt, height);
2883		}
2884
2885		CurrentDragX = xl;
2886		CurrentDragY = yt;
2887		if (Scr->OpaqueMove) {
2888		  if (MoveFunction == F_MOVEPUSH && !moving_icon) {
2889		        SetupWindow (tmp_win, xl, yt,
2890				tmp_win->frame_width, tmp_win->frame_height, -1);
2891		  } else {
2892			XMoveWindow(dpy, DragWindow, xl, yt);
2893		  }
2894		  if (! moving_icon) WMapSetupWindow (tmp_win, xl, yt, -1, -1);
2895		}
2896		else {
2897		    MoveOutline(dragroot, xl + Scr->currentvs->x,
2898			yt + Scr->currentvs->y, width, height,
2899			tmp_win->frame_bw,
2900			moving_icon ? 0 : tmp_win->title_height + tmp_win->frame_bw3D);
2901		}
2902	    }
2903	    DisplayPosition (tmp_win, CurrentDragX, CurrentDragY);
2904	}
2905	XUnmapWindow (dpy, Scr->SizeWindow);
2906
2907	if (!Scr->OpaqueMove && DragWindow == None)
2908	    UninstallRootColormap();
2909        break;
2910    }
2911    case F_MOVETITLEBAR:
2912    {
2913        Window grabwin;
2914	int deltax = 0, newx = 0;
2915	int origNum;
2916	SqueezeInfo *si;
2917
2918	if (DeferExecution(context, func, Scr->MoveCursor))
2919	    return TRUE;
2920
2921	PopDownMenu();
2922	if (tmp_win->squeezed ||
2923		!tmp_win->squeeze_info ||
2924		!tmp_win->title_w ||
2925		context == C_ICON ) {
2926	    XBell (dpy, 0);
2927	    break;
2928	}
2929
2930	/* If the SqueezeInfo isn't copied yet, do it now */
2931	if (!tmp_win->squeeze_info_copied) {
2932	    SqueezeInfo *s = malloc(sizeof(SqueezeInfo));
2933	    if (!s)
2934		break;
2935	    *s = *tmp_win->squeeze_info;
2936	    tmp_win->squeeze_info = s;
2937	    tmp_win->squeeze_info_copied = 1;
2938	}
2939	si = tmp_win->squeeze_info;
2940
2941	if (si->denom != 0) {
2942	    int target_denom = tmp_win->frame_width;
2943	    /*
2944	     * If not pixel based, scale the denominator to equal the
2945	     * window width, so the numerator equals pixels.
2946	     * That way we can just modify it by pixel units, just
2947	     * like the other case.
2948	     */
2949
2950	    if (si->denom != target_denom) {
2951		float scale = (float)target_denom / si->denom;
2952		si->num *= scale;
2953		si->denom = target_denom; /* s->denom *= scale; */
2954	    }
2955	}
2956
2957	/* now move the mouse */
2958	if (tmp_win->winbox) {
2959	    XTranslateCoordinates (dpy, Scr->Root, tmp_win->winbox->window,
2960		eventp->xbutton.x_root, eventp->xbutton.y_root,
2961		&eventp->xbutton.x_root, &eventp->xbutton.y_root, &JunkChild);
2962	}
2963	/*
2964	 * the event is always a button event, since key events
2965	 * are "weeded out" - although incompletely only
2966	 * F_MOVE and F_RESIZE - in HandleKeyPress().
2967	 */
2968	rootw = eventp->xbutton.root;
2969
2970	EventHandler[EnterNotify] = HandleUnknown;
2971	EventHandler[LeaveNotify] = HandleUnknown;
2972
2973	if (!Scr->NoGrabServer) {
2974	    XGrabServer(dpy);
2975	}
2976
2977	grabwin = Scr->Root;
2978	if (tmp_win->winbox) grabwin = tmp_win->winbox->window;
2979	XGrabPointer(dpy, grabwin, True,
2980	    ButtonPressMask | ButtonReleaseMask |
2981	    ButtonMotionMask | PointerMotionMask, /* PointerMotionHintMask */
2982	    GrabModeAsync, GrabModeAsync, grabwin, Scr->MoveCursor, CurrentTime);
2983
2984#if 0	/* what's this for ? */
2985	if (! tmp_win->icon || w != tmp_win->icon->w)
2986	{
2987	    XTranslateCoordinates(dpy, w, tmp_win->frame,
2988		eventp->xbutton.x,
2989		eventp->xbutton.y,
2990		&DragX, &DragY, &JunkChild);
2991
2992	    w = tmp_win->frame;
2993	}
2994#endif
2995
2996	DragWindow = None;
2997
2998	XGetGeometry(dpy, tmp_win->title_w, &JunkRoot, &origDragX, &origDragY,
2999	    &DragWidth, &DragHeight, &DragBW,
3000	    &JunkDepth);
3001
3002	origX = eventp->xbutton.x_root;
3003	origNum = si->num;
3004
3005	if (menuFromFrameOrWindowOrTitlebar) {
3006	  /* warp the pointer to the middle of the window */
3007	  XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0,
3008		       origDragX + DragWidth / 2,
3009		       origDragY + DragHeight / 2);
3010	  XFlush(dpy);
3011	}
3012
3013	while (TRUE)
3014	{
3015	    long releaseEvent = menuFromFrameOrWindowOrTitlebar ?
3016	                          ButtonPress : ButtonRelease;
3017	    long movementMask = menuFromFrameOrWindowOrTitlebar ?
3018	                          PointerMotionMask : ButtonMotionMask;
3019
3020	    /* block until there is an interesting event */
3021	    XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask |
3022				    EnterWindowMask | LeaveWindowMask |
3023				    ExposureMask | movementMask |
3024				    VisibilityChangeMask, &Event);
3025
3026	    /* throw away enter and leave events until release */
3027	    if (Event.xany.type == EnterNotify ||
3028		Event.xany.type == LeaveNotify) continue;
3029
3030	    if (Event.type == MotionNotify) {
3031		/* discard any extra motion events before a logical release */
3032		while (XCheckMaskEvent(dpy,
3033					movementMask | releaseEvent, &Event)) {
3034		    if (Event.type == releaseEvent) {
3035			break;
3036		    }
3037		}
3038	    }
3039
3040	    if (!DispatchEvent2())
3041		continue;
3042
3043	    if (Event.type == releaseEvent)
3044		break;
3045
3046	    /* something left to do only if the pointer moved */
3047	    if (Event.type != MotionNotify)
3048		continue;
3049
3050	    /* get current pointer pos, useful when there is lag */
3051	    XQueryPointer(dpy, rootw, &eventp->xmotion.root, &JunkChild,
3052		&eventp->xmotion.x_root, &eventp->xmotion.y_root,
3053		&JunkX, &JunkY, &JunkMask);
3054
3055	    FixRootEvent(eventp);
3056	    if (tmp_win->winbox) {
3057		XTranslateCoordinates(dpy, Scr->Root, tmp_win->winbox->window,
3058		    eventp->xmotion.x_root, eventp->xmotion.y_root,
3059		    &eventp->xmotion.x_root, &eventp->xmotion.y_root, &JunkChild);
3060	    }
3061
3062	    if (!Scr->NoRaiseMove && Scr->OpaqueMove && !WindowMoved)
3063	      RaiseFrame(w);
3064
3065	    deltax = eventp->xmotion.x_root - origX;
3066	    newx = origNum + deltax;
3067
3068	    /*
3069	     * Clamp to left and right.
3070	     * If we're in pixel size, keep within [ 0, frame_width >.
3071	     * If we're proportional, don't cross the 0.
3072	     * Also don't let the nominator get bigger than the denominator.
3073	     * Keep within [ -denom, -1] or [ 0, denom >.
3074	     */
3075	    {
3076		int wtmp = tmp_win->frame_width; /* or si->denom; if it were != 0 */
3077		if (origNum < 0) {
3078		    if (newx >= 0)
3079			newx = -1;
3080		    else if (newx < -wtmp)
3081			newx = -wtmp;
3082		} else if (origNum >= 0) {
3083		    if (newx < 0)
3084			newx = 0;
3085		    else if (newx >= wtmp)
3086			newx = wtmp - 1;
3087		}
3088	    }
3089
3090	    si->num = newx;
3091	    /* This, finally, actually moves the title bar */
3092	    /* XXX pressing a second button should cancel and undo this */
3093	    SetFrameShape(tmp_win);
3094	}
3095	break;
3096    }
3097    case F_FUNCTION:
3098	{
3099	    MenuRoot *mroot;
3100	    MenuItem *mitem;
3101
3102	    if ((mroot = FindMenuRoot(action)) == NULL)
3103	    {
3104		if (!action) action = "undef";
3105		fprintf (stderr, "%s: couldn't find function \"%s\"\n",
3106			 ProgramName, (char *)action);
3107		return TRUE;
3108	    }
3109
3110	    if (NeedToDefer(mroot) && DeferExecution(context, func, Scr->SelectCursor))
3111		return TRUE;
3112	    else
3113	    {
3114		for (mitem = mroot->first; mitem != NULL; mitem = mitem->next)
3115		{
3116		    if (!ExecuteFunction (mitem->func, mitem->action, w,
3117					  tmp_win, eventp, context, pulldown))
3118		      /* pebl FIXME: the focus should be updated here,
3119			 or the function would operate on the same window */
3120		      break;
3121		}
3122	    }
3123	}
3124	break;
3125
3126    case F_DEICONIFY:
3127    case F_ICONIFY:
3128	if (DeferExecution(context, func, Scr->SelectCursor))
3129	    return TRUE;
3130
3131	if (tmp_win->isicon)
3132	{
3133	    DeIconify(tmp_win);
3134	}
3135        else if (func == F_ICONIFY)
3136	{
3137	    Iconify (tmp_win, eventp->xbutton.x_root - 5,
3138		     eventp->xbutton.y_root - 5);
3139	}
3140	break;
3141
3142    case F_SQUEEZE:
3143	if (DeferExecution(context, func, Scr->SelectCursor))
3144	    return TRUE;
3145
3146	Squeeze (tmp_win);
3147	break;
3148
3149    case F_SHOWBGRD:
3150	ShowBackground (Scr->currentvs);
3151	break;
3152
3153    case F_RAISELOWER:
3154	if (DeferExecution(context, func, Scr->SelectCursor))
3155	    return TRUE;
3156
3157	if (!WindowMoved) {
3158	    if (tmp_win->icon && w == tmp_win->icon->w) {
3159		RaiseLowerFrame(w, ONTOP_DEFAULT);
3160	    } else {
3161		RaiseLower(tmp_win);
3162		WMapRaiseLower (tmp_win);
3163	    }
3164	}
3165	break;
3166
3167    case F_RAISE:
3168	if (DeferExecution(context, func, Scr->SelectCursor))
3169	    return TRUE;
3170
3171	/* check to make sure raise is not from the WindowFunction */
3172	if (tmp_win->icon && (w == tmp_win->icon->w) && Context != C_ROOT)
3173	    XRaiseWindow(dpy, tmp_win->icon->w);
3174	else {
3175	    RaiseWindow (tmp_win);
3176	    WMapRaise   (tmp_win);
3177	}
3178	break;
3179
3180    case F_LOWER:
3181	if (DeferExecution(context, func, Scr->SelectCursor))
3182	    return TRUE;
3183
3184	if (tmp_win->icon && (w == tmp_win->icon->w))
3185	    XLowerWindow(dpy, tmp_win->icon->w);
3186	else {
3187	    LowerWindow(tmp_win);
3188	    WMapLower (tmp_win);
3189	}
3190	break;
3191
3192    case F_RAISEICONS:
3193	for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3194	    if (t->icon && t->icon->w) {
3195		XRaiseWindow (dpy, t->icon->w);
3196	    }
3197	}
3198	break;
3199
3200    case F_FOCUS:
3201	if (DeferExecution(context, func, Scr->SelectCursor))
3202	    return TRUE;
3203
3204	if (tmp_win->isicon == FALSE)
3205	{
3206	    if (!Scr->FocusRoot && Scr->Focus == tmp_win)
3207	    {
3208		FocusOnRoot();
3209	    }
3210	    else
3211	    {
3212		InstallWindowColormaps (0, tmp_win);
3213		SetFocus (tmp_win, eventp->xbutton.time);
3214		Scr->FocusRoot = FALSE;
3215	    }
3216	}
3217	break;
3218
3219    case F_DESTROY:
3220	if (DeferExecution(context, func, Scr->DestroyCursor))
3221	    return TRUE;
3222
3223	if (tmp_win->iconmgr || tmp_win->iswinbox || tmp_win->wspmgr
3224	    || (Scr->workSpaceMgr.occupyWindow
3225		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3226	    XBell(dpy, 0);
3227	    break;
3228	}
3229	XKillClient(dpy, tmp_win->w);
3230	if (ButtonPressed != -1) {
3231	    XEvent kev;
3232
3233	    XMaskEvent (dpy, ButtonReleaseMask, &kev);
3234	    if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3235	    XPutBackEvent (dpy, &kev);
3236	}
3237	break;
3238
3239    case F_DELETE:
3240	if (DeferExecution(context, func, Scr->DestroyCursor))
3241	    return TRUE;
3242
3243	if (tmp_win->iconmgr) {		/* don't send ourself a message */
3244	    HideIconManager ();
3245	    break;
3246	}
3247	if (tmp_win->iswinbox || tmp_win->wspmgr
3248	    || (Scr->workSpaceMgr.occupyWindow
3249		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3250	    XBell (dpy, 0);
3251	    break;
3252	}
3253	if (tmp_win->protocols & DoesWmDeleteWindow) {
3254	    SendDeleteWindowMessage (tmp_win, LastTimestamp());
3255	    if (ButtonPressed != -1) {
3256		XEvent kev;
3257
3258		XMaskEvent (dpy, ButtonReleaseMask, &kev);
3259		if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3260		XPutBackEvent (dpy, &kev);
3261	    }
3262	    break;
3263	}
3264	XBell (dpy, 0);
3265	break;
3266
3267    case F_DELETEORDESTROY:
3268	if (DeferExecution(context, func, Scr->DestroyCursor)) return TRUE;
3269
3270	if (tmp_win->iconmgr) {
3271	    HideIconManager ();
3272	    break;
3273	}
3274	if (tmp_win->iswinbox || tmp_win->wspmgr
3275	    || (Scr->workSpaceMgr.occupyWindow
3276		&& tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
3277	    XBell (dpy, 0);
3278	    break;
3279	}
3280	if (tmp_win->protocols & DoesWmDeleteWindow) {
3281	    SendDeleteWindowMessage (tmp_win, LastTimestamp());
3282	} else {
3283	    XKillClient(dpy, tmp_win->w);
3284	}
3285	if (ButtonPressed != -1) {
3286	    XEvent kev;
3287
3288	    XMaskEvent (dpy, ButtonReleaseMask, &kev);
3289	    if (kev.xbutton.window == tmp_win->w) kev.xbutton.window = Scr->Root;
3290	    XPutBackEvent (dpy, &kev);
3291	}
3292	break;
3293
3294    case F_SAVEYOURSELF:
3295	if (DeferExecution (context, func, Scr->SelectCursor))
3296	  return TRUE;
3297
3298	if (tmp_win->protocols & DoesWmSaveYourself)
3299	  SendSaveYourselfMessage (tmp_win, LastTimestamp());
3300	else
3301	  XBell (dpy, 0);
3302	break;
3303
3304    case F_CIRCLEUP:
3305	XCirculateSubwindowsUp(dpy, Scr->Root);
3306	break;
3307
3308    case F_CIRCLEDOWN:
3309	XCirculateSubwindowsDown(dpy, Scr->Root);
3310	break;
3311
3312    case F_EXEC:
3313	PopDownMenu();
3314	if (!Scr->NoGrabServer) {
3315	    XUngrabServer (dpy);
3316	    XSync (dpy, 0);
3317	}
3318	XUngrabPointer (dpy, CurrentTime);
3319	XSync (dpy, 0);
3320	Execute(action);
3321	break;
3322
3323    case F_UNFOCUS:
3324	FocusOnRoot();
3325	break;
3326
3327    case F_CUT:
3328	strcpy(tmp, action);
3329	strcat(tmp, "\n");
3330	XStoreBytes(dpy, tmp, strlen(tmp));
3331	break;
3332
3333    case F_CUTFILE:
3334	ptr = XFetchBytes(dpy, &count);
3335	if (ptr) {
3336	    if (sscanf (ptr, "%s", tmp) == 1) {
3337		XFree (ptr);
3338		ptr = ExpandFilename(tmp);
3339		if (ptr) {
3340#ifdef VMS
3341		    fd = open (ptr, O_RDONLY, 0);
3342#else
3343		    fd = open (ptr, 0);
3344#endif
3345		    if (fd >= 0) {
3346			count = read (fd, buff, MAX_FILE_SIZE - 1);
3347			if (count > 0) XStoreBytes (dpy, buff, count);
3348			close(fd);
3349		    } else {
3350			fprintf (stderr,
3351				 "%s:  unable to open cut file \"%s\"\n",
3352				 ProgramName, tmp);
3353		    }
3354		    if (ptr != tmp) free (ptr);
3355		}
3356	    } else {
3357		XFree(ptr);
3358	    }
3359	} else {
3360	    fprintf(stderr, "%s:  cut buffer is empty\n", ProgramName);
3361	}
3362	break;
3363
3364    case F_WARPTOSCREEN:
3365	{
3366	    if (strcmp (action, WARPSCREEN_NEXT) == 0) {
3367		WarpToScreen (Scr->screen + 1, 1);
3368	    } else if (strcmp (action, WARPSCREEN_PREV) == 0) {
3369		WarpToScreen (Scr->screen - 1, -1);
3370	    } else if (strcmp (action, WARPSCREEN_BACK) == 0) {
3371		WarpToScreen (PreviousScreen, 0);
3372	    } else {
3373		WarpToScreen (atoi (action), 0);
3374	    }
3375	}
3376	break;
3377
3378    case F_COLORMAP:
3379	{
3380	    if (strcmp (action, COLORMAP_NEXT) == 0) {
3381		BumpWindowColormap (tmp_win, 1);
3382	    } else if (strcmp (action, COLORMAP_PREV) == 0) {
3383		BumpWindowColormap (tmp_win, -1);
3384	    } else {
3385		BumpWindowColormap (tmp_win, 0);
3386	    }
3387	}
3388	break;
3389
3390    case F_WARPTO:
3391	{
3392	    register TwmWindow *tw;
3393	    int len;
3394
3395	    len = strlen(action);
3396
3397#ifdef WARPTO_FROM_ICONMGR
3398		if (len == 0 && tmp_win && tmp_win->iconmgr)
3399		{
3400			printf ("curren iconmgr entry: %s", tmp_win->iconmgr->Current);
3401		}
3402#endif /* #ifdef WARPTO_FROM_ICONMGR */
3403	    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3404		if (!strncmp(action, tw->full_name, len)) break;
3405		if (match (action, tw->full_name)) break;
3406	    }
3407	    if (!tw) {
3408		for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3409		    if (!strncmp(action, tw->class.res_name, len)) break;
3410		    if (match (action, tw->class.res_name)) break;
3411		}
3412		if (!tw) {
3413		    for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3414			if (!strncmp(action, tw->class.res_class, len)) break;
3415			if (match (action, tw->class.res_class)) break;
3416		    }
3417		}
3418	    }
3419
3420	    if (tw) {
3421		if (Scr->WarpUnmapped || tw->mapped) {
3422		    if (!tw->mapped) DeIconify (tw);
3423		    WarpToWindow (tw, Scr->RaiseOnWarp);
3424		}
3425	    } else {
3426		XBell (dpy, 0);
3427	    }
3428	}
3429	break;
3430
3431    case F_WARPTOICONMGR:
3432	{
3433	    TwmWindow *tw;
3434	    int len;
3435	    Window raisewin = None, iconwin = None;
3436
3437	    len = strlen(action);
3438	    if (len == 0) {
3439		if (tmp_win && tmp_win->iconmanagerlist) {
3440		    raisewin = tmp_win->iconmanagerlist->iconmgr->twm_win->frame;
3441		    iconwin = tmp_win->iconmanagerlist->icon;
3442		} else if (Scr->iconmgr->active) {
3443		    raisewin = Scr->iconmgr->twm_win->frame;
3444		    iconwin = Scr->iconmgr->active->w;
3445		}
3446	    } else {
3447		for (tw = Scr->FirstWindow; tw != NULL; tw = tw->next) {
3448		    if (strncmp (action, tw->icon_name, len) == 0) {
3449			if (tw->iconmanagerlist &&
3450			    tw->iconmanagerlist->iconmgr->twm_win->mapped) {
3451			    raisewin = tw->iconmanagerlist->iconmgr->twm_win->frame;
3452			    break;
3453			}
3454		    }
3455		}
3456	    }
3457
3458	    if (raisewin) {
3459		RaiseFrame(raisewin);
3460		XWarpPointer (dpy, None, iconwin, 0,0,0,0, 5, 5);
3461	    } else {
3462		XBell (dpy, 0);
3463	    }
3464	}
3465	break;
3466
3467    case F_RING:  /* Taken from vtwm version 5.3 */
3468	if (DeferExecution (context, func, Scr->SelectCursor)) return TRUE;
3469	if ( tmp_win->ring.next || tmp_win->ring.prev ) {
3470	    /* It's in the ring, let's take it out. */
3471	   TwmWindow *prev = tmp_win->ring.prev, *next = tmp_win->ring.next;
3472
3473	    /*
3474	    * 1. Unlink window
3475	    * 2. If window was only thing in ring, null out ring
3476	    * 3. If window was ring leader, set to next (or null)
3477	    */
3478	    if (prev) prev->ring.next = next;
3479	    if (next) next->ring.prev = prev;
3480	    if (Scr->Ring == tmp_win)
3481		Scr->Ring = (next != tmp_win ? next : (TwmWindow *) NULL);
3482
3483	    if (!Scr->Ring || Scr->RingLeader == tmp_win)
3484		Scr->RingLeader = Scr->Ring;
3485	    tmp_win->ring.next = tmp_win->ring.prev = NULL;
3486	} else {
3487	    /* Not in the ring, so put it in. */
3488	    if (Scr->Ring) {
3489		tmp_win->ring.next = Scr->Ring->ring.next;
3490		if (Scr->Ring->ring.next->ring.prev)
3491		    Scr->Ring->ring.next->ring.prev = tmp_win;
3492		Scr->Ring->ring.next = tmp_win;
3493		tmp_win->ring.prev = Scr->Ring;
3494	    } else {
3495		tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
3496	    }
3497	}
3498	/*tmp_win->ring.cursor_valid = False;*/
3499	break;
3500
3501    case F_WARPRING:
3502	switch (((char *)action)[0]) {
3503	  case 'n':
3504	    WarpAlongRing (&eventp->xbutton, True);
3505	    break;
3506	  case 'p':
3507	    WarpAlongRing (&eventp->xbutton, False);
3508	    break;
3509	  default:
3510	    XBell (dpy, 0);
3511	    break;
3512	}
3513	break;
3514
3515    case F_FILE:
3516	action = ExpandFilename(action);
3517#ifdef VMS
3518	fd = open (action, O_RDONLY, 0);
3519#else
3520	fd = open(action, 0);
3521#endif
3522	if (fd >= 0)
3523	{
3524	    count = read(fd, buff, MAX_FILE_SIZE - 1);
3525	    if (count > 0)
3526		XStoreBytes(dpy, buff, count);
3527
3528	    close(fd);
3529	}
3530	else
3531	{
3532	    fprintf (stderr, "%s:  unable to open file \"%s\"\n",
3533		     ProgramName, (char *)action);
3534	}
3535	break;
3536
3537    case F_REFRESH:
3538	{
3539	    XSetWindowAttributes attributes;
3540	    unsigned long valuemask;
3541
3542	    valuemask = (CWBackPixel | CWBackingStore | CWSaveUnder);
3543	    attributes.background_pixel = Scr->Black;
3544	    attributes.backing_store = NotUseful;
3545	    attributes.save_under = False;
3546	    w = XCreateWindow (dpy, Scr->Root, 0, 0,
3547			       (unsigned int) Scr->rootw,
3548			       (unsigned int) Scr->rooth,
3549			       (unsigned int) 0,
3550			       CopyFromParent, (unsigned int) CopyFromParent,
3551			       (Visual *) CopyFromParent, valuemask,
3552			       &attributes);
3553	    XMapWindow (dpy, w);
3554	    XDestroyWindow (dpy, w);
3555	    XFlush (dpy);
3556	}
3557	break;
3558
3559    case F_OCCUPY:
3560	if (DeferExecution(context, func, Scr->SelectCursor))
3561	    return TRUE;
3562	Occupy (tmp_win);
3563	break;
3564
3565    case F_OCCUPYALL:
3566	if (DeferExecution(context, func, Scr->SelectCursor))
3567	    return TRUE;
3568	OccupyAll (tmp_win);
3569	break;
3570
3571    case F_GOTOWORKSPACE:
3572	GotoWorkSpaceByName (Scr->currentvs, action);
3573	break;
3574
3575    case F_PREVWORKSPACE:
3576	GotoPrevWorkSpace (Scr->currentvs);
3577	break;
3578
3579    case F_NEXTWORKSPACE:
3580	GotoNextWorkSpace (Scr->currentvs);
3581	break;
3582
3583    case F_RIGHTWORKSPACE:
3584	GotoRightWorkSpace (Scr->currentvs);
3585	break;
3586
3587    case F_LEFTWORKSPACE:
3588	GotoLeftWorkSpace (Scr->currentvs);
3589	break;
3590
3591    case F_UPWORKSPACE:
3592	GotoUpWorkSpace (Scr->currentvs);
3593	break;
3594
3595    case F_DOWNWORKSPACE:
3596	GotoDownWorkSpace (Scr->currentvs);
3597	break;
3598
3599    case F_MENU:
3600	if (action && ! strncmp (action, "WGOTO : ", 8)) {
3601	    GotoWorkSpaceByName (/* XXXXX */ Scr->currentvs,
3602		((char *)action) + 8);
3603	}
3604	else {
3605	    MenuItem *item;
3606
3607	    item = ActiveItem;
3608	    while (item && item->sub) {
3609		if (!item->sub->defaultitem) break;
3610		if (item->sub->defaultitem->func != F_MENU) break;
3611		item = item->sub->defaultitem;
3612	    }
3613	    if (item && item->sub && item->sub->defaultitem) {
3614		ExecuteFunction (item->sub->defaultitem->func,
3615				 item->sub->defaultitem->action,
3616				 w, tmp_win, eventp, context, pulldown);
3617	    }
3618	}
3619	break;
3620
3621    case F_WINREFRESH:
3622	if (DeferExecution(context, func, Scr->SelectCursor))
3623	    return TRUE;
3624
3625	if (context == C_ICON && tmp_win->icon && tmp_win->icon->w)
3626	    w = XCreateSimpleWindow(dpy, tmp_win->icon->w,
3627		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
3628	else
3629	    w = XCreateSimpleWindow(dpy, tmp_win->frame,
3630		0, 0, 9999, 9999, 0, Scr->Black, Scr->Black);
3631
3632	XMapWindow(dpy, w);
3633	XDestroyWindow(dpy, w);
3634	XFlush(dpy);
3635	break;
3636
3637    case F_ADOPTWINDOW:
3638	adoptWindow ();
3639	break;
3640
3641    case F_TRACE:
3642	DebugTrace (action);
3643	break;
3644
3645    case F_CHANGESIZE:
3646	ChangeSize (action, tmp_win);
3647	break;
3648
3649    case F_QUIT:
3650	Done(0);
3651	break;
3652    }
3653
3654    if (ButtonPressed == -1) XUngrabPointer(dpy, CurrentTime);
3655    return do_next_action;
3656}
3657
3658
3659
3660/***********************************************************************
3661 *
3662 *  Procedure:
3663 *	DeferExecution - defer the execution of a function to the
3664 *	    next button press if the context is C_ROOT
3665 *
3666 *  Inputs:
3667 *	context	- the context in which the mouse button was pressed
3668 *	func	- the function to defer
3669 *	cursor	- the cursor to display while waiting
3670 *
3671 ***********************************************************************
3672 */
3673
3674int DeferExecution(int context, int func, Cursor cursor)
3675{
3676    if ((context == C_ROOT) || (context == C_ALTERNATE))
3677    {
3678	LastCursor = cursor;
3679	if (func == F_ADOPTWINDOW) {
3680	    XGrabPointer(dpy, Scr->Root, True,
3681		ButtonPressMask | ButtonReleaseMask,
3682		GrabModeAsync, GrabModeAsync,
3683		None, cursor, CurrentTime);
3684	} else {
3685	    XGrabPointer(dpy, Scr->Root, True,
3686		ButtonPressMask | ButtonReleaseMask,
3687		GrabModeAsync, GrabModeAsync,
3688		Scr->Root, cursor, CurrentTime);
3689	}
3690	RootFunction = func;
3691
3692	return (TRUE);
3693    }
3694
3695    return (FALSE);
3696}
3697
3698
3699
3700/***********************************************************************
3701 *
3702 *  Procedure:
3703 *	ReGrab - regrab the pointer with the LastCursor;
3704 *
3705 ***********************************************************************
3706 */
3707
3708void ReGrab(void)
3709{
3710    XGrabPointer(dpy, Scr->Root, True,
3711	ButtonPressMask | ButtonReleaseMask,
3712	GrabModeAsync, GrabModeAsync,
3713	Scr->Root, LastCursor, CurrentTime);
3714}
3715
3716
3717
3718/***********************************************************************
3719 *
3720 *  Procedure:
3721 *	NeedToDefer - checks each function in the list to see if it
3722 *		is one that needs to be defered.
3723 *
3724 *  Inputs:
3725 *	root	- the menu root to check
3726 *
3727 ***********************************************************************
3728 */
3729
3730int NeedToDefer(MenuRoot *root)
3731{
3732    MenuItem *mitem;
3733
3734    for (mitem = root->first; mitem != NULL; mitem = mitem->next)
3735    {
3736	switch (mitem->func)
3737	{
3738	case F_IDENTIFY:
3739	case F_RESIZE:
3740	case F_MOVE:
3741	case F_FORCEMOVE:
3742	case F_DEICONIFY:
3743	case F_ICONIFY:
3744	case F_RAISELOWER:
3745	case F_RAISE:
3746	case F_LOWER:
3747	case F_FOCUS:
3748	case F_DESTROY:
3749	case F_WINREFRESH:
3750	case F_ZOOM:
3751	case F_FULLZOOM:
3752	case F_HORIZOOM:
3753        case F_RIGHTZOOM:
3754        case F_LEFTZOOM:
3755        case F_TOPZOOM:
3756        case F_BOTTOMZOOM:
3757        case F_SQUEEZE:
3758	case F_AUTORAISE:
3759	case F_AUTOLOWER:
3760	    return TRUE;
3761	}
3762    }
3763    return FALSE;
3764}
3765
3766
3767
3768/***********************************************************************
3769 *
3770 *  Procedure:
3771 *	Execute - execute the string by /bin/sh
3772 *
3773 *  Inputs:
3774 *	s	- the string containing the command
3775 *
3776 ***********************************************************************
3777 */
3778
3779void Execute(char *s)
3780{
3781#ifdef VMS
3782    createProcess(s);
3783#else
3784    static char buf[256];
3785    char *ds = DisplayString (dpy);
3786    char *colon, *dot1;
3787    char oldDisplay[256];
3788    char *doisplay;
3789    int restorevar = 0;
3790    Bool replace;
3791    char *subs, *name, *news;
3792    int len;
3793
3794    oldDisplay[0] = '\0';
3795    doisplay=getenv("DISPLAY");
3796    if (doisplay)
3797	strcpy (oldDisplay, doisplay);
3798
3799    /*
3800     * Build a display string using the current screen number, so that
3801     * X programs which get fired up from a menu come up on the screen
3802     * that they were invoked from, unless specifically overridden on
3803     * their command line.
3804     */
3805    colon = strrchr (ds, ':');
3806    if (colon) {			/* if host[:]:dpy */
3807	strcpy (buf, "DISPLAY=");
3808	strcat (buf, ds);
3809	colon = buf + 8 + (colon - ds);	/* use version in buf */
3810	dot1 = strchr (colon, '.');	/* first period after colon */
3811	if (!dot1) dot1 = colon + strlen (colon);  /* if not there, append */
3812	(void) sprintf (dot1, ".%d", Scr->screen);
3813	putenv (buf);
3814	restorevar = 1;
3815    }
3816    replace = False;
3817    subs = strstr (s, "$currentworkspace");
3818    name = GetCurrentWorkSpaceName (Scr->currentvs);
3819    if (subs && name) {
3820	len = strlen (s) - strlen ("$currentworkspace") + strlen (name);
3821	news = (char*) malloc (len + 1);
3822	*subs = '\0';
3823	strcpy (news, s);
3824	*subs = '$';
3825	strcat (news, name);
3826	subs += strlen ("$currentworkspace");
3827	strcat (news, subs);
3828	s = news;
3829	replace = True;
3830    }
3831    subs = strstr (s, "$redirect");
3832    if (subs) {
3833	if (captive) {
3834	    name = (char*) malloc (21 + strlen (captivename) + 1);
3835	    sprintf (name, "-xrm 'ctwm.redirect:%s'", captivename);
3836	} else {
3837	    name = (char*) malloc (1);
3838	    *name = '\0';
3839	}
3840	len = strlen (s) - strlen ("$redirect") + strlen (name);
3841	news = (char*) malloc (len + 1);
3842	*subs = '\0';
3843	strcpy (news, s);
3844	*subs = '$';
3845	strcat (news, name);
3846	subs += strlen ("$redirect");
3847	strcat (news, subs);
3848	s = news;
3849	free (name);
3850	replace = True;
3851    }
3852#ifdef USE_SIGNALS
3853  {
3854    SigProc	sig;
3855
3856    sig = signal (SIGALRM, SIG_IGN);
3857    (void) system (s);
3858    signal (SIGALRM, sig);
3859  }
3860#else  /* USE_SIGNALS */
3861    (void) system (s);
3862#endif  /* USE_SIGNALS */
3863
3864    if (restorevar) {		/* why bother? */
3865	(void) sprintf (buf, "DISPLAY=%s", oldDisplay);
3866	putenv (buf);
3867    }
3868    if (replace) free (s);
3869#endif
3870}
3871
3872
3873
3874Window lowerontop = -1;
3875
3876void PlaceTransients (TwmWindow *tmp_win, int where)
3877{
3878    int	sp, sc;
3879    TwmWindow *t;
3880    XWindowChanges xwc;
3881    xwc.stack_mode = where;
3882
3883    sp = tmp_win->frame_width * tmp_win->frame_height;
3884    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3885	if (t != tmp_win &&
3886	    ((t->transient && t->transientfor == tmp_win->w) ||
3887	     t->group == tmp_win->w)) {
3888	    if (t->frame) {
3889		sc = t->frame_width * t->frame_height;
3890		if (sc < ((sp * Scr->TransientOnTop) / 100)) {
3891		    xwc.sibling = tmp_win->frame;
3892		    XConfigureWindow(dpy, t->frame, CWSibling | CWStackMode, &xwc);
3893		    if (lowerontop == t->frame) {
3894			lowerontop = (Window)-1;
3895		    }
3896		}
3897	    }
3898	}
3899    }
3900}
3901
3902#include <assert.h>
3903
3904void PlaceOntop (int ontop, int where)
3905{
3906    TwmWindow *t;
3907    XWindowChanges xwc;
3908    xwc.stack_mode = where;
3909
3910    lowerontop = (Window)-1;
3911
3912    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
3913	if (t->ontoppriority > ontop) {
3914	    XConfigureWindow(dpy, t->frame, CWStackMode, &xwc);
3915	    PlaceTransients(t, Above);
3916	    if (lowerontop == (Window)-1) {
3917		lowerontop = t->frame;
3918	    }
3919	}
3920    }
3921}
3922
3923void MapRaised (TwmWindow *tmp_win)
3924{
3925    XMapWindow(dpy, tmp_win->frame);
3926    RaiseWindow(tmp_win);
3927}
3928
3929void RaiseWindow (TwmWindow *tmp_win)
3930{
3931    XWindowChanges xwc;
3932    int xwcm;
3933
3934    if (tmp_win->ontoppriority == ONTOP_MAX) {
3935	XRaiseWindow(dpy, tmp_win->frame);
3936	if (lowerontop == (Window)-1) {
3937	    lowerontop = tmp_win->frame;
3938	} else if (lowerontop == tmp_win->frame) {
3939	    lowerontop = (Window)-1;
3940	}
3941    } else {
3942	if (lowerontop == (Window)-1) {
3943	    PlaceOntop(tmp_win->ontoppriority, Above);
3944	}
3945	xwcm = CWStackMode;
3946	if (lowerontop != (Window)-1) {
3947	    xwc.stack_mode = Below;
3948	    xwc.sibling = lowerontop;
3949	    xwcm |= CWSibling;
3950	} else {
3951	    xwc.stack_mode = Above;
3952	}
3953	XConfigureWindow(dpy, tmp_win->frame, xwcm, &xwc);
3954    }
3955    PlaceTransients(tmp_win, Above);
3956}
3957
3958void RaiseLower (TwmWindow *tmp_win)
3959{
3960    XWindowChanges xwc;
3961
3962    PlaceOntop(tmp_win->ontoppriority, Below);
3963    PlaceTransients(tmp_win, Below);
3964    lowerontop = (Window)-1;
3965    xwc.stack_mode = Opposite;
3966    XConfigureWindow(dpy, tmp_win->frame, CWStackMode, &xwc);
3967    PlaceOntop(tmp_win->ontoppriority, Above);
3968    PlaceTransients(tmp_win, Above);
3969}
3970
3971void RaiseLowerFrame (Window frame, int ontop)
3972{
3973    XWindowChanges xwc;
3974
3975    PlaceOntop(ontop, Below);
3976    lowerontop = (Window)-1;
3977    xwc.stack_mode = Opposite;
3978    XConfigureWindow(dpy, frame, CWStackMode, &xwc);
3979    PlaceOntop(ontop, Above);
3980}
3981
3982void LowerWindow (TwmWindow *tmp_win)
3983{
3984    XLowerWindow(dpy, tmp_win->frame);
3985    if (tmp_win->frame == lowerontop) {
3986	lowerontop = (Window)-1;
3987    }
3988    PlaceTransients(tmp_win, Above);
3989}
3990
3991void RaiseFrame (Window frame)
3992{
3993    TwmWindow *tmp_win;
3994
3995    tmp_win = GetTwmWindow(frame);
3996
3997    if (tmp_win != NULL) {
3998	RaiseWindow(tmp_win);
3999    } else {
4000	XRaiseWindow(dpy, frame);
4001    }
4002}
4003
4004/***********************************************************************
4005 *
4006 *  Procedure:
4007 *	FocusOnRoot - put input focus on the root window
4008 *
4009 ***********************************************************************
4010 */
4011
4012void FocusOnRoot(void)
4013{
4014    SetFocus ((TwmWindow *) NULL, LastTimestamp());
4015    InstallColormaps(0, &Scr->RootColormaps);
4016    if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4017}
4018
4019static void ReMapOne(TwmWindow *t, TwmWindow *leader)
4020{
4021    if (t->icon_on)
4022	Zoom(t->icon->w, t->frame);
4023    else if (leader->icon)
4024	Zoom(leader->icon->w, t->frame);
4025
4026    if (!t->squeezed)
4027	XMapWindow(dpy, t->w);
4028    t->mapped = TRUE;
4029    if (Scr->Root != Scr->CaptiveRoot)	/* XXX dubious test */
4030	XReparentWindow (dpy, t->frame, Scr->Root, t->frame_x, t->frame_y);
4031    if (Scr->NoRaiseDeicon)
4032	XMapWindow(dpy, t->frame);
4033    else
4034	MapRaised(t);
4035    SetMapStateProp(t, NormalState);
4036
4037    if (t->icon && t->icon->w) {
4038	XUnmapWindow(dpy, t->icon->w);
4039	IconDown(t);
4040	if (Scr->ShrinkIconTitles)
4041	    t->icon->title_shrunk = True;
4042    }
4043    if (t->iconmanagerlist) {
4044	WList *wl;
4045
4046	for (wl = t->iconmanagerlist; wl != NULL; wl = wl->nextv)
4047	    XUnmapWindow(dpy, wl->icon);
4048    }
4049    t->isicon = FALSE;
4050    t->icon_on = FALSE;
4051    WMapDeIconify(t);
4052}
4053
4054static void ReMapTransients(TwmWindow *tmp_win)
4055{
4056    TwmWindow *t;
4057
4058    /* find t such that it is a transient or group member window */
4059    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
4060	if (t != tmp_win &&
4061		((t->transient && t->transientfor == tmp_win->w) ||
4062		 (t->group == tmp_win->w && t->isicon))) {
4063	    ReMapOne(t, tmp_win);
4064	}
4065    }
4066}
4067
4068void DeIconify(TwmWindow *tmp_win)
4069{
4070    TwmWindow *t = tmp_win;
4071    int isicon = FALSE;
4072
4073    /* de-iconify the main window */
4074    if (Scr->WindowMask)
4075	XRaiseWindow (dpy, Scr->WindowMask);
4076    if (tmp_win->isicon)
4077    {
4078	isicon = TRUE;
4079	if (tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w)
4080	    Zoom(tmp_win->icon->w, tmp_win->frame);
4081	else if (tmp_win->group != (Window) 0)
4082	{
4083	    t = GetTwmWindow(tmp_win->group);
4084	    if (t && t->icon_on && t->icon && t->icon->w)
4085	    {
4086		Zoom(t->icon->w, tmp_win->frame);
4087	    }
4088	}
4089    }
4090
4091    ReMapOne(tmp_win, t);
4092
4093    if (isicon &&
4094	(Scr->WarpCursor ||
4095	 LookInList(Scr->WarpCursorL, tmp_win->full_name, &tmp_win->class)))
4096      WarpToWindow (tmp_win, 0);
4097
4098    /* now de-iconify and window group transients */
4099    ReMapTransients(tmp_win);
4100
4101    if (! Scr->WindowMask && Scr->DeIconifyFunction.func != 0) {
4102	char *action;
4103	XEvent event;
4104
4105	action = Scr->DeIconifyFunction.item ?
4106		Scr->DeIconifyFunction.item->action : NULL;
4107	ExecuteFunction (Scr->DeIconifyFunction.func, action,
4108			   (Window) 0, tmp_win, &event, C_ROOT, FALSE);
4109    }
4110    XSync (dpy, 0);
4111}
4112
4113
4114static void UnmapTransients(TwmWindow *tmp_win, int iconify, unsigned long eventMask)
4115{
4116    TwmWindow *t;
4117
4118    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
4119	if (t != tmp_win &&
4120		((t->transient && t->transientfor == tmp_win->w) ||
4121		 t->group == tmp_win->w)) {
4122	    if (iconify) {
4123		if (t->icon_on)
4124		    Zoom(t->icon->w, tmp_win->icon->w);
4125		else if (tmp_win->icon)
4126		    Zoom(t->frame, tmp_win->icon->w);
4127	    }
4128
4129	    /*
4130	     * Prevent the receipt of an UnmapNotify, since that would
4131	     * cause a transition to the Withdrawn state.
4132	     */
4133	    t->mapped = FALSE;
4134	    XSelectInput(dpy, t->w, eventMask & ~StructureNotifyMask);
4135	    XUnmapWindow(dpy, t->w);
4136	    XUnmapWindow(dpy, t->frame);
4137	    XSelectInput(dpy, t->w, eventMask);
4138	    if (t->icon && t->icon->w) XUnmapWindow(dpy, t->icon->w);
4139	    SetMapStateProp(t, IconicState);
4140	    if (t == Scr->Focus) {
4141		SetFocus ((TwmWindow *) NULL, LastTimestamp());
4142		if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4143	    }
4144	    if (t->iconmanagerlist)
4145		XMapWindow(dpy, t->iconmanagerlist->icon);
4146	    t->isicon = TRUE;
4147	    t->icon_on = FALSE;
4148	    WMapIconify (t);
4149	}
4150    }
4151}
4152
4153void Iconify(TwmWindow *tmp_win, int def_x, int def_y)
4154{
4155    TwmWindow *t;
4156    int iconify;
4157    XWindowAttributes winattrs;
4158    unsigned long eventMask;
4159    WList *wl;
4160    Window leader = (Window)-1;
4161    Window blanket = (Window)-1;
4162
4163    iconify = (!tmp_win->iconify_by_unmapping);
4164    t = (TwmWindow*) 0;
4165    if (tmp_win->transient) {
4166	leader = tmp_win->transientfor;
4167	t = GetTwmWindow(leader);
4168    }
4169    else
4170    if ((leader = tmp_win->group) != 0 && leader != tmp_win->w) {
4171	t = GetTwmWindow(leader);
4172    }
4173    if (t && t->icon_on) iconify = False;
4174    if (iconify)
4175    {
4176	if (!tmp_win->icon || !tmp_win->icon->w)
4177	    CreateIconWindow(tmp_win, def_x, def_y);
4178	else
4179	    IconUp(tmp_win);
4180	if (visible (tmp_win)) {
4181	    if (Scr->WindowMask) {
4182		XRaiseWindow (dpy, Scr->WindowMask);
4183		XMapWindow(dpy, tmp_win->icon->w);
4184	    }
4185	    else
4186		XMapRaised(dpy, tmp_win->icon->w);
4187	}
4188    }
4189    if (tmp_win->iconmanagerlist) {
4190      for (wl = tmp_win->iconmanagerlist; wl != NULL; wl = wl->nextv) {
4191	XMapWindow(dpy, wl->icon);
4192      }
4193    }
4194
4195    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
4196    eventMask = winattrs.your_event_mask;
4197
4198    /* iconify transients and window group first */
4199    UnmapTransients(tmp_win, iconify, eventMask);
4200
4201    if (iconify) Zoom(tmp_win->frame, tmp_win->icon->w);
4202
4203    /*
4204     * Prevent the receipt of an UnmapNotify, since that would
4205     * cause a transition to the Withdrawn state.
4206     */
4207    tmp_win->mapped = FALSE;
4208
4209    if ((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
4210	XSetWindowAttributes attr;
4211	XGetWindowAttributes(dpy, tmp_win->frame, &winattrs);
4212	attr.backing_store = NotUseful;
4213	attr.save_under    = False;
4214	blanket = XCreateWindow (dpy, Scr->Root, winattrs.x, winattrs.y,
4215				 winattrs.width, winattrs.height, (unsigned int) 0,
4216				 CopyFromParent, (unsigned int) CopyFromParent,
4217				 (Visual *) CopyFromParent, CWBackingStore | CWSaveUnder, &attr);
4218	XMapWindow (dpy, blanket);
4219    }
4220    XSelectInput(dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
4221    XUnmapWindow(dpy, tmp_win->w);
4222    XUnmapWindow(dpy, tmp_win->frame);
4223    XSelectInput(dpy, tmp_win->w, eventMask);
4224    SetMapStateProp(tmp_win, IconicState);
4225
4226    if ((Scr->IconifyStyle != ICONIFY_NORMAL) && !Scr->WindowMask) {
4227      switch (Scr->IconifyStyle) {
4228        case ICONIFY_MOSAIC:  MosaicFade    (tmp_win, blanket); break;
4229        case ICONIFY_ZOOMIN:  ZoomInWindow  (tmp_win, blanket); break;
4230        case ICONIFY_ZOOMOUT: ZoomOutWindow (tmp_win, blanket); break;
4231	case ICONIFY_SWEEP:   SweepWindow   (tmp_win, blanket); break;
4232      }
4233      XDestroyWindow (dpy, blanket);
4234    }
4235    if (tmp_win == Scr->Focus) {
4236	SetFocus ((TwmWindow *) NULL, LastTimestamp());
4237	if (! Scr->ClickToFocus) Scr->FocusRoot = TRUE;
4238    }
4239    tmp_win->isicon = TRUE;
4240    tmp_win->icon_on = iconify ? TRUE : FALSE;
4241    WMapIconify (tmp_win);
4242    if (! Scr->WindowMask && Scr->IconifyFunction.func != 0) {
4243	char *action;
4244	XEvent event;
4245
4246	action = Scr->IconifyFunction.item ? Scr->IconifyFunction.item->action : NULL;
4247	ExecuteFunction (Scr->IconifyFunction.func, action,
4248			   (Window) 0, tmp_win, &event, C_ROOT, FALSE);
4249    }
4250    XSync (dpy, 0);
4251}
4252
4253void AutoSqueeze (TwmWindow *tmp_win)
4254{
4255    if (tmp_win->iconmgr) return;
4256    if (Scr->RaiseWhenAutoUnSqueeze && tmp_win->squeezed) XRaiseWindow (dpy, tmp_win->frame);
4257    Squeeze (tmp_win);
4258}
4259
4260void Squeeze (TwmWindow *tmp_win)
4261{
4262    long fx, fy, savex, savey;
4263    int  neww, newh, south;
4264    int	 grav = ((tmp_win->hints.flags & PWinGravity)
4265		      ? tmp_win->hints.win_gravity : NorthWestGravity);
4266    XWindowAttributes winattrs;
4267    unsigned long eventMask;
4268#ifdef GNOME
4269    unsigned char	*prop;
4270    unsigned long	nitems, bytesafter;
4271    Atom		actual_type;
4272    int			actual_format;
4273    long		gwkspc;
4274#endif /* GNOME */
4275    if (tmp_win->squeezed) {
4276	tmp_win->squeezed = False;
4277#ifdef GNOME
4278 	XGetWindowAttributes (dpy, tmp_win->w, &winattrs);
4279	eventMask = winattrs.your_event_mask;
4280	XSelectInput (dpy, tmp_win->w, eventMask & ~PropertyChangeMask);
4281 	if (XGetWindowProperty (dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
4282			       XA_CARDINAL, &actual_type, &actual_format,
4283			       &nitems, &bytesafter, &prop)
4284	    != Success || nitems == 0) {
4285	    gwkspc = 0;
4286	} else {
4287	    gwkspc = (int)*prop;
4288	    XFree ((char *)prop);
4289	}
4290 	gwkspc &= ~WIN_STATE_SHADED;
4291 	XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32,
4292			 PropModeReplace, (unsigned char *)&gwkspc, 1);
4293	XSelectInput(dpy, tmp_win->w, eventMask);
4294#endif /* GNOME */
4295	if (!tmp_win->isicon) XMapWindow (dpy, tmp_win->w);
4296	SetupWindow (tmp_win, tmp_win->actual_frame_x, tmp_win->actual_frame_y,
4297		     tmp_win->actual_frame_width, tmp_win->actual_frame_height, -1);
4298	ReMapTransients(tmp_win);
4299	return;
4300    }
4301
4302    newh = tmp_win->title_height + 2 * tmp_win->frame_bw3D;
4303    if (newh < 3) { XBell (dpy, 0); return; }
4304    switch (grav) {
4305	case SouthWestGravity :
4306	case SouthGravity :
4307	case SouthEastGravity :
4308	    south = True; break;
4309	default :
4310	    south = False; break;
4311    }
4312    if (tmp_win->title_height && !tmp_win->AlwaysSqueezeToGravity) south = False;
4313
4314    tmp_win->squeezed = True;
4315    tmp_win->actual_frame_width  = tmp_win->frame_width;
4316    tmp_win->actual_frame_height = tmp_win->frame_height;
4317    savex = fx = tmp_win->frame_x;
4318    savey = fy = tmp_win->frame_y;
4319    neww  = tmp_win->actual_frame_width;
4320    if (south) fy += tmp_win->frame_height - newh;
4321    if (tmp_win->squeeze_info) {
4322	fx  += tmp_win->title_x - tmp_win->frame_bw3D;
4323	neww = tmp_win->title_width + 2 * (tmp_win->frame_bw + tmp_win->frame_bw3D);
4324    }
4325    XGetWindowAttributes(dpy, tmp_win->w, &winattrs);
4326    eventMask = winattrs.your_event_mask;
4327#ifdef GNOME
4328    XSelectInput (dpy, tmp_win->w, eventMask & ~(StructureNotifyMask | PropertyChangeMask));
4329    if (XGetWindowProperty (dpy, tmp_win->w, _XA_WIN_STATE, 0L, 32, False,
4330			    XA_CARDINAL, &actual_type, &actual_format, &nitems,
4331			    &bytesafter, &prop)
4332	!= Success || nitems == 0) {
4333	gwkspc = 0;
4334    } else {
4335	gwkspc = (int)*prop;
4336	XFree ((char *)prop);
4337    }
4338    gwkspc |= WIN_STATE_SHADED;
4339    XChangeProperty (dpy, tmp_win->w, _XA_WIN_STATE, XA_CARDINAL, 32,
4340		     PropModeReplace, (unsigned char *)&gwkspc, 1);
4341#else
4342    XSelectInput (dpy, tmp_win->w, eventMask & ~StructureNotifyMask);
4343#endif /* GNOME */
4344    XUnmapWindow(dpy, tmp_win->w);
4345    XSelectInput(dpy, tmp_win->w, eventMask);
4346
4347    if (fx + neww >= Scr->rootw - Scr->BorderRight)
4348        fx = Scr->rootw - Scr->BorderRight - neww;
4349    if (fy + newh >= Scr->rooth - Scr->BorderBottom)
4350        fy = Scr->rooth - Scr->BorderBottom - newh;
4351    SetupWindow (tmp_win, fx, fy, neww, newh, -1);
4352    tmp_win->actual_frame_x = savex;
4353    tmp_win->actual_frame_y = savey;
4354
4355    /* Now make the group members disappear */
4356    UnmapTransients(tmp_win, 0, eventMask);
4357}
4358
4359static void Identify (TwmWindow *t)
4360{
4361    int i, n, twidth, width, height;
4362    int x, y;
4363    unsigned int wwidth, wheight, bw, depth;
4364    Window junk;
4365    int px, py, dummy;
4366    unsigned udummy;
4367    unsigned char	*prop;
4368    unsigned long	nitems, bytesafter;
4369    Atom		actual_type;
4370    int			actual_format;
4371    XRectangle inc_rect;
4372    XRectangle logical_rect;
4373    Bool first = True;
4374
4375    n = 0;
4376    (void) sprintf(Info[n++], "Twm version:  %s", Version);
4377    (void) sprintf(Info[n], "Compile time options :");
4378#ifdef XPM
4379    (void) strcat (Info[n], " XPM");
4380    first = False;
4381#endif
4382#ifdef IMCONV
4383    if (!first) (void) strcat(Info[n], ", ");
4384    (void) strcat (Info[n], "IMCONV");
4385    first = False;
4386#endif
4387#ifdef USEM4
4388    if (!first) (void) strcat(Info[n], ", ");
4389    (void) strcat (Info[n], "USEM4");
4390    first = False;
4391#endif
4392#ifdef GNOME
4393    if (!first) (void) strcat(Info[n], ", ");
4394    (void) strcat (Info[n], "GNOME");
4395    first = False;
4396#endif
4397#ifdef SOUNDS
4398    if (!first) (void) strcat(Info[n], ", ");
4399    (void) strcat (Info[n], "SOUNDS");
4400    first = False;
4401#endif
4402#ifdef DEBUG
4403    if (!first) (void) strcat(Info[n], ", ");
4404    (void) strcat (Info[n], "debug");
4405    first = False;
4406#endif
4407    if (!first) (void) strcat(Info[n], ", ");
4408    (void) strcat (Info[n], "I18N");
4409    first = False;
4410    n++;
4411    Info[n++][0] = '\0';
4412
4413    if (t) {
4414	XGetGeometry (dpy, t->w, &JunkRoot, &JunkX, &JunkY,
4415		      &wwidth, &wheight, &bw, &depth);
4416	(void) XTranslateCoordinates (dpy, t->w, Scr->Root, 0, 0,
4417				      &x, &y, &junk);
4418	(void) sprintf(Info[n++], "Name               = \"%s\"", t->full_name);
4419	(void) sprintf(Info[n++], "Class.res_name     = \"%s\"", t->class.res_name);
4420	(void) sprintf(Info[n++], "Class.res_class    = \"%s\"", t->class.res_class);
4421	Info[n++][0] = '\0';
4422	(void) sprintf(Info[n++], "Geometry/root (UL)  = %dx%d+%d+%d (Inner: %dx%d+%d+%d)",
4423		       wwidth + 2 * (bw + t->frame_bw3D),
4424		       wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
4425		       x - (bw + t->frame_bw3D),
4426		       y - (bw + t->frame_bw3D + t->title_height),
4427		       wwidth, wheight, x, y);
4428	(void) sprintf(Info[n++], "Geometry/root (LR)  = %dx%d-%d-%d (Inner: %dx%d-%d-%d)",
4429		       wwidth + 2 * (bw + t->frame_bw3D),
4430		       wheight + 2 * (bw + t->frame_bw3D) + t->title_height,
4431		       Scr->rootw - (x + wwidth + bw + t->frame_bw3D),
4432		       Scr->rooth - (y + wheight + bw + t->frame_bw3D),
4433		       wwidth, wheight,
4434		       Scr->rootw - (x + wwidth), Scr->rooth - (y + wheight));
4435	(void) sprintf(Info[n++], "Border width       = %d", bw);
4436	(void) sprintf(Info[n++], "3D border width    = %d", t->frame_bw3D);
4437	(void) sprintf(Info[n++], "Depth              = %d", depth);
4438
4439	if (XGetWindowProperty (dpy, t->w, _XA_WM_CLIENT_MACHINE, 0L, 64, False,
4440				XA_STRING, &actual_type, &actual_format, &nitems,
4441				&bytesafter, &prop) == Success) {
4442	    if (nitems && prop) {
4443		(void) sprintf(Info[n++], "Client machine     = %s", (char*)prop);
4444		XFree ((char *) prop);
4445	    }
4446	}
4447	Info[n++][0] = '\0';
4448    }
4449
4450    (void) sprintf(Info[n++], "Click to dismiss....");
4451
4452    /* figure out the width and height of the info window */
4453    height = n * (Scr->DefaultFont.height+2);
4454    width = 1;
4455    for (i = 0; i < n; i++)
4456    {
4457	XmbTextExtents(Scr->DefaultFont.font_set, Info[i],
4458		       strlen(Info[i]), &inc_rect, &logical_rect);
4459
4460	twidth = logical_rect.width;
4461	if (twidth > width)
4462	    width = twidth;
4463    }
4464    if (InfoLines) XUnmapWindow(dpy, Scr->InfoWindow);
4465
4466    width += 10;		/* some padding */
4467    height += 10;		/* some padding */
4468    if (XQueryPointer (dpy, Scr->Root, &JunkRoot, &JunkChild,
4469		       &dummy, &dummy, &px, &py, &udummy)) {
4470	px -= (width / 2);
4471	py -= (height / 3);
4472	if (px + width + BW2 >= Scr->rootw)
4473	  px = Scr->rootw - width - BW2;
4474	if (py + height + BW2 >= Scr->rooth)
4475	  py = Scr->rooth - height - BW2;
4476	if (px < 0) px = 0;
4477	if (py < 0) py = 0;
4478    } else {
4479	px = py = 0;
4480    }
4481    XMoveResizeWindow(dpy, Scr->InfoWindow, px, py, width, height);
4482    XMapRaised(dpy, Scr->InfoWindow);
4483    InfoLines  = n;
4484    InfoWidth  = width;
4485    InfoHeight = height;
4486}
4487
4488
4489
4490 void SetMapStateProp(TwmWindow *tmp_win, int state)
4491{
4492    unsigned long data[2];		/* "suggested" by ICCCM version 1 */
4493
4494    data[0] = (unsigned long) state;
4495    data[1] = (unsigned long) (tmp_win->iconify_by_unmapping ? None :
4496			   (tmp_win->icon ? tmp_win->icon->w : None));
4497
4498    XChangeProperty (dpy, tmp_win->w, _XA_WM_STATE, _XA_WM_STATE, 32,
4499		 PropModeReplace, (unsigned char *) data, 2);
4500}
4501
4502
4503
4504Bool GetWMState (Window w, int *statep, Window *iwp)
4505{
4506    Atom actual_type;
4507    int actual_format;
4508    unsigned long nitems, bytesafter;
4509    unsigned long *datap = NULL;
4510    Bool retval = False;
4511
4512    if (XGetWindowProperty (dpy, w, _XA_WM_STATE, 0L, 2L, False, _XA_WM_STATE,
4513			    &actual_type, &actual_format, &nitems, &bytesafter,
4514			    (unsigned char **) &datap) != Success || !datap)
4515      return False;
4516
4517    if (nitems <= 2) {			/* "suggested" by ICCCM version 1 */
4518	*statep = (int) datap[0];
4519	*iwp = (Window) datap[1];
4520	retval = True;
4521    }
4522
4523    XFree ((char *) datap);
4524    return retval;
4525}
4526
4527
4528
4529int WarpToScreen (int n, int inc)
4530{
4531    Window dumwin;
4532    int x, y, dumint;
4533    unsigned int dummask;
4534    ScreenInfo *newscr = NULL;
4535
4536    while (!newscr) {
4537					/* wrap around */
4538	if (n < 0)
4539	  n = NumScreens - 1;
4540	else if (n >= NumScreens)
4541	  n = 0;
4542
4543	newscr = ScreenList[n];
4544	if (!newscr) {			/* make sure screen is managed */
4545	    if (inc) {			/* walk around the list */
4546		n += inc;
4547		continue;
4548	    }
4549	    fprintf (stderr, "%s:  unable to warp to unmanaged screen %d\n",
4550		     ProgramName, n);
4551	    XBell (dpy, 0);
4552	    return (1);
4553	}
4554    }
4555
4556    if (Scr->screen == n) return (0);	/* already on that screen */
4557
4558    PreviousScreen = Scr->screen;
4559    XQueryPointer (dpy, Scr->Root, &dumwin, &dumwin, &x, &y,
4560		   &dumint, &dumint, &dummask);
4561
4562    XWarpPointer (dpy, None, newscr->Root, 0, 0, 0, 0, x, y);
4563    Scr = newscr;
4564    return (0);
4565}
4566
4567
4568
4569
4570/*
4571 * BumpWindowColormap - rotate our internal copy of WM_COLORMAP_WINDOWS
4572 */
4573
4574int BumpWindowColormap (TwmWindow *tmp, int inc)
4575{
4576    int i, j, previously_installed;
4577    ColormapWindow **cwins;
4578
4579    if (!tmp) return (1);
4580
4581    if (inc && tmp->cmaps.number_cwins > 0) {
4582	cwins = (ColormapWindow **) malloc(sizeof(ColormapWindow *)*
4583					   tmp->cmaps.number_cwins);
4584	if (cwins) {
4585	    if ((previously_installed =
4586		 /* SUPPRESS 560 */(Scr->cmapInfo.cmaps == &tmp->cmaps &&
4587				    tmp->cmaps.number_cwins))) {
4588		for (i = tmp->cmaps.number_cwins; i-- > 0; )
4589		    tmp->cmaps.cwins[i]->colormap->state = 0;
4590	    }
4591
4592	    for (i = 0; i < tmp->cmaps.number_cwins; i++) {
4593		j = i - inc;
4594		if (j >= tmp->cmaps.number_cwins)
4595		    j -= tmp->cmaps.number_cwins;
4596		else if (j < 0)
4597		    j += tmp->cmaps.number_cwins;
4598		cwins[j] = tmp->cmaps.cwins[i];
4599	    }
4600
4601	    free((char *) tmp->cmaps.cwins);
4602
4603	    tmp->cmaps.cwins = cwins;
4604
4605	    if (tmp->cmaps.number_cwins > 1)
4606		memset (tmp->cmaps.scoreboard, 0,
4607		       ColormapsScoreboardLength(&tmp->cmaps));
4608
4609	    if (previously_installed) {
4610		InstallColormaps(PropertyNotify, NULL);
4611	    }
4612	}
4613    } else
4614	FetchWmColormapWindows (tmp);
4615    return (1);
4616}
4617
4618
4619
4620void ShowIconManager (void)
4621{
4622    IconMgr   *i;
4623    WorkSpace *wl;
4624
4625    if (! Scr->workSpaceManagerActive) return;
4626
4627    if (Scr->NoIconManagers) return;
4628    for (wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
4629	for (i = wl->iconmgr; i != NULL; i = i->next) {
4630	    if (i->count == 0) continue;
4631	    if (visible (i->twm_win)) {
4632		SetMapStateProp (i->twm_win, NormalState);
4633		XMapWindow (dpy, i->twm_win->w);
4634		MapRaised (i->twm_win);
4635		if (i->twm_win->icon && i->twm_win->icon->w)
4636		    XUnmapWindow (dpy, i->twm_win->icon->w);
4637	    }
4638	    i->twm_win->mapped = TRUE;
4639	    i->twm_win->isicon = FALSE;
4640	}
4641    }
4642}
4643
4644
4645void HideIconManager (void)
4646{
4647    IconMgr   *i;
4648    WorkSpace *wl;
4649
4650    if (Scr->NoIconManagers) return;
4651    for (wl = Scr->workSpaceMgr.workSpaceList; wl != NULL; wl = wl->next) {
4652	for (i = wl->iconmgr; i != NULL; i = i->next) {
4653	    SetMapStateProp (i->twm_win, WithdrawnState);
4654	    XUnmapWindow(dpy, i->twm_win->frame);
4655	    if (i->twm_win->icon && i->twm_win->icon->w) XUnmapWindow (dpy, i->twm_win->icon->w);
4656	    i->twm_win->mapped = FALSE;
4657	    i->twm_win->isicon = TRUE;
4658	}
4659    }
4660}
4661
4662
4663
4664
4665void DestroyMenu (MenuRoot *menu)
4666{
4667    MenuItem *item;
4668
4669    if (menu->w) {
4670	XDeleteContext (dpy, menu->w, MenuContext);
4671	XDeleteContext (dpy, menu->w, ScreenContext);
4672	if (Scr->Shadow) XDestroyWindow (dpy, menu->shadow);
4673	XDestroyWindow(dpy, menu->w);
4674    }
4675
4676    for (item = menu->first; item; ) {
4677	MenuItem *tmp = item;
4678	item = item->next;
4679	free ((char *) tmp);
4680    }
4681}
4682
4683
4684
4685/*
4686 * warping routines
4687 */
4688
4689void WarpAlongRing (XButtonEvent *ev, Bool forward)
4690{
4691    TwmWindow *r, *head;
4692
4693    if (Scr->RingLeader)
4694      head = Scr->RingLeader;
4695    else if (!(head = Scr->Ring))
4696      return;
4697
4698    if (forward) {
4699	for (r = head->ring.next; r != head; r = r->ring.next) {
4700	    if (!r) break;
4701	    if (r->mapped && (Scr->WarpRingAnyWhere || visible (r))) break;
4702	}
4703    } else {
4704	for (r = head->ring.prev; r != head; r = r->ring.prev) {
4705	    if (!r) break;
4706	    if (r->mapped && (Scr->WarpRingAnyWhere || visible (r))) break;
4707	}
4708    }
4709
4710    /* Note: (Scr->Focus != r) is necessary when we move to a workspace that
4711       has a single window and we want warping to warp to it. */
4712    if (r && (r != head || Scr->Focus != r)) {
4713	TwmWindow *p = Scr->RingLeader, *t;
4714
4715	Scr->RingLeader = r;
4716	WarpToWindow (r, 1);
4717
4718	if (p && p->mapped &&
4719	    (t = GetTwmWindow(ev->window)) &&
4720	    p == t) {
4721	    p->ring.cursor_valid = True;
4722	    p->ring.curs_x = ev->x_root - t->frame_x;
4723	    p->ring.curs_y = ev->y_root - t->frame_y;
4724#ifdef DEBUG
4725	    fprintf(stderr, "WarpAlongRing: cursor_valid := True; x := %d (%d-%d), y := %d (%d-%d)\n", Tmp_win->ring.curs_x, ev->x_root, t->frame_x, Tmp_win->ring.curs_y, ev->y_root, t->frame_y);
4726#endif
4727	    /*
4728	     * The check if the cursor position is inside the window is now
4729	     * done in WarpToWindow().
4730	     */
4731	}
4732    }
4733}
4734
4735
4736
4737void WarpToWindow (TwmWindow *t, int must_raise)
4738{
4739    int x, y;
4740
4741    if (t->ring.cursor_valid) {
4742	x = t->ring.curs_x;
4743	y = t->ring.curs_y;
4744#ifdef DEBUG
4745	fprintf(stderr, "WarpToWindow: cursor_valid; x == %d, y == %d\n", x, y);
4746#endif
4747
4748	/*
4749	 * XXX is this correct with 3D borders? Easier check possible?
4750	 * frame_bw is for the left border.
4751	 */
4752	if (x < t->frame_bw)
4753	    x = t->frame_bw;
4754	if (x >= t->frame_width + t->frame_bw)
4755	    x  = t->frame_width + t->frame_bw - 1;
4756	if (y < t->title_height + t->frame_bw)
4757	    y = t->title_height + t->frame_bw;
4758	if (y >= t->frame_height + t->frame_bw)
4759	    y  = t->frame_height + t->frame_bw - 1;
4760#ifdef DEBUG
4761	fprintf(stderr, "WarpToWindow: adjusted    ; x := %d, y := %d\n", x, y);
4762#endif
4763    } else {
4764	x = t->frame_width / 2;
4765	y = t->frame_height / 2;
4766#ifdef DEBUG
4767	fprintf(stderr, "WarpToWindow: middle; x := %d, y := %d\n", x, y);
4768#endif
4769    }
4770#if 0
4771    int dest_x, dest_y;
4772    Window child;
4773
4774    /*
4775     * Check if the proposed position actually is visible. If not, raise the window.
4776     * "If the coordinates are contained in a mapped
4777     * child of dest_w, that child is returned to child_return."
4778     * We'll need to check for the right child window; the frame probably.
4779     * (What about XXX window boxes?)
4780     *
4781     * Alternatively, use XQueryPointer() which returns the root window
4782     * the pointer is in, but XXX that won't work for VirtualScreens.
4783     */
4784    if (XTranslateCoordinates(dpy, t->frame, Scr->Root, x, y, &dest_x, &dest_y, &child)) {
4785	if (child != t->frame)
4786	    must_raise = 1;
4787    }
4788#endif
4789    if (t->auto_raise || must_raise) AutoRaiseWindow (t);
4790    if (! visible (t)) {
4791	WorkSpace *wlist;
4792
4793	for (wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL; wlist = wlist->next) {
4794	    if (OCCUPY (t, wlist)) break;
4795	}
4796	if (wlist != NULL) GotoWorkSpace (Scr->currentvs, wlist);
4797    }
4798    XWarpPointer (dpy, None, Scr->Root, 0, 0, 0, 0, x + t->frame_x, y + t->frame_y);
4799#ifdef DEBUG
4800    {
4801	Window root_return;
4802	Window child_return;
4803	int root_x_return;
4804	int root_y_return;
4805	int win_x_return;
4806	int win_y_return;
4807	unsigned int mask_return;
4808
4809	if (XQueryPointer(dpy, t->frame, &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask_return)) {
4810	    fprintf(stderr, "XQueryPointer: root_return=%x, child_return=%x, root_x_return=%d, root_y_return=%d, win_x_return=%d, win_y_return=%d\n", root_return, child_return, root_x_return, root_y_return, win_x_return, win_y_return);
4811	}
4812    }
4813#endif
4814}
4815
4816
4817
4818
4819/*
4820 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
4821 * client messages will have the following form:
4822 *
4823 *     event type	ClientMessage
4824 *     message type	_XA_WM_PROTOCOLS
4825 *     window		tmp->w
4826 *     format		32
4827 *     data[0]		message atom
4828 *     data[1]		time stamp
4829 */
4830static void send_clientmessage (Window w, Atom a, Time timestamp)
4831{
4832    XClientMessageEvent ev;
4833
4834    ev.type = ClientMessage;
4835    ev.window = w;
4836    ev.message_type = _XA_WM_PROTOCOLS;
4837    ev.format = 32;
4838    ev.data.l[0] = a;
4839    ev.data.l[1] = timestamp;
4840    XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
4841}
4842
4843void SendDeleteWindowMessage (TwmWindow *tmp, Time timestamp)
4844{
4845    send_clientmessage (tmp->w, _XA_WM_DELETE_WINDOW, timestamp);
4846}
4847
4848void SendSaveYourselfMessage (TwmWindow *tmp, Time timestamp)
4849{
4850    send_clientmessage (tmp->w, _XA_WM_SAVE_YOURSELF, timestamp);
4851}
4852
4853
4854void SendTakeFocusMessage (TwmWindow *tmp, Time timestamp)
4855{
4856    send_clientmessage (tmp->w, _XA_WM_TAKE_FOCUS, timestamp);
4857}
4858
4859int MoveMenu (XEvent *eventp)
4860{
4861    int    XW, YW, newX, newY, cont;
4862    Bool   newev;
4863    unsigned long event_mask;
4864    XEvent ev;
4865
4866    if (! ActiveMenu) return (1);
4867    if (! ActiveMenu->pinned) return (1);
4868
4869    XW = eventp->xbutton.x_root - ActiveMenu->x;
4870    YW = eventp->xbutton.y_root - ActiveMenu->y;
4871    XGrabPointer (dpy, ActiveMenu->w, True,
4872		ButtonPressMask  | ButtonReleaseMask | ButtonMotionMask,
4873		GrabModeAsync, GrabModeAsync,
4874		None, Scr->MoveCursor, CurrentTime);
4875
4876    newX = ActiveMenu->x;
4877    newY = ActiveMenu->y;
4878    cont = TRUE;
4879    event_mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask | ExposureMask;
4880    XMaskEvent (dpy, event_mask, &ev);
4881    while (cont) {
4882	ev.xbutton.x_root -= Scr->rootx;
4883	ev.xbutton.y_root -= Scr->rooty;
4884	switch (ev.xany.type) {
4885	    case ButtonRelease :
4886		cont = FALSE;
4887	    case MotionNotify :
4888		if (!cont) {
4889		    newev = False;
4890		    while (XCheckMaskEvent (dpy, ButtonMotionMask | ButtonReleaseMask, &ev)) {
4891			newev = True;
4892			if (ev.type == ButtonRelease) break;
4893		    }
4894		    if (ev.type == ButtonRelease) continue;
4895		    if (newev) {
4896			ev.xbutton.x_root -= Scr->rootx;
4897			ev.xbutton.y_root -= Scr->rooty;
4898		    }
4899		}
4900        	newX = ev.xbutton.x_root - XW;
4901        	newY = ev.xbutton.y_root - YW;
4902		if (Scr->DontMoveOff)
4903                {
4904                    ConstrainByBorders1 (&newX, ActiveMenu->width,
4905                                        &newY, ActiveMenu->height);
4906		}
4907		XMoveWindow (dpy, ActiveMenu->w, newX, newY);
4908		XMaskEvent (dpy, event_mask, &ev);
4909		break;
4910	    case ButtonPress :
4911		cont = FALSE;
4912		newX = ActiveMenu->x;
4913		newY = ActiveMenu->y;
4914		break;
4915	    case Expose:
4916	    case NoExpose:
4917                Event = ev;
4918                DispatchEvent ();
4919		XMaskEvent (dpy, event_mask, &ev);
4920		break;
4921	}
4922    }
4923    XUngrabPointer (dpy, CurrentTime);
4924    if (ev.xany.type == ButtonRelease) ButtonPressed = -1;
4925    /*XPutBackEvent (dpy, &ev);*/
4926    XMoveWindow (dpy, ActiveMenu->w, newX, newY);
4927    ActiveMenu->x = newX;
4928    ActiveMenu->y = newY;
4929    MenuOrigins [MenuDepth - 1].x = newX;
4930    MenuOrigins [MenuDepth - 1].y = newY;
4931
4932    return (1);
4933}
4934
4935/***********************************************************************
4936 *
4937 *  Procedure:
4938 *      DisplayPosition - display the position in the dimensions window
4939 *
4940 *  Inputs:
4941 *      tmp_win - the current twm window
4942 *      x, y    - position of the window
4943 *
4944 ***********************************************************************
4945 */
4946
4947void DisplayPosition (TwmWindow *tmp_win, int x, int y)
4948{
4949    char str [100];
4950    char signx = '+';
4951    char signy = '+';
4952
4953    if (x < 0) {
4954	x = -x;
4955	signx = '-';
4956    }
4957    if (y < 0) {
4958	y = -y;
4959	signy = '-';
4960    }
4961    (void) sprintf (str, " %c%-4d %c%-4d ", signx, x, signy, y);
4962    XRaiseWindow (dpy, Scr->SizeWindow);
4963
4964    Draw3DBorder (Scr->SizeWindow, 0, 0,
4965		Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
4966		Scr->SizeFont.height + SIZE_VINDENT * 2,
4967		2, Scr->DefaultC, off, False, False);
4968
4969    FB(Scr->DefaultC.fore, Scr->DefaultC.back);
4970    XmbDrawImageString (dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
4971			Scr->NormalGC, Scr->SizeStringOffset,
4972			Scr->SizeFont.ascent + SIZE_VINDENT , str, 13);
4973}
4974
4975void MosaicFade (TwmWindow *tmp_win, Window blanket)
4976{
4977    int		srect;
4978    int		i, j, nrects;
4979    Pixmap	mask;
4980    GC		gc;
4981    XGCValues	gcv;
4982    XRectangle *rectangles;
4983    int  width = tmp_win->frame_width;
4984    int height = tmp_win->frame_height;
4985
4986    srect = (width < height) ? (width / 20) : (height / 20);
4987    mask  = XCreatePixmap (dpy, blanket, width, height, 1);
4988
4989    gcv.foreground = 1;
4990    gc = XCreateGC (dpy, mask, GCForeground, &gcv);
4991    XFillRectangle (dpy, mask, gc, 0, 0, width, height);
4992    gcv.function = GXclear;
4993    XChangeGC (dpy, gc, GCFunction, &gcv);
4994
4995    nrects = ((width * height) / (srect * srect)) / 10;
4996    rectangles = (XRectangle*) malloc (nrects * sizeof (XRectangle));
4997    for (j = 0; j < nrects; j++) {
4998	rectangles [j].width  = srect;
4999	rectangles [j].height = srect;
5000    }
5001    for (i = 0; i < 10; i++) {
5002	for (j = 0; j < nrects; j++) {
5003	    rectangles [j].x = ((lrand48 () %  width) / srect) * srect;
5004	    rectangles [j].y = ((lrand48 () % height) / srect) * srect;
5005	}
5006	XFillRectangles (dpy, mask, gc, rectangles, nrects);
5007	XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5008	XFlush (dpy);
5009	waitamoment (0.020);
5010    }
5011    XFreePixmap (dpy, mask);
5012    XFreeGC (dpy, gc);
5013    free (rectangles);
5014}
5015
5016void ZoomInWindow (TwmWindow *tmp_win, Window blanket)
5017{
5018  Pixmap	mask;
5019  GC		gc, gcn;
5020  XGCValues	gcv;
5021
5022  int i, nsteps = 20;
5023  int w = tmp_win->frame_width;
5024  int h = tmp_win->frame_height;
5025  int step = (MAX (w, h)) / (2.0 * nsteps);
5026
5027  mask = XCreatePixmap (dpy, blanket, w, h, 1);
5028  gcv.foreground = 1;
5029  gc  = XCreateGC (dpy, mask, GCForeground, &gcv);
5030  gcv.function = GXclear;
5031  gcn = XCreateGC (dpy, mask, GCForeground | GCFunction, &gcv);
5032
5033  for (i = 0; i < nsteps; i++) {
5034    XFillRectangle (dpy, mask, gcn, 0, 0, w, h);
5035    XFillArc (dpy, mask, gc, (w / 2) - ((nsteps - i) * step),
5036	                     (h / 2) - ((nsteps - i) * step),
5037	                     2 * (nsteps - i) * step,
5038	                     2 * (nsteps - i) * step,
5039	                     0, 360*64);
5040    XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5041    XFlush (dpy);
5042    waitamoment (0.020);
5043  }
5044}
5045
5046void ZoomOutWindow (TwmWindow *tmp_win, Window blanket)
5047{
5048  Pixmap	mask;
5049  GC		gc;
5050  XGCValues	gcv;
5051
5052  int i, nsteps = 20;
5053  int w = tmp_win->frame_width;
5054  int h = tmp_win->frame_height;
5055  int step = (MAX (w, h)) / (2.0 * nsteps);
5056
5057  mask  = XCreatePixmap (dpy, blanket, w, h, 1);
5058  gcv.foreground = 1;
5059  gc = XCreateGC (dpy, mask, GCForeground, &gcv);
5060  XFillRectangle (dpy, mask, gc, 0, 0, w, h);
5061  gcv.function = GXclear;
5062  XChangeGC (dpy, gc, GCFunction, &gcv);
5063
5064  for (i = 0; i < nsteps; i++) {
5065    XFillArc (dpy, mask, gc, (w / 2) - (i * step),
5066	                     (h / 2) - (i * step),
5067	                     2 * i * step,
5068	                     2 * i * step,
5069	                     0, 360*64);
5070    XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5071    XFlush (dpy);
5072    waitamoment (0.020);
5073  }
5074}
5075
5076void FadeWindow (TwmWindow *tmp_win, Window blanket)
5077{
5078  Pixmap	mask, stipple;
5079  GC		gc;
5080  XGCValues	gcv;
5081  static unsigned char stipple_bits[] = { 0x0F, 0x0F,
5082					  0xF0, 0xF0,
5083					  0x0F, 0x0F,
5084					  0xF0, 0xF0,
5085					  0x0F, 0x0F,
5086					  0xF0, 0xF0,
5087					  0x0F, 0x0F,
5088					  0xF0, 0xF0,
5089  };
5090  int w = tmp_win->frame_width;
5091  int h = tmp_win->frame_height;
5092
5093  stipple = XCreateBitmapFromData (dpy, blanket, (char *)stipple_bits, 8, 8);
5094  mask    = XCreatePixmap (dpy, blanket, w, h, 1);
5095  gcv.background = 0;
5096  gcv.foreground = 1;
5097  gcv.stipple    = stipple;
5098  gcv.fill_style = FillOpaqueStippled;
5099  gc = XCreateGC (dpy, mask, GCBackground | GCForeground | GCFillStyle | GCStipple, &gcv);
5100  XFillRectangle (dpy, mask, gc, 0, 0, w, h);
5101
5102  XShapeCombineMask (dpy, blanket, ShapeBounding, 0, 0, mask, ShapeSet);
5103  XFlush (dpy);
5104  waitamoment (10.0);
5105  XFreePixmap (dpy, stipple);
5106}
5107
5108void SweepWindow (TwmWindow *tmp_win, Window	blanket)
5109{
5110  float step = 0.0;
5111  int i, nsteps = 20;
5112  int dir = 0, dist = tmp_win->frame_x, dist1;
5113
5114  dist1 = tmp_win->frame_y;
5115  if (dist1 < dist) { dir = 1; dist = dist1; }
5116  dist1 = tmp_win->vs->w - (tmp_win->frame_x + tmp_win->frame_width);
5117  if (dist1 < dist) { dir = 2; dist = dist1; }
5118  dist1 = tmp_win->vs->h - (tmp_win->frame_y + tmp_win->frame_height);
5119  if (dist1 < dist) { dir = 3; dist = dist1; }
5120
5121  switch (dir) {
5122    case 0: step = tmp_win->frame_x + tmp_win->frame_width;  break;
5123    case 1: step = tmp_win->frame_y + tmp_win->frame_height; break;
5124    case 2: step = tmp_win->vs->w - tmp_win->frame_x;        break;
5125    case 3: step = tmp_win->vs->h - tmp_win->frame_y;        break;
5126  }
5127  step /= (float) nsteps;
5128  step /= (float) nsteps;
5129  for (i = 0; i < 20; i++) {
5130    int x = tmp_win->frame_x;
5131    int y = tmp_win->frame_y;
5132    switch (dir) {
5133      case 0: x -= i * i * step; break;
5134      case 1: y -= i * i * step; break;
5135      case 2: x += i * i * step; break;
5136      case 3: y += i * i * step; break;
5137    }
5138    XMoveWindow (dpy, blanket, x, y);
5139    XFlush (dpy);
5140    waitamoment (0.020);
5141  }
5142}
5143
5144void waitamoment (float timeout)
5145{
5146#ifdef VMS
5147  lib$wait (&timeout);
5148#else
5149  struct timeval timeoutstruct;
5150  int usec = timeout * 1000000;
5151  timeoutstruct.tv_usec = usec % (unsigned long) 1000000;
5152  timeoutstruct.tv_sec  = usec / (unsigned long) 1000000;
5153  select (0, (void *) 0, (void *) 0, (void *) 0, &timeoutstruct);
5154#endif
5155}
5156
5157void packwindow (TwmWindow *tmp_win, char *direction)
5158{
5159    int			cons, newx, newy;
5160    int			x, y, px, py, junkX, junkY;
5161    unsigned int	junkK;
5162    Window		junkW;
5163
5164    if (!strcmp (direction,   "left")) {
5165	cons  = FindConstraint (tmp_win, J_LEFT);
5166	if (cons == -1) return;
5167    	newx  = cons;
5168	newy  = tmp_win->frame_y;
5169    } else
5170    if (!strcmp (direction,  "right")) {
5171	cons  = FindConstraint (tmp_win, J_RIGHT);
5172	if (cons == -1) return;
5173    	newx  = cons;
5174	newx -= tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5175	newy  = tmp_win->frame_y;
5176    } else
5177    if (!strcmp (direction,    "top")) {
5178	cons  = FindConstraint (tmp_win, J_TOP);
5179	if (cons == -1) return;
5180	newx  = tmp_win->frame_x;
5181    	newy  = cons;
5182    } else
5183    if (!strcmp (direction, "bottom")) {
5184	cons  = FindConstraint (tmp_win, J_BOTTOM);
5185	if (cons == -1) return;
5186	newx  = tmp_win->frame_x;
5187    	newy  = cons;
5188	newy -= tmp_win->frame_height  + 2 * tmp_win->frame_bw;
5189    } else return;
5190
5191    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &x, &y, &junkK);
5192    px = x - tmp_win->frame_x + newx;
5193    py = y - tmp_win->frame_y + newy;
5194    XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, px, py);
5195    XRaiseWindow(dpy, tmp_win->frame);
5196    XMoveWindow (dpy, tmp_win->frame, newx, newy);
5197    SetupWindow (tmp_win, newx, newy, tmp_win->frame_width, tmp_win->frame_height, -1);
5198}
5199
5200void fillwindow (TwmWindow *tmp_win, char *direction)
5201{
5202    int	cons, newx, newy, save;
5203    unsigned int neww, newh;
5204    int	i;
5205    int	winx = tmp_win->frame_x;
5206    int	winy = tmp_win->frame_y;
5207    int	winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5208    int	winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5209
5210    if (!strcmp (direction, "left")) {
5211	cons = FindConstraint (tmp_win, J_LEFT);
5212	if (cons == -1) return;
5213    	newx = cons;
5214	newy = tmp_win->frame_y;
5215	neww = winw + winx - newx;
5216	newh = winh;
5217	neww -= 2 * tmp_win->frame_bw;
5218	newh -= 2 * tmp_win->frame_bw;
5219	ConstrainSize (tmp_win, &neww, &newh);
5220    } else
5221    if (!strcmp (direction, "right")) {
5222	for (i = 0; i < 2; i++) {
5223	    cons = FindConstraint (tmp_win, J_RIGHT);
5224	    if (cons == -1) return;
5225    	    newx = tmp_win->frame_x;
5226	    newy = tmp_win->frame_y;
5227    	    neww = cons - winx;
5228	    newh = winh;
5229	    save = neww;
5230	    neww -= 2 * tmp_win->frame_bw;
5231	    newh -= 2 * tmp_win->frame_bw;
5232	    ConstrainSize (tmp_win, &neww, &newh);
5233	    if ((neww != winw) || (newh != winh) ||
5234                (cons == Scr->rootw - Scr->BorderRight))
5235                break;
5236	    neww = save;
5237	    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5238	}
5239    } else
5240    if (!strcmp (direction, "top")) {
5241	cons = FindConstraint (tmp_win, J_TOP);
5242	if (cons == -1) return;
5243    	newx = tmp_win->frame_x;
5244	newy = cons;
5245	neww = winw;
5246	newh = winh + winy - newy;
5247	neww -= 2 * tmp_win->frame_bw;
5248	newh -= 2 * tmp_win->frame_bw;
5249	ConstrainSize (tmp_win, &neww, &newh);
5250    } else
5251    if (!strcmp (direction, "bottom")) {
5252	for (i = 0; i < 2; i++) {
5253	    cons = FindConstraint (tmp_win, J_BOTTOM);
5254	    if (cons == -1) return;
5255    	    newx = tmp_win->frame_x;
5256	    newy = tmp_win->frame_y;
5257    	    neww = winw;
5258	    newh = cons - winy;
5259	    save = newh;
5260	    neww -= 2 * tmp_win->frame_bw;
5261	    newh -= 2 * tmp_win->frame_bw;
5262	    ConstrainSize (tmp_win, &neww, &newh);
5263	    if ((neww != winw) || (newh != winh) ||
5264                (cons == Scr->rooth - Scr->BorderBottom))
5265                break;
5266	    newh = save;
5267	    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5268	}
5269	}
5270	else if (!strcmp (direction, "vertical"))
5271	{
5272		if (tmp_win->zoomed == ZOOM_NONE)
5273		{
5274			tmp_win->save_frame_height = tmp_win->frame_height;
5275			tmp_win->save_frame_width = tmp_win->frame_width;
5276			tmp_win->save_frame_y = tmp_win->frame_y;
5277			tmp_win->save_frame_x = tmp_win->frame_x;
5278
5279			tmp_win->frame_y++;
5280			newy = FindConstraint (tmp_win, J_TOP);
5281			tmp_win->frame_y--;
5282			newh = FindConstraint (tmp_win, J_BOTTOM) - newy;
5283			newh -= 2 * tmp_win->frame_bw;
5284
5285			newx = tmp_win->frame_x;
5286			neww = tmp_win->frame_width;
5287
5288			ConstrainSize (tmp_win, &neww, &newh);
5289
5290			/* if the bottom of the window has moved up
5291			 * it will be pushed down */
5292			if (newy + newh <
5293			    tmp_win->save_frame_y + tmp_win->save_frame_height)
5294			  newy = tmp_win->save_frame_y +
5295			    tmp_win->save_frame_height - newh;
5296			tmp_win->zoomed = F_ZOOM;
5297			SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5298		}
5299		else
5300		{
5301			fullzoom (tmp_win, tmp_win->zoomed);
5302		}
5303		return;
5304	}
5305    else return;
5306    SetupWindow (tmp_win, newx, newy, neww, newh, -1);
5307}
5308
5309void jump (TwmWindow *tmp_win, int  direction, char *action)
5310{
5311    int			fx, fy, px, py, step, status, cons;
5312    int			fwidth, fheight;
5313    int			junkX, junkY;
5314    unsigned int	junkK;
5315    Window		junkW;
5316
5317    if (! action) return;
5318    status = sscanf (action, "%d", &step);
5319    if (status != 1) return;
5320    if (step < 1) return;
5321
5322    fx = tmp_win->frame_x;
5323    fy = tmp_win->frame_y;
5324    XQueryPointer (dpy, Scr->Root, &junkW, &junkW, &junkX, &junkY, &px, &py, &junkK);
5325    px -= fx; py -= fy;
5326
5327    fwidth  = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5328    fheight = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5329    switch (direction) {
5330	case J_LEFT   :
5331	    cons  = FindConstraint (tmp_win, J_LEFT);
5332	    if (cons == -1) return;
5333	    fx -= step * Scr->XMoveGrid;
5334	    if (fx < cons) fx = cons;
5335	    break;
5336	case J_RIGHT  :
5337	    cons  = FindConstraint (tmp_win, J_RIGHT);
5338	    if (cons == -1) return;
5339	    fx += step * Scr->XMoveGrid;
5340	    if (fx + fwidth > cons) fx = cons - fwidth;
5341	    break;
5342	case J_TOP    :
5343	    cons  = FindConstraint (tmp_win, J_TOP);
5344	    if (cons == -1) return;
5345	    fy -= step * Scr->YMoveGrid;
5346	    if (fy < cons) fy = cons;
5347	    break;
5348	case J_BOTTOM :
5349	    cons  = FindConstraint (tmp_win, J_BOTTOM);
5350	    if (cons == -1) return;
5351	    fy += step * Scr->YMoveGrid;
5352	    if (fy + fheight > cons) fy = cons - fheight;
5353	    break;
5354    }
5355    /* Pebl Fixme: don't warp if jump happens through iconmgr */
5356    XWarpPointer (dpy, Scr->Root, Scr->Root, 0, 0, 0, 0, fx + px, fy + py);
5357    if (!Scr->NoRaiseMove)
5358        XRaiseWindow (dpy, tmp_win->frame);
5359    SetupWindow (tmp_win, fx, fy, tmp_win->frame_width, tmp_win->frame_height, -1);
5360}
5361
5362int FindConstraint (TwmWindow *tmp_win, int direction)
5363{
5364    TwmWindow	*t;
5365    int		w, h;
5366    int		winx = tmp_win->frame_x;
5367    int		winy = tmp_win->frame_y;
5368    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5369    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5370    int 	ret;
5371
5372    switch (direction) {
5373	case J_LEFT   : if (winx < Scr->BorderLeft) return -1;
5374			ret = Scr->BorderLeft; break;
5375	case J_RIGHT  : if (winx + winw > Scr->rootw - Scr->BorderRight) return -1;
5376			ret = Scr->rootw - Scr->BorderRight; break;
5377	case J_TOP    : if (winy < Scr->BorderTop) return -1;
5378			ret = Scr->BorderTop; break;
5379	case J_BOTTOM : if (winy + winh > Scr->rooth - Scr->BorderBottom) return -1;
5380			ret = Scr->rooth - Scr->BorderBottom; break;
5381	default       : return -1;
5382    }
5383    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5384	if (t == tmp_win) continue;
5385	if (!visible (t)) continue;
5386	if (!t->mapped) continue;
5387	w = t->frame_width  + 2 * t->frame_bw;
5388	h = t->frame_height + 2 * t->frame_bw;
5389
5390	switch (direction) {
5391	    case J_LEFT :
5392		if (winx        <= t->frame_x + w) continue;
5393		if (winy        >= t->frame_y + h) continue;
5394		if (winy + winh <= t->frame_y    ) continue;
5395		ret = MAX (ret, t->frame_x + w);
5396		break;
5397	    case J_RIGHT :
5398		if (winx + winw >= t->frame_x    ) continue;
5399		if (winy        >= t->frame_y + h) continue;
5400		if (winy + winh <= t->frame_y    ) continue;
5401		ret = MIN (ret, t->frame_x);
5402		break;
5403	    case J_TOP :
5404		if (winy        <= t->frame_y + h) continue;
5405		if (winx        >= t->frame_x + w) continue;
5406		if (winx + winw <= t->frame_x    ) continue;
5407		ret = MAX (ret, t->frame_y + h);
5408		break;
5409	    case J_BOTTOM :
5410		if (winy + winh >= t->frame_y    ) continue;
5411		if (winx        >= t->frame_x + w) continue;
5412		if (winx + winw <= t->frame_x    ) continue;
5413		ret = MIN (ret, t->frame_y);
5414		break;
5415	}
5416    }
5417    return ret;
5418}
5419
5420void TryToPack (TwmWindow *tmp_win, int *x, int *y)
5421{
5422    TwmWindow	*t;
5423    int		newx, newy;
5424    int		w, h;
5425    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5426    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5427
5428    newx = *x;
5429    newy = *y;
5430    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5431	if (t == tmp_win) continue;
5432	if (t->winbox != tmp_win->winbox) continue;
5433	if (t->vs != tmp_win->vs) continue;
5434	if (!t->mapped) continue;
5435
5436	w = t->frame_width  + 2 * t->frame_bw;
5437	h = t->frame_height + 2 * t->frame_bw;
5438	if (newx >= t->frame_x + w) continue;
5439	if (newy >= t->frame_y + h) continue;
5440	if (newx + winw <= t->frame_x) continue;
5441	if (newy + winh <= t->frame_y) continue;
5442
5443	if (newx + Scr->MovePackResistance > t->frame_x + w) { /* left */
5444	    newx = MAX (newx, t->frame_x + w);
5445	    continue;
5446	}
5447	if (newx + winw < t->frame_x + Scr->MovePackResistance) { /* right */
5448	    newx = MIN (newx, t->frame_x - winw);
5449	    continue;
5450	}
5451	if (newy + Scr->MovePackResistance > t->frame_y + h) { /* top */
5452	    newy = MAX (newy, t->frame_y + h);
5453	    continue;
5454	}
5455	if (newy + winh < t->frame_y + Scr->MovePackResistance) { /* bottom */
5456	    newy = MIN (newy, t->frame_y - winh);
5457	    continue;
5458	}
5459    }
5460    *x = newx;
5461    *y = newy;
5462}
5463
5464void TryToPush (TwmWindow *tmp_win, int x, int y, int dir)
5465{
5466    TwmWindow	*t;
5467    int		newx, newy, ndir;
5468    Boolean	move;
5469    int		w, h;
5470    int		winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5471    int		winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5472
5473    for (t = Scr->FirstWindow; t != NULL; t = t->next) {
5474	if (t == tmp_win) continue;
5475	if (t->winbox != tmp_win->winbox) continue;
5476	if (t->vs != tmp_win->vs) continue;
5477	if (!t->mapped) continue;
5478
5479	w = t->frame_width  + 2 * t->frame_bw;
5480	h = t->frame_height + 2 * t->frame_bw;
5481	if (x >= t->frame_x + w) continue;
5482	if (y >= t->frame_y + h) continue;
5483	if (x + winw <= t->frame_x) continue;
5484	if (y + winh <= t->frame_y) continue;
5485
5486	move = False;
5487	if ((dir == 0 || dir == J_LEFT) &&
5488	    (x + Scr->MovePackResistance > t->frame_x + w)) {
5489	    newx = x - w;
5490	    newy = t->frame_y;
5491	    ndir = J_LEFT;
5492	    move = True;
5493	}
5494	else
5495	if ((dir == 0 || dir == J_RIGHT) &&
5496	   (x + winw < t->frame_x + Scr->MovePackResistance)) {
5497	    newx = x + winw;
5498	    newy = t->frame_y;
5499	    ndir = J_RIGHT;
5500	    move = True;
5501	}
5502	else
5503	if ((dir == 0 || dir == J_TOP) &&
5504	    (y + Scr->MovePackResistance > t->frame_y + h)) {
5505	    newx = t->frame_x;
5506	    newy = y - h;
5507	    ndir = J_TOP;
5508	    move = True;
5509	}
5510	else
5511	if ((dir == 0 || dir == J_BOTTOM) &&
5512	    (y + winh < t->frame_y + Scr->MovePackResistance)) {
5513	    newx = t->frame_x;
5514	    newy = y + winh;
5515	    ndir = J_BOTTOM;
5516	    move = True;
5517	}
5518	if (move) {
5519	    TryToPush (t, newx, newy, ndir);
5520	    TryToPack (t, &newx, &newy);
5521            ConstrainByBorders (tmp_win,
5522				&newx, t->frame_width  + 2 * t->frame_bw,
5523                                &newy, t->frame_height + 2 * t->frame_bw);
5524	    SetupWindow (t, newx, newy, t->frame_width, t->frame_height, -1);
5525	}
5526    }
5527}
5528
5529void TryToGrid (TwmWindow *tmp_win, int *x, int *y)
5530{
5531    int	w    = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5532    int	h    = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5533    int	grav = ((tmp_win->hints.flags & PWinGravity)
5534		      ? tmp_win->hints.win_gravity : NorthWestGravity);
5535
5536    switch (grav) {
5537	case ForgetGravity :
5538	case StaticGravity :
5539	case NorthWestGravity :
5540	case NorthGravity :
5541	case WestGravity :
5542	case CenterGravity :
5543	    *x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
5544                + Scr->BorderLeft;
5545	    *y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid
5546                + Scr->BorderTop;
5547	    break;
5548	case NorthEastGravity :
5549	case EastGravity :
5550	    *x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
5551                  Scr->XMoveGrid) - w + Scr->BorderLeft;
5552	    *y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) *
5553                Scr->YMoveGrid + Scr->BorderTop;
5554	    break;
5555	case SouthWestGravity :
5556	case SouthGravity :
5557	    *x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
5558                + Scr->BorderLeft;
5559	    *y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid)
5560                - h + Scr->BorderTop;
5561	    break;
5562	case SouthEastGravity :
5563	    *x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
5564                  Scr->XMoveGrid) - w + Scr->BorderLeft;
5565	    *y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) *
5566                  Scr->YMoveGrid) - h + Scr->BorderTop;
5567	    break;
5568    }
5569}
5570
5571int WarpCursorToDefaultEntry (MenuRoot *menu)
5572{
5573    MenuItem	*item;
5574    Window	 root;
5575    int		 i, x, y, xl, yt;
5576    unsigned int w, h, bw, d;
5577
5578    for (i = 0, item = menu->first; item != menu->last; item = item->next) {
5579	if (item == menu->defaultitem) break;
5580	i++;
5581     }
5582     if (!XGetGeometry (dpy, menu->w, &root, &x, &y, &w, &h, &bw, &d)) return 0;
5583     xl = x + (menu->width / 2);
5584     yt = y + (i + 0.5) * Scr->EntryHeight;
5585
5586     XWarpPointer (dpy, Scr->Root, Scr->Root,
5587		   Event.xbutton.x_root, Event.xbutton.y_root,
5588		   menu->width, menu->height, xl, yt);
5589     return 1;
5590}
5591
5592