Index: kdesktop/lock/lockprocess.cc =================================================================== --- kdesktop/lock/lockprocess.cc.orig +++ kdesktop/lock/lockprocess.cc @@ -36,6 +36,8 @@ #include <kstdguiitem.h> #include <kpixmapeffect.h> #include <kpixmap.h> +#include <kwin.h> +#include <kwinmodule.h> #include <qframe.h> #include <qlabel.h> @@ -93,6 +95,8 @@ static Window gVRootData = 0; static Atom gXA_VROOT; static Atom gXA_SCREENSAVER_VERSION; +extern Atom qt_wm_state; + //=========================================================================== // // Screen saver handling process. Handles screensaver window, @@ -108,7 +112,9 @@ LockProcess::LockProcess(bool child, boo mVisibility(false), mRestoreXF86Lock(false), mForbidden(false), - mAutoLogout(false) + mAutoLogout(false), + mVkbdProcess(NULL), + mKWinModule(NULL) { setupSignals(); @@ -909,10 +915,14 @@ bool LockProcess::checkPass() { if (mAutoLogout) killTimer(mAutoLogoutTimerId); + + showVkbd(); PasswordDlg passDlg( this, &greetPlugin); int ret = execDialog( &passDlg ); + + hideVkbd(); XWindowAttributes rootAttr; XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), @@ -992,9 +1002,13 @@ bool LockProcess::x11Event(XEvent *event { switch (event->type) { - case KeyPress: case ButtonPress: case MotionNotify: + case ButtonRelease: + if( forwardVkbdEvent( event )) + return true; // filter out + // fall through + case KeyPress: if (mBusy || !mDialogs.isEmpty()) break; mBusy = true; @@ -1031,11 +1045,30 @@ bool LockProcess::x11Event(XEvent *event case ConfigureNotify: // from SubstructureNotifyMask on the root window if(event->xconfigure.event == qt_xrootwin()) stayOnTop(); + for( QValueList< VkbdWindow >::Iterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) { + if( (*it).id == event->xconfigure.window ) { + (*it).rect = QRect( event->xconfigure.x, event->xconfigure.y, + event->xconfigure.width, event->xconfigure.height ); + break; + } + } break; case MapNotify: // from SubstructureNotifyMask on the root window + windowAdded( event->xmap.window, false ); if( event->xmap.event == qt_xrootwin()) stayOnTop(); break; + case DestroyNotify: + for( QValueList< VkbdWindow >::Iterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) + if( (*it).id == event->xdestroywindow.window ) { + mVkbdWindows.remove( it ); + break; + } + break; } // We have grab with the grab window being the root window. @@ -1060,17 +1093,24 @@ bool LockProcess::x11Event(XEvent *event void LockProcess::stayOnTop() { - if(!mDialogs.isEmpty()) + if(!mDialogs.isEmpty() || !mVkbdWindows.isEmpty()) { // this restacking is written in a way so that // if the stacking positions actually don't change, // all restacking operations will be no-op, // and no ConfigureNotify will be generated, // thus avoiding possible infinite loops - XRaiseWindow( qt_xdisplay(), mDialogs.first()->winId()); // raise topmost + if( !mVkbdWindows.isEmpty()) + XRaiseWindow( qt_xdisplay(), mVkbdWindows.first().id ); + else + XRaiseWindow( qt_xdisplay(), mDialogs.first()->winId()); // raise topmost // and stack others below it - Window* stack = new Window[ mDialogs.count() + 1 ]; + Window* stack = new Window[ mDialogs.count() + mVkbdWindows.count() + 1 ]; int count = 0; + for( QValueList< VkbdWindow >::ConstIterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) + stack[ count++ ] = (*it).id; for( QValueList< QWidget* >::ConstIterator it = mDialogs.begin(); it != mDialogs.end(); ++it ) @@ -1169,4 +1209,200 @@ void LockProcess::msgBox( QMessageBox::I execDialog( &box ); } +static int run_vkbd = -1; +void LockProcess::showVkbd() +{ + if( run_vkbd == - 1 ) { + int status = system( "hal-find-by-property --key system.formfactor.subtype --string tabletpc" ); +// status = 0; // enable for testing + run_vkbd = ( WIFEXITED( status ) && WEXITSTATUS( status ) == 0 + && !KStandardDirs::findExe( "xvkbd" ).isEmpty()) ? 1 : 0; + } + if( run_vkbd ) { + mVkbdWindows.clear(); + mVkbdLastEventWindow = None; + mKWinModule = new KWinModule( NULL, KWinModule::INFO_WINDOWS ); + connect( mKWinModule, SIGNAL( windowAdded( WId )), SLOT( windowAdded( WId ))); + mVkbdProcess = new KProcess; + *mVkbdProcess << "xvkbd" << "-compact" << "-geometry" << "-0-0" << "-xdm"; + mVkbdProcess->start(); + } +} + +void LockProcess::hideVkbd() +{ + if( mVkbdProcess != NULL ) { + mVkbdProcess->kill(); + delete mVkbdProcess; + mVkbdProcess = NULL; + delete mKWinModule; + mKWinModule = NULL; + mVkbdWindows.clear(); + } +} + +void LockProcess::windowAdded( WId w ) +{ + windowAdded( w, true ); +} + +void LockProcess::windowAdded( WId w, bool managed ) +{ + KWin::WindowInfo info = KWin::windowInfo( w, 0, NET::WM2WindowClass ); + if( info.windowClassClass().lower() != "xvkbd" ) + return; + // Unmanaged windows (i.e. popups) don't currently work anyway, since they + // don't have WM_CLASS set anyway. I could perhaps try tricks with X id + // ranges if really needed. + if( managed ) { + // withdraw the window, wait for it to be withdrawn, reparent it directly + // to root at the right position + XWithdrawWindow( qt_xdisplay(), w, qt_xscreen()); + for(;;) { + Atom type; + int format; + unsigned long length, after; + unsigned char *data; + int r = XGetWindowProperty( qt_xdisplay(), w, qt_wm_state, 0, 2, + false, AnyPropertyType, &type, &format, + &length, &after, &data ); + bool withdrawn = true; + if ( r == Success && data && format == 32 ) { + Q_UINT32 *wstate = (Q_UINT32*)data; + withdrawn = (*wstate == WithdrawnState ); + XFree( (char *)data ); + } + if( withdrawn ) + break; + } + } + XSelectInput( qt_xdisplay(), w, StructureNotifyMask ); + XWindowAttributes attr_geom; + if( !XGetWindowAttributes( qt_xdisplay(), w, &attr_geom )) + return; + int x = XDisplayWidth( qt_xdisplay(), qt_xscreen()) - attr_geom.width; + int y = XDisplayHeight( qt_xdisplay(), qt_xscreen()) - attr_geom.height; + if( managed ) { + XSetWindowAttributes attr; + attr.override_redirect = True; + XChangeWindowAttributes( qt_xdisplay(), w, CWOverrideRedirect, &attr ); + XReparentWindow( qt_xdisplay(), w, qt_xrootwin(), x, y ); + XMapWindow( qt_xdisplay(), w ); + } + VkbdWindow data; + data.id = w; + data.rect = QRect( x, y, attr_geom.width, attr_geom.height ); + mVkbdWindows.prepend( data ); +} + +bool LockProcess::forwardVkbdEvent( XEvent* event ) +{ + if( mVkbdProcess == NULL ) + return false; + QPoint pos; + Time time; + switch( event->type ) + { + case ButtonPress: + case ButtonRelease: + pos = QPoint( event->xbutton.x, event->xbutton.y ); + time = event->xbutton.time; + break; + case MotionNotify: + pos = QPoint( event->xmotion.x, event->xmotion.y ); + time = event->xmotion.time; + break; + default: + return false; + } + // vkbd windows are kept topmost, so just find the first one in the position + for( QValueList< VkbdWindow >::ConstIterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) { + if( (*it).rect.contains( pos )) { + // Find the subwindow where the event should actually go. + // Not exactly cheap in the number of X roundtrips but oh well. + Window window = (*it).id; + Window root, child; + int root_x, root_y, x, y; + unsigned int mask; + for(;;) { + if( !XQueryPointer( qt_xdisplay(), window, &root, &child, &root_x, &root_y, &x, &y, &mask )) + return false; + if( child == None ) + break; + window = child; + } + switch( event->type ) + { + case ButtonPress: + case ButtonRelease: + event->xbutton.x = x; + event->xbutton.y = y; + event->xbutton.subwindow = None; + break; + case MotionNotify: + event->xmotion.x = x; + event->xmotion.y = y; + event->xmotion.subwindow = None; + break; + } + event->xany.window = window; + sendVkbdFocusInOut( window, time ); + XSendEvent( qt_xdisplay(), window, False, 0, event ); + return true; + } + } + sendVkbdFocusInOut( None, time ); + return false; +} + +// Fake EnterNotify/LeaveNotify events as the mouse moves. They're not sent by X +// because of the grab and having them makes xvkbd highlight the buttons (but +// not needed otherwise it seems). +void LockProcess::sendVkbdFocusInOut( WId window, Time t ) +{ + if( mVkbdLastEventWindow == window ) + return; + if( mVkbdLastEventWindow != None ) { + XEvent e; + e.xcrossing.type = LeaveNotify; + e.xcrossing.display = qt_xdisplay(); + e.xcrossing.window = mVkbdLastEventWindow; + e.xcrossing.root = qt_xrootwin(); + e.xcrossing.subwindow = None; + e.xcrossing.time = t; + e.xcrossing.x = 0; + e.xcrossing.y = 0; + e.xcrossing.x_root = -1; + e.xcrossing.y_root = -1; + e.xcrossing.mode = NotifyNormal; + e.xcrossing.detail = NotifyAncestor; + e.xcrossing.same_screen = True; + e.xcrossing.focus = False; + e.xcrossing.state = 0; + XSendEvent( qt_xdisplay(), mVkbdLastEventWindow, False, 0, &e ); + } + mVkbdLastEventWindow = window; + if( mVkbdLastEventWindow != None ) { + XEvent e; + e.xcrossing.type = EnterNotify; + e.xcrossing.display = qt_xdisplay(); + e.xcrossing.window = mVkbdLastEventWindow; + e.xcrossing.root = qt_xrootwin(); + e.xcrossing.subwindow = None; + e.xcrossing.time = t; + e.xcrossing.x = 0; + e.xcrossing.y = 0; + e.xcrossing.x_root = 0; + e.xcrossing.y_root = 0; + e.xcrossing.mode = NotifyNormal; + e.xcrossing.detail = NotifyAncestor; + e.xcrossing.same_screen = True; + e.xcrossing.focus = False; + e.xcrossing.state = 0; + XSendEvent( qt_xdisplay(), mVkbdLastEventWindow, False, 0, &e ); + } +} + #include "lockprocess.moc" Index: kdesktop/lock/lockprocess.h =================================================================== --- kdesktop/lock/lockprocess.h.orig +++ kdesktop/lock/lockprocess.h @@ -23,6 +23,7 @@ #include <X11/Xlib.h> class KLibrary; +class KWinModule; struct GreeterPluginHandle { KLibrary *library; @@ -53,7 +54,7 @@ public: void msgBox( QMessageBox::Icon type, const QString &txt ); int execDialog( QDialog* dlg ); - + public slots: void quitSaver(); void preparePopup(); @@ -70,6 +71,7 @@ private slots: void suspend(); void checkDPMSActive(); void slotDeadTimePassed(); + void windowAdded( WId ); private: void configure(); @@ -93,6 +95,11 @@ private: void stayOnTop(); void lockXF86(); void unlockXF86(); + void showVkbd(); + void hideVkbd(); + bool forwardVkbdEvent( XEvent* event ); + void sendVkbdFocusInOut( WId window, Time t ); + void windowAdded( WId window, bool managed ); void resume( bool force ); static QVariant getConf(void *ctx, const char *key, const QVariant &dflt); @@ -125,6 +132,15 @@ private: int mAutoLogoutTimerId; int mAutoLogoutTimeout; bool mAutoLogout; + KProcess* mVkbdProcess; + KWinModule* mKWinModule; + struct VkbdWindow + { + WId id; + QRect rect; + }; + QValueList< VkbdWindow > mVkbdWindows; + WId mVkbdLastEventWindow; }; #endif