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