Skip to content

Input focus

Sergii edited this page Dec 4, 2018 · 26 revisions

This page is dedicated to description of input focus management problems, findings and my vision of solutions.

Points of interest

There are several user actions which triggers focus switch:

  1. Window open (including application's start) or close (including application's quit). [actions.c: wSetFocusTo()] [event.c: handleMapRequest(), handleMapNotify(), handleUnmapNotify(), handleDestroyNotify()]
  2. Click on desired window titlebar. [actions.c: wSetFocusTo()]
  3. Click inside desired window. [actions.c: wSetFocusTo()]
  4. Double-click on application icon (in Dock or Icon Yard). [dock.c: iconDblClick(); appicon.c: iconDblClick()]
  5. Switch between applications with 'Cmd-Tab' key equivalent. [cycling.c: ]
  6. Miniaturize/deminiaturize window (with mouse or keyboard).
  7. Hide/unhide application (with mouse or keyboard).
  8. Workspace switch.

Focus Handling Concepts

Here I describe desired behavior of focus management part of Workspace Window Manager (WM).

Anatomy of X11 application from WM's POV

GNUstep applications are special

In X11 world it's normal to pass focus management functions to window manager. X11 applications are responsible for window creation/destroying and providing hints to window manager if particular window can receive input focus. When window appears on screen window manager starts managing window to perform its duties. When window is withdrawn from screen, window manager stops management of this window (drops information about window meta data).

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 window becomes withdrawn from screen (unmapped). So window manager stops managing such windows.

GNUstep's part

Essential part of communicating with window manager resides in Sources/x11/XGServerEvent.m file of GUI backend (gnustep-back). To make things work right focus management should be intact in window manager and in GNUstep application GUI backend.

Process receiving of TakeFocus atom from Window Manager

- (NSEvent *)_handleTakeFocusAtom:(XEvent)xEvent forContext:(NSGraphicsContext *)gcontext

Processing of ButtonPress event

  if (generic.flags.useWindowMakerIcons == 1)
    {
      /*
       * We must hand over control of our icon/miniwindow
       * to Window Maker.
       */
      if ((cWin->win_attrs.window_style
           & (NSMiniWindowMask | NSIconWindowMask)) != 0
          && eventType == NSLeftMouseDown /*&& clickCount == 1*/)
        {
          if (cWin->parent == None)
            break;
          xEvent.xbutton.window = cWin->parent;
          XUngrabPointer(dpy, CurrentTime);
          XSendEvent(dpy, cWin->parent, True, ButtonPressMask, &xEvent);
          XFlush(dpy);
          if (clickCount != 2) {
            break;
          }
        }
    }

Window Manager's part

WApplication (application.h)

Every running application has WApplication instance. Normal X11 application exists only if at least one window was mapped.

WApplication instance for registered application should contain defined:

  • app_icon - appplication icon
  • windows - list of windows (at least one) which belongs to application
  • menu_win for GNUstep application

GNUstep application minimal appearance is appicon and menu. For focus handling/switching tasks menu_win must be set to make correct focus switching.

Description of some field inside struct WApplication {}

main_window - this is the invisible window that identifies application as a group of windows. Also it's called as "group leader". Every WWindow and WAppIcon contains field Window main_window. That's how application icon, menu and windows/panels can be identified as single application.

main_window_desc - generated WWindow structure for main_window. So main_window can be used as managed WWindow.

last_focused - should be set to last window of application that has focus before FocusOut, hide, workspace switch events. Set in wSetFocusTo().

last_workspace - contains workspace number of last_focused window workspace. If, for exmaple, application has 2 windows on different workspaces, double-click on appicon should: switch to workspace where last_focused window resides, set focus to that window (activate application). Set in wSetFocusTo().

New GNUstep application handling by WM

On application start wApplicationCreate() is called:

  • creates wapp->windows array
  • adds wwin to this array
  • saved wwin to wapp->menu_win if it's main menu (normally it is)

When new window opens (MapRequest/MapNotify, event.c) wApplicationAdd() is called:

  • adds wwin into wapp->windows array
  • wapp->refcount++

When window is closed (UnmapNotify, event.c) and window is not main menu wApplicationRemoveWindow() is called:

  • removes wwin from wapp->windows array
  • wapp->refcount--

When application quits (DestroyNotify, event.c):

  • several calls to wApplicationRemoveWindow() is performed
  • wApplicationDestroy() is called:
    • wUnmanageWindow() for wapp->menu_win is called
    • wapp->windows array destroyed
    • wapp->refcount--
    • wapp is freed