-
Notifications
You must be signed in to change notification settings - Fork 70
Input focus
This page is dedicated to description of input focus management problems, findings and my vision of solutions, notes.
Focused window - window with titlebar of black color and white title text. Focused window receives keyboard input. In OpenStep focused window often referred as key window.
Main menu - separate window that represents vertically aligned menu items with titlebar and application name on it. Only GNUstep applications have main menu. Other application toolkits use concept of in-window menu (menu bar).
Active application - it's an application that has visible focused window. For GNUstep - application that has main menu visible on screen.
WM - window manager, part of Workspace application that responsible for various window management tasks (draws titlebar and resizebar, application icons, dock, workspaces etc).
Here I describe desired behavior of focus management part of WM.
- If there's no application to focus - Workspace should grab focus - Workspace's main menu should become visible.
- If last application window was closed:
- GNUstep: main menu stays on screen, application is active no matter what application was focused before;
- Xlib: focus switches to previously active application.
- If application opens window or attention panel - focus switches to that window/panel.
There are several actions which either directly or indirectly trigger focus switch:
- Window/panel opening (application start, windows creation, Open/Save dialogs, alert panels).
- Window closing:
- initiated by application (user selects menu item, use keyboard shortcuts);
- initiated by WM (titlebbar "Close"/"Kill" button click);
- GNUstep application deactivation: main menu, some panels.
- Click on window decorations (title and resize bars).
- Click inside window (contents managed by the application).
- Double-click on application icon (in Dock or Icon Yard).
- Switch between applications with 'Cmd-Tab' key equivalent.
- Miniaturize/deminiaturize window (with mouse or keyboard).
- Hide/unhide application (with mouse or keyboard).
- Shade/unshade window (with mouse or keyboard).
- Workspace switch.
In X11 world it's usual to pass focus handling to window manager. X11 applications are responsible for window creating/destroying and providing hints to window manager if particular window can receive input focus. When window appears on screen (MapRequest or MapNotify events) window manager starts managing window to perform its duties. When window disappears from screen (UnmapNotify event), window manager stops management of this window (drops information about window meta data - WWindow) with wUnmanageWindow() function call.
WM takes into account several Xlib windows:
-
group leader - special window that is not visible on screen, with
XWMHints.window_group
set to window created withXCreateWindow()
; - one or more windows with
XWMHints.window_group
set to group leader window.
When X11 application starts, WM registers windows with wManageWindow()
(window.c)
function call and creates WWindow
structure. During this process WM saves X11's ID
of the window in client_win
field and group leader into Window main_window
.
After WWindow
structure was filled, WM can identify window's application by reading
it's main_window
field. That's simple. Set of WWindow is a linked list (every WWindow
has a link next and previous WWindow).
In other words - application can exist if at least 2 windows were created: group leader
and window with XWMHints.window_group
set to group leader window ID. So group leader
is a key for searching and identifying windows which belongs to the application.
Another meta information is created on application windows appearing: WApplication. This structure contains some useful information about application - main window ("group leader" again), application icon, last focused window, array of windows, last workspace application was active on, etc. Set of WApplication is also a linked list.
When one application's window closes (UnmapNotify), WWindow structure is removed from WApplicatiton and deleted. When application is destroyed (DestroyNotify) WApplication is deleted.
GNUstep applications are special because they have special window: main menu. When GNUstep application deactivates it hides main menu (and vice versa: main menu appears on screen when application becomes active). From window manager perspective main menu window becomes withdrawn from screen (unmapped). So window manager stops managing such windows. It can be a problem because minimal GNUstep application contains only main menu and application becomes invisible for WM on main menu withdrawal from screen.
That's why I added menu_win
field to WApplication structure and prevent deletion of
main menu window's WWindow
. This is important part of focus switch to GNUstep application.
For example, Cmd-Tab switch panel basically uses WApplication information. When user makes
selection, WM needs some application's window to switch focus to. For GNUstep applications
it's main menu window at least.
Unlike other applications every GNUstep application has its own focus management engine. Roughly it works like this:
- if window loses focus (
FocusOut
event):- if focused window that belongs to this app - transfer focus to it;
- if focused window doesn't belong to this app - app deactivates.
- if window receives focus (
FocusIn
event,WM_TAKE_FOCUS
protocol message):- if app is active - set focus to window;
- if app is inactive - activates it (main menu becomes visible) and set focus to window.
Custom focus handling of GNUstep application should be treated specially by WM. For example, application activation generates extra events (MapNotify, FocusIn/FocusOut). WM has its own window focus stack and should correctly respond to events when GNUstep application change focused window.
In X11 paradigm 2 objects exists: X server and X client. All user applications are X clients. There is special kind of client - window manager - application that helps managing application windows (place on screen, iconify) and handle events reported by X server.
To make things work right, focus management should be intact at both sides: WM and GNUstep application (gnustep-back).
Essential part of communicating with window manager resides in Sources/x11/XGServerEvent.m
file of GUI backend (gnustep-back). GNUstep application receives TakeFocus atom from WM
and handles it in -_handleTakeFocusAtom:forContext:
method
(resides in Sources/x11/XGServerEvent.m).
Here I want to descibe essential parts of WM which are involved focus management (either initiate focus change or make changes to focus related data structure inside WM and changing input focus of windows).
Every running application has WApplication instance. Application exists only if at least one window was mapped. GNUstep application exists until main application menu exists. If window was unmapped from screen WM unmanages this window (remove WWindow structure), window will be managed again upon receiving MapNotify event (WM/src/event.c, handleMapNotify()).
WApplication structure also holds GNUstep application main menu window. Thus WApplication instance must contain:
-
WAppIcon *app_icon
- appplication icon -
WArray *windows
- list of windows (at least one) which belongs to application -
WWindow *menu_win
- main application menu window for GNUstep application
It means:
- X11 application must contain at least
app_icon
and one element inwindows
array. - GNUstep application must contain at least
app_icon
andmenu_win
. For application without windowswindows
array must contain only one element -menu_win
.
To service this purpose Workspace WM - unlike WindowMaker - doesn't unmanage menu_win
when application deactivates - it's treated specially:
- On application start: menu window sends MapNotify, WM starts managing it.
- On application deactivation (focus switch to other app, application hiding): menu window disappears from screen - UnmapNotify event occurs - but WM doesn't unmanage it.
- On application activation: GNUstep maps main menu window, MapRequest event is generated (instead of MapNotify). WM recognizes it as main menu window and calls wWindowMap() to proceed with menu window appearing on screen.
-
main_window
- this is the invisible window that identifies application as a group of windows (it's a group leader). Every WWindow and WAppIcon contains fieldWindow main_window
. That's how application icon, menu and windows/panels can be connected to present single application. -
main_window_desc
- generated WWindow structure formain_window
. Somain_window
can be used as managed WWindow. -
last_focused
- should be set to last window of application that has focus beforeFocusOut
, hide, workspace switch events. Set inwSetFocusTo()
. -
last_workspace
- contains workspace number oflast_focused
window workspace. If, for exmaple, application has 2 windows on different workspaces, double-click on appicon should: switch to workspace wherelast_focused
window resides, set focus to that window (activate application). This field is set inwSetFocusTo()
.
On application start wApplicationCreate() is called:
- creates wapp->windows array
- adds
wwin
to this array - saved
wwin
towapp->menu_win
if it's main menu (usually it is)
When new window opens (MapRequest/MapNotify, event.c) wApplicationAdd() is called:
- adds
wwin
intowapp->windows
array wapp->refcount++
When window is closed (UnmapNotify, event.c) and window is not main menu
wApplicationRemoveWindow()
is called:
- removes
wwin
fromwapp->windows
array wapp->refcount--
When application quits (DestroyNotify, event.c):
- several calls to
wApplicationRemoveWindow()
is performed -
wApplicationDestroy()
is called:-
wUnmanageWindow()
forwapp->menu_win
is called -
wapp->windows
array destroyed wapp->refcount--
-
wapp
is freed
-
There are several user actions which triggers focus switch:
-
Window opening (including application's start) or closing (including application).
actions.c:
wSetFocusTo()
event.c:
handleMapRequest()
,handleMapNotify()
,handleUnmapNotify()
,handleDestroyNotify()
-
Click on window decorations (title and resize bars).
actions.c:
wSetFocusTo()
-
Click inside window (contents managed by the application).
actions.c:
wSetFocusTo()
-
Double-click on application icon (in Dock or Icon Yard).
dock.c:
iconDblClick()
; appicon.c:iconDblClick()
-
Switch between applications with 'Cmd-Tab' key equivalent.
switchpanel.c:
makeWindowListArray()
cycling.c:StartWindozeCycle()
-
Miniaturize/deminiaturize window (with mouse or keyboard).
-
Hide/unhide application (with mouse or keyboard).
-
Workspace switch.
workspace.c:
wWorkspaceForceChange()
Copyright (c) Sergii Stoian