diff --git a/README b/README index 71c763543..ab4a84769 100644 --- a/README +++ b/README @@ -1,60 +1,189 @@ -This is KWin, kwm next generation. - - - /*! - Different focus policies: - - */ -FocusPolicy=ClickToFocus | FocusFollowsMouse | FocusUnderMouse | FocusStrictlyUnderMouse - - - /** - Different Alt-Tab-Styles: - - */ -AltTabStyle=KDE | CDE - - -Have fun, - - Matthias Ettrich + + This README is meant as an explanation of various window manager related +mechanisms that application developers need to be aware of. As some of these +concepts may be difficult to understand for people not having the required +background knowledge (since sometimes it's difficult even for people who +do have the knowledge), the mechanisms are first briefly explained, and +then an example of fixing the various problems is given. + + For comments, questions, suggestions and whatever use the kwin@kde.org +mailing list. + + +Table of contents: +================== + +- Window relations + - how to make the window manager know which windows belong together +- Focus stealing prevention + - how to solve cases where focus stealing prevention doesn't work + properly automatically + + + +Window relations: +================= + +(For now, this explanation of window relations is mainly meant for +focus stealing prevention. To be extended later.) + + All windows created by an application should be organized in a tree +with the root being the application's main window. Note that this is about +toplevel windows, not widgets inside the windows. For example, if you +have KWrite running, with a torn-off toolbar (i.e. a standalone toolbar), +a file save dialog open, and the file save dialog showing a dialog +for creating a directory, the window hiearchy should look like this: + + + KWrite mainwindow + / \ + / \ + file save dialog torn-off toolbar + \ + \ + create directory dialog + + Each subwindow (i.e. all except for the KWrite mainwindow) points to its +main window (which in turn may have another main window, as in the case +of the file save dialog). When the window manager knows these relations, +it can better arrange the windows (keeping subwindows above their +main windows, preventing activation of a main window of a modal dialog, +and similar). Failing to provide this information to the window manager +may have various results, for example having dialogs positioned below +the main window, + +The window property used by subwindows to point to their mainwindows is +called WM_TRANSIENT_FOR. It can be seen by running +'xprop | grep WM_TRANSIENT_FOR' and clicking on a window. If the property +is not present, the window does not (claim to) have any mainwindow. +If the property is present, it's value is the window id of its main window; +window id of any window can be found out by running 'xwininfo'. A window +having WM_TRANSIENT_FOR poiting to another window is said to be transient +for that window. + + In some cases, the WM_TRANSIENT_FOR property may not point to any other +existing window, having value of 0, or pointing to the screen number +('xwininfo -root'). These special values mean that the window is transient +for all other windows in its window group. This should be used only +in rare cases, everytime a specific main window is known, WM_TRANSIENT_FOR +should be pointing to it instead of using one of these special values. +(The explanation why is beyond the scope of this document - just accept it +as a fact.) + + With Qt, the WM_TRANSIENT_FOR property is set by Qt automatically, based +on the toplevel widget's parent. If the toplevel widget is of a normal +type (i.e. not a dialog, toolbar, etc.), Qt doesn't set WM_TRANSIENT_FOR +on it. For special widgets, such as dialogs, WM_TRANSIENT_FOR is set +to point to the widget's parent, if it has a specific parent, otherwise +WM_TRANSIENT_FOR points to the root window. + + As already said above, WM_TRANSIENT_FOR poiting to the root window should +be usually avoided, so everytime the widget's main widget is known, the widget +should get it passed as a parent in its constructor. +(TODO KDialog etc. classes should not have a default argument for the parent +argument, and comments like 'just pass 0 as the parent' should go.) + + + +Focus stealing prevention: +========================== + + Since KDE3.2 KWin has a feature called focus stealing prevention. As the name +suggests, it prevents unexpected changes of focus. With older versions of KWin, +if any application opened a new dialog, it became active, and +if the application's main window was on another virtual desktop, also +the virtual desktop was changed. This was annoying, and also sometimes led +to dialogs mistakenly being closed because they received keyboard input that +was meant for the previously active window. + + The basic principle of focus stealing prevention is that the window with most +recent user activity wins. Any window of an application will become active +when being shown only if this application was the most recently used one. +KWin itself, and some of the related kdecore classes should take care +of the common cases, so usually there's no need for any special handling +in applications. Qt/KDE applications, that is. Applications using other +toolkits should in most cases work fine too. If they don't support +the window property _NET_WM_USER_TIME, the window manager may fail to detect +the user timestamp properly, resulting either in other windows becoming active +while the user works with this application, or this application may sometimes +steal focus (this second case should be very rare though). + + There are also cases where KDE applications needs special handling. The two +most common cases are when windows relations are not setup properly to make +KWin realize that they belong to the same application, and when the user +activity is not represented by manipulating with the application windows +themselves. + + Also note that focus stealing prevention implemented in the window manager +can only help with focus stealing between different applications. +If an application itself suddenly pops up a dialog, KWin cannot do anything about +it, and its the application's job to handle this case. + + +Window relations: +----------------- + + The common case here is when a dialog is shown for an application, but this +dialog is not provided by the application itself, but by some other process. +For example, dialogs with warnings about accepted cookies are provided +by KCookieJar, instead of being shown by Konqueror. In the normal case, +from KWin's point of view the cookie dialog would be an attempt of another +application to show a dialog, and KWin wouldn't allow activation of this +window. + + The solution is to tell the window manager about the relation between +the Konqueror main window and the cookie dialog, by making the dialog +point to the mainwindow. Note that this is not special to focus stealing +prevention, subwindows such as dialogs, toolbars and similar should always +point to their mainwindow. See the section on window relations for full +description. + + The WM_TRANSIENT_FOR property that's set on dialogs to point to their +mainwindow should in the cookie dialog case point to the Konqueror window +for which it has been shown. This is solved in kcookiejar by including +the window id in the DCOP call. When the cookie dialog is shown, its +WM_TRANSIENT_FOR property is manually set using the XSetTransientForHint() +call (see kdelibs/kioslave/http/kcookiejar/kcookiewin.cpp). The arguments +to XSetTransientForHint() call are the X display (i.e. qt_xdisplay()), +the window id on which the WM_TRANSIENT_FOR property is to be set +(i.e. use QWidget::winId()), and the window id of the mainwindow. + + + Simple short HOWTO: + + To put it simply: Let's say you have a daemon application that has +DCOP call "showDialog( QString text )", and when this is called, it shows +a dialog with the given text. This won't work properly with focus stealing +prevention. The DCOP call should be changed to +"showDialog( QString text, long id )". The caller should pass something like +myMainWindow->winId() as the second argument. In the daemon, before +the dialog is shown, a call to XSetTransientHint() should be added: + + XSetTransientForHint( qt_xdisplay(), dialog->winId(), id_of_mainwindow ); + + That's it. + +Non-standard user activity: +--------------------------- + + The most common case in KDE will be DCOP calls. For example, KDesktop's DCOP +call "KDesktopIface popupExecuteCommand". Executing this DCOP call e.g. +from Konsole as 'dcop kdesktop KDesktopIface popupExecuteCommand" will lead +to showing the minicli, but the last user activity timestamp gained from events +sent by X server will be older than user activity timestamp of Konsole, and +would normally result in minicli not being active. Therefore, before showing +the minicli, kdesktop needs to call KApplication::updateUserTimestamp(). + + However, this shouldn't be done with all DCOP calls. If a DCOP call is not +a result of direct user action, calling KApplication::updateUserTimestamp() +would lead to focus stealing. For example, let's assume for a moment +that KMail would use this DCOP call in case it detects the modem is not +connected, allowing to you to start KPPP or whatever tool you use. If KMail +would be configured to check mail every 10 minutes, this would lead to minicli +possibly suddenly showing up at every check. Basically, doing the above change +to kdesktop's minicli means that the popupExecuteCommand() DCOP call is only +for user scripting. (TODO write about focus transferring?) + + Simply said, KApplication::updateUserTimestamp() should be called only +as a result of user action. Unfortunately, I'm not aware of any universal +way how to handle this, so every case will have to be considered separately.