This is an automated email from the git hooks/post-receive script. x2go pushed a change to branch feature/rootless-mode in repository x2gokdriveclient. at a8f41b9 rootless mode for x2gokdriveclient This branch includes the following new commits: new a8f41b9 rootless mode for x2gokdriveclient The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gokdriveclient.git
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