No focus on map hint II.
- From: Lubos Lunak <l lunak suse cz>
- To: wm-spec-list gnome org
- Subject: No focus on map hint II.
- Date: Thu, 15 May 2003 15:38:03 +0200
Hello,
here's my revised proposal for the "don't focus this window on map" hint. It
took some time, but now I have KWin version that is quite good at preventing
unwanted focus stealing from active application. The single hint from the
original proposal of course wasn't enough for this, I had to add few more
things. I'm not sure which of those should be added to the spec, so I'll list
them all, and I'll see. I have no problem with keeping the WM/pager specific
ones as KDE-only.
Just in case somebody is interested, the comment from sources describing the
mechanism is attached as kwin.txt (I had to write it down first before
implementing it, otherwise I'd go crazy because of it ;) ).
First, the hint that started this all. The only thing I added when compared
to the last proposal is that clients should also update the user time in the
hint in the visible window, so that the WM is able to detect last user
activity in the window if key events don't propagate up to the frame. I also
think the name MAP_TIMESTAMP no longer fits, so I suggest _NET_WM_USER_TIME
as the name of the property.
The patch is attached as user_time.txt . I'd like this one to get in the spec.
(I added the note about CARDINAL[] because I use it in code, and it would
break code that would strictly request the property to contain only one
CARDINAL value - any complaints about that?).
Now, if WM refuses to activate a window, it should mark it somehow, so that
the user knows some window tried to get activated, but the WM refused it
(and, also, I'm considering using the same mechanism for cases when e.g.
Konqueror finishes loading a HTML page). I used a
_NET_WM_STATE_DEMANDS_ATTENTION property. I first thought about using the
Urgency hint from ICCCM, but I eventually decided not to. I wasn't sure if it
would be ok to set this hint from the WM, and moreover the meaning of this
state is actually somewhat opposite to being urgent.
The patch is in demands_attention.txt . As this is only KWin->Kicker thingy,
I don't care that much if this gets in the spec, I can prefix it with _KDE,
but if somebody else would use the same feature, we should use the same name.
The patch for startup notification spec is in startup-notification.txt . The
only change from the last proposal is saying that the property should be used
even if the newly mapped window has _NET_WM_USER_TIME, but it's older
(happens when the newly started app doesn't create the window itself, but
instead tells already running instance to do so and then exits, e.g.
Mozilla).
That should be it. Now some problems I'm not quite sure what to do with them.
- Mozilla startup, if it's already running - in this case, newly launched
mozilla tells the running instance to open a new window, and exits. The
problem is, KWin detects this as inactive application opening another window,
and refuses activation of the window. That's logical in most cases, but I
failed to find a way how to detect this Mozilla case. For KDE applications
that work the same way, I ended up with reading WM_WINDOW_ROLE on the
windows, and if both of them contain # (hash), they are considered not
belonging to the same application, even if they really are. KMainWindow class
by default set WM_WINDOW_ROLE for toplevel windows as <something>-#<number>,
and since KWord's , KWrite's, Konqueror's etc. mainwindows from the user's
point of view are different applications, even if it's the same process.
Mozilla doesn't set WM_WINDOW_ROLE at all, BTW. GEdit has some strange long
'bonobo-mdi-window ...' string as WM_WINDOW_ROLE, so I think the extra #
wouldn't offend anybody ;). Could something like this find its way in the
spec somehow?
- I'm considering extending some request from clients to say whether they come
from a pager-like tool or from an application. For example, say that you
click on the taskbar and want to activate the matching window. The taskbar
sends the WM _NET_ACTIVE_WINDOW for the window, and the WM can't do anything
better than to activate the window (but it would be stupid if it didn't).
Now, however, some applicattions use _NET_ACTIVE_WINDOW on themselves
(because it's simpler than making sure the window is shown, and doing
XSetInputFocus()). The same if the app uses _NET_WM_DESKTOP on its window.
Should these calls be forbidden to do from normal applications, or is it ok
to extend the request? One of the the data.l[] fields in the messages could
be used to mark from pager/from app, and for the _NET_ACTIVE_WINDOW message
from app, another field could be used for the app's currently active window,
so that the WM would obey the request only if the app's other window is the
window with focus. As said in my other mail, those messages currently leave
unused fields as undefined, but in these cases getting garbage in those new
fields wouldn't really break anything.
--
Lubos Lunak
KDE developer
---------------------------------------------------------------------
SuSE CR, s.r.o. e-mail: l lunak suse cz , l lunak kde org
Drahobejlova 27 tel: +420 2 9654 2373
190 00 Praha 9 fax: +420 2 9654 2374
Czech Republic http://www.suse.cz/
/*
Prevention of focus stealing:
KWin tries to prevent unwanted changes of focus, that would result
from mapping a new window. Also, some nasty applications may try
to force focus change even in cases when ICCCM 4.2.7 doesn't allow it
(e.g. they may try to activate their main window because the user
definitely "needs" to see something happened - misusing
of QWidget::setActiveWindow() may be such case).
There are 4 ways how a window may become active:
- the user changes the active window (e.g. focus follows mouse, clicking
on some window's titlebar) - the change of focus will
be done by KWin, so there's nothing to solve in this case
- the change of active window will be requested using the _NET_ACTIVE_WINDOW
message (handled in RootInfo::changeActiveWindow()) - such requests
will be obeyed, because this request is meant mainly for e.g. taskbar
asking the WM to change the active window as a result of some user action.
Normal applications should use this request only rarely in special cases.
See also below the discussion of _NET_ACTIVE_WINDOW_TRANSFER.
- the change of active window will be done by performing XSetInputFocus()
on a window that's not currently active. ICCCM 4.2.7 describes when
the application may perform change of input focus. In order to handle
misbehaving applications, KWin will try to detect focus changes to
windows that don't belong to currently active application, and restore
focus back to the currently active window, instead of activating the window
that got focus (unfortunately there's no way to FocusChangeRedirect similar
to e.g. SubstructureRedirect, so there will be short time when the focus
will be changed). The check itself that's done is
Workspace::allowClientActivation() (see below).
- a new window will be mapped - this is the most complicated case. If
the new window belongs to the currently active application, it may be safely
mapped on top and activated. The same if there's no active window,
or the active window is the desktop. These checks are done by
Workspace::allowClientActivation().
Following checks need to compare times. One time is the timestamp
of last user action in the currently active window, the other time is
the timestamp of the action that originally caused mapping of the new window
(e.g. when the application was started). If the first time is newer than
the second one, the window will not be activated, as that indicates
futher user actions took place after the action leading to this new
mapped window. This check is done by Workspace::allowClientActivationTimestamp().
There are several ways how to get the timestamp of action that caused
the new mapped window (done in Client::readUserTimeMapTimestamp()) :
- the window may have the _NET_WM_USER_TIME property. This way
the application may either explicitly request that the window is not
activated (by using 0 timestamp), or the property contains the time
of last user action in the application.
- KWin itself tries to detect time of last user action in every window,
by watching KeyPress and ButtonPress events on windows. This way some
events may be missed (if they don't propagate to the toplevel window),
but it's good as a fallback for applications that don't provide
_NET_WM_USER_TIME, and missing some events may at most lead
to unwanted focus stealing.
- the timestamp may come from application startup notification.
Application startup notification, if it exists for the new mapped window,
should include time of the user action that caused it.
- if there's no timestamp available, it's checked whether the new window
belongs to some already running application - if yes, the timestamp
will be 0 (i.e. refuse activation)
- if the window is from session restored window, the timestamp will
be 0 too, unless this application was the active one at the time
when the session was saved, in which case the window will be
activated if there wasn't any user interaction since the time
KWin was started.
- as the last resort, the _KDE_NET_USER_CREATION_TIME timestamp
is used. For every toplevel window that is created (see CreateNotify
handling), this property is set to the at that time current time.
Since at this time it's known that the new window doesn't belong
to any existing application (better said, the application doesn't
have any other window mapped), it is either the very first window
of the application, or its the only window of the application
that was hidden before. The latter case is handled by removing
the property from windows before withdrawing them, making
the timestamp empty for next mapping of the window. In the sooner
case, the timestamp will be used. This helps in case when
an application is launched without application startup notification,
it creates its mainwindow, and starts its initialization (that
may possibly take long time). The timestamp used will be older
than any user action done after launching this application.
- if no timestamp is found at all, the window is activated.
The check whether two windows belong to the same application (same
process) is done in Client::belongToSameApplication(). Not 100% reliable,
but hopefully 99,99% reliable.
As a somewhat special case, window activation is always enabled when
session saving is in progress. When session saving, the session
manager allows only one application to interact with the user.
Not allowing window activation in such case would result in e.g. dialogs
not becoming active, so focus stealing prevention would cause here
more harm than good.
Windows that attempted to become active but KWin prevented this will
be marked as demanding user attention. They'll get
the _NET_WM_STATE_DEMANDS_ATTENTION state, and the taskbar should mark
them specially (blink, etc.). The state will be reset when the window
eventually really becomes active.
There are one more ways how a window can become obstrusive, window stealing
focus: By showing above the active window, by either raising itself,
or by moving itself on the active desktop.
- KWin will refuse raising non-active window above the active one,
unless they belong to the same application. Applications shouldn't
raise their windows anyway (unless the app wants to raise one
of its windows above another of its windows).
- KWin activates windows moved to the current desktop (as that seems
logical from the user's point of view, after sending the window
there directly from KWin, or e.g. using pager). This means
applications shouldn't send their windows to another desktop
(SELI TODO - but what if they do?)
Special cases I can think of:
- konqueror reusing, i.e. kfmclient tells running Konqueror instance
to open new window
- without focus stealing prevention - no problem
- with ASN (application startup notification) - ASN is forwarded,
and because it's newer than the instance's user timestamp,
it takes precedence
- without ASN - user timestamp needs to be reset, otherwise it would
be used, and it's old; moreover this new window mustn't be detected
as window belonging to already running application, or it wouldn't
be activated - see Client::sameAppWindowRoleMatch() for the (rather ugly)
hack
- konqueror preloading, i.e. window is created in advance, and kfmclient
tells this Konqueror instance to show it later
- without focus stealing prevention - no problem
- with ASN - ASN is forwarded, and because it's newer than the instance's
user timestamp, it takes precedence
- without ASN - user timestamp needs to be reset, otherwise it would
be used, and it's old; also, creation timestamp is changed to
the time the instance starts (re-)initializing the window,
this ensures creation timestamp will still work somewhat even in this case
- KUniqueApplication - when the window is already visible, and the new instance
wants it to activate
- without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
!!!! - with ASN - should forward ASN, and running instance should use this ASN's timestamp - how?
!!!! - without ASN - _NET_ACTIVE_WINDOW is used, this results in focus stealing,
but there's no other way, as creation timestamp will have roughly the same time
as the _NET_ACTIVE_WINDOW request, and there's no other way how to activate
the other window when the application is started from Konsole without ASN
- when one application wants to activate another application's window (e.g. KMail
activating already running KAddressBook window ?)
- without focus stealing prevention - _NET_ACTIVE_WINDOW - no problem
- with ASN - can't be here, it's the KUniqueApp case then
!!!! - without ASN - _NET_ACTIVE_WINDOW - focus stealing :(
- see _KDE_NET_WM_TRANSFER_ACTIVE_WINDOW
*/
--- wm-spec.sgml.sav 2003-01-03 21:31:14.000000000 +0100
+++ wm-spec.sgml 2003-05-15 14:37:39.000000000 +0200
@@ -930,6 +930,7 @@ _NET_WM_STATE_HIDDEN, ATOM
_NET_WM_STATE_FULLSCREEN, ATOM
_NET_WM_STATE_ABOVE, ATOM
_NET_WM_STATE_BELOW, ATOM
+_NET_WM_STATE_DEMANDS_ATTENTION, ATOM
]]></programlisting>
<para>
An implementation MAY add new atoms to this list. Implementations
@@ -1009,6 +1010,12 @@ windows (see <xref linkend="STACKINGORDE
</para>
<para>
+_NET_WM_STATE_DEMANDS_ATTENTION indicates that some action in or with the window
+happened. For example, it may be set by the WM if the window requested activation
+but the WM refused it, or the application may set it if it finished some work.
+ </para>
+
+ <para>
To change the state of a mapped window, a Client MUST send a _NET_WM_STATE
client message to the root window (window is the respective window, type
_NET_WM_STATE, format 32, l[0]=<the action, as listed below>,
--- wm-spec.sgml.sav 2003-01-03 21:31:14.000000000 +0100
+++ wm-spec.sgml 2003-05-15 14:42:30.000000000 +0200
@@ -1205,6 +1205,41 @@ _NET_WM_HANDLED_ICONS
for iconified windows.
</para>
</sect2>
+ <sect2><title>_NET_WM_USER_TIME</title>
+ <programlisting><![CDATA[
+_NET_WM_USER_TIME CARDINAL[]/32
+]]></programlisting>
+ <para>
+This property contains the XServer time at which last user activity in this
+window took place.
+ </para>
+ <para>
+Clients should keep the last timestamp from events KeyPress,
+ButtonPress and ButtonRelease (not KeyRelease, as the client may possibly
+get events for modifier key releases for some global actions), and set
+this timestamp in this property on every new toplevel window before mapping it.
+They should start setting the property only after first receiving a KeyPress,
+ButtonPress or ButtonRelease event, they shouldn't set it before receiving first input
+event. If the client has the active window, it should also update this
+property on the window whenever there's user activity. The special value of zero
+on a newly mapped window means that the window shouldn't initially get focus
+after being mapped.
+ </para>
+ <para>
+Rationale: This property allows a Window Manager to alter the focus,
+stacking, and/or placement behavior of windows when they are
+mapped depending on whether the new window was created by a user
+action or is a "pop-up" window activated by a timer or some other
+event.
+ </para>
+ <para>
+Note: The property is specified as CARDINAL[] in order to simplify implementation
+in toolkits - always only the first element should be read. The toolkit can always
+use PropModeAppend to set the property, which will set the property if it's not
+set yet, but won't overwrite any previous value set before in the application.
+In such case, the toolkit should remove the property after the window is withdrawn.
+ </para>
+ </sect2>
</sect1>
<sect1>
<title>Window Manager Protocols</title>
--- startup-notification.txt.sav 2002-10-23 20:05:14.000000000 +0200
+++ startup-notification.txt 2003-05-15 14:50:11.000000000 +0200
@@ -234,6 +234,17 @@ The following keys may be provided optio
This desktop is relative to the screen provided by
the SCREEN key.
+ TIMESTAMP
+
+ X server timestamp of the user action that caused this
+ launch. For example window manager that doesn't allow
+ stealing of focus by newly mapped windows while the user
+ works in an application can use this timestamp for windows
+ that have matching _NET_STARTUP_ID property if they don't
+ have any _NET_WM_USER_TIME property set or if it's older.
+ See the description of _NET_WM_USER_TIME in the WM spec
+ for details.
+
DESCRIPTION
a short description suitable for display in a dialog that
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]