f52b8e48cd
svn path=/trunk/KDE/kdebase/workspace/; revision=659202
189 lines
9.2 KiB
Text
189 lines
9.2 KiB
Text
|
|
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. QX11Info::display()),
|
|
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( QX11Info::display(), 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.
|