diff options
Diffstat (limited to 'kdesktop/lock/lockprocess.cc')
-rw-r--r-- | kdesktop/lock/lockprocess.cc | 244 |
1 files changed, 239 insertions, 5 deletions
diff --git a/kdesktop/lock/lockprocess.cc b/kdesktop/lock/lockprocess.cc index d589232a1..cdd5581e7 100644 --- a/kdesktop/lock/lockprocess.cc +++ b/kdesktop/lock/lockprocess.cc @@ -39,6 +39,8 @@ #include <kstdguiitem.h> #include <kpixmapeffect.h> #include <kpixmap.h> +#include <kwin.h> +#include <kwinmodule.h> #include <tqframe.h> #include <tqlabel.h> @@ -119,6 +121,8 @@ static void segv_handler(int) sleep(1); } +extern Atom qt_wm_state; + //=========================================================================== // // Screen saver handling process. Handles screensaver window, @@ -135,6 +139,8 @@ LockProcess::LockProcess(bool child, bool useBlankOnly) mRestoreXF86Lock(false), mForbidden(false), mAutoLogout(false), + mVkbdProcess(NULL), + mKWinModule(NULL), mPipeOpen(false), mPipeOpen_out(false), mInfoMessageDisplayed(false), @@ -1120,9 +1126,11 @@ bool LockProcess::checkPass() if (mAutoLogout) killTimer(mAutoLogoutTimerId); + showVkbd(); PasswordDlg passDlg( this, &greetPlugin); - int ret = execDialog( &passDlg ); + hideVkbd(); + if (mForceReject == true) { ret = TQDialog::Rejected; } @@ -1251,9 +1259,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; @@ -1290,11 +1302,30 @@ bool LockProcess::x11Event(XEvent *event) case ConfigureNotify: // from SubstructureNotifyMask on the root window if(event->xconfigure.event == qt_xrootwin()) stayOnTop(); + for( TQValueList< VkbdWindow >::Iterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) { + if( (*it).id == event->xconfigure.window ) { + (*it).rect = TQRect( 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( TQValueList< 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. @@ -1319,17 +1350,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( TQValueList< VkbdWindow >::ConstIterator it = mVkbdWindows.begin(); + it != mVkbdWindows.end(); + ++it ) + stack[ count++ ] = (*it).id; for( TQValueList< TQWidget* >::ConstIterator it = mDialogs.begin(); it != mDialogs.end(); ++it ) @@ -1428,4 +1466,200 @@ void LockProcess::msgBox( TQMessageBox::Icon type, const TQString &txt ) 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, TQT_SIGNAL( windowAdded( WId )), TQT_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 = TQRect( x, y, attr_geom.width, attr_geom.height ); + mVkbdWindows.prepend( data ); +} + +bool LockProcess::forwardVkbdEvent( XEvent* event ) +{ + if( mVkbdProcess == NULL ) + return false; + TQPoint pos; + Time time; + switch( event->type ) + { + case ButtonPress: + case ButtonRelease: + pos = TQPoint( event->xbutton.x, event->xbutton.y ); + time = event->xbutton.time; + break; + case MotionNotify: + pos = TQPoint( 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( TQValueList< 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" |