Proposal for a smarter behavior for raising windows on mouse click (#2)
- From: Lubos Lunak <l lunak suse cz>
- To: wm-spec-list gnome org
- Subject: Proposal for a smarter behavior for raising windows on mouse click (#2)
- Date: Thu, 22 Apr 2004 14:41:44 +0200
Hello,
ok, this took same time :-/ (releases, releases). But now I have a fully
working (I hope) implementation. The kdelibs+KWin patches are already in KDE
CVS HEAD, attached is a Qt patch that might be useful to you, and a patch for
the spec.
I changed a bit how it works, so now it is:
- apps specifies that it supports the protocol
- user presses mouse button on the window (inside it)
- instead of raising/activating/whatever the WM would normally do, it sends
the client a _NET_WM_TAKE_ACTIVITY [1] message
- the client saves the message and waits for next ButtonRelease
- if the mouse hasn't moved, the client sends the message back (the same way
_NET_WM_PING works), otherwise it was DND, moving a scrollbar, selecting text
etc.[2] so it doesn't do anything, and therefore there will be no
raising/activating
- the WM receives the reply, and does what it would otherwise do right when
getting the ButtonPress (unless there was another raising/activation
meanwhile as a result of the mouse events)
Looks simple, and seems to be working.
Any comments, flames, questions, is anybody going to try to implement it as
well (the Qt patch may help you, it's for Qt-3.3.x)?
[1] _NET_WM_TAKE_ACTIVITY is probably a bad name right now, it originally
worked much like WM_TAKE_FOCUS. Any better name?
[2] Just checking if the mouse hasn't moved (further than minimal drag
distance) actually quite nicely solves the to-timeout-or-not-to-timeout
problem.
--
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/
--- wm-spec.xml.sav 2004-01-29 14:17:38.000000000 +0100
+++ wm-spec.xml 2004-04-22 14:27:41.183510664 +0200
@@ -1439,6 +1439,46 @@ respond to this protocol within a reason
See also the implementation notes on <link linkend="KILLINGWINDOWS">killing hung processes</link>.
</para>
</sect2>
+ <sect2>
+ <title>_NET_WM_TAKE_ACTIVITY</title>
+ <para>
+This protocol allows preventing raising of windows during DND operations.
+When the Window Manager detects a mouse press in a Client window, it can
+use this protocol to find out if the window should be raised and/or activated
+as a result of this event, or if the event leads to an operation where altering
+the window state is not desired. Instead of immediatelly altering the window
+state, the Window Manager can send a client message as follows: </para>
+ <programlisting><![CDATA[
+type = ClientMessage
+window = the respective client window
+message_type = WM_PROTOCOLS
+format = 32
+data.l[0] = _NET_WM_TAKE_ACTIVITY
+data.l[1] = timestamp
+data.l[2] = the respective client window
+other data.l[] elements = <the WM is free to use these fields for any data it wants>
+]]></programlisting>
+ <para>
+A participating Client after receiving this message MUST wait for next mouse button release
+event and MUST either ignore the message if the mouse events lead to a DND-like operation,
+or it MUST send it back to the root
+window immediately, by setting window = root, and calling XSendEvent with
+the same event mask like all other root window messages in this specification use,
+The Window Manager can uniquely
+identify the message by the timestamp and the data.l[2] field if necessary.
+The Client MUST NOT alter any field in the event other than the window.
+Operations that allow the Client to ignore the message are any mouse operations that
+involve pressing mouse button, keeping it down and moving the mouse (DND, moving a scrollbar,
+selecting text,etc.).
+ </para>
+ <para>
+It's entirely up to the Window Manager how to react on a reply to the message. Usually,
+it should perform the same set of operations it would do immediatelly after a user clicks
+on the Client window (raising,activating,etc. depending on its configuration). Note however
+that that the Window Manager should not do anything if such operations have meanwhile taken
+place for another window (e.g. if a new Client window has been mapped as a result of the click).
+ </para>
+ </sect2>
</sect1>
<sect1>
<title>Implementation notes</title>
--- src/kernel/qapplication_x11.cpp.sav 2004-04-14 13:52:48.000000000 +0200
+++ src/kernel/qapplication_x11.cpp 2004-04-22 10:09:23.945448952 +0200
@@ -216,6 +216,12 @@ Q_EXPORT Atom qt_wm_protocols = 0; // w
Q_EXPORT Atom qt_wm_delete_window = 0; // delete window protocol
Q_EXPORT Atom qt_wm_take_focus = 0; // take focus window protocol
+Atom qt_net_wm_take_activity = 0; // _NET_WM_TAKE_ACTIVITY protocol
+XEvent* qt_take_activity = 0; // event from _NET_WM_TAKE_ACTIVITY
+static QGuardedPtr<QWidget> qt_take_activity_window = 0;
+QPoint qt_take_activity_pos;
+Time qt_take_activity_time = 0;
+
Atom qt_qt_scrolldone = 0; // scroll synchronization
Atom qt_net_wm_context_help = 0; // context help
Atom qt_net_wm_ping = 0; // _NET_WM_PING protocol
@@ -403,6 +409,7 @@ extern bool qt_check_selection_sentinel(
static void qt_save_rootinfo();
bool qt_try_modal( QWidget *, XEvent * );
+static void qt_process_take_activity( XEvent* event );
int qt_ncols_option = 216; // used in qcolor_x11.cpp
int qt_visual_option = -1;
@@ -1841,6 +1848,7 @@ void qt_init_internal( int *argcptr, cha
qt_x11_intern_atom( "WM_STATE", &qt_wm_state );
qt_x11_intern_atom( "WM_CHANGE_STATE", &qt_wm_change_state );
qt_x11_intern_atom( "WM_TAKE_FOCUS", &qt_wm_take_focus );
+ qt_x11_intern_atom( "_NET_WM_TAKE_ACTIVITY", &qt_net_wm_take_activity );
qt_x11_intern_atom( "WM_CLIENT_LEADER", &qt_wm_client_leader);
qt_x11_intern_atom( "WM_WINDOW_ROLE", &qt_window_role);
qt_x11_intern_atom( "SM_CLIENT_ID", &qt_sm_client_id);
@@ -3088,6 +3096,15 @@ int QApplication::x11ClientMessage(QWidg
False, SubstructureNotifyMask|SubstructureRedirectMask, event );
}
}
+ else if ( a == qt_net_wm_take_activity ) {
+ if ( qt_take_activity_time > qt_x_time )
+ qt_x_time = qt_take_activity_time;
+ if( qt_take_activity == 0 )
+ qt_take_activity = new XEvent;
+ *qt_take_activity = *event;
+ qt_take_activity_window = w;
+ qt_take_activity_time = event->xclient.data.l[1];
+ }
} else if ( event->xclient.message_type == qt_qt_scrolldone ) {
widget->translateScrollDoneEvent(event);
} else if ( event->xclient.message_type == qt_xdnd_position ) {
@@ -3126,6 +3143,7 @@ int QApplication::x11ClientMessage(QWidg
*/
int QApplication::x11ProcessEvent( XEvent* event )
{
+ qt_process_take_activity( event );
switch ( event->type ) {
case ButtonPress:
ignoreNextMouseReleaseEvent = FALSE;
@@ -5922,6 +5940,39 @@ bool QApplication::isEffectEnabled( Qt::
}
}
+void qt_process_take_activity( XEvent* event )
+{
+ switch ( event->type ) {
+ case ButtonPress:
+ // save position of the button press - if the mouse is moved far away before the release,
+ // consider it dragging/selecting and don't do any activation
+ qt_take_activity_pos = QPoint( event->xbutton.x_root, event->xbutton.y_root );
+ break;
+ case ButtonRelease:
+ if( qt_take_activity && qt_take_activity_window
+ && ( qt_take_activity_pos - QPoint( event->xbutton.x_root, event->xbutton.y_root )).manhattanLength()
+ <= QApplication::startDragDistance() ) {
+ Window root = QPaintDevice::x11AppRootWindow( qt_take_activity_window->x11Screen() );
+ XEvent* event = qt_take_activity;
+ if (event->xclient.window != root) {
+ event->xclient.window = root;
+ XSendEvent( event->xclient.display, event->xclient.window,
+ False, SubstructureNotifyMask|SubstructureRedirectMask, event );
+ }
+ }
+ delete qt_take_activity;
+ qt_take_activity = 0;
+ break;
+ case MotionNotify:
+ if(( qt_take_activity_pos - QPoint( event->xbutton.x_root, event->xbutton.y_root )).manhattanLength() >
+ QApplication::startDragDistance() ) {
+ delete qt_take_activity;
+ qt_take_activity = 0;
+ }
+ break;
+ }
+}
+
/*****************************************************************************
Session management support
*****************************************************************************/
--- src/kernel/qwidget_x11.cpp.sav 2004-03-02 15:10:21.000000000 +0100
+++ src/kernel/qwidget_x11.cpp 2004-04-22 10:02:45.350044664 +0200
@@ -100,6 +100,7 @@ extern Atom qt_wm_state;
extern Atom qt_wm_change_state;
extern Atom qt_wm_delete_window;
extern Atom qt_wm_take_focus;
+extern Atom qt_net_wm_take_activity;
extern Atom qt_wm_client_leader;
extern Atom qt_window_role;
extern Atom qt_sm_client_id;
@@ -615,11 +616,12 @@ void QWidget::create( WId window, bool i
XResizeWindow( dpy, id, crect.width(), crect.height() );
XStoreName( dpy, id, qAppName() );
- Atom protocols[4];
+ Atom protocols[5];
int n = 0;
protocols[n++] = qt_wm_delete_window; // support del window protocol
protocols[n++] = qt_wm_take_focus; // support take focus window protocol
protocols[n++] = qt_net_wm_ping; // support _NET_WM_PING protocol
+ protocols[n++] = qt_net_wm_take_activity; // support _NET_WM_TAKE_ACTIVITY protocol
if ( testWFlags( WStyle_ContextHelp ) )
protocols[n++] = qt_net_wm_context_help;
XSetWMProtocols( dpy, id, protocols, n );
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]