1 /* $Id: misc.c,v 1.1 2004/08/28 19:25:46 dannybackx Exp $ */
2 /*****************************************************************************/
3 /**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
4 /**                          Salt Lake City, Utah                           **/
5 /**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
6 /**                        Cambridge, Massachusetts                         **/
7 /**                                                                         **/
8 /**                           All Rights Reserved                           **/
9 /**                                                                         **/
10 /**    Permission to use, copy, modify, and distribute this software and    **/
11 /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
12 /**    granted, provided that the above copyright notice appear  in  all    **/
13 /**    copies and that both  that  copyright  notice  and  this  permis-    **/
14 /**    sion  notice appear in supporting  documentation,  and  that  the    **/
15 /**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
16 /**    in publicity pertaining to distribution of the  software  without    **/
17 /**    specific, written prior permission.                                  **/
18 /**                                                                         **/
19 /**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
20 /**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
21 /**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
22 /**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
23 /**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
24 /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
25 /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
26 /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
27 /*****************************************************************************/
28 /****************************************************************************
29  * This module is based on Twm, but has been siginificantly modified
30  * by Rob Nation
31  ****************************************************************************/
32 /***********************************************************************
33  * The rest of it is all my fault -- MLM
34  * mwm - "LessTif Window Manager"
35  ***********************************************************************/
36 
37 #include <LTconfig.h>
38 
39 #include <stdio.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 
50 #include <X11/Intrinsic.h>
51 #include <X11/keysym.h>
52 
53 #include <Xm/Xm.h>
54 #include <Xm/MwmUtil.h>
55 #include <Xm/XmosP.h>
56 #if XmVERSION >= 2
57 #include <XmI/XmI.h>
58 #endif
59 
60 #include "mwm.h"
61 
62 
63 static Time lastTimestamp = CurrentTime;
64 
65 /*
66  * find a window in the tree
67  */
68 static MwmWindow *
find_by_window(MwmWindow * win,Window w)69 find_by_window(MwmWindow *win, Window w)
70 {
71     MwmWindow *child, *tmp;
72 
73     if (win->w == w)
74 	return win;
75 
76     /* search breadth first */
77     for (tmp = win->next; tmp != NULL; tmp = tmp->next)
78     {
79 	if ((child = find_by_window(tmp, w)) != NULL)
80 	    return child;
81     }
82 
83     /* then depth */
84     for (tmp = win->child; tmp != NULL; tmp = tmp->child)
85     {
86 	if ((child = find_by_window(tmp, w)) != NULL)
87 	    return child;
88     }
89 
90     return NULL;
91 }
92 
93 
94 /*
95  * find a window in the tree.  Returns a SIBLING in a window group.  The
96  * caller must determine if the ancestor is valid.
97  */
98 static MwmWindow *
find_by_group(MwmWindow * win,XID group)99 find_by_group(MwmWindow *win, XID group)
100 {
101     MwmWindow *child, *tmp;
102 
103     if (win->wmhints && (win->wmhints->flags & WindowGroupHint) &&
104 	win->wmhints->window_group == group)
105     {
106 	return win;
107     }
108 
109     tmp = win->next;
110     if (tmp != NULL)
111     {
112 	if ((child = find_by_group(tmp, group)) != NULL)
113         {
114 	    return child;
115         }
116     }
117 
118     tmp = win->child;
119     if (tmp != NULL)
120     {
121 	if ((child = find_by_group(tmp, group)) != NULL)
122         {
123 	    return child;
124         }
125     }
126 
127     return NULL;
128 }
129 
130 /*
131  * add a new child to a window group
132  */
133 static void
add_child(MwmWindow * win,MwmWindow * child)134 add_child(MwmWindow *win, MwmWindow *child)
135 {
136     if (win == NULL || child == NULL)
137 	return;
138 
139     if (win->child == NULL)
140     {
141 	win->child = child;
142 	child->ancestor = win;
143 	child->next = child->prev = NULL;
144 	return;
145     }
146 
147     child->ancestor = win;
148     child->next = win->child;
149     child->next->prev = child;
150     win->child = child;
151 }
152 
153 /*
154  * remove a child from a window group
155  * the child itself may have transient children.  These must be taken
156  * care of elsewhere
157  */
158 static void
remove_child(MwmWindow * win,MwmWindow * child)159 remove_child(MwmWindow *win, MwmWindow *child)
160 {
161     if (child == NULL || win == NULL)
162 	return;
163 
164     if (win->child == child)
165     {
166 	win->child = child->next;
167 	if (child->next != NULL)
168 	    child->next->prev = NULL;
169 	child->next = child->prev = child->ancestor = NULL;
170 	return;
171     }
172 
173     if (child->next != NULL)
174 	child->next->prev = child->prev;
175     child->prev->next = child->next;
176     child->next = child->prev = child->ancestor = NULL;
177 }
178 
179 /*
180  * print one window tree
181  */
182 static void
print_window_tree(MwmWindow * win,int depth)183 print_window_tree(MwmWindow *win, int depth)
184 {
185     int i;
186     MwmWindow *tmp;
187 
188     if (win == NULL)
189     {
190 	for (i = 0; i < depth; i++)
191 	    printf(" ");
192 	printf("none\n");
193 	return;
194     }
195 
196     for (tmp = win; tmp != NULL; tmp = tmp->next)
197     {
198 	for (i = 0; i < depth; i++)
199 	    printf(" ");
200 	if (tmp->name)
201 	    printf("Window: %08lx %s\n", tmp->w, tmp->name);
202 	else
203 	    printf("Window: %08lx\n", tmp->w);
204 	for (i = 0; i < depth; i++)
205 	    printf(" ");
206 	printf("Children:\n");
207 	print_window_tree(win->child, depth + 4);
208     }
209 }
210 
211 /*
212  * see if a window is in a subtree
213  */
214 static Boolean
window_in_subtree(MwmWindow * win,MwmWindow * sub)215 window_in_subtree(MwmWindow *win, MwmWindow *sub)
216 {
217     MwmWindow *tmp;
218 
219     if (sub == win)
220 	return True;
221 
222     for (tmp = win->child; tmp != NULL; tmp = tmp->next)
223     {
224 	if (window_in_subtree(tmp, sub))
225 	    return True;
226     }
227     return False;
228 }
229 
230 /*
231  * Removes expose events for a specific window from the queue
232  */
233 extern int
MISC_FlushExpose(Window w)234 MISC_FlushExpose(Window w)
235 {
236     XEvent dummy;
237     int i = 0;
238 
239     while (XCheckTypedWindowEvent(dpy, w, Expose, &dummy))
240 	i++;
241     return i;
242 }
243 
244 /*
245  * Start/Stops the auto-raise timer
246  */
247 extern void
MISC_SetTimer(int delay)248 MISC_SetTimer(int delay)
249 {
250 #ifdef HAVE_SETITIMER
251     struct itimerval value;
252 
253     value.it_value.tv_usec = 1000 * (delay % 1000);
254     value.it_value.tv_sec = delay / 1000;
255     value.it_interval.tv_usec = 0;
256     value.it_interval.tv_sec = 0;
257     setitimer(ITIMER_REAL, &value, NULL);
258 #endif
259 }
260 
261 /*
262  * Records the time of the last processed event. Used in XSetInputFocus
263  */
264 extern Boolean
MISC_StashEventTime(XEvent * ev)265 MISC_StashEventTime(XEvent *ev)
266 {
267     Time NewTimestamp = CurrentTime;
268 
269     switch (ev->type)
270     {
271     case KeyPress:
272     case KeyRelease:
273 	NewTimestamp = ev->xkey.time;
274 	break;
275     case ButtonPress:
276     case ButtonRelease:
277 	NewTimestamp = ev->xbutton.time;
278 	break;
279     case MotionNotify:
280 	NewTimestamp = ev->xmotion.time;
281 	break;
282     case EnterNotify:
283     case LeaveNotify:
284 	NewTimestamp = ev->xcrossing.time;
285 	break;
286     case PropertyNotify:
287 	NewTimestamp = ev->xproperty.time;
288 	break;
289     case SelectionClear:
290 	NewTimestamp = ev->xselectionclear.time;
291 	break;
292     case SelectionRequest:
293 	NewTimestamp = ev->xselectionrequest.time;
294 	break;
295     case SelectionNotify:
296 	NewTimestamp = ev->xselection.time;
297 	break;
298     default:
299 	return False;
300     }
301     if (NewTimestamp > lastTimestamp)
302 	lastTimestamp = NewTimestamp;
303     return True;
304 }
305 
306 /*
307  * fetch the last saved time
308  */
309 extern Time
MISC_FetchEventTime()310 MISC_FetchEventTime()
311 {
312     return lastTimestamp;
313 }
314 
315 /******************************************************************************
316  *
317  * Cleare the CIRCULATED field of the window flags.
318  *
319  *****************************************************************************/
320 extern void
MISC_SetFocusSequence(ScreenInfo * scr)321 MISC_SetFocusSequence(ScreenInfo *scr)
322 {
323     MwmWindow *temp;
324     int i = 0;
325 
326     temp = scr->mwm_root.next;
327     while (temp != NULL)
328     {
329 	temp->focus_sequence = i++;
330 	temp = temp->next;
331     }
332 }
333 
334 /*****************************************************************************
335  *
336  * Grab the pointer and keyboard
337  *
338  ****************************************************************************/
339 extern Boolean
MISC_Grab(ScreenInfo * scr,int cursor)340 MISC_Grab(ScreenInfo *scr, int cursor)
341 {
342     int i = 0, val = 0;
343     unsigned int mask;
344 
345     XSync(dpy, 0);
346     /* move the keyboard focus prior to grabbing the pointer to
347      * eliminate the enterNotify and exitNotify events that go
348      * to the windows */
349     if (scr->mwm_last_focus == NULL)
350 	scr->mwm_last_focus = scr->mwm_focus;
351     WIN_SetFocus(scr, scr->no_focus_win, NULL);
352 
353     mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
354 	PointerMotionMask | EnterWindowMask | LeaveWindowMask;
355     while ((i < 1000) && (val = XGrabPointer(dpy, scr->root_win, True, mask,
356 				   GrabModeAsync, GrabModeAsync, scr->root_win,
357 					  scr->cursors[cursor], CurrentTime) !=
358 			  GrabSuccess))
359     {
360 	i++;
361 
362 	/* If you go too fast, other windows may not get a change to release
363 	 * any grab that they have. */
364 	_XmMicroSleep(1000);
365     }
366 
367     /* If we fall out of the loop without grabbing the pointer, its
368        time to give up */
369     XSync(dpy, 0);
370 
371     if (val != GrabSuccess)
372 	return False;
373 
374     return True;
375 }
376 
377 
378 /*****************************************************************************
379  *
380  * UnGrab the pointer and keyboard
381  *
382  ****************************************************************************/
383 extern void
MISC_Ungrab(ScreenInfo * scr)384 MISC_Ungrab(ScreenInfo *scr)
385 {
386     Window w;
387 
388     XSync(dpy, 0);
389     XUngrabPointer(dpy, CurrentTime);
390 
391     if (scr->mwm_last_focus != NULL)
392     {
393 	w = scr->mwm_last_focus->w;
394 
395 	/* if the window still exists, focus on it */
396 	if (w)
397 	{
398 	    WIN_SetFocusInTree(scr->mwm_last_focus);
399 	    WIN_SetFocus(scr, w, scr->mwm_last_focus);
400 	}
401 	scr->mwm_last_focus = NULL;
402     }
403     XSync(dpy, 0);
404 }
405 
406 /*
407  * Wait for all mouse buttons to be released
408  * This can ease some confusion on the part of the user sometimes
409  *
410  * Discard superflous button events during this wait period.
411  */
412 extern void
MISC_WaitForButtonsUp(ScreenInfo * scr)413 MISC_WaitForButtonsUp(ScreenInfo *scr)
414 {
415     Bool AllUp = False;
416     XEvent JunkEvent;
417     unsigned int mask;
418 
419     while (!AllUp)
420     {
421 	XAllowEvents(dpy, ReplayPointer, CurrentTime);
422 	XQueryPointer(dpy, scr->root_win, &JunkRoot, &JunkChild,
423 		      &JunkX, &JunkY, &JunkX, &JunkY, &mask);
424 
425 	if ((mask & (Button1Mask | Button2Mask | Button3Mask |
426 		     Button4Mask | Button5Mask)) == 0)
427 	    AllUp = True;
428     }
429     XSync(dpy, 0);
430     while (XCheckMaskEvent(dpy,
431 			ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
432 			   &JunkEvent))
433     {
434 	MISC_StashEventTime(&JunkEvent);
435 	XAllowEvents(dpy, ReplayPointer, CurrentTime);
436     }
437 
438 }
439 
440 /*
441  * For menus, move, and resize operations, we can effect keyboard
442  * shortcuts by warping the pointer.
443  */
444 extern void
MISC_KeyboardShortcut(ScreenInfo * scr,XEvent * event,int ReturnEvent)445 MISC_KeyboardShortcut(ScreenInfo *scr, XEvent *event, int ReturnEvent)
446 {
447     int x, y, x_root, y_root;
448     int move_size, x_move, y_move;
449     KeySym keysym;
450 
451     /* Pick the size of the cursor movement */
452     move_size = scr->components[MWM_MENU].f_height + HEIGHT_EXTRA;
453     if (event->xkey.state & ControlMask)
454 	move_size = 1;
455     if (event->xkey.state & ShiftMask)
456 	move_size = 100;
457 
458     keysym = XLookupKeysym(&event->xkey, 0);
459 
460     x_move = 0;
461     y_move = 0;
462     switch (keysym)
463     {
464     case XK_Up:
465     case XK_k:
466     case XK_p:
467 	y_move = -move_size;
468 	break;
469     case XK_Down:
470     case XK_n:
471     case XK_j:
472 	y_move = move_size;
473 	break;
474     case XK_Left:
475     case XK_b:
476     case XK_h:
477 	x_move = -move_size;
478 	break;
479     case XK_Right:
480     case XK_f:
481     case XK_l:
482 	x_move = move_size;
483 	break;
484     case XK_Return:
485     case XK_space:
486 	/* beat up the event */
487 	event->type = ReturnEvent;
488 	break;
489     default:
490 	break;
491     }
492     XQueryPointer(dpy, scr->root_win, &JunkRoot, &event->xany.window,
493 		  &x_root, &y_root, &x, &y, &JunkMask);
494 
495     if ((x_move != 0) || (y_move != 0))
496     {
497 	/* beat up the event */
498 	XWarpPointer(dpy, None, scr->root_win, 0, 0, 0, 0, x_root + x_move,
499 		     y_root + y_move);
500 
501 	/* beat up the event */
502 	event->type = MotionNotify;
503 	event->xkey.x += x_move;
504 	event->xkey.y += y_move;
505 	event->xkey.x_root += x_move;
506 	event->xkey.y_root += y_move;
507     }
508 }
509 
510 /*
511  * find the root of a window tree
512  */
513 extern MwmWindow *
MISC_RootOfTree(MwmWindow * win)514 MISC_RootOfTree(MwmWindow *win)
515 {
516     MwmWindow *anc;
517 
518     if (win == NULL)
519 	return NULL;
520 
521     anc = win;
522     while (anc->ancestor != NULL)
523 	anc = anc->ancestor;
524 
525     return anc;
526 }
527 
528 /*
529  * print the window tree
530  */
531 extern void
MISC_PrintTree(ScreenInfo * scr)532 MISC_PrintTree(ScreenInfo *scr)
533 {
534     print_window_tree(scr->mwm_root.next, 0);
535 }
536 
537 /*
538  * add a window to the window tree
539  */
540 extern void
MISC_AddToTree(ScreenInfo * scr,MwmWindow * win)541 MISC_AddToTree(ScreenInfo *scr, MwmWindow *win)
542 {
543     MwmWindow *tmp;
544 
545     if ((win->flags & TRANSIENT) && win->transientfor != None)
546     {
547 	tmp = find_by_window(&scr->mwm_root, win->transientfor);
548 	if (tmp)
549 	{
550 	    add_child(tmp, win);
551 	    return;
552 	}
553     }
554 
555     if (win->wmhints && (win->wmhints->flags & WindowGroupHint))
556     {
557 	tmp = find_by_group(&scr->mwm_root, win->wmhints->window_group);
558 	if (tmp && tmp->ancestor != NULL)
559 	{
560 	    add_child(tmp->ancestor, win);
561 	    return;
562 	}
563     }
564 
565     win->next = scr->mwm_root.next;
566     if (scr->mwm_root.next != NULL)
567 	scr->mwm_root.next->prev = win;
568     win->prev = &scr->mwm_root;
569     scr->mwm_root.next = win;
570 }
571 
572 /*
573  * remove a window from the window tree
574  */
575 extern void
MISC_RemoveFromTree(ScreenInfo * scr,MwmWindow * win)576 MISC_RemoveFromTree(ScreenInfo *scr, MwmWindow *win)
577 {
578     if (win->ancestor != NULL)
579     {
580 	remove_child(win->ancestor, win);
581     }
582     else
583     {
584 	if (win->prev != NULL)
585 	    win->prev->next = win->next;
586 	if (win->next != NULL)
587 	    win->next->prev = win->prev;
588     }
589 }
590 
591 /*
592  * after starting, fix up the transients whose parent windows weren't
593  * captured yet
594  */
595 extern void
MISC_FixupTransients(ScreenInfo * scr)596 MISC_FixupTransients(ScreenInfo *scr)
597 {
598     MwmWindow *tmp, *ptr;
599 
600     for (tmp = scr->mwm_root.next; tmp != NULL;)
601     {
602 	ptr = tmp->next;
603 	if (tmp->flags & TRANSIENT)
604 	{
605 	    tmp->ancestor = NULL;
606 	    MISC_RemoveFromTree(scr, tmp);
607 	    tmp->prev = tmp->next = NULL;
608 	    MISC_AddToTree(scr, tmp);
609 	}
610 	tmp = ptr;
611     }
612 
613     /* under the assumption that their parent may not be found, clear the
614      * transient flag */
615     for (tmp = scr->mwm_root.next; tmp != NULL; tmp = tmp->next)
616     {
617 	if (tmp->flags & TRANSIENT)
618 	{
619 	    tmp->flags &= ~TRANSIENT;
620 	    tmp->ancestor = NULL;
621 	}
622     }
623 }
624 
625 extern void
MISC_DestroyChildren(ScreenInfo * scr,MwmWindow * win)626 MISC_DestroyChildren(ScreenInfo *scr, MwmWindow *win)
627 {
628     MwmWindow *child, *tmp;
629 
630     for (child = win->child; child != NULL; child = tmp)
631     {
632 	tmp = child->next;
633 	WIN_DestroyWindow(scr, child);
634     }
635 }
636 
637 /* Debugging purposes only */
638 extern const char *
_MwmPrintF(int x)639 _MwmPrintF(int x)
640 {
641 	switch(x) {
642 	case F_NOP:	return "F_NOP";
643 	case F_BEEP:	return "F_BEEP";
644 	case F_CHANGE_WINDOWS_DESK:	return "F_CHANGE_WINDOWS_DESK";
645 	case F_CIRCULATE_DOWN:	return "F_CIRCULATE_DOWN";
646 	case F_CIRCULATE_UP:	return "F_CIRCULATE_UP";
647 	case F_CLOSE:	return "F_CLOSE";
648 	case F_DESK:	return "F_DESK";
649 	case F_EXEC:	return "F_EXEC";
650 	case F_FOCUS:	return "F_FOCUS";
651 	case F_FOCUS_COLOR:	return "F_FOCUS_COLOR";
652 	case F_FOCUS_KEY:	return "F_FOCUS_KEY";
653 	case F_GOTO_PAGE:	return "F_GOTO_PAGE";
654 	case F_ICONIFY:	return "F_ICONIFY";
655 	case F_LOWER:	return "F_LOWER";
656 	case F_MAXIMIZE:	return "F_MAXIMIZE";
657 	case F_MOVE:	return "F_MOVE";
658 	case F_MOVECURSOR:	return "F_MOVECURSOR";
659 	case F_NEXT_CMAP:	return "F_NEXT_CMAP";
660 	case F_NEXT_KEY:	return "F_NEXT_KEY";
661 	case F_NORMALIZE:	return "F_NORMALIZE";
662 	case F_NORM_AND_RAISE:	return "F_NORM_AND_RAISE";
663 	case F_PACK_ICONS:	return "F_PACK_ICONS";
664 	case F_PASS_KEYS:	return "F_PASS_KEYS";
665 	case F_POPUP:	return "F_POPUP";
666 	case F_PREV_CMAP:	return "F_PREV_CMAP";
667 	case F_PREV_KEY:	return "F_PREV_KEY";
668 	case F_QUIT:	return "F_QUIT";
669 	case F_RAISE:	return "F_RAISE";
670 	case F_RAISE_IT:	return "F_RAISE_IT";
671 	case F_RAISELOWER:	return "F_RAISELOWER";
672 	case F_RESIZE:	return "F_RESIZE";
673 	case F_RESTART:	return "F_RESTART";
674 	case F_REFRESH:	return "F_REFRESH";
675 	case F_REFRESH_WIN:	return "F_REFRESH_WIN";
676 	case F_RESTORE_AND_RAISE:	return "F_RESTORE_AND_RAISE";
677 	case F_SCREEN:	return "F_SCREEN";
678 	case F_SCROLL:	return "F_SCROLL";
679 	case F_SEND_MSG:	return "F_SEND_MSG";
680 	case F_SET_BEHAVIOR:	return "F_SET_BEHAVIOR";
681 	case F_STICK:	return "F_STICK";
682 	case F_TITLE:	return "F_TITLE";
683 	case F_TOGGLE_PAGE:	return "F_TOGGLE_PAGE";
684 	case F_WARP:	return "F_WARP";
685 	case F_WINDOWLIST:	return "F_WINDOWLIST";
686 	case F_W_POPUP:	return "F_W_POPUP";
687 	default:	return "??";
688 	}
689 }
690 
691 extern const char *
_MwmPrintC(int x)692 _MwmPrintC(int x)
693 {
694 	static char res[256];
695 
696 	res[0] = 0;
697 	if (x == C_NO_CONTEXT)
698 		return "C_NO_CONTEXT";
699 	if (x & C_WINDOW)
700 		strcat(res, " | C_WINDOW");
701 	if (x & C_TITLE)
702 		strcat(res, " | C_TITLE");
703 	if (x & C_ICON)
704 		strcat(res, " | C_ICON");
705 	if (x & C_ROOT)
706 		strcat(res, " | C_ROOT");
707 	if (x & C_FRAME)
708 		strcat(res, " | C_FRAME");
709 	if (x & C_MENUB)
710 		strcat(res, " | C_MENUB");
711 	if (x & C_MINIMIZEB)
712 		strcat(res, " | C_MINIMIZEB");
713 	if (x & C_MAXIMIZEB)
714 		strcat(res, " | C_MAXIMIZEB");
715 	return &res[2];
716 }
717