[X2Go-Commits] [x2gokdriveclient] 01/01: rootless mode for x2gokdriveclient

git-admin at x2go.org git-admin at x2go.org
Mon Jan 31 16:48:05 CET 2022


This is an automated email from the git hooks/post-receive script.

x2go pushed a commit to branch feature/rootless-mode
in repository x2gokdriveclient.

commit a8f41b949861f972f2c89e0f163e5b507a6ea1a6
Author: Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
Date:   Mon Jan 31 09:47:49 2022 -0600

    rootless mode for x2gokdriveclient
---
 client.cpp           | 487 +++++++++++++++++++++++++++++++++++++++++++--------
 client.h             |  44 ++++-
 displayarea.cpp      | 146 ++++++++-------
 displayarea.h        |  11 +-
 extwin.cpp           | 332 +++++++++++++++++++++++++++++++++++
 extwin.h             |  80 +++++++++
 menuframe.cpp        |   8 +-
 x2gokdriveclient.pro |   4 +-
 8 files changed, 947 insertions(+), 165 deletions(-)

diff --git a/client.cpp b/client.cpp
index 6dba871..9e8aa29 100644
--- a/client.cpp
+++ b/client.cpp
@@ -54,6 +54,7 @@
 #include <QMenu>
 #include <QClipboard>
 #include "screenidentifier.h"
+#include "extwin.h"
 
 #ifdef Q_OS_LINUX
 #include <xcbclip.h>
@@ -97,7 +98,7 @@ FrameRegion::~FrameRegion()
 }
 
 
-Frame::Frame(uint32_t width, uint32_t height, int32_t x, int32_t y,  uint32_t numOfRegions, uint32_t crc)
+Frame::Frame(uint32_t width, uint32_t height, int32_t x, int32_t y,  uint32_t numOfRegions, uint32_t crc, uint32_t winId)
 
 {
     this->crc=crc;
@@ -105,6 +106,7 @@ Frame::Frame(uint32_t width, uint32_t height, int32_t x, int32_t y,  uint32_t nu
     this->y=y;
     this->width=width;
     this->height=height;
+    this->winId=winId;
     this->numOfRegions=numOfRegions;
 }
 
@@ -159,17 +161,15 @@ Client::Client()
     clipboard=new XCBClip(this);
     #endif
 
-    setGeometry(0,0,800,600);
-
-    displayArea=new DisplayArea(this);
-
-    displayArea->setObjectName("DisplayArea");
-
-    displayArea->setStyleSheet("QFrame#DisplayArea{background-color:black;border-image:url(:res/x2gobg.svg) 0 0 0 0 stretch stretch;}");
+    parseOptions();
+    if(!rootless)
+        initDesktopMode();
+    else
+    {
+        hide();
+        ((QGuiApplication*)QGuiApplication::instance())->setQuitOnLastWindowClosed(false);
+    }
 
-    displayArea->show();
-    setCentralWidget(displayArea);
-    setWindowIcon(QIcon(":res/x2goclient.png"));
     clientSocket=new QTcpSocket(this);
 
 
@@ -201,18 +201,39 @@ Client::Client()
     {
         slotScreenAdded(screen);
     }
+    QTimer::singleShot(10,this,SLOT(connectToServer()));
+}
+
+Client::~Client()
+{
+    KDRStdErr()<<"Client destructor"<<KDR_ENDL;
+
+    freeMessageBuffer();
 
+    if(currentFrame)
+        delete currentFrame;
+    if(currentCursor)
+        delete currentCursor;
+}
 
+void Client::initDesktopMode()
+{
+    setGeometry(0,0,800,600);
+    displayArea=new DisplayArea(this,this);
+    displayArea->setObjectName("DisplayArea");
+    displayArea->setStyleSheet("QFrame#DisplayArea{background-color:black;border-image:url(:res/x2gobg.svg) 0 0 0 0 stretch stretch;}");
+    displayArea->show();
+    setCentralWidget(displayArea);
+    setWindowIcon(QIcon(":res/x2goclient.png"));
     QMenu* menuCon=menuBar()->addMenu(tr("&Connection"));
     menu=menuBar()->addMenu(tr("&View"));
     FSMenuBar=new MenuFrame(this, menu);
-//     FSMenuBar->hide();
+    //     FSMenuBar->hide();
     menuBar()->hide();
 
     connect(FSMenuBar, SIGNAL(editCaption()), this, SLOT(editWindowTitle()));
     connect(this, SIGNAL(windowTitleChanged(const QString &)), FSMenuBar, SLOT(setCaption(const QString &)));
 
-
     actRandr=menu->addAction(QIcon(":res/randr.svg"), tr("Multiple monitors"),this, SLOT(slotEnableRandr()));
     actRandr->setCheckable(true);
     menu->addSeparator();
@@ -227,28 +248,24 @@ Client::Client()
     actRestore=menu->addAction(QIcon(":res/view-restore.svg"), tr("Restore") ,this, SLOT(slotRestore()));
     menuBar()->addSeparator();
 
-
     actDisconnect=menuCon->addAction( tr("Disconnect"),this, SLOT(slotDisconnect()));
     actDisconnect->setIcon(QIcon(":res/network-disconnect.svg"));
     actDisconnect->setToolTip(tr("Disconnect"));
-
-    QTimer::singleShot(10,this,SLOT(connectToServer()));
-
     connect(menu,SIGNAL(aboutToShow()), this, SLOT(slotSetupMenu()));
-    setWindowTitle(tr("X2GoKDrive Client"));
-
+    setWindowTitle(mainWndTitle);
 }
-
-Client::~Client()
+void Client::slotGrabDisplay()
 {
-    KDRStdErr()<<"Client destructor"<<KDR_ENDL;
-
-    freeMessageBuffer();
-
-    if(currentFrame)
-        delete currentFrame;
-    if(currentCursor)
-        delete currentCursor;
+    if(hasUpdates)
+    {
+        //         Client::KDRStdErr()<<"save display";
+        if(!rootless)
+        {
+            QScreen *screen = QGuiApplication::primaryScreen();
+            displayPix = screen->grabWindow(winId());
+        }
+        hasUpdates=false;
+    }
 }
 
 void Client::slotIdentifyScreen()
@@ -306,6 +323,9 @@ void Client::slotIdentifyScreen()
     menu->show();
     //hide the identifier, when no screen actions are hovered
     QTimer::singleShot(100,this, SLOT(slotIdentifyScreen()));
+    QTimer *tm=new QTimer(this);
+    connect(tm, &QTimer::timeout, this, &Client::slotGrabDisplay);
+    tm->start(1000);
 }
 
 void Client::editWindowTitle()
@@ -382,7 +402,7 @@ void Client::resizeToSaved()
     setWindowState(Qt::WindowNoState);
     setWindowState(windowState() & ~Qt::WindowMaximized);
 #endif
-    KDRStdErr()<<"restore to "<<savedSize.width()<<"x"<<savedSize.height()<<" "<<savedPosition.x()<<","<<savedPosition.y();
+    KDRStdErr()<<"restore to "<<savedSize.width()<<"x"<<savedSize.height()<<" "<<savedPosition.x()<<","<<savedPosition.y()<<KDR_ENDL;
     resize(savedSize);
     move(savedPosition);
     showNormal();
@@ -506,11 +526,13 @@ void Client::slotSetupMenu()
             actDisp[i]->setVisible(false);
         }
     }
-    displayArea->slotGrabDisplay();
+    slotGrabDisplay();
 }
 
 bool Client::isDisplayPointer(QMouseEvent* event)
 {
+    if(rootless)
+        return true;
     if(event->x() > FSMenuBar->x() && event->x() < FSMenuBar->x() + FSMenuBar->width() &&
        event->y() > FSMenuBar->y() && event->y() < FSMenuBar->y() + FSMenuBar->height() &&
        event->button()== Qt::NoButton && event->buttons() == 0)
@@ -531,19 +553,15 @@ void Client::freeMessageBuffer()
         delete []messageBuffer;
     messageBuffer=0l;
 }
-void Client::connectToServer()
+
+void Client::parseOptions()
 {
-    setWindowTitle("X2GO SESSION");
+    mainWndTitle=tr("X2GoKDrive Client");
     QStringList args=QCoreApplication::arguments();
-    QString host="localhost";
-    int port=15000;
     QString optFile;
-
-
     for(int i=0;i<args.count();++i)
     {
         KDRStdErr()<<args[i];
-
         if(args[i]=="--debug")
         {
             debug=true;
@@ -562,17 +580,14 @@ void Client::connectToServer()
         }
         if(args[i]=="--title")
         {
-            setWindowTitle("X2GO-"+args[++i]);
-            KDRStdErr()<<"set title to "<<windowTitle();
+            mainWndTitle=args[++i];
             continue;
         }
-
         if(args[i]=="--width")
         {
             width=args[++i].toUInt();
             continue;
         }
-
         if(args[i]=="--height")
         {
             height=args[++i].toUInt();
@@ -582,12 +597,15 @@ void Client::connectToServer()
         {
             fullscreen=true;
         }
-
         if(args[i]=="--randr")
         {
             useRandr=true;
         }
-
+        if(args[i]=="--rootless")
+        {
+            rootless=true;
+            useRandr=true;
+        }
         if(args[i]=="--screen")
         {
             multidisp=true;
@@ -617,7 +635,6 @@ void Client::connectToServer()
                 KDRStdErr()<<"Unsupported selections mode: "<<smode;
             }
         }
-
         if(args[i].indexOf("option")!=-1)
         {
             QStringList parts=args[i].split(",");
@@ -626,13 +643,13 @@ void Client::connectToServer()
                 if(part.indexOf("options=")==0)
                 {
                     optFile=part.replace("options=","");
-#ifdef Q_OS_WIN
+                    #ifdef Q_OS_WIN
                     QStringList parts=optFile.split("options:");
                     optFile=parts[0]+"options";
-#else
+                    #else
                     QStringList parts=optFile.split(":");
                     optFile=parts[0];
-#endif
+                    #endif
                     QString title=optFile;
                     title=title.replace("S-","X2GO-");
                     parts=title.split("/");
@@ -640,8 +657,8 @@ void Client::connectToServer()
                     {
                         if(title.indexOf("X2GO-")==0)
                         {
-                          setWindowTitle(title);
-                          break;
+                            mainWndTitle=title;
+                            break;
                         }
                     }
                     break;
@@ -649,16 +666,12 @@ void Client::connectToServer()
             }
         }
     }
-
-
-    KDRStdErr()<<"options file: "<<optFile;
-
+    KDRStdErr()<<"options file: "<<optFile<<KDR_ENDL;
     if(optFile.length())
     {
         QFile fl(optFile);
         if(fl.open(QIODevice::ReadOnly | QIODevice::Text))
         {
-
             QTextStream in(&fl);
             QString str=in.readAll();
             QStringList parts=str.split(",");
@@ -669,7 +682,6 @@ void Client::connectToServer()
                     host=part.replace("connect=","");
                     continue;
                 }
-
                 if(part.indexOf("cookie=")==0)
                 {
                     cookie=part.replace("cookie=","");
@@ -680,14 +692,12 @@ void Client::connectToServer()
                     }
                     continue;
                 }
-
                 if(part.indexOf("port=")==0)
                 {
                     port=part.replace("port=","").toInt();
                     continue;
                 }
             }
-
         }
         else
         {
@@ -695,8 +705,13 @@ void Client::connectToServer()
             exitOnError(tr("Error! Can't open options File for reading"));
         }
     }
+}
 
-    KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port;
+void Client::connectToServer()
+{
+//     setWindowTitle("X2GO SESSION");
+
+    KDRStdErr(false)<<"Connecting to remote host "<<host<<":"<<port<<KDR_ENDL;
     clientSocket->connectToHost(host, port);
 }
 
@@ -715,6 +730,21 @@ QPixmap Client::getPixmapFromCache(uint32_t crc)
     return frameCache[crc];
 }
 
+void Client::setDisplayPix(QPixmap pix, int32_t x, int32_t y)
+{
+    if(x==-1 || y==-1)
+    {
+        displayPix=pix;
+    }
+    else
+    {
+        QPainter pt;
+        pt.begin(&displayPix);
+        pt.drawPixmap(x,y,pix);
+        pt.end();
+    }
+}
+
 
 void Client::renderFrame()
 {
@@ -723,15 +753,18 @@ void Client::renderFrame()
         //we got a whole display copy
         if(currentFrame->x==-1)
         {
-            KDRStdErr()<<"got initial Image: "<<currentFrame->width<<"x"<<currentFrame->height<<" "<<QSizeToStr(currentFrame->regions[0]->pix.size());
-            displayArea->resize(currentFrame->width, currentFrame->height);
-            this->resize(currentFrame->width, currentFrame->height);
-            displayArea->setDisplayPix(currentFrame->regions[0]->pix);
+            KDRStdErr()<<"got initial Image: "<<currentFrame->width<<"x"<<currentFrame->height<<" "<<QSizeToStr(currentFrame->regions[0]->pix.size())<<KDR_ENDL;
+            if(!rootless)
+            {
+                displayArea->resize(currentFrame->width, currentFrame->height);
+                this->resize(currentFrame->width, currentFrame->height);
+            }
+            setDisplayPix(currentFrame->regions[0]->pix);
         }
         else
         {
-            //             KDRStdErr()<<"got Screen update:"<<currentFrame->width<< currentFrame->height<<currentFrame->regions[0]->pix.size();
-            displayArea->setDisplayPix(currentFrame->regions[0]->pix, currentFrame->x, currentFrame->y);
+//                 KDRStdErr()<<"got Screen update: "<<currentFrame->x<<":"<< currentFrame->y<<" "<<KDR_ENDL;
+                setDisplayPix(currentFrame->regions[0]->pix, currentFrame->x, currentFrame->y);
         }
     }
     else
@@ -778,13 +811,38 @@ void Client::renderFrame()
         }
     }
     wantRepaint=true;
-    displayArea->repaint(currentFrame->x, currentFrame->y, currentFrame->width, currentFrame->height);
-    //     KDRStdErr()<<"repaint: "<<currentFrame->x<<currentFrame->y<< currentFrame->width<< currentFrame->height<<displayArea->geometry();
+    if(!rootless)
+        displayArea->repaint(currentFrame->x, currentFrame->y, currentFrame->width, currentFrame->height);
+    else
+    {
+        if(currentFrame->winId)
+        {
+//             KDRStdErr()<<"Paint currentFrame: "<<currentFrame->x<<":"<<currentFrame->y<<" "<< currentFrame->width<<"x"<< currentFrame->height<<KDR_HEX<<" for WIndow: "<<currentFrame->winId<<KDR_ENDL;
+            ExtWin* win=findExtWinById(currentFrame->winId);
+            if(win)
+            {
+                win->getDisplayArea()->repaint(currentFrame->x-win->geometry().x(), currentFrame->y-win->geometry().y(), currentFrame->width, currentFrame->height);
+            }
+            else
+            {
+                KDRStdErr()<<"Window not found: "<<KDR_HEX<<currentFrame->winId<<KDR_ENDL;
+            }
+        }
+        else
+        {
+            foreach(ExtWin* win, extWindows)
+            {
+                win->getDisplayArea()->repaint(currentFrame->x-win->geometry().x(), currentFrame->y-win->geometry().y(), currentFrame->width, currentFrame->height);
+            }
+        }
+    }
+    setUptodate();
 }
 
 void Client::setUptodate()
 {
     wantRepaint=false;
+    hasUpdates=true;
 }
 
 
@@ -793,9 +851,12 @@ void Client::getImageFrame()
 {
     if(currentFrame)
         delete currentFrame;
+    uint32_t winId=0;
+    if(rootless)
+        winId=*((uint32_t*)messageBuffer+7);
     currentFrame=new Frame(*((uint32_t*)messageBuffer+1), *((uint32_t*)messageBuffer+2),*((uint32_t*)messageBuffer+3),
                            *((uint32_t*)messageBuffer+4),
-                           *((uint32_t*)messageBuffer+5), *((uint32_t*)messageBuffer+6));
+                           *((uint32_t*)messageBuffer+5), *((uint32_t*)messageBuffer+6), winId);
 
     //     KDRStdErr()<<"got frame "<<currentFrame->crc<<currentFrame->numOfRegions;
     if(!currentFrame->numOfRegions)
@@ -834,7 +895,8 @@ void Client::getCursorImage()
     }
     cursorCache.insert(currentCursor->serialNumber, cursor);
     freeMessageBuffer();
-    displayArea->setCursor(*cursor);
+    if(!rootless)
+        displayArea->setCursor(*cursor);
 }
 
 
@@ -893,7 +955,8 @@ void Client::getCursor()
                 requestCacheRebuild();
             return;
         }
-        displayArea->setCursor(*cursorCache[currentCursor->serialNumber]);
+        if(!rootless)
+            displayArea->setCursor(*cursorCache[currentCursor->serialNumber]);
     }
     else
     {
@@ -1020,6 +1083,200 @@ void Client::getDeletedCursorsList()
     freeMessageBuffer();
 }
 
+ExtWin* Client::getExtWin(uint32_t id)
+{
+    foreach( ExtWin* win, extWindows)
+    {
+        if(win->getExtWinId()==id)
+            return win;
+    }
+    return NULL;
+}
+ExtWin* Client::findExtWinById(uint32_t extWinId)
+{
+    for(QList<ExtWin*>::iterator j=extWindows.begin(); j!=extWindows.end(); ++j)
+    {
+        if((*j)->getExtWinId()== extWinId)
+        {
+            return (*j);
+        }
+    }
+    return NULL;
+}
+
+void Client::getWinUpdateBuffer()
+{
+    //process window updates  from messageBuffer
+    KDRStdErr()<<"get winupdate buffer size: "<<winUpdateSize<<KDR_ENDL;
+    int readFromBuf=0;
+    while(readFromBuf < winUpdateSize)
+    {
+        uint32_t extWinId=*((uint32_t*)(messageBuffer+readFromBuf));
+        readFromBuf+=sizeof(uint32_t);
+        uint8_t state=*((uint8_t*)(messageBuffer+readFromBuf));
+        readFromBuf+=sizeof(uint8_t);
+
+        ExtWin* win;
+        if(state!=2)
+        {
+            win=getExtWin(extWinId);
+            if(!win)
+            {
+                KDRStdErr()<<"Error: Wind "<<KDR_HEX<<extWinId<<" not found in the list"<<KDR_DEC<<KDR_ENDL;
+                freeMessageBuffer();
+                return;
+            }
+        }
+        if(state==3)
+        {
+            KDRStdErr()<<"win deleted: "<<KDR_HEX<<extWinId<<KDR_ENDL;
+            extWindows.removeAll(win);
+            win->close();
+            delete win;
+        }
+        else
+        {
+            int16_t x, y;
+            uint16_t w,h,bw;
+            uint8_t visibility;
+            uint8_t winType;
+            uint16_t nameSize;
+            QString name;
+            uint32_t parId=*((uint32_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint32_t);
+            uint32_t nextSibId=*((uint32_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint32_t);
+            uint32_t transWinId=*((uint32_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint32_t);
+            x=*((int16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+            y=*((int16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+            w=*((uint16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+            h=*((uint16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+            bw=*((uint16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+
+            visibility=*((uint8_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint8_t);
+
+            winType=*((uint8_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint8_t);
+
+            nameSize=*((uint16_t*)(messageBuffer+readFromBuf));
+            readFromBuf+=sizeof(uint16_t);
+            if(nameSize)
+            {
+                name=QString::fromUtf8(messageBuffer+readFromBuf, nameSize);
+                readFromBuf+=nameSize;
+            }
+            if(state==1)
+            {
+//                 KDRStdErr()<<"win changed: "<<KDR_HEX<<wptr;
+            }
+            else
+            {
+                Qt::WindowFlags flags;
+                switch(winType)
+                {
+                    case WINDOW_TYPE_DIALOG:
+                        flags=Qt::Dialog;
+                        break;
+                    case WINDOW_TYPE_SPLASH:
+                        flags=Qt::SplashScreen;
+                        break;
+                    case WINDOW_TYPE_COMBO:
+                    case WINDOW_TYPE_POPUP_MENU:
+                    case WINDOW_TYPE_DROPDOWN_MENU:
+                        flags=Qt::Window|Qt::FramelessWindowHint|Qt::BypassWindowManagerHint|Qt::X11BypassWindowManagerHint|Qt::WindowStaysOnTopHint;
+                        break;
+                    case WINDOW_TYPE_TOOLTIP:
+                        flags=Qt::ToolTip;
+                        break;
+                    default:flags=Qt::Window ;
+                }
+
+                KDRStdErr()<<"new win: "<<KDR_HEX<<extWinId;
+                if(parId==0)
+                {
+                    KDRStdErr()<<"win is top window: "<<KDR_HEX<<parId;
+                    win=new ExtWin(extWinId,this, 0, flags);
+                    win->setParent(0);
+                }
+                else
+                {
+                    ExtWin* parentWin=findExtWinById(parId);
+                    KDRStdErr()<<"win has parent!!!!!: "<<KDR_HEX<<parId;
+                    if(!parentWin)
+                    {
+                        KDRStdErr()<<"parent Win not found in list: "<<KDR_HEX<<parId;
+                        parentWin=(ExtWin*) this;
+                    }
+                    win=new ExtWin(extWinId,this, 0,flags);
+                    win->setParent(0);
+                }
+                if(transWinId)
+                {
+                    ExtWin* transWin=findExtWinById(transWinId);
+                    if(!transWin)
+                    {
+                        KDRStdErr()<<"trans Win not found in list: "<<KDR_HEX<<transWinId;
+                    }
+                    else
+                    {
+                        KDRStdErr()<<"trans Window: "<<KDR_HEX<<transWinId;
+//                         win->setParent(transWin);
+                        win->setTransWinId(transWinId);
+                        if(winType==WINDOW_TYPE_DIALOG)
+                        {
+                            win->setModality(ExtWin::MOD_SINGLE);
+                        }
+                    }
+                }
+//                 KDRStdErr()<<"my Geometry: "<<KDR_HEX<<QRectToStr(win->geometry());
+
+                win->setWindowType(winType);
+
+                win->setParentId(parId);
+//                 KDRStdErr()<<"Place above"<<KDR_HEX<<nextSibPtr;
+                win->setNextSibId(nextSibId);
+                extWindows.append(win);
+                win->resize(w,h);
+                win->move(x,y);
+                win->showNormal();
+                win->raise();
+                win->activateWindow();
+//                 win->update();
+            }
+            KDRStdErr()<<KDR_DEC<<name<<" "<<x<<":"<<y<<" "<<w<<"x"<<h<<" bw - "<<bw<<" "<<visibility<<KDR_ENDL;
+            if(win->geometry().width() != w || win->geometry().height() != h)
+                win->resize(w,h);
+            if(win->geometry().x()!=x || win->geometry().y()!=y)
+                win->move(x,y);
+            if(win->windowTitle()!=name)
+                win->setWindowTitle(name);
+//             win->raise();
+            if(win->getParentId()!=parId)
+            {
+                #warning inplement this
+                //set new parent and remap window
+                win->setParentId(parId);
+                KDRStdErr()<<"Reparent window";
+            }
+            if(win->getNextSibId()!=nextSibId)
+            {
+                #warning inplement this
+                // set sib and restack windows
+                win->setNextSibId(nextSibId);
+                KDRStdErr()<<"Check if need to Restack window???";
+            }
+        }
+    }
+    freeMessageBuffer();
+}
+
 void Client::getDeletedFramesList()
 {
     //process list from messageBuffer
@@ -1046,6 +1303,28 @@ void Client::getDeletedFramesList()
     freeMessageBuffer();
 }
 
+const QList<ExtWin*> Client::getSiblings(ExtWin* win)
+{
+    QList<ExtWin*> siblings;
+    for(int i=0;i<extWindows.count();++i)
+    {
+        if((extWindows[i] != win) && (win->getParentId() == extWindows[i]->getParentId()))
+        {
+            siblings<<extWindows[i];
+        }
+    }
+    return siblings;
+}
+
+
+void Client::getWinUpdate()
+{
+    //get list of updated windows
+    bytesReady=0;
+    winUpdateSize=bytesLeftToRead=*((uint32_t*)messageBuffer+1);
+    currentDataType=WINUPDATEBUFFER;
+    freeMessageBuffer();
+}
 
 void Client::getDeletedCursors()
 {
@@ -1257,6 +1536,11 @@ void Client::readDataHeader()
             reinitCaches();
             break;
         }
+        case WINUPDATE:
+        {
+            getWinUpdate();
+            break;
+        }
         default:
         {
             KDRStdErr()<<"Unsupported header type: "<<data_type;
@@ -1334,6 +1618,11 @@ void Client::dataArrived()
                 getDeletedFramesList();
                 break;
             }
+            case WINUPDATEBUFFER:
+            {
+                getWinUpdateBuffer();
+                break;
+            }
             default:
             {
                 KDRStdErr()<<"not ready";
@@ -1349,7 +1638,8 @@ void Client::dataArrived()
 
 void Client::socketConnected()
 {
-    displayArea->setStyleSheet("QFrame#DisplayArea{background-color:black;}");
+    if(!rootless)
+        displayArea->setStyleSheet("QFrame#DisplayArea{background-color:black;}");
     KDRStdErr(false)<<"Connected to server"<<KDR_ENDL<<"Established X server connection"<<KDR_ENDL;
 
     if(cookie.length())
@@ -1359,7 +1649,7 @@ void Client::socketConnected()
             KDRStdErr(false)<<"Wrong length of cookie should be 32, not "<<cookie.length()<<KDR_ENDL;
             exitOnError(tr("Wrong cookie length"));
         }
-        KDRStdErr()<<"Sending Cookie to server";
+        KDRStdErr()<<"Sending Cookie to server"<<KDR_ENDL;
         if(clientSocket->write(cookie.toLatin1().data(), 32)!=32)
         {
             KDRStdErr(false)<<"Failed to send auth cookie to server"<<KDR_ENDL;
@@ -1390,6 +1680,11 @@ void Client::checkServerVersion()
 
 void Client::initGeometry()
 {
+    if(rootless)
+    {
+        geometryChanged();
+        return;
+    }
     if(geometry().width() != width || geometry().height() != height )
         resize(width, height);
     currentGeometry=geometry();
@@ -1446,6 +1741,8 @@ void Client::sendEvent(char* event)
 
 void Client::moveEvent(QMoveEvent* )
 {
+    if(rootless)
+        return;
     if(geometryDelay->isActive())
         geometryDelay->stop();
     geometryDelay->start();
@@ -1454,6 +1751,8 @@ void Client::moveEvent(QMoveEvent* )
 
 void Client::resizeEvent(QResizeEvent* )
 {
+    if(rootless)
+        return;
     if(geometryDelay->isActive())
         geometryDelay->stop();
     geometryDelay->start();
@@ -1551,6 +1850,31 @@ void Client::sendClientVersion()
     sendEvent(evmsg);
 }
 
+
+void Client::changeWindow(ExtWin* win)
+{
+    char evmsg[EVLENGTH]{};
+    uint32_t etype;
+    uint32_t extWinId=win->getExtWinId();
+    uint32_t sibId=win->getNextSibId();
+    uint16_t x,y,w,h;
+    x=win->adjustGeometry().x();
+    y=win->adjustGeometry().y();
+    w=win->adjustGeometry().width();
+    h=win->adjustGeometry().height();
+    etype=WINCHANGE;
+    KDRStdErr()<<"Request change for "<<KDR_HEX<<extWinId<<KDR_ENDL;
+    memcpy(evmsg,(char*)&etype,4);
+    memcpy(evmsg+4,(char*)&extWinId,4);
+    memcpy(evmsg+8,(char*)&sibId,4);
+    memcpy(evmsg+12,(char*)&x,2);
+    memcpy(evmsg+14,(char*)&y,2);
+    memcpy(evmsg+16,(char*)&w,2);
+    memcpy(evmsg+18,(char*)&h,2);
+    sendEvent(evmsg);
+    QTimer::singleShot(1,win,SLOT(update()));
+}
+
 //requesting on demand selection
 void Client::requestSelectionFromServer(SelectionType sel)
 {
@@ -1572,13 +1896,21 @@ void Client::geometryChanged()
         return;
     }
 
+    QGuiApplication* app=(QGuiApplication*)QGuiApplication::instance();
     QRect newGeometry;
-    newGeometry.setTopLeft(geometry().topLeft());
-    newGeometry.setSize(geometry().size());
+    if(rootless)
+    {
+        newGeometry=app->screens()[0]->availableVirtualGeometry();
+        hide();
+    }
+    else
+    {
+        newGeometry.setTopLeft(geometry().topLeft());
+        newGeometry.setSize(geometry().size());
+    }
 
-    KDRStdErr()<<"geometry changed: "<<QRectToStr(newGeometry)<<" "<<QRectToStr(currentGeometry);
+    KDRStdErr()<<"geometry changed: "<<QRectToStr(newGeometry)<<" "<<QRectToStr(currentGeometry)<<KDR_ENDL;
 
-    QGuiApplication* app=(QGuiApplication*)QGuiApplication::instance();
     QRect  newScreens[4];
 
     bool screensChanged=false;
@@ -1900,5 +2232,6 @@ void Client::reinitCaches()
         delete currentCursor;
     currentFrame=0;
     currentCursor=0;
-    displayArea->repaint(0, 0, displayArea->width(), displayArea->height());
+    if(!rootless)
+        displayArea->repaint(0, 0, displayArea->width(), displayArea->height());
 }
diff --git a/client.h b/client.h
index 344eaf7..9e98cc0 100644
--- a/client.h
+++ b/client.h
@@ -61,6 +61,7 @@ enum OS_VERSION{OS_LINUX, OS_WINDOWS, OS_DARWIN};
 //This event only sent by web client at the moment
 #define KEEPALIVE 12
 #define CACHEREBUILD 13
+#define WINCHANGE 14
 
 #define ShiftMask (1<<0)
 #define LockMask (1<<1)
@@ -84,7 +85,9 @@ enum SelectionMime{STRING,UTF_STRING,PIXMAP};
 enum SelectionType{PRIMARY,CLIPBOARD};
 enum ClipboardMode{CLIP_BOTH, CLIP_SERVER, CLIP_CLIENT, CLIP_NONE};
 
-
+enum WinType{WINDOW_TYPE_DESKTOP, WINDOW_TYPE_DOCK, WINDOW_TYPE_TOOLBAR, WINDOW_TYPE_MENU, WINDOW_TYPE_UTILITY, WINDOW_TYPE_SPLASH,
+    WINDOW_TYPE_DIALOG, WINDOW_TYPE_DROPDOWN_MENU, WINDOW_TYPE_POPUP_MENU, WINDOW_TYPE_TOOLTIP, WINDOW_TYPE_NOTIFICATION,
+    WINDOW_TYPE_COMBO, WINDOW_TYPE_DND, WINDOW_TYPE_NORMAL};
 
 #include <QMainWindow>
 #include <QAbstractSocket>
@@ -100,11 +103,13 @@ class XCBClip;
 #endif
 
 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
-#define KDR_ENDL Qt::endl
+#define KDR_ENDL Qt::endl>>Qt::dec
 #define KDR_HEX Qt::hex
+#define KDR_DEC Qt::dec
 #else
-#define KDR_ENDL endl
+#define KDR_ENDL endl>>dec
 #define KDR_HEX hex
+#define KDR_DEC dec
 #endif
 
 
@@ -122,9 +127,9 @@ public:
 class Frame
 {
 public:
-    Frame(uint32_t width, uint32_t height, int32_t x, int32_t y,  uint32_t numOfRegions, uint32_t crc);
+    Frame(uint32_t width, uint32_t height, int32_t x, int32_t y,  uint32_t numOfRegions, uint32_t crc, uint32_t winId);
     ~Frame();
-    uint32_t width, height, crc, numOfRegions;
+    uint32_t width, height, crc, numOfRegions, winId;
     int32_t x, y;
     QVector<FrameRegion*> regions;
 };
@@ -163,6 +168,7 @@ class QMenu;
 class QAction;
 class QLabel;
 class ScreenIdentifier;
+class ExtWin;
 
 class Client : public QMainWindow
 {
@@ -176,6 +182,7 @@ public:
     void setUptodate();
     QPixmap getPixmapFromCache(uint32_t crc);
     QPixmap addPixmapToCache();
+    QPixmap getDisplayPix(){return displayPix;}
     bool isDisplayPointer(QMouseEvent* event);
     void addToSelectionOutput(OutputChunk* chunk);
     bool serverSupportsExtSelection(){return serverExtSelection;}
@@ -187,9 +194,16 @@ public:
     static QTextStream& KDRStdErr(bool dbg=true);
     static QString QRectToStr(const QRect& rec);
     static QString QSizeToStr(const QSizeF& sz);
+    void changeWindow(ExtWin* win);
+    bool isRootless(){return rootless;}
+    const QList<ExtWin*> getExtWindows(){return extWindows;}
+    const QList<ExtWin*> getSiblings(ExtWin* win);
+    ExtWin* findExtWinById(uint32_t extWinId);
 
 public slots:
     void sendOutputSelChunk();
+    void slotGrabDisplay();
+
 
 
 private slots:
@@ -224,8 +238,8 @@ public slots:
 
 
 private:
-    enum{ HEADER, FRAMEREGION, REGIONDATA ,CURSORDATA, CURSORLIST, FRAMELIST, SELECTIONBUFFER } currentDataType;
-    enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION, DEMANDCLIENTSELECTION,REINIT};
+    enum{ HEADER, FRAMEREGION, REGIONDATA ,CURSORDATA, CURSORLIST, FRAMELIST, SELECTIONBUFFER, WINUPDATEBUFFER } currentDataType;
+    enum HeaderType{ FRAME, DELETEDFRAMES, CURSOR, DELETEDCURSORS, SELECTION, SERVER_VERSION, DEMANDCLIENTSELECTION,REINIT,WINUPDATE};
 
     void getServerversion();
     void getClientSelection();
@@ -243,6 +257,8 @@ private:
     void getDeletedCursorsList();
     void getSelection();
     void getSelectionBuffer();
+    void getWinUpdate();
+    void getWinUpdateBuffer();
     void renderFrame();
     void freeMessageBuffer();
     void setCursor();
@@ -250,16 +266,24 @@ private:
     void setFS(int screenNumber);
     void reinitCaches();
     void initGeometry();
+    void setDisplayPix(QPixmap pix, int32_t x=-1, int32_t y=-1);
     bool wantRepaint=false;
+    bool hasUpdates=false;
 #ifndef Q_OS_LINUX
     void sendSelectionToServer(SelectionType selection);
     void setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify=false);
 #endif
+    void parseOptions();
+    void initDesktopMode();
+    ExtWin* getExtWin(uint32_t id);
 
     static bool debug;
     //initial values
     int width=800;
     int height=600;
+    QString host="localhost";
+    int port=15000;
+
 
     //feature version of server
     quint16 serverVersion=0;
@@ -268,8 +292,11 @@ private:
     bool multidisp=false;
     int dispNumber=1;
     bool serverExtSelection=false;
+    bool rootless=false;
     QString cookie;
 
+    QString mainWndTitle;
+
     MenuFrame *FSMenuBar;
     QMenu* menu;
 
@@ -320,10 +347,12 @@ private:
 
     uint32_t deletedFramesSize=0;
     uint32_t deletedCursorsSize=0;
+    int winUpdateSize=0;
 
     QHash <uint32_t, QPixmap> frameCache;
     QHash <uint32_t, QCursor*> cursorCache;
     QList <OutputChunk*> outputSelectionQueue;
+    QList <ExtWin*> extWindows;
 
     int frameCount=0;
 
@@ -336,6 +365,7 @@ private:
     int cacheSize=0;
     ScreenIdentifier *screenIdentifier=0l;
     QLabel* fr;
+    QPixmap displayPix;
 
     //selection
 #ifdef Q_OS_LINUX
diff --git a/displayarea.cpp b/displayarea.cpp
index c4622e7..de09197 100644
--- a/displayarea.cpp
+++ b/displayarea.cpp
@@ -18,6 +18,7 @@
  *
  */
 #include "displayarea.h"
+#include "extwin.h"
 #include <QPainter>
 #include <QPaintEvent>
 #include <QMouseEvent>
@@ -77,21 +78,19 @@ LRESULT CALLBACK LowLevelKeyboardProc(
         memcpy(evmsg,(char*)&etype,4);
         memcpy(evmsg+4,(char*)&mods,4);
         memcpy(evmsg+8,(char*)&key,4);
-        display->parent->sendEvent(evmsg);
+        display->client->sendEvent(evmsg);
         return -1;
     }
     return CallNextHookEx(0, nCode, wParam, lParam);
 }
 #endif
 
-DisplayArea::DisplayArea(Client* parent):QFrame(parent)
+DisplayArea::DisplayArea(Client* client, QWidget* parentWidget):QFrame(parentWidget)
 {
-    this->parent=parent;
+    this->client=client;
+    this->parentWidget=parentWidget;
     setMouseTracking(true);
     setFocusPolicy(Qt::StrongFocus);
-    QTimer *tm=new QTimer(this);
-    connect(tm, &QTimer::timeout, this, &DisplayArea::slotGrabDisplay);
-    tm->start(1000);
     grabKeyboard();
 
 }
@@ -117,90 +116,95 @@ DisplayArea::~DisplayArea()
 {
 }
 
-void DisplayArea::slotGrabDisplay()
-{
-    if(hasUpdates)
-    {
-//         Client::KDRStdErr()<<"save display";
-        QScreen *screen = QGuiApplication::primaryScreen();
-        displayPix = screen->grabWindow(winId());
-        hasUpdates=false;
-    }
-}
-
-
-void DisplayArea::setDisplayPix(QPixmap pix, int32_t x, int32_t y)
-{
-    if(x==-1 || y==-1)
-    {
-       displayPix=pix;
-    }
-    else
-    {
-        QPainter pt;
-        pt.begin(&displayPix);
-        pt.drawPixmap(x,y,pix);
-        pt.end();
-    }
-}
-
-
-
 void DisplayArea::paintEvent(QPaintEvent* ev)
 {
-//     Client::KDRStdErr()<<"paint event:"<<ev->rect();
     QPainter painter;
-    if(! displayPix.isNull())
+    bool disp=false;
+    if(! client->getDisplayPix().isNull())
     {
         painter.begin(this);
-        if(parent->needUpdate())
+        if(client->needUpdate())
         {
-            Frame* currentFrame=parent->getCurrentFrame();
+            Frame* currentFrame=client->getCurrentFrame();
             QPixmap pix;
             if(!currentFrame->crc)
             {
-                pix=displayPix;
+//                 Client::KDRStdErr()<<"Draw DISPLAY PIX "<<KDR_ENDL;
+                pix=client->getDisplayPix();
+                disp=true;
             }
             else
             {
-                pix=parent->getPixmapFromCache(currentFrame->crc);
+//                 Client::KDRStdErr()<<"Draw PIX from cache"<<KDR_ENDL;
+                pix=client->getPixmapFromCache(currentFrame->crc);
             }
             if(currentFrame->x==-1 || currentFrame->y==-1)
             {
                 currentFrame->x=currentFrame->y=0;
             }
-            painter.drawPixmap(currentFrame->x,currentFrame->y,pix);
-            parent->setUptodate();
-            hasUpdates=true;
+            if(!client->isRootless())
+            {
+                if(!disp)
+                    painter.drawPixmap(currentFrame->x,currentFrame->y,pix);
+                else
+                    painter.drawPixmap(currentFrame->x,currentFrame->y,pix,currentFrame->x,currentFrame->y,currentFrame->width,currentFrame->height);
+            }
+            else
+            {
+                    if(!disp)
+                    {
+                        painter.drawPixmap(currentFrame->x-((ExtWin*)parentWidget)->adjustGeometry().x(),currentFrame->y-((ExtWin*)parentWidget)->adjustGeometry().y(),pix);
+                    }
+                    else
+                        painter.drawPixmap(currentFrame->x-((ExtWin*)parentWidget)->adjustGeometry().x(),currentFrame->y-((ExtWin*)parentWidget)->adjustGeometry().y(),pix,
+                                           currentFrame->x,currentFrame->y,ev->rect().width(),ev->rect().height());
+            }
         }
         else
         {
-            painter.drawPixmap(ev->rect(),displayPix,ev->rect());
-
+//             Client::KDRStdErr()<<"Win update event: "<<KDR_HEX<<((ExtWin*)parentWidget)->getPtr()<< " "<<Client::QRectToStr(ev->rect()) <<KDR_ENDL;
             char evmsg[EVLENGTH]={};
             int32_t etype, x, y, width, height;
             etype=UPDATE;
+            width=ev->rect().width();
+            height=ev->rect().height();
 
-            width=ev->rect().width()+ev->rect().x();
-            height=ev->rect().height()+ev->rect().y();
-            x=0;
-            y=0;
-
-
+            if(!client->isRootless())
+            {
+                x=ev->rect().x();
+                y=ev->rect().y();
+                painter.drawPixmap(ev->rect(),client->getDisplayPix(),ev->rect());
+            }
+            else
+            {
+                x=((ExtWin*)parentWidget)->adjustGeometry().x()+ev->rect().x();
+                y=((ExtWin*)parentWidget)->adjustGeometry().y()+ev->rect().y();
+//                 painter.drawPixmap(0,0,client->getDisplayPix(),((ExtWin*)parentWidget)->adjustGeometry().x(),((ExtWin*)parentWidget)->adjustGeometry().y(),ev->rect().width(),ev->rect().height());
+
+                #warning check this later
+                //this is making the window to flicker a bit, but it's the best I can do at the moment. We should try to get the displaypix not from the main image in
+                //x2gokdrive, but maybe directly from the window drawable to get the image not covered by windows on top and save it for every window separately
+                painter.drawPixmap(ev->rect().x(),ev->rect().y(), ev->rect().width(), ev->rect().height(),
+                                   client->getDisplayPix(), ev->rect().x()+((ExtWin*)parentWidget)->adjustGeometry().x(),
+                                   ev->rect().y()+((ExtWin*)parentWidget)->adjustGeometry().y(), ev->rect().width(), ev->rect().height());
+            }
+//             Client::KDRStdErr()<<"Request update:"<<KDR_HEX<<((ExtWin*)parentWidget)->getExtWinId() <<KDR_DEC<<" "<<x<<":"<<y<<" - "<<width<<"x"<<height<<KDR_ENDL<<"====================="<<KDR_ENDL;
             memcpy(evmsg,(char*)&etype,4);
             memcpy(evmsg+4,(char*)&width,4);
             memcpy(evmsg+8,(char*)&height,4);
             memcpy(evmsg+12,(char*)&x,4);
             memcpy(evmsg+16,(char*)&y,4);
-
-            parent->sendEvent(evmsg);
-
-
+            if(client->isRootless())
+            {
+                uint32_t win_id=(((ExtWin*)parentWidget)->getExtWinId());
+                memcpy(evmsg+20,(char*)&win_id,4);
+            }
+            client->sendEvent(evmsg);
         }
         painter.end();
     }
     else
-        Client::KDRStdErr()<<"DISPLAY PIX IS NULL";
+        Client::KDRStdErr()<<"DISPLAY PIX IS NULL"<<KDR_ENDL;
 }
 
 uint32_t DisplayArea::X11MouseButtonsState(Qt::MouseButtons qtMouseButtons)
@@ -237,7 +241,7 @@ uint32_t DisplayArea::X11MouseButton(Qt::MouseButton qtMouseButton)
 void DisplayArea::mouseMoveEvent(QMouseEvent* event)
 {
 
-    if(!parent->isDisplayPointer(event))
+    if(!client->isDisplayPointer(event))
         return;
     char evmsg[EVLENGTH]={};
     uint32_t etype, x, y;
@@ -245,14 +249,22 @@ void DisplayArea::mouseMoveEvent(QMouseEvent* event)
 
 //     Client::KDRStdErr()<<"kbd mods"<<event->modifiers();
 
-    x=event->x();
-    y=event->y();
+    if(!client->isRootless())
+    {
+        x=event->x();
+        y=event->y();
+    }
+    else
+    {
+        x=event->x()+parentWidget->geometry().x();
+        y=event->y()+parentWidget->geometry().y();
+    }
 
     memcpy(evmsg,(char*)&etype,4);
     memcpy(evmsg+4,(char*)&x,4);
     memcpy(evmsg+8,(char*)&y,4);
 
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 
 //     Client::KDRStdErr()<<"mouse move"<<event->x()<<event->y();
 
@@ -272,7 +284,7 @@ void DisplayArea::mousePressEvent(QMouseEvent* event)
     memcpy(evmsg+4,(char*)&state,4);
     memcpy(evmsg+8,(char*)&button,4);
 
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 
 //     Client::KDRStdErr()<<"mouse press"<<event->button()<<event->buttons()<<button<<state;
 }
@@ -291,7 +303,7 @@ void DisplayArea::mouseReleaseEvent(QMouseEvent* event)
     memcpy(evmsg+4,(char*)&state,4);
     memcpy(evmsg+8,(char*)&button,4);
 
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 
 //     Client::KDRStdErr()<<"mouse release"<<event->button()<<event->buttons()<<button<<state;
 }
@@ -320,7 +332,7 @@ void DisplayArea::wheelEvent(QWheelEvent* event)
     memcpy(evmsg+8,(char*)&button,4);
 
 //     Client::KDRStdErr()<<etype<<state<<button;
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 
     etype=MOUSERELEASE;
     state=X11MouseButtonsState(event->buttons());
@@ -341,7 +353,7 @@ void DisplayArea::wheelEvent(QWheelEvent* event)
     memcpy(evmsg+8,(char*)&button,4);
 
 //     Client::KDRStdErr()<<etype<<state<<button;
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 }
 
 /*
@@ -390,7 +402,7 @@ void DisplayArea::keyPressEvent(QKeyEvent* event)
     memcpy(evmsg,(char*)&etype,4);
     memcpy(evmsg+4,(char*)&state,4);
     memcpy(evmsg+8,(char*)&key,4);
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 }
 
 void DisplayArea::keyReleaseEvent(QKeyEvent* event)
@@ -414,7 +426,7 @@ void DisplayArea::keyReleaseEvent(QKeyEvent* event)
     memcpy(evmsg+4,(char*)&state,4);
     memcpy(evmsg+8,(char*)&key,4);
 
-    parent->sendEvent(evmsg);
+    client->sendEvent(evmsg);
 }
 
 uint32_t DisplayArea::qtModifiers2pc105(Qt::KeyboardModifiers qtModifiers)
diff --git a/displayarea.h b/displayarea.h
index 337689c..e32bc9b 100644
--- a/displayarea.h
+++ b/displayarea.h
@@ -36,17 +36,12 @@ class DisplayArea : public QFrame
 #endif
     Q_OBJECT
 public:
-    DisplayArea(Client* parent);
+    DisplayArea(Client* client, QWidget* parentWidget);
     ~DisplayArea();
-    void setDisplayPix(QPixmap pix, int32_t x=-1, int32_t y=-1);
 private:
     QPainter* painter=0l;
-    Client* parent;
-    QPixmap displayPix;
-    bool hasUpdates=false;
-
-public slots:
-    void slotGrabDisplay();
+    Client* client;
+    QWidget* parentWidget;
 
 protected:
     void paintEvent(QPaintEvent *);
diff --git a/extwin.cpp b/extwin.cpp
new file mode 100644
index 0000000..354e212
--- /dev/null
+++ b/extwin.cpp
@@ -0,0 +1,332 @@
+/*
+ * QT Client for X2GoKDrive
+ * Copyright (C) 2018  Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
+ * Copyright (C) 2018  phoca-GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "extwin.h"
+#include "displayarea.h"
+#include "client.h"
+#include <QApplication>
+#include <QTimer>
+ExtWin::ExtWin(uint32_t extWinId, Client *client, QWidget* parent, Qt::WindowFlags flags): QMainWindow(parent, flags)
+{
+    displayArea=new DisplayArea((Client*)client,this);
+    displayArea->setObjectName("DisplayArea");
+    displayArea->setStyleSheet("QFrame#DisplayArea{background-color:black;}");
+    displayArea->show();
+    setCentralWidget(displayArea);
+    setWindowIcon(QIcon(":res/x2goclient.png"));
+    this->extWinId=extWinId;
+    this->client=client;
+    setFocusPolicy(Qt::StrongFocus);
+//     QTimer::singleShot(10,this,SLOT(update()));
+    switch(windowType)
+    {
+        case WINDOW_TYPE_DROPDOWN_MENU:
+            setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu);
+            break;
+        case WINDOW_TYPE_POPUP_MENU:
+            setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu);
+            break;
+        case WINDOW_TYPE_DIALOG:
+            setAttribute(Qt::WA_X11NetWmWindowTypeDialog);
+            break;
+        case WINDOW_TYPE_TOOLTIP:
+            setAttribute(Qt::WA_X11NetWmWindowTypeToolTip);
+            break;
+    }
+}
+
+void ExtWin::setWinSize(int w, int h)
+{
+    displayArea->resize(w,h);
+}
+
+void ExtWin::moveEvent(QMoveEvent* ev)
+{
+    if((windowType != WINDOW_TYPE_DROPDOWN_MENU)&&(windowType != WINDOW_TYPE_POPUP_MENU))
+    {
+        Client::KDRStdErr()<<"Move "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+        client->changeWindow(this);
+    }
+    QMainWindow::moveEvent(ev);
+}
+
+
+void ExtWin::resizeEvent(QResizeEvent* ev)
+{
+    if((windowType != WINDOW_TYPE_DROPDOWN_MENU)&&(windowType != WINDOW_TYPE_POPUP_MENU))
+    {
+        Client::KDRStdErr()<<"Resize "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+        client->changeWindow(this);
+    }
+    QMainWindow::resizeEvent(ev);
+}
+
+void ExtWin::showEvent(QShowEvent *ev)
+{
+    Client::KDRStdErr()<<"Show "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+    QMainWindow::showEvent(ev);
+}
+
+void ExtWin::hideEvent(QHideEvent *ev)
+{
+    Client::KDRStdErr()<<"Hide "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+    QMainWindow::hideEvent(ev);
+}
+
+void ExtWin::focusInEvent(QFocusEvent *ev)
+{
+    Client::KDRStdErr()<<"Focus In "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+    QMainWindow::focusInEvent(ev);
+}
+
+void ExtWin::focusOutEvent(QFocusEvent *ev)
+{
+    Client::KDRStdErr()<<"Focus Out "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+    QMainWindow::focusOutEvent(ev);
+}
+
+xcb_window_t getRootNativeId(xcb_window_t winId)
+{
+    xcb_query_tree_cookie_t cookie;
+    xcb_query_tree_reply_t *reply;
+    xcb_window_t root_id=0;
+    xcb_connection_t *connection = QX11Info::connection();
+    cookie = xcb_query_tree(connection, winId);
+    if ((reply = xcb_query_tree_reply(connection, cookie, NULL)))
+    {
+        root_id=reply->root;
+        free(reply);
+    }
+    return root_id;
+}
+
+xcb_window_t getParentNativeId(xcb_window_t winId)
+{
+    xcb_query_tree_cookie_t cookie;
+    xcb_query_tree_reply_t *reply;
+    xcb_window_t parent_id=0;
+    xcb_connection_t *connection = QX11Info::connection();
+    cookie = xcb_query_tree(connection, winId);
+    if ((reply = xcb_query_tree_reply(connection, cookie, NULL)))
+    {
+        parent_id=reply->parent;
+        free(reply);
+    }
+    return parent_id;
+}
+
+xcb_window_t getCommonParent(xcb_window_t win1, xcb_window_t win2)
+{
+    QList<xcb_window_t> win1_preds;
+    xcb_window_t root=getRootNativeId(win1);
+    xcb_window_t w_next_pred=getParentNativeId(win1);
+    while(w_next_pred!=0)
+    {
+        win1_preds<<w_next_pred;
+        if(w_next_pred==root)
+            break;
+        w_next_pred=getParentNativeId(w_next_pred);
+    }
+    //now we have all preds of w1
+    w_next_pred=getParentNativeId(win2);
+    while(w_next_pred!=0)
+    {
+        if(win1_preds.indexOf(w_next_pred)!=-1)
+        {
+            return w_next_pred;
+        }
+        w_next_pred=getParentNativeId(w_next_pred);
+    }
+    return 0;
+}
+
+xcb_window_t getTopWinId(xcb_window_t myId, xcb_window_t commonParentId)
+{
+    xcb_window_t parent_id=getParentNativeId(myId);
+    while(parent_id!=commonParentId)
+    {
+        myId=parent_id;
+        parent_id=getParentNativeId(myId);
+    }
+    return myId;
+}
+
+ExtWin* ExtWin::findWinByTopWinId(xcb_window_t topWinId, QList<ExtWin*> &siblings)
+{
+    for(int i=0; i< siblings.count(); ++i)
+    {
+        if(siblings[i]->topWinId==topWinId)
+        {
+            return siblings[i];
+        }
+    }
+    return 0;
+}
+
+ExtWin* ExtWin::findWinByZInd(int zInd, QList<ExtWin*> &siblings)
+{
+    for(int i=0; i< siblings.count(); ++i)
+    {
+        if(siblings[i]->zOrderInd==zInd)
+        {
+            return siblings[i];
+        }
+    }
+    return 0;
+}
+
+QRect ExtWin::adjustGeometry()
+{
+    return geometry();
+    /*
+    if(transWinId)
+    {
+        ExtWin* trwin=client->findExtWinById(transWinId);
+        if(!trwin)
+        {
+            Client::KDRStdErr()<<"Error finding trans window  "<<transWinId<<KDR_ENDL;
+            return geometry();
+        }
+        QRect trGeom=trwin->adjustGeometry();
+        QRect geom=geometry();
+        return QRect(trGeom.x()+geom.x(), trGeom.y()+geom.y(),geom.width(),geom.height());
+    }
+    return geometry();*/
+}
+
+void ExtWin::slotCheckStackOrder()
+{
+    QList<ExtWin*> siblings=client->getSiblings(this);
+    //         Client::KDRStdErr()<<"Have Siblings: "<<siblings.count()<<KDR_ENDL;
+    if(siblings.count())
+    {
+        xcb_window_t commonParent=getCommonParent(this->winId(), siblings[0]->winId());
+        //             Client::KDRStdErr()<<"common parent: "<<KDR_HEX<<commonParent<<KDR_ENDL;
+        siblings<<this;
+        xcb_connection_t *connection = QX11Info::connection();
+        xcb_query_tree_cookie_t cookie;
+        xcb_query_tree_reply_t *reply;
+        for(int i=0;i<siblings.count();++i)
+            siblings[i]->topWinId=getTopWinId(siblings[i]->winId(),commonParent);
+        cookie = xcb_query_tree(connection, commonParent);
+        if ((reply = xcb_query_tree_reply(connection, cookie, NULL)))
+        {
+            xcb_window_t *children = xcb_query_tree_children(reply);
+            int z=0;
+            //                 Client::KDRStdErr()<<"Current stack for parent: "<<KDR_ENDL;
+            for (int i = 0; i < xcb_query_tree_children_length(reply); i++)
+            {
+                ExtWin* win=findWinByTopWinId(children[i], siblings);
+                if(win)
+                {
+                    win->zOrderInd=z++;
+                    //                         Client::KDRStdErr()<<win->zOrderInd<<" - "<<KDR_HEX<< win->extWinId<<" "<<win->windowTitle()<<"->";
+                }
+            }
+            free(reply);
+            //                 Client::KDRStdErr()<<"End of stack "<<KDR_ENDL;
+            uint32_t newNextSib;
+            if(zOrderInd==0)
+            {
+                newNextSib=0;
+            }
+            else
+            {
+                ExtWin* win=findWinByZInd(zOrderInd-1, siblings);
+                if(!win)
+                {
+                    Client::KDRStdErr()<<"Error finding window with z-order "<<zOrderInd-1<<KDR_ENDL;
+                    return ;
+                }
+                newNextSib=win->extWinId;
+            }
+            if(nextSibId != newNextSib)
+            {
+                //if some modal windows are not ordered correct, fix it and try again
+                if(!checkModality(siblings))
+                {
+                    QTimer::singleShot(5,this,SLOT(slotCheckStackOrder()));
+                    return ;
+                }
+                Client::KDRStdErr()<<"Need to restack "<<KDR_HEX<<extWinId<<" Above "<<newNextSib<<KDR_ENDL;
+                nextSibId=newNextSib;
+                client->changeWindow(this);
+            }
+            /*else
+             *               {
+             *                    Client::KDRStdErr()<<"Stack order is ok"<<KDR_ENDL;
+        }*/
+        }
+    }
+}
+
+bool ExtWin::checkModality(QList<ExtWin*> &siblings)
+{
+    bool mod_res=true;
+
+    for(int i=0;i<siblings.count();++i)
+    {
+        ExtWin* w=siblings[i];
+        if(w->modality==MOD_SINGLE)
+        {
+            if(w->transWinId)
+            {
+                ExtWin* trwin=client->findExtWinById(w->transWinId);
+                if(!trwin)
+                {
+                    Client::KDRStdErr()<<"Error finding trans window  "<<w->transWinId<<KDR_ENDL;
+                }
+                else
+                {
+                    if(trwin->zOrderInd > w->zOrderInd)
+                    {
+                        Client::KDRStdErr()<<"Transwin "<<KDR_HEX<<w->transWinId<< " on top of modal win "<<w->extWinId<<KDR_ENDL;
+//                         QTimer::singleShot(1,trwin,SLOT(update()));
+//                         QTimer::singleShot(2,w,SLOT(update()));
+//                         trwin->update();
+                        w->raise();
+                        mod_res=false;
+                    }
+                }
+            }
+        }
+    }
+
+    return mod_res;
+}
+
+bool ExtWin::nativeEvent(const QByteArray &eventType, void *message, long *result)
+{
+
+/*    if((windowType == WINDOW_TYPE_DROPDOWN_MENU)||(windowType == WINDOW_TYPE_POPUP_MENU))
+    {
+        return QMainWindow::nativeEvent(eventType, message, result);
+    }*/
+    if(!eventType.compare("xcb_generic_event_t") && parentId==0)
+    {
+        slotCheckStackOrder();
+           /* xcb_generic_event_t* ev = static_cast<xcb_generic_event_t *>(message);
+
+            Client::KDRStdErr()<<"XCB EV "<<(ev->response_type & ~0x80) <<" "<<Client::QRectToStr(geometry())<<KDR_ENDL;
+            return QMainWindow::nativeEvent(eventType, message, result);*/
+
+//         Client::KDRStdErr()<<"Check win: "<<KDR_HEX<<extWinPtr<<KDR_ENDL;
+    }
+    return QMainWindow::nativeEvent(eventType, message, result);
+}
diff --git a/extwin.h b/extwin.h
new file mode 100644
index 0000000..b9fad18
--- /dev/null
+++ b/extwin.h
@@ -0,0 +1,80 @@
+/*
+ * QT Client for X2GoKDrive
+ * Copyright (C) 2018  Oleksandr Shneyder <o.shneyder at phoca-gmbh.de>
+ * Copyright (C) 2018  phoca-GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef EXTWIN_H
+#define EXTWIN_H
+
+#include <QMainWindow>
+#ifdef Q_OS_LINUX
+#include <QX11Info>
+#else
+#warning add code for WIN EVENTS
+#endif
+
+#include "client.h"
+
+class DisplayArea;
+class Client;
+class ExtWin : public QMainWindow
+{
+    Q_OBJECT
+public:
+    enum {MOD_NONE,MOD_SINGLE, MOD_GROUP};
+    ExtWin(uint32_t extWinId, Client *client, QWidget* parent, Qt::WindowFlags flags = Qt::WindowFlags());
+    uint32_t getPtr(){return extWinId;}
+    void setWinSize(int w, int h);
+    void setParentId(uint32_t id){parentId=id;}
+    void setTransWinId(uint32_t id){transWinId=id;}
+    void setNextSibId(uint32_t id){nextSibId=id;}
+    void setWindowType(uint8_t tp){windowType=tp;}
+    uint64_t getParentId(){return parentId;}
+    uint64_t getNextSibId(){return nextSibId;}
+    uint64_t getExtWinId(){return extWinId;}
+    DisplayArea* getDisplayArea(){return displayArea;}
+    void setModality(int mod){modality=mod;}
+    QRect adjustGeometry();
+
+private:
+    ExtWin* findWinByTopWinId(xcb_window_t topWinId, QList<ExtWin*> &siblings);
+    ExtWin* findWinByZInd(int zInd, QList<ExtWin*> &siblings);
+    bool checkModality(QList<ExtWin*> &siblings);
+    DisplayArea* displayArea;
+    Client* client;
+    uint32_t extWinId;
+    uint32_t parentId=0;
+    uint32_t nextSibId=0;
+    uint32_t transWinId=0;
+    uint8_t windowType=WINDOW_TYPE_NORMAL;
+    xcb_window_t topWinId=0;
+    int zOrderInd;
+    int modality=MOD_NONE;
+
+private slots:
+    void slotCheckStackOrder();
+
+protected:
+    void resizeEvent(QResizeEvent*);
+    void moveEvent(QMoveEvent*);
+    void showEvent(QShowEvent *event);
+    void hideEvent(QHideEvent *event);
+    void focusInEvent(QFocusEvent *event);
+    void focusOutEvent(QFocusEvent *event);
+    bool nativeEvent(const QByteArray &eventType, void *message, long *result);
+};
+#endif //EXTWIN_H
diff --git a/menuframe.cpp b/menuframe.cpp
index a15b0a9..576185d 100644
--- a/menuframe.cpp
+++ b/menuframe.cpp
@@ -305,13 +305,13 @@ void MenuFrame::setCaption(const QString& text)
     {
         case TOP:
         case BOTTOM:
-            setMinimumWidth(defaultSpacing*4+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4);
-            setMaximumWidth(defaultSpacing*4+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4);
+            setMinimumWidth(defaultSpacing*4+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4+4);
+            setMaximumWidth(defaultSpacing*4+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4+4);
             break;
         case LEFT:
         case RIGHT:
-            setMinimumHeight(defaultSpacing*5+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4);
-            setMaximumHeight(defaultSpacing*5+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4);
+            setMinimumHeight(defaultSpacing*5+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4+4);
+            setMaximumHeight(defaultSpacing*5+defaultMargin*2+capt->sizeHint().width()+bPin->sizeHint().width()*4+4);
             break;
     }
 
diff --git a/x2gokdriveclient.pro b/x2gokdriveclient.pro
index e106f41..903bf63 100644
--- a/x2gokdriveclient.pro
+++ b/x2gokdriveclient.pro
@@ -20,8 +20,8 @@ RESOURCES += resources.qrc
 
 
 # Input
-SOURCES += main.cpp client.cpp displayarea.cpp menuframe.cpp screenidentifier.cpp
-HEADERS += client.h displayarea.h menuframe.h screenidentifier.h
+SOURCES += main.cpp client.cpp displayarea.cpp menuframe.cpp screenidentifier.cpp extwin.cpp
+HEADERS += client.h displayarea.h menuframe.h screenidentifier.h extwin.h
 
 linux {
 SOURCES += xcbclip.cpp

--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdriveclient.git


More information about the x2go-commits mailing list