LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File lock-xvkbd.diff of Package kdebase3 (Project openSUSE:Factory)

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