josh@mit-vax.LCS.MIT.EDU (Joshua Marantz) (10/11/88)
The "Cycle" key is an excellent feature of DEC's UIS window manager that allows you to manage windows without taking your hands off the keyboard. It does this by assigning a specific function key on the LK-201 keyboard (F5) to pop the Least Recently Used window and set the keyboard focus to it. I have found that this is better for me than taking my hands off the keyboard, pointing the mouse at the window I want, and returning my hands to the keyboard. Of course, most of the applications that I use are keyboard driven. I have implemented this feature for "twm" using XGrabKey. The diffs are at the end of this posting. My intention is to hype this feature for all window managers. I hope that whoever maintains them would consider putting in this feature. Does anyone know whether the DECwindows and Open Look specifications provide for a "Cycle" key? I'd hope that at least DECwindows does, since they were successful with it in UIS. My real hope was that I could leave my cycle-key processor hanging around, independent of the window manager. That way I could set up the cycle-key-daemon when I logged in, without having to modify the window manager. I was hoping that twm would intercept my XRaiseWindow calls and do the right thing with its own environment. Maybe when the ICCCM settles down, this sort of thing will be possible. My twm implementation is quick and dirty -- it has hardwired Sun-specific keycodes for L9 and L10. For twm, it would be best to provide keyboard bindings in .twmrc (using XGrabKey, which seems to do the right thing), and have a new function f.cycle that does both an f.raise and an f.focus. -Joshua Marantz Viewlogic Systems, Inc. "diff -c" for events.c as of "twm Version 3.0 6/3/88" follows ---------------------------------------------------------------- *** original-events.c Tue Sep 20 17:36:02 1988 --- events.c Mon Oct 10 16:45:53 1988 *************** *** 74,79 **** --- 74,86 ---- int DragHeight; static int enter_flag; + #define CYCLE 1 + #if CYCLE + extern void HandleKeyPress (); + extern void HandleKeyRelease (); + static void cycle_init (); + #endif + /*********************************************************************** * * Procedure: *************** *** 82,88 **** *********************************************************************** */ - void InitEvents() { int i; --- 89,94 ---- *************** *** 107,112 **** --- 113,123 ---- EventHandler[ConfigureNotify] = HandleConfigureNotify; EventHandler[ClientMessage] = HandleClientMessage; EventHandler[PropertyNotify] = HandlePropertyNotify; + #if CYCLE + EventHandler[KeyPress] = HandleKeyPress; + EventHandler[KeyRelease] = HandleKeyRelease; + cycle_init (); + #endif } /*********************************************************************** *************** *** 1176,1178 **** --- 1187,1288 ---- fprintf(stderr, "type = %d\n", event.type); #endif } + + + #if CYCLE + /* Begin cycle-key code */ + + #define CYCLE_KEY 102 /* L9 */ + #define UNFOCUS_KEY 104 /* L10 */ + + static void cycle_init() { + XGrabKey (dpy, + CYCLE_KEY, + AnyModifier, /* modifiers */ + DefaultRootWindow (dpy), + True, /* Owner events */ + GrabModeAsync, /* pointer mode */ + GrabModeAsync); /* keybard mode */ + XGrabKey (dpy, + UNFOCUS_KEY, + AnyModifier, /* modifiers */ + DefaultRootWindow (dpy), + True, /* Owner events */ + GrabModeAsync, /* pointer mode */ + GrabModeAsync); /* keybard mode */ + } + + static void unfocus() { + ExecuteFunction (F_UNFOCUS, NULL, None, NULL, event, C_TITLE, FALSE); + } + + static int get_twm_frame(window, t) + Window window; + TwmWindow **t; + { + if (XFindContext(dpy, window, TwmContext, t) == XCNOENT) + return (FALSE); + else + return (TRUE); + } + + static void cycle_windows() { + Window root_return, parent_return, *children; + int status, nchildren, i; + TwmWindow *t; + + status = XQueryTree (dpy, + DefaultRootWindow (dpy), + &root_return, + &parent_return, + &children, + &nchildren); + + if (status == 1) { + + /* + Find an acceptable window to pop. Note that XQueryTree + returns windows in the array "children" ordered according to + the visual stack, from bottom to top. + */ + for (i = 0; i < nchildren; i++) { + + /* If twm knows about it and it is not an icon, pop it */ + if (get_twm_frame (children[i], &t) && + (children[i] != t -> icon_w)) + { + /* Always raise the window */ + ExecuteFunction (F_RAISE, NULL, children[i], + t, event, C_TITLE, FALSE); + + /* Only set its focus if it accepts input */ + if ((t -> wmhints != NULL) && + (t -> wmhints -> input == True)) /* Want input */ + ExecuteFunction (F_FOCUS, NULL, children[i], + t, event, C_TITLE, FALSE); + else + unfocus (); + + break; + } + } + XFree (children); + } + } + + void HandleKeyPress() { + XKeyEvent *kep; + + if (event.type == KeyPress) { + kep = (XKeyEvent *) &event; + + if (kep -> keycode == CYCLE_KEY) + cycle_windows (); + else if (kep -> keycode == UNFOCUS_KEY) + unfocus (); + } + } + + void HandleKeyRelease() { + } + #endif
josh@mit-vax.LCS.MIT.EDU (Joshua Marantz) (10/11/88)
After having used my twm cycle key for a while, I realize it has a couple of problems. When running in a focussed environment, it is helpful if the window manager sets the focus to new windows as they are created, and resets the focus to the most recently used window when the window with the focus is destroyed. Another difference between UIS and the modified twm is that if you use the mouse to pop a UIS window, the window that previously held the focus will be first on the cycle-stack. To do this with twm, twm's internal list of windows would have to be used rather than XQueryTree. This requires more extensive changes to twm. Emacs 18.52 has a problem in that is blackens its text cursor whenever the mouse cursor enters its window, regardless of the focus. This is very misleading. Another problem is that xclock seems to have set the "input" field of the window manager hints to True, causing focus to be (uselessly) set to it as I cycle around. The way I coded it, the focus should be set to the root when popping an output-only window, though I'm not even sure I'd like that. As it stands, I cannot use the input field to differentiate xclock from other applications. Are there any other ways to decide an application does not want the kbd? -Joshua Marantz Viewlogic Systems, Inc.
swick@ATHENA.MIT.EDU (Ralph R. Swick) (10/11/88)
> Another problem is that xclock seems to have set the > "input" field of the window manager hints to True This is a bug and has been fixed for R3.