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@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@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@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