diff options
Diffstat (limited to 'src-qt5/core/libLumina')
21 files changed, 2023 insertions, 333 deletions
diff --git a/src-qt5/core/libLumina/DesktopSettings.cpp b/src-qt5/core/libLumina/DesktopSettings.cpp index 30bd5bc2..47dc29de 100644 --- a/src-qt5/core/libLumina/DesktopSettings.cpp +++ b/src-qt5/core/libLumina/DesktopSettings.cpp @@ -9,7 +9,7 @@ #include <QDir> #include <QDebug> -#include <unistd.h> +#include <unistd.h> #include <pwd.h> #include <grp.h> @@ -25,6 +25,16 @@ DesktopSettings::~DesktopSettings(){ if(!files.isEmpty()){ stop(); } } +DesktopSettings* DesktopSettings::instance(){ + static DesktopSettings *set = 0; + if(set==0){ + //First-time init + set = new DesktopSettings(); + set->start(); + } + return set; +} + //Start/stop routines void DesktopSettings::start(){ files.clear(); settings.clear(); //clear the internal hashes (just in case) @@ -35,7 +45,7 @@ void DesktopSettings::start(){ } parseSystemSettings(); //set the runmode appropriately locateFiles(); // - + } void DesktopSettings::stop(){ @@ -111,7 +121,7 @@ void DesktopSettings::parseSystemSettings(){ //Now determine the runmode for this user struct passwd *pw = getpwuid(getuid()); - if(pw!=0){ + if(pw!=0){ QString cuser = QString(pw->pw_name); free(pw); //done with this structure if( settings[path]->value("fulluser_users", QStringList()).toStringList().contains(cuser) ){ runmode = DesktopSettings::UserFull; } @@ -123,10 +133,10 @@ void DesktopSettings::parseSystemSettings(){ gid_t grpList[100]; int grpSize = 100; if( getgrouplist(cuser.toLocal8Bit(), getgid(), grpList, &grpSize) > 0 ){ - QStringList groups; - for(int i=0; i<grpSize; i++){ + QStringList groups; + for(int i=0; i<grpSize; i++){ struct group *g = getgrgid(grpList[i]); - if(g!=0){ + if(g!=0){ groups << QString(g->gr_name); free(g); } @@ -136,18 +146,18 @@ void DesktopSettings::parseSystemSettings(){ if( (fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::UserFull; } else{ fromfile = settings[path]->value("fullsystem_groups", QStringList()).toStringList(); - fromfile.removeDuplicates(); + fromfile.removeDuplicates(); if((fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::SystemFull; } else{ fromfile = settings[path]->value("staticinterface_groups", QStringList()).toStringList(); - fromfile.removeDuplicates(); + fromfile.removeDuplicates(); if((fromfile+groups).removeDuplicates() > 0 ){ runmode = DesktopSettings::SystemInterface; } } } - } //end group list read + } //end group list read } }else{ - runmode = DesktopSettings::SystemFull; //could not read user name - assume system files only + runmode = DesktopSettings::SystemFull; //could not read user name - assume system files only } break; //found this file - go ahead and stop now (no hierarchy for this special file) @@ -246,7 +256,7 @@ void DesktopSettings::fileChanged(QString file){ QList< DesktopSettings::File > types = files.keys(); for(int i=0; i<types.length(); i++){ if(files[types[i]].contains(file)){ - emit FileModified(types[i]); + emit FileModified(types[i]); break; } } diff --git a/src-qt5/core/libLumina/DesktopSettings.h b/src-qt5/core/libLumina/DesktopSettings.h index 57eede9d..57a85791 100644 --- a/src-qt5/core/libLumina/DesktopSettings.h +++ b/src-qt5/core/libLumina/DesktopSettings.h @@ -30,6 +30,8 @@ public: DesktopSettings(QObject *parent = 0); ~DesktopSettings(); + static DesktopSettings* instance(); + //Start/stop routines void start(); void stop(); diff --git a/src-qt5/core/libLumina/LDesktopUtils.cpp b/src-qt5/core/libLumina/LDesktopUtils.cpp index d76c68e9..4454d29b 100644 --- a/src-qt5/core/libLumina/LDesktopUtils.cpp +++ b/src-qt5/core/libLumina/LDesktopUtils.cpp @@ -15,12 +15,12 @@ static QStringList fav; -QString LDesktopUtils::LuminaDesktopVersion(){ - QString ver = "1.2.2"; +QString LDesktopUtils::LuminaDesktopVersion(){ + QString ver = "1.3.1"; #ifdef GIT_VERSION ver.append( QString(" (Git Revision: %1)").arg(GIT_VERSION) ); #endif - return ver; + return ver; } QString LDesktopUtils::LuminaDesktopBuildDate(){ @@ -88,7 +88,7 @@ QStringList LDesktopUtils::listFavorites(){ fav.removeDuplicates(); lastRead = cur; } - + return fav; } @@ -138,7 +138,7 @@ void LDesktopUtils::removeFavorite(QString path){ void LDesktopUtils::upgradeFavorites(int){ //fromoldversionnumber //NOTE: Version number syntax: <major>*1000000 + <minor>*1000 + <revision> // Example: 1.2.3 -> 1002003 -} +} void LDesktopUtils::LoadSystemDefaults(bool skipOS){ //Will create the Lumina configuration files based on the current system template (if any) @@ -156,15 +156,15 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QDesktopWidget *desk =QApplication::desktop(); QRect screenGeom; for(int i=0; i<desk->screenCount(); i++){ - if(desk->screenGeometry(i).x()==0){ - screen = QString::number(i); - screenGeom = desk->screenGeometry(i); - break; + if(desk->screenGeometry(i).x()==0){ + screen = QString::number(i); + screenGeom = desk->screenGeometry(i); + break; } } //Now setup the default "desktopsettings.conf" and "sessionsettings.conf" files QStringList deskset, sesset;//, lopenset; - + // -- SESSION SETTINGS -- QStringList tmp = sysDefaults.filter("session_"); if(tmp.isEmpty()){ tmp = sysDefaults.filter("session."); }//for backwards compat @@ -177,36 +177,36 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ if(val.isEmpty()){ continue; } QString istrue = (val.toLower()=="true") ? "true": "false"; //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file - + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file + if(var.contains("_default_")){ val = LUtils::AppToAbsolute(val); } //got an application/binary //Special handling for values which need to exist first - if(var.endsWith("_ifexists") ){ + if(var.endsWith("_ifexists") ){ var = var.remove("_ifexists"); //remove this flag from the variable //Check if the value exists (absolute path only) if(!QFile::exists(val)){ continue; } //skip this line - value/file does not exist } - + //Parse/save the value QString sset; //temporary strings if(var=="session_enablenumlock"){ sset = "EnableNumlock="+ istrue; } else if(var=="session_playloginaudio"){ sset = "PlayStartupAudio="+istrue; } else if(var=="session_playlogoutaudio"){ sset = "PlayLogoutAudio="+istrue; } - else if(var=="session_default_terminal"){ + else if(var=="session_default_terminal"){ LXDG::setDefaultAppForMime("application/terminal", val); - //sset = "default-terminal="+val; - }else if(var=="session_default_filemanager"){ + //sset = "default-terminal="+val; + }else if(var=="session_default_filemanager"){ LXDG::setDefaultAppForMime("inode/directory", val); //sset = "default-filemanager="+val; - //loset = "directory="+val; - }else if(var=="session_default_webbrowser"){ - //loset = "webbrowser="+val; + //loset = "directory="+val; + }else if(var=="session_default_webbrowser"){ + //loset = "webbrowser="+val; LXDG::setDefaultAppForMime("x-scheme-handler/http", val); LXDG::setDefaultAppForMime("x-scheme-handler/https", val); - }else if(var=="session_default_email"){ + }else if(var=="session_default_email"){ LXDG::setDefaultAppForMime("application/email",val); - //loset = "email="+val; + //loset = "email="+val; } //Put the line into the file (overwriting any previous assignment as necessary) /*if(!loset.isEmpty()){ @@ -218,7 +218,7 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ if(!sset.isEmpty()){ int index = sesset.indexOf(QRegExp(sset.section("=",0,0)+"=*", Qt::CaseSensitive, QRegExp::Wildcard)); if(index<0){ sesset << sset; } //new line - else{ sesset[index] = sset; } //overwrite the other line + else{ sesset[index] = sset; } //overwrite the other line } } //if(!lopenset.isEmpty()){ lopenset.prepend("[default]"); } //the session options exist within this set @@ -232,11 +232,11 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ if(val.isEmpty()){ continue; } QString istrue = (val.toLower()=="true") ? "true": "false"; //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file val = LUtils::AppToAbsolute(val); //Special handling for values which need to exist first - if(var.endsWith("_ifexists") ){ + if(var.endsWith("_ifexists") ){ var = var.remove("_ifexists"); //remove this flag from the variable //Check if the value exists (absolute path only) if(!QFile::exists(val)){ continue; } //skip this line - value/file does not exist @@ -259,8 +259,8 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ if(val.isEmpty()){ continue; } QString istrue = (val.toLower()=="true") ? "true": "false"; //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file if(var=="desktop_visiblepanels"){ deskset << "panels="+val; } else if(var=="desktop_backgroundfiles"){ deskset << "background\\filelist="+val; } else if(var=="desktop_backgroundrotateminutes"){ deskset << "background\\minutesToChange="+val; } @@ -282,8 +282,8 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ if(val.isEmpty()){ continue; } QString istrue = (val.toLower()=="true") ? "true": "false"; //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file if(var==(panvar+"_pixelsize")){ //qDebug() << "Panel Size:" << val; if(val.contains("%")){ @@ -293,7 +293,7 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ else if(last=="w"){ val = QString::number( qRound(screenGeom.width()*val.toDouble())/100 ); }//adjust value to a percentage of the width of the screen } //qDebug() << " -- Adjusted:" << val; - deskset << "height="+val; + deskset << "height="+val; } else if(var==(panvar+"_autohide")){ deskset << "hidepanel="+istrue; } else if(var==(panvar+"_location")){ deskset << "location="+val.toLower(); } @@ -314,8 +314,8 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QString val = tmp[i].section("=",1,1).section("#",0,0).toLower().simplified(); if(val.isEmpty()){ continue; } //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file if(var=="menu_plugins"){ deskset << "itemlist="+val; } } if(!tmp.isEmpty()){ deskset << ""; } //space between sections @@ -328,7 +328,7 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QString var = tmp[i].section("=",0,0).toLower().simplified(); QString val = tmp[i].section("=",1,1).section("#",0,0).simplified(); //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } + if(var.contains(".")){ var.replace(".","_"); } //Now parse the variable and put the value in the proper file qDebug() << "Favorite entry:" << var << val; val = LUtils::AppToAbsolute(val); //turn any relative files into absolute @@ -346,19 +346,19 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QString var = tmp[i].section("=",0,0).toLower().simplified(); QString val = tmp[i].section("=",1,1).section("#",0,0).simplified(); //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } + if(var.contains(".")){ var.replace(".","_"); } //Now parse the variable and put the value in the proper file val = LUtils::AppToAbsolute(val); //turn any relative files into absolute if(var=="quicklaunch_add_ifexists" && QFile::exists(val)){ quickL << val; } else if(var=="quicklaunch_add"){ quickL << val; } } - if(!quickL.isEmpty()){ + if(!quickL.isEmpty()){ if(sesset.isEmpty()){ sesset << "[General]"; } //everything is in this section - sesset << "QuicklaunchApps="+quickL.join(", "); + sesset << "QuicklaunchApps="+quickL.join(", "); } //Now do any theme settings - QStringList themesettings = LTHEME::currentSettings(); + QStringList themesettings = LTHEME::currentSettings(); //List: [theme path, colorspath, iconsname, font, fontsize] //qDebug() << "Current Theme Color:" << themesettings[1]; tmp = sysDefaults.filter("theme_"); @@ -370,19 +370,19 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified(); if(val.isEmpty()){ continue; } //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } - //Now parse the variable and put the value in the proper file + if(var.contains(".")){ var.replace(".","_"); } + //Now parse the variable and put the value in the proper file if(var=="theme_themefile"){ themesettings[0] = val; } else if(var=="theme_colorfile"){ themesettings[1] = val; } else if(var=="theme_iconset"){ themesettings[2] = val; } else if(var=="theme_font"){ themesettings[3] = val; } - else if(var=="theme_fontsize"){ + else if(var=="theme_fontsize"){ if(val.endsWith("%")){ val = QString::number( (screenGeom.height()*val.section("%",0,0).toDouble())/100 )+"px"; } - themesettings[4] = val; + themesettings[4] = val; } } //qDebug() << " - Now Color:" << themesettings[1] << setTheme; - + //Now double check that the custom theme/color files exist and reset it will the full path as necessary if(setTheme){ QStringList systhemes = LTHEME::availableSystemThemes(); @@ -414,15 +414,15 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ //Ensure that the settings directory exists QString setdir = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop"; - if(!QFile::exists(setdir)){ - QDir dir; - dir.mkpath(setdir); + if(!QFile::exists(setdir)){ + QDir dir; + dir.mkpath(setdir); } //Now save the settings files if(setTheme){ LTHEME::setCurrentSettings( themesettings[0], themesettings[1], themesettings[2], themesettings[3], themesettings[4]); } LUtils::writeFile(setdir+"/sessionsettings.conf", sesset, true); LUtils::writeFile(setdir+"/desktopsettings.conf", deskset, true); - + //Now run any extra config scripts or utilities as needed tmp = sysDefaults.filter("usersetup_run"); if(tmp.isEmpty()){ tmp = sysDefaults.filter("usersetup.run"); } @@ -431,25 +431,25 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){ QString var = tmp[i].section("=",0,0).toLower().simplified(); QString val = tmp[i].section("=",1,1).section("#",0,0).simplified(); //Change in 0.8.5 - use "_" instead of "." within variables names - need backwards compat for a little while - if(var.contains(".")){ var.replace(".","_"); } + if(var.contains(".")){ var.replace(".","_"); } //Now parse the variable and put the value in the proper file if(var=="usersetup_run"){ qDebug() << "Running user setup command:" << val; QProcess::execute(val); } } - + } bool LDesktopUtils::checkUserFiles(QString lastversion){ - //internal version conversion examples: + //internal version conversion examples: // [1.0.0 -> 1000000], [1.2.3 -> 1002003], [0.6.1 -> 6001] //returns true if something changed int oldversion = LDesktopUtils::VersionStringToNumber(lastversion); int nversion = LDesktopUtils::VersionStringToNumber(QApplication::applicationVersion()); bool newversion = ( oldversion < nversion ); //increasing version number bool newrelease = ( lastversion.contains("-devel", Qt::CaseInsensitive) && QApplication::applicationVersion().contains("-release", Qt::CaseInsensitive) ); //Moving from devel to release - + QString confdir = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/"; //Check for the desktop settings file QString dset = confdir+"desktopsettings.conf"; @@ -461,7 +461,7 @@ bool LDesktopUtils::checkUserFiles(QString lastversion){ } //Convert the favorites framework as necessary (change occured with 0.8.4) if(newversion || newrelease){ - LDesktopUtils::upgradeFavorites(oldversion); + LDesktopUtils::upgradeFavorites(oldversion); } //Convert from the old desktop numbering system to the new one (change occured with 1.0.1) if(oldversion<=1000001){ @@ -488,7 +488,7 @@ bool LDesktopUtils::checkUserFiles(QString lastversion){ } LUtils::writeFile(dset, DS, true); } - + //Check the fluxbox configuration files dset = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/"; if(!QFile::exists(dset+"fluxbox-init")){ @@ -517,7 +517,7 @@ bool LDesktopUtils::checkUserFiles(QString lastversion){ int LDesktopUtils::VersionStringToNumber(QString version){ version = version.section("-",0,0); //trim any extra labels off the end int maj, mid, min; //major/middle/minor version numbers (<Major>.<Middle>.<Minor>) - maj = mid = min = 0; + maj = mid = min = 0; bool ok = true; maj = version.section(".",0,0).toInt(&ok); if(ok){ mid = version.section(".",1,1).toInt(&ok); }else{ maj = 0; } diff --git a/src-qt5/core/libLumina/LIconCache.h b/src-qt5/core/libLumina/LIconCache.h index 0344e0f3..f58a5510 100644 --- a/src-qt5/core/libLumina/LIconCache.h +++ b/src-qt5/core/libLumina/LIconCache.h @@ -69,7 +69,7 @@ private: private slots: void IconLoaded(QString id, QDateTime sync, QByteArray *data); - + signals: void InternalIconLoaded(QString, QDateTime, QByteArray*); //INTERNAL SIGNAL - DO NOT USE in other classes/objects void IconAvailable(QString); //way for classes to listen/reload icons as they change diff --git a/src-qt5/core/libLumina/LuminaX11.cpp b/src-qt5/core/libLumina/LuminaX11.cpp index ab6cd880..dd98d12c 100644 --- a/src-qt5/core/libLumina/LuminaX11.cpp +++ b/src-qt5/core/libLumina/LuminaX11.cpp @@ -75,9 +75,6 @@ void LXCB::createWMAtoms(){ i--; } } - - - } // === WindowList() === @@ -91,13 +88,13 @@ QList<WId> LXCB::WindowList(bool rawlist){ if( 1 == xcb_ewmh_get_client_list_reply( &EWMH, cookie, &winlist, NULL) ){ //qDebug() << " - Loop over items"; unsigned int wkspace = CurrentWorkspace(); - for(unsigned int i=0; i<winlist.windows_len; i++){ + for(unsigned int i=0; i<winlist.windows_len; i++){ //Filter out the Lumina Desktop windows if(WindowClass(winlist.windows[i]) == "Lumina Desktop Environment"){ continue; } //Also filter out windows not on the active workspace else if( (WindowWorkspace(winlist.windows[i])!=wkspace) && !rawlist ){ continue; } else{ - output << winlist.windows[i]; + output << winlist.windows[i]; } } } @@ -182,7 +179,7 @@ void LXCB::SetCurrentWorkspace(int number){ event.data.data32[4] = 0; xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); - + //EWMH function (does not seem to be recognized by Fluxbox) xcb_ewmh_request_change_showing_desktop(&EWMH, QX11Info::appScreen(), number); } @@ -193,7 +190,7 @@ QString LXCB::WindowClass(WId win){ QString out; if(win==0){ return ""; } xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class_unchecked(QX11Info::connection(), win); - if(cookie.sequence == 0){ return out; } + if(cookie.sequence == 0){ return out; } xcb_icccm_get_wm_class_reply_t value; if( 1== xcb_icccm_get_wm_class_reply( QX11Info::connection(), cookie, &value, NULL) ){ out = QString::fromUtf8(value.class_name); @@ -210,7 +207,7 @@ unsigned int LXCB::WindowWorkspace(WId win){ uint32_t wkspace = 0; xcb_get_property_cookie_t scookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win); xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop_unchecked(&EWMH, win); - if(cookie.sequence == 0){ return wkspace; } + if(cookie.sequence == 0){ return wkspace; } xcb_ewmh_get_wm_desktop_reply(&EWMH, cookie, &wkspace, NULL); xcb_ewmh_get_atoms_reply_t reply; if(1==xcb_ewmh_get_wm_state_reply(&EWMH,scookie, &reply, NULL)){ @@ -220,7 +217,7 @@ unsigned int LXCB::WindowWorkspace(WId win){ } } //qDebug() << " - done: " << wkspace; - return wkspace; + return wkspace; } // === WindowGeometry() === @@ -500,7 +497,7 @@ QIcon LXCB::WindowIcon(WId win){ uint* dat = iter.data; //dat+=2; //remember the first 2 element offset for(int i=0; i<image.byteCount()/4; ++i, ++dat){ - ((uint*)image.bits())[i] = *dat; + ((uint*)image.bits())[i] = *dat; } icon.addPixmap(QPixmap::fromImage(image)); //layer this pixmap onto the icon //Now see if there are any more icons available @@ -1514,7 +1511,7 @@ void LXCB::WM_Set_Client_List(QList<WId> list, bool stacking){ xcb_ewmh_set_client_list_stacking(&EWMH, QX11Info::appScreen(), list.length(), array); }else{ xcb_ewmh_set_client_list(&EWMH, QX11Info::appScreen(), list.length(), array); - } + } } @@ -1528,7 +1525,7 @@ unsigned int LXCB::WM_Get_Number_Desktops(){ } void LXCB::WM_SetNumber_Desktops(unsigned int number){ - //NOTE: number should be at least 1 + //NOTE: number should be at least 1 xcb_ewmh_set_number_of_desktops(&EWMH, QX11Info::appScreen(), number); } @@ -1555,7 +1552,7 @@ QList<QPoint> LXCB::WM_Get_Desktop_Viewport(){ out << QPoint( reply.desktop_viewport[i].x, reply.desktop_viewport[i].y ); } xcb_ewmh_get_desktop_viewport_reply_wipe(&reply); //clean up the reply structure first - } + } return out; } diff --git a/src-qt5/core/libLumina/LuminaXDG.h b/src-qt5/core/libLumina/LuminaXDG.h index d159ef43..cc250c7e 100644 --- a/src-qt5/core/libLumina/LuminaXDG.h +++ b/src-qt5/core/libLumina/LuminaXDG.h @@ -12,8 +12,6 @@ // *.desktop Exec Compliance Updated: 9/9/2014 // Mime Application Version Compliance: 1.0.1 (11/14/14) (Skips random *.desktop parsing: ~80% compliant) //=========================================== - - #ifndef _LUMINA_LIBRARY_XDG_H #define _LUMINA_LIBRARY_XDG_H @@ -83,7 +81,7 @@ public: bool saveDesktopFile(bool merge = true); //This will use the "filePath" variable for where to save the file - bool setAutoStarted(bool autostart = true); + bool setAutoStarted(bool autostart = true); }; // ======================== @@ -127,29 +125,29 @@ private: XDGDesktop *desk; void loadExtraInfo(); - + public: //Couple overloaded contructors LFileInfo(); LFileInfo(QString filepath); LFileInfo(QFileInfo info); - ~LFileInfo(){ - desk->deleteLater(); + ~LFileInfo(){ + desk->deleteLater(); } - + //Functions for accessing the extra information // -- Return the mimetype for the file QString mimetype(); - + // -- Return the icon file to use for this file QString iconfile(); //Note: This string is auto-formatted for use in the LXDG::findIcon() routine. - + // -- Check if this is an XDG desktop file bool isDesktopFile(); - + // -- Allow access to the internal XDG desktop data structure XDGDesktop* XDG(); - + //Other file type identification routines bool isImage(); //Is a readable image file (for thumbnail support) bool isAVFile(); //Is an audio/video file @@ -165,7 +163,7 @@ public: //static XDGDesktop* loadDesktopFile(QString filepath, bool&ok, QObject *parent = 0); //static bool saveDesktopFile(XDGDesktop *dFile, bool merge = true); //Check a *.desktop file for validity (showAll skips the DE-exclusivity checks) - //static bool checkValidity(XDGDesktop *dFile, bool showAll = true); + //static bool checkValidity(XDGDesktop *dFile, bool showAll = true); //Check for a valid executable static bool checkExec(QString exec); //Get a list of all the directories where *.desktop files exist @@ -210,7 +208,7 @@ public: static QStringList findAVFileExtensions(); //Load all the "globs2" mime database files static QStringList loadMimeFileGlobs2(); - + //Find all the autostart *.desktop files static QList<XDGDesktop*> findAutoStartFiles(bool includeInvalid = false); //static bool setAutoStarted(bool autostart, XDGDesktop *app); diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.cpp b/src-qt5/core/libLumina/NativeEmbedWidget.cpp new file mode 100644 index 00000000..80c843e0 --- /dev/null +++ b/src-qt5/core/libLumina/NativeEmbedWidget.cpp @@ -0,0 +1,208 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "NativeEmbedWidget.h" + +#include <QPainter> +#include <QX11Info> +#include <QDebug> + +#include <xcb/xproto.h> +#include <xcb/xcb_aux.h> +#include <xcb/xcb_image.h> +#include <xcb/composite.h> +#include <X11/extensions/Xdamage.h> + +#define NORMAL_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_POINTER_MOTION | \ + XCB_EVENT_MASK_BUTTON_MOTION | \ + XCB_EVENT_MASK_EXPOSURE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_ENTER_WINDOW| \ + XCB_EVENT_MASK_PROPERTY_CHANGE) + +inline void registerClientEvents(WId id){ + uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK}; + xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); +} + +// ============ +// PRIVATE +// ============ +//Simplification functions for the XCB/XLib interactions +void NativeEmbedWidget::syncWinSize(QSize sz){ + if(WIN==0){ return; } + if(!sz.isValid()){ sz = this->size(); } //use the current widget size + //qDebug() << "Sync Window Size:" << sz; + if(sz == winSize){ return; } //no change + const uint32_t valList[2] = {(uint32_t) sz.width(), (uint32_t) sz.height()}; + const uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + xcb_configure_window(QX11Info::connection(), WIN->id(), mask, valList); + winSize = sz; //save this for checking later +} + +void NativeEmbedWidget::syncWidgetSize(QSize sz){ + //qDebug() << "Sync Widget Size:" << sz; + this->resize(sz); +} + +void NativeEmbedWidget::hideWindow(){ + xcb_unmap_window(QX11Info::connection(), WIN->id()); +} + +void NativeEmbedWidget::showWindow(){ + xcb_map_window(QX11Info::connection(), WIN->id()); +} + +QImage NativeEmbedWidget::windowImage(QRect geom){ + //Pull the XCB pixmap out of the compositing layer + xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection()); + xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix); + if(pix==0){ return QImage(); } + + //Convert this pixmap into a QImage + xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, geom.x(), geom.y(), geom.width(), geom.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP); + if(ximg == 0){ return QImage(); } + QImage img(ximg->data, ximg->width, ximg->height, ximg->stride, QImage::Format_ARGB32_Premultiplied); + img = img.copy(); //detach this image from the XCB data structures + xcb_image_destroy(ximg); + + //Cleanup the XCB data structures + xcb_free_pixmap(QX11Info::connection(), pix); + + return img; + +} + +// ============ +// PUBLIC +// ============ +NativeEmbedWidget::NativeEmbedWidget(QWidget *parent) : QWidget(parent){ + WIN = 0; //nothing embedded yet + this->setSizeIncrement(2,2); +} + +bool NativeEmbedWidget::embedWindow(NativeWindow *window){ + WIN = window; + //PIXBACK = xcb_generate_id(QX11Info::connection()); + xcb_reparent_window(QX11Info::connection(), WIN->id(), this->winId(), 0, 0); + //Now send the embed event to the app + //qDebug() << " - send _XEMBED event"; + /*xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = WIN->id(); + event.type = obj->ATOMS["_XEMBED"]; //_XEMBED + event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; + event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY + event.data.data32[2] = 0; + event.data.data32[3] = this->winId(); //WID of the container + event.data.data32[4] = 0; + + xcb_send_event(QX11Info::connection(), 0, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); + */ + //Now setup any redirects and return + xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]); + xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]); + + //Now create/register the damage handler + // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore) + // -- Retested 6/29/17 (no change) Ken Moore + //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer + //xcb_damage_create(QX11Info::connection(), dmgID, WIN->id(), XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself). + Damage dmgID = XDamageCreate(QX11Info::display(), WIN->id(), XDamageReportRawRectangles); + + WIN->addDamageID( (uint) dmgID); //save this for later + WIN->addFrameWinID(this->winId()); + connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change + + registerClientEvents(WIN->id()); + registerClientEvents(this->winId()); + return true; +} + +bool NativeEmbedWidget::detachWindow(){ + xcb_reparent_window(QX11Info::connection(), WIN->id(), QX11Info::appRootWindow(), -1, -1); + WIN = 0; + return true; +} + +bool NativeEmbedWidget::isEmbedded(){ + return (WIN!=0); +} + +// ============== +// PUBLIC SLOTS +// ============== +void NativeEmbedWidget::resyncWindow(){ + if(WIN==0){ return; } + return; //skip the stuff below (not working) + QRect geom = WIN->geometry(); + //Send an artificial configureNotify event to the window with the global position/size included + xcb_configure_notify_event_t event; + event.x = geom.x() + this->pos().x(); + event.y = geom.y() + this->pos().y(); + event.width = this->width(); + event.height = this->height(); + event.border_width = 0; + event.above_sibling = XCB_NONE; + event.override_redirect = false; + event.window = WIN->id(); + event.event = WIN->id(); + event.response_type = XCB_CONFIGURE_NOTIFY; + xcb_send_event(QX11Info::connection(), false, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (const char *) &event); + + xcb_flush(QX11Info::connection()); +} + +void NativeEmbedWidget::repaintWindow(){ + this->update(); +} +// ============== +// PROTECTED +// ============== +void NativeEmbedWidget::resizeEvent(QResizeEvent *ev){ + QWidget::resizeEvent(ev); + if(WIN!=0){ + syncWinSize(ev->size()); + } //syncronize the window with the new widget size +} + +void NativeEmbedWidget::showEvent(QShowEvent *ev){ + if(WIN!=0){ showWindow(); } + QWidget::showEvent(ev); +} + +void NativeEmbedWidget::hideEvent(QHideEvent *ev){ + if(WIN!=0){ hideWindow(); } + QWidget::hideEvent(ev); +} + +void NativeEmbedWidget::paintEvent(QPaintEvent *ev){ + //QWidget::paintEvent(ev); //ensure all the Qt-compositing is done first + if(this->size()!=winSize){ return; } //do not paint here - waiting to re-sync the sizes + if(WIN==0){ QWidget::paintEvent(ev); return; } + //Need to paint the image from the window onto the widget as an overlay + QRect geom = QRect(0,0,this->width(), this->height()); //always paint the whole window + //qDebug() << "Get Paint image:" << ev->rect() << geom; + //geom = ev->rect(); //atomic updates + //geom.adjust(-1,-1,1,1); //add an additional pixel in each direction to be painted + //geom = geom.intersected(QRect(0,0,this->width(), this->height())); //ensure intersection with actual window + QImage img = windowImage(geom); + if(!img.isNull()){ + if(img.size() != geom.size()){ return; } + QPainter P(this); + P.drawImage( geom , img, QRect(geom.topLeft(), img.size()), Qt::NoOpaqueDetection); //1-to-1 mapping + //qDebug() << "Painted Rect:" << ev->rect() << this->geometry(); + //Note: Qt::NoOpaqueDetection Speeds up the paint by bypassing the checks to see if there are [semi-]transparent pixels + // Since this is an embedded image - we fully expect there to be transparency most of the time. + } + //qDebug() << "Done Painting"; +} diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.h b/src-qt5/core/libLumina/NativeEmbedWidget.h new file mode 100644 index 00000000..78c11dfc --- /dev/null +++ b/src-qt5/core/libLumina/NativeEmbedWidget.h @@ -0,0 +1,55 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This is a container object for embedding a native window into a QWidget +// and maintaining a 1-to-1 mapping of sizing and other properties +// while also providing compositing effects between the two windows +//=========================================== +#ifndef _LUMINA_NATIVE_EMBED_WIDGET_H +#define _LUMINA_NATIVE_EMBED_WIDGET_H + +#include "NativeWindow.h" +#include <QWidget> +#include <QTimer> +#include <QResizeEvent> +#include <QShowEvent> +#include <QHideEvent> +#include <QPaintEvent> + +class NativeEmbedWidget : public QWidget{ + Q_OBJECT +private: + NativeWindow *WIN; + QSize winSize; + +private slots: + //Simplification functions + void syncWinSize(QSize sz = QSize()); + void syncWidgetSize(QSize sz); + void hideWindow(); + void showWindow(); + QImage windowImage(QRect geom); + + +public: + NativeEmbedWidget(QWidget *parent); + + bool embedWindow(NativeWindow *window); + bool detachWindow(); + bool isEmbedded(); //status of the embed + +public slots: + void resyncWindow(); + void repaintWindow(); + +protected: + void resizeEvent(QResizeEvent *ev); + void showEvent(QShowEvent *ev); + void hideEvent(QHideEvent *ev); + void paintEvent(QPaintEvent *ev); +}; + +#endif diff --git a/src-qt5/core/libLumina/NativeEventFilter.cpp b/src-qt5/core/libLumina/NativeEventFilter.cpp new file mode 100644 index 00000000..d6c2da50 --- /dev/null +++ b/src-qt5/core/libLumina/NativeEventFilter.cpp @@ -0,0 +1,272 @@ +//=========================================== +// Lumina-desktop source code +// Copyright (c) 2015-2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "NativeEventFilter.h" +#include <QCoreApplication> +#include <QDebug> + +//#include <xcb/xcb_aux.h> +//#include <xcb/damage.h> + +//================================================== +// NOTE: All the XCB interactions and atoms are accessed via: +// obj->XCB->EWMH.(atom name) +// obj->XCB->(do something) +//================================================== + +/* +List of XCB response types (since almost impossible to find good docs on XCB) +switch (xcb_generic_event_t*->response_type & ~0x80) +case values: +XCB_KEY_[PRESS | RELEASE] +XCB_BUTTON_[PRESS | RELEASE] +XCB_MOTION_NOTIFY +XCB_ENTER_NOTIFY +XCB_LEAVE_NOTIFY +XCB_FOCUS_[IN | OUT] +XCB_KEYMAP_NOTIFY +XCB_EXPOSE +XCB_GRAPHICS_EXPOSURE +XCB_VISIBILITY_NOTIFY +XCB_CREATE_NOTIFY +XCB_DESTROY_NOTIFY +XCB_UNMAP_NOTIFY +XCB_MAP_[NOTIFY | REQUEST] +XCB_REPARENT_NOTIFY +XCB_CONFIGURE_[NOTIFY | REQUEST] +XCB_GRAVITY_NOTIFY +XCB_RESIZE_REQUEST +XCB_CIRCULATE_[NOTIFY | REQUEST] +XCB_PROPERTY_NOTIFY +XCB_SELECTION_[CLEAR | REQUEST | NOTIFY] +XCB_COLORMAP_NOTIFY +XCB_CLIENT_MESSAGE +*/ + +//SYSTEM TRAY STANDARD DEFINITIONS +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +//#include <LuminaX11.h> +#include <QX11Info> +#include <xcb/xcb_ewmh.h> +#include <xcb/xcb_keysyms.h> +#include <xcb/damage.h> + +#define DEBUG 0 + +//Special objects/variables for XCB parsing +static xcb_ewmh_connection_t EWMH; +//static LXCB *XCB = 0; +static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE = 0; + +inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilter *obj){ + //qDebug() << "Got Property Event:" << ev->window << ev->atom; + NativeWindow::Property prop = NativeWindow::None; + //Now determine which properties are getting changed, and update the native window as appropriate + if(ev->atom == EWMH._NET_WM_NAME){ prop = NativeWindow::Title; } + else if(ev->atom == EWMH._NET_WM_ICON){ prop = NativeWindow::Icon; } + else if(ev->atom == EWMH._NET_WM_ICON_NAME){ prop = NativeWindow::ShortTitle; } + else if(ev->atom == EWMH._NET_WM_DESKTOP){ prop = NativeWindow::Workspace; } + else if(ev->atom == EWMH._NET_WM_WINDOW_TYPE ){ prop = NativeWindow::WinTypes; } + else if( ev->atom == EWMH._NET_WM_STATE){ prop = NativeWindow::States; } + //Send out the signal if necessary + if(prop!=NativeWindow::None){ + if(DEBUG){ qDebug() << "Detected Property Change:" << ev->window << prop; } + obj->emit WindowPropertyChanged(ev->window, prop); + }else{ + //qDebug() << "Unknown Property Change:" << ev->window << ev->atom; + } +} + + +//Constructor for the Event Filter wrapper +NativeEventFilter::NativeEventFilter() : QObject(){ + EF = new EventFilter(this); + if(EWMH.nb_screens <=0){ + xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH); + if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){ + qDebug() << "Error with XCB atom initializations"; + } + } + if(_NET_SYSTEM_TRAY_OPCODE==0){ + //_NET_SYSTEM_TRAY_OPCODE + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(QX11Info::connection(), 0, 23,"_NET_SYSTEM_TRAY_OPCODE"); + xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(QX11Info::connection(), cookie, NULL); + if(r){ + _NET_SYSTEM_TRAY_OPCODE = r->atom; + free(r); + } + } +} + +void NativeEventFilter::start(){ + if(DEBUG){ qDebug() << " - Install event filter..."; } + QCoreApplication::instance()->installNativeEventFilter(EF); + if(DEBUG){ qDebug() << " - Run request check..."; } + +} + +void NativeEventFilter::stop(){ + QCoreApplication::instance()->installNativeEventFilter(0); +} + +//============================= +// EventFilter Class +//============================= + +//Constructor for the XCB event filter +EventFilter::EventFilter(NativeEventFilter *parent) : QAbstractNativeEventFilter(){ + obj = parent; +} + +//This function format taken directly from the Qt5.3 documentation +bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *){ + //qDebug() << "New Event"; + if(eventType=="xcb_generic_event_t"){ + //Convert to known event type (for X11 systems) + xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message); + //Now parse the event and emit signals as necessary + switch( ev->response_type & ~0x80){ +//============================== +// INTERACTIVITY EVENTS +//============================== + case XCB_KEY_PRESS: + //This is a keyboard key press + //qDebug() << "Key Press Event" + obj->emit KeyPressed( ((xcb_key_press_event_t *) ev)->detail, ((xcb_key_press_event_t *) ev)->root ); + break; + case XCB_KEY_RELEASE: + //This is a keyboard key release + //qDebug() << "Key Release Event"; + obj->emit KeyReleased( ((xcb_key_release_event_t *) ev)->detail, ((xcb_key_release_event_t *) ev)->root ); + break; + case XCB_BUTTON_PRESS: + //This is a mouse button press + //qDebug() << "Button Press Event"; + obj->emit MousePressed( ((xcb_button_press_event_t *) ev)->detail, ((xcb_button_press_event_t *) ev)->root ); + break; + case XCB_BUTTON_RELEASE: + //This is a mouse button release + //qDebug() << "Button Release Event"; + obj->emit MouseReleased( ((xcb_button_release_event_t *) ev)->detail, ((xcb_button_release_event_t *) ev)->root ); + break; + case XCB_MOTION_NOTIFY: + //This is a mouse movement event + if(DEBUG){ qDebug() << "Motion Notify Event"; } + obj->emit MouseMovement(); + break; + case XCB_ENTER_NOTIFY: + //This is a mouse movement event when mouse goes over a new window + //qDebug() << "Enter Notify Event"; + obj->emit MouseEnterWindow( ((xcb_enter_notify_event_t *) ev)->root ); + break; + case XCB_LEAVE_NOTIFY: + //This is a mouse movement event when mouse goes leaves a window + //qDebug() << "Leave Notify Event"; + obj->emit MouseLeaveWindow( ((xcb_leave_notify_event_t *) ev)->root ); + break; +//============================== + case XCB_EXPOSE: + //qDebug() << "Expose Notify Event:"; + //qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window; + break; +//============================== + case XCB_MAP_NOTIFY: + //qDebug() << "Window Map Event:" << ((xcb_map_notify_event_t *)ev)->window; + obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, true); + break; //This is just a notification that a window was mapped - nothing needs to change here + case XCB_MAP_REQUEST: + //qDebug() << "Window Map Request Event"; + obj->emit WindowCreated( ((xcb_map_request_event_t *) ev)->window ); + break; +//============================== + case XCB_CREATE_NOTIFY: + //qDebug() << "Window Create Event"; + break; +//============================== + case XCB_UNMAP_NOTIFY: + //qDebug() << "Window Unmap Event:" << ((xcb_unmap_notify_event_t *)ev)->window; + obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, false); + break; +//============================== + case XCB_DESTROY_NOTIFY: + //qDebug() << "Window Closed Event:" << ((xcb_destroy_notify_event_t *)ev)->window; + obj->emit WindowDestroyed( ((xcb_destroy_notify_event_t *) ev)->window ); + break; +//============================== + case XCB_FOCUS_IN: + //qDebug() << "Focus In Event:"; + break; +//============================== + case XCB_FOCUS_OUT: + //qDebug() << "Focus Out Event:"; + break; +//============================== + case XCB_PROPERTY_NOTIFY: + //qDebug() << "Property Notify Event:"; + ParsePropertyEvent((xcb_property_notify_event_t*)ev, obj); + break; +//============================== + case XCB_CLIENT_MESSAGE: + //qDebug() << "Client Message Event"; + //qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window; + if( ((xcb_client_message_event_t*)ev)->type == _NET_SYSTEM_TRAY_OPCODE && ((xcb_client_message_event_t*)ev)->format == 32){ + //data32[0] is timestamp, [1] is opcode, [2] is window handle + if(SYSTEM_TRAY_REQUEST_DOCK == ((xcb_client_message_event_t*)ev)->data.data32[1]){ + obj->emit TrayWindowCreated( ((xcb_client_message_event_t*)ev)->data.data32[2] ); + //addTrayApp( ((xcb_client_message_event_t*)ev)->data.data32[2] ); + } + //Ignore the System Tray messages at the moment + } + break; +//============================== + case XCB_CONFIGURE_NOTIFY: + //qDebug() << "Configure Notify Event"; + /*obj->emit WindowPropertiesChanged( ((xcb_configure_notify_event_t*)ev)->window, + QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size, + QList<QVariant>() << QPoint(((xcb_configure_notify_event_t*)ev)->x, ((xcb_configure_notify_event_t*)ev)->y) << + QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );*/ + obj->emit WindowPropertyChanged( ((xcb_configure_notify_event_t*)ev)->window, NativeWindow::Size, + QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) ); + break; +//============================== + case XCB_CONFIGURE_REQUEST: + //qDebug() << "Configure Request Event"; + obj->emit RequestWindowPropertiesChange( ((xcb_configure_request_event_t*)ev)->window, + QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size, + QList<QVariant>() << QPoint(((xcb_configure_request_event_t*)ev)->x, ((xcb_configure_request_event_t*)ev)->y) << + QSize(((xcb_configure_request_event_t*)ev)->width, ((xcb_configure_request_event_t*)ev)->height) ); + break; +//============================== + case XCB_RESIZE_REQUEST: + //qDebug() << "Resize Request Event"; + obj->emit RequestWindowPropertyChange( ((xcb_resize_request_event_t*)ev)->window, + NativeWindow::Size, QSize(((xcb_resize_request_event_t*)ev)->width, ((xcb_resize_request_event_t*)ev)->height) ); + break; +//============================== + case XCB_SELECTION_CLEAR: + //qDebug() << "Selection Clear Event"; + break; +//============================== + case 85: //not sure what event this is - but it seems to come up very often (just hide the notice) + case 0: + case XCB_GE_GENERIC: + break; //generic event - don't do anything special + default: + //if( (ev->response_type & ~0x80)==TrayDmgID){ + obj->emit PossibleDamageEvent( ((xcb_damage_notify_event_t*)ev)->drawable ); + //checkDamageID( ((xcb_damage_notify_event_t*)ev)->drawable ); + //}else{ + //qDebug() << "Default Event:" << (ev->response_type & ~0x80); + //} +//============================== + } + } + return false; + //never stop event handling (this will not impact the X events themselves - just the internal Qt application) +} diff --git a/src-qt5/core/libLumina/NativeEventFilter.h b/src-qt5/core/libLumina/NativeEventFilter.h new file mode 100644 index 00000000..2b184f99 --- /dev/null +++ b/src-qt5/core/libLumina/NativeEventFilter.h @@ -0,0 +1,70 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class provides the XCB event handling/registrations that are needed +//=========================================== +#ifndef _LUMINA_DESKTOP_NATIVE_EVENT_FILTER_H +#define _LUMINA_DESKTOP_NATIVE_EVENT_FILTER_H + +#include <QAbstractNativeEventFilter> +#include <QObject> +#include <QByteArray> + +#include "NativeWindow.h" + + +class NativeEventFilter : public QObject{ + Q_OBJECT +private: + QAbstractNativeEventFilter* EF; + WId WMFlag; //used to flag a running WM process + +public: + NativeEventFilter(); + ~NativeEventFilter(){} + + void start(); + void stop(); + +signals: + //Window Signals + void WindowCreated(WId); + void WindowDestroyed(WId); + void WindowPropertyChanged(WId, NativeWindow::Property); + void WindowPropertyChanged(WId, NativeWindow::Property, QVariant); + void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>); + void RequestWindowPropertyChange(WId, NativeWindow::Property, QVariant); + void RequestWindowPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>); + + //System Tray Signals + void TrayWindowCreated(WId); + void TrayWindowDestroyed(WId); + + //Miscellaneos Signals + void PossibleDamageEvent(WId); + + //Input Event Signals + void KeyPressed(int, WId); + void KeyReleased(int, WId); + void MousePressed(int, WId); + void MouseReleased(int, WId); + void MouseMovement(); + void MouseEnterWindow(WId); + void MouseLeaveWindow(WId); +}; + +class EventFilter : public QAbstractNativeEventFilter{ +public: + EventFilter(NativeEventFilter *parent); + ~EventFilter(){} + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *); + +private: + NativeEventFilter *obj; +}; + +#endif diff --git a/src-qt5/core/libLumina/NativeKeyToQt.cpp b/src-qt5/core/libLumina/NativeKeyToQt.cpp new file mode 100644 index 00000000..06056be7 --- /dev/null +++ b/src-qt5/core/libLumina/NativeKeyToQt.cpp @@ -0,0 +1,528 @@ + +#include <NativeWindowSystem.h> + +#include <QKeySequence> +#include <QX11Info> + +// XCB/X11 Includes +#define XK_MISCELLANY +#define XK_XKB_KEYS +#define XK_LATIN1 +#define XK_LATIN2 +#define XK_LATIN3 +#define XK_LATIN4 +#define XK_LATIN8 +#define XK_LATIN9 +//NOTE: Look at the keysymdef.h file for additional define/characters which we may need later +#include <X11/keysymdef.h> +#include <xcb/xcb_keysyms.h> + + +//Small simplification functions +Qt::Key NativeWindowSystem::KeycodeToQt(int keycode){ + static xcb_key_symbols_t *SYM = 0; + if(SYM==0){ SYM = xcb_key_symbols_alloc(QX11Info::connection()); } + xcb_keysym_t symbol = xcb_key_symbols_get_keysym(SYM, keycode,0); + //not sure about the "column" input - we want raw keys though so ignore the "modified" key states (columns) for now + //qDebug() << "Try to convert keycode to Qt::Key:" << keycode << symbol; + //Now map this symbol to the appropriate Qt::Key enumeration + switch(symbol){ + //FUNCTION KEYS + case XK_F1: return Qt::Key_F1; + case XK_F2: return Qt::Key_F2; + case XK_F3: return Qt::Key_F3; + case XK_F4: return Qt::Key_F4; + case XK_F5: return Qt::Key_F5; + case XK_F6: return Qt::Key_F6; + case XK_F7: return Qt::Key_F7; + case XK_F8: return Qt::Key_F8; + case XK_F9: return Qt::Key_F9; + case XK_F10: return Qt::Key_F10; + case XK_F11: return Qt::Key_F11; + case XK_F12: return Qt::Key_F12; + case XK_F13: return Qt::Key_F13; + case XK_F14: return Qt::Key_F14; + case XK_F15: return Qt::Key_F15; + case XK_F16: return Qt::Key_F16; + case XK_F17: return Qt::Key_F17; + case XK_F18: return Qt::Key_F18; + case XK_F19: return Qt::Key_F19; + case XK_F20: return Qt::Key_F20; + case XK_F21: return Qt::Key_F21; + case XK_F22: return Qt::Key_F22; + case XK_F23: return Qt::Key_F23; + case XK_F24: return Qt::Key_F24; + case XK_F25: return Qt::Key_F25; + case XK_F26: return Qt::Key_F26; + case XK_F27: return Qt::Key_F27; + case XK_F28: return Qt::Key_F28; + case XK_F29: return Qt::Key_F29; + case XK_F30: return Qt::Key_F30; + case XK_F31: return Qt::Key_F31; + case XK_F32: return Qt::Key_F32; + case XK_F33: return Qt::Key_F33; + case XK_F34: return Qt::Key_F34; + case XK_F35: return Qt::Key_F35; + //Miscellaneous Keys + case XK_BackSpace: return Qt::Key_Backspace; + case XK_Delete: return Qt::Key_Delete; + //case XK_LineFeed: return Qt::Key_Backspace; + case XK_Clear: return Qt::Key_Clear; + case XK_Return: return Qt::Key_Return; + case XK_Pause: return Qt::Key_Pause; + case XK_Scroll_Lock: return Qt::Key_ScrollLock; + case XK_Sys_Req: return Qt::Key_SysReq; + case XK_Escape: return Qt::Key_Escape; + case XK_Select: return Qt::Key_Select; + case XK_Print: return Qt::Key_Print; + //case XK_Execute: return Qt::Key_Execute; + case XK_Insert: return Qt::Key_Insert; + case XK_Undo: return Qt::Key_Undo; + case XK_Redo: return Qt::Key_Redo; + case XK_Menu: return Qt::Key_Menu; + case XK_Find: return Qt::Key_Find; + case XK_Cancel: return Qt::Key_Cancel; + case XK_Help: return Qt::Key_Help; + //case XK_Break: return Qt::Key_Break; + //case XK_Mode_switch: return Qt::Key_Backspace; + //case XK_script_switch: return Qt::Key_Backspace; + case XK_Num_Lock: return Qt::Key_NumLock; + //Cursor Controls + case XK_Home: return Qt::Key_Home; + case XK_Left: return Qt::Key_Left; + case XK_Up: return Qt::Key_Up; + case XK_Right: return Qt::Key_Right; + case XK_Down: return Qt::Key_Down; + //case XK_Prior: return Qt::Key_Backspace; + case XK_Page_Up: return Qt::Key_PageUp; + case XK_Page_Down: return Qt::Key_PageDown; + //case XK_Next: return Qt::Key_Backspace; + case XK_End: return Qt::Key_End; + //case XK_Begin: return Qt::Key_Backspace; + // Keypad Functions and numbers + case XK_KP_Space: return Qt::Key_Space; + case XK_KP_Tab: return Qt::Key_Tab; + case XK_KP_Enter: return Qt::Key_Enter; + case XK_KP_F1: return Qt::Key_F1; + case XK_KP_F2: return Qt::Key_F2; + case XK_KP_F3: return Qt::Key_F3; + case XK_KP_F4: return Qt::Key_F4; + case XK_KP_Home: return Qt::Key_Home; + case XK_KP_Left: return Qt::Key_Left; + case XK_KP_Up: return Qt::Key_Up; + case XK_KP_Right: return Qt::Key_Right; + case XK_KP_Down: return Qt::Key_Down; + //case XK_KP_Prior: return Qt::Key_ + case XK_KP_Page_Up: return Qt::Key_PageUp; + //case XK_KP_Next: return Qt::Key_ + case XK_KP_Page_Down: return Qt::Key_PageDown; + case XK_KP_End: return Qt::Key_End; + //case XK_KP_Begin: return Qt::Key_ + case XK_KP_Insert: return Qt::Key_Insert; + case XK_KP_Delete: return Qt::Key_Delete; + case XK_KP_Equal: return Qt::Key_Equal; + case XK_KP_Multiply: return Qt::Key_Asterisk; + case XK_KP_Add: return Qt::Key_Plus; + case XK_KP_Separator: return Qt::Key_Comma; //X11 definitions say this is often comma + case XK_KP_Subtract: return Qt::Key_Minus; + case XK_KP_Decimal: return Qt::Key_Period; + case XK_KP_Divide: return Qt::Key_Slash; + case XK_KP_0: return Qt::Key_0; + case XK_KP_1: return Qt::Key_1; + case XK_KP_2: return Qt::Key_2; + case XK_KP_3: return Qt::Key_3; + case XK_KP_4: return Qt::Key_4; + case XK_KP_5: return Qt::Key_5; + case XK_KP_6: return Qt::Key_6; + case XK_KP_7: return Qt::Key_7; + case XK_KP_8: return Qt::Key_8; + case XK_KP_9: return Qt::Key_9; + // Modifier Keys + case XK_Shift_L: return Qt::Key_Shift; + case XK_Shift_R: return Qt::Key_Shift; + case XK_Control_L: return Qt::Key_Control; + case XK_Control_R: return Qt::Key_Control; + case XK_Caps_Lock: return Qt::Key_CapsLock; + //case XK_Shift_Lock: return Qt::Key_ShiftLock; + case XK_Meta_L: return Qt::Key_Meta; + case XK_Meta_R: return Qt::Key_Meta; + case XK_Alt_L: return Qt::Key_Alt; + case XK_Alt_R: return Qt::Key_Alt; + case XK_Super_L: return Qt::Key_Super_L; + case XK_Super_R: return Qt::Key_Super_R; + case XK_Hyper_L: return Qt::Key_Hyper_L; + case XK_Hyper_R: return Qt::Key_Hyper_R; + case XK_space: return Qt::Key_Space; + case XK_exclam: return Qt::Key_Exclam; + case XK_quotedbl: return Qt::Key_QuoteDbl; + case XK_numbersign: return Qt::Key_NumberSign; + case XK_dollar: return Qt::Key_Dollar; + case XK_percent: return Qt::Key_Percent; + case XK_ampersand: return Qt::Key_Ampersand; + case XK_apostrophe: return Qt::Key_Apostrophe; + case XK_parenleft: return Qt::Key_ParenLeft; + case XK_parenright: return Qt::Key_ParenRight; + case XK_asterisk: return Qt::Key_Asterisk; + case XK_plus: return Qt::Key_Plus; + case XK_comma: return Qt::Key_Comma; + case XK_minus: return Qt::Key_Minus; + case XK_period: return Qt::Key_Period; + case XK_slash: return Qt::Key_Slash; + case XK_0: return Qt::Key_0; + case XK_1: return Qt::Key_1; + case XK_2: return Qt::Key_2; + case XK_3: return Qt::Key_3; + case XK_4: return Qt::Key_4; + case XK_5: return Qt::Key_5; + case XK_6: return Qt::Key_6; + case XK_7: return Qt::Key_7; + case XK_8: return Qt::Key_8; + case XK_9: return Qt::Key_9; + case XK_colon: return Qt::Key_Colon; + case XK_semicolon: return Qt::Key_Semicolon; + case XK_less: return Qt::Key_Less; + case XK_equal: return Qt::Key_Equal; + case XK_greater: return Qt::Key_Greater; + case XK_question: return Qt::Key_Question; + case XK_at: return Qt::Key_At; + case XK_A: return Qt::Key_A; + case XK_B: return Qt::Key_B; + case XK_C: return Qt::Key_C; + case XK_D: return Qt::Key_D; + case XK_E: return Qt::Key_E; + case XK_F: return Qt::Key_F; + case XK_G: return Qt::Key_G; + case XK_H: return Qt::Key_H; + case XK_I: return Qt::Key_I; + case XK_J: return Qt::Key_J; + case XK_K: return Qt::Key_K; + case XK_L: return Qt::Key_L; + case XK_M: return Qt::Key_M; + case XK_N: return Qt::Key_N; + case XK_O: return Qt::Key_O; + case XK_P: return Qt::Key_P; + case XK_Q: return Qt::Key_Q; + case XK_R: return Qt::Key_R; + case XK_S: return Qt::Key_S; + case XK_T: return Qt::Key_T; + case XK_U: return Qt::Key_U; + case XK_V: return Qt::Key_V; + case XK_W: return Qt::Key_W; + case XK_X: return Qt::Key_X; + case XK_Y : return Qt::Key_Y; + case XK_Z: return Qt::Key_Z; + case XK_bracketleft: return Qt::Key_BracketLeft; + case XK_backslash: return Qt::Key_Backslash; + case XK_bracketright: return Qt::Key_BracketRight; + case XK_asciicircum: return Qt::Key_AsciiCircum; + case XK_underscore: return Qt::Key_Underscore; + case XK_grave: return Qt::Key_Agrave; + case XK_a: return Qt::Key_A; + case XK_b: return Qt::Key_B; + case XK_c: return Qt::Key_C; + case XK_d: return Qt::Key_D; + case XK_e: return Qt::Key_E; + case XK_f : return Qt::Key_F; + case XK_g: return Qt::Key_G; + case XK_h: return Qt::Key_H; + case XK_i: return Qt::Key_I; + case XK_j: return Qt::Key_J; + case XK_k: return Qt::Key_K; + case XK_l: return Qt::Key_L; + case XK_m: return Qt::Key_M; + case XK_n: return Qt::Key_N; + case XK_o: return Qt::Key_O; + case XK_p: return Qt::Key_P; + case XK_q: return Qt::Key_Q; + case XK_r: return Qt::Key_R; + case XK_s: return Qt::Key_S; + case XK_t : return Qt::Key_T; + case XK_u: return Qt::Key_U; + case XK_v: return Qt::Key_V; + case XK_w: return Qt::Key_W; + case XK_x: return Qt::Key_X; + case XK_y: return Qt::Key_Y; + case XK_z: return Qt::Key_Z; + case XK_braceleft: return Qt::Key_BraceLeft; + case XK_bar: return Qt::Key_Bar; + case XK_braceright: return Qt::Key_BraceRight; + case XK_asciitilde: return Qt::Key_AsciiTilde; + + case XK_nobreakspace: return Qt::Key_nobreakspace; + case XK_exclamdown: return Qt::Key_exclamdown; + case XK_cent: return Qt::Key_cent; + case XK_sterling: return Qt::Key_sterling; + case XK_currency: return Qt::Key_currency; + case XK_yen: return Qt::Key_yen; + case XK_brokenbar: return Qt::Key_brokenbar; + case XK_section: return Qt::Key_section; + case XK_diaeresis: return Qt::Key_diaeresis; + case XK_copyright: return Qt::Key_copyright; + case XK_ordfeminine: return Qt::Key_ordfeminine; + case XK_guillemotleft: return Qt::Key_guillemotleft; + case XK_notsign: return Qt::Key_notsign; + case XK_hyphen: return Qt::Key_hyphen; + case XK_registered: return Qt::Key_registered; + case XK_macron: return Qt::Key_macron; + case XK_degree: return Qt::Key_degree; + case XK_plusminus: return Qt::Key_plusminus; + case XK_twosuperior: return Qt::Key_twosuperior; + case XK_threesuperior: return Qt::Key_threesuperior; + case XK_acute: return Qt::Key_acute; + case XK_mu: return Qt::Key_mu; + case XK_paragraph: return Qt::Key_paragraph; + case XK_periodcentered: return Qt::Key_periodcentered; + case XK_cedilla: return Qt::Key_cedilla; + case XK_onesuperior: return Qt::Key_onesuperior; + case XK_masculine: return Qt::Key_masculine; + case XK_guillemotright: return Qt::Key_guillemotright; + case XK_onequarter: return Qt::Key_onequarter; + case XK_onehalf: return Qt::Key_onehalf; + case XK_threequarters: return Qt::Key_threequarters; + case XK_questiondown: return Qt::Key_questiondown; + case XK_Agrave: return Qt::Key_Agrave; + case XK_Aacute: return Qt::Key_Aacute; + case XK_Acircumflex: return Qt::Key_Acircumflex; + case XK_Atilde: return Qt::Key_Atilde; + case XK_Adiaeresis: return Qt::Key_Adiaeresis; + case XK_Aring: return Qt::Key_Aring; + case XK_AE: return Qt::Key_AE; + case XK_Ccedilla: return Qt::Key_Ccedilla; + case XK_Egrave: return Qt::Key_Egrave; + case XK_Eacute: return Qt::Key_Eacute; + case XK_Ecircumflex: return Qt::Key_Ecircumflex; + case XK_Ediaeresis: return Qt::Key_Ediaeresis; + case XK_Igrave: return Qt::Key_Igrave; + case XK_Iacute: return Qt::Key_Iacute; + case XK_Icircumflex: return Qt::Key_Icircumflex; + case XK_Idiaeresis: return Qt::Key_Idiaeresis; + case XK_ETH: return Qt::Key_ETH; + //case XK_Eth: return Qt::Key_Eth; + case XK_Ntilde: return Qt::Key_Ntilde; + case XK_Ograve: return Qt::Key_Ograve; + case XK_Oacute: return Qt::Key_Oacute; + case XK_Ocircumflex: return Qt::Key_Ocircumflex; + case XK_Otilde: return Qt::Key_Otilde; + case XK_Odiaeresis: return Qt::Key_Odiaeresis; + case XK_multiply: return Qt::Key_multiply; + //case XK_Oslash: return Qt::Key_AsciiTilde; + case XK_Ooblique: return Qt::Key_Ooblique; + case XK_Ugrave: return Qt::Key_Ugrave; + case XK_Uacute: return Qt::Key_Uacute; + case XK_Ucircumflex: return Qt::Key_Ucircumflex; + case XK_Udiaeresis: return Qt::Key_Udiaeresis; + case XK_Yacute: return Qt::Key_Yacute; + case XK_THORN: return Qt::Key_THORN; + //case XK_Thorn: return Qt::Key_AsciiTilde; + case XK_ssharp: return Qt::Key_ssharp; + /*case XK_agrave: return Qt::Key_AsciiTilde; + case XK_aacute: return Qt::Key_AsciiTilde; + case XK_acircumflex: return Qt::Key_AsciiTilde; + case XK_atilde: return Qt::Key_AsciiTilde; + case XK_adiaeresis: return Qt::Key_AsciiTilde; + case XK_aring: return Qt::Key_AsciiTilde; + case XK_ae: return Qt::Key_AsciiTilde; + case XK_ccedilla: return Qt::Key_AsciiTilde; + case XK_egrave: return Qt::Key_AsciiTilde; + case XK_eacute: return Qt::Key_AsciiTilde; + case XK_ecircumflex: return Qt::Key_AsciiTilde; + case XK_ediaeresis: return Qt::Key_AsciiTilde; + case XK_igrave: return Qt::Key_AsciiTilde; + case XK_iacute: return Qt::Key_AsciiTilde; + case XK_icircumflex: return Qt::Key_AsciiTilde; + case XK_idiaeresis: return Qt::Key_AsciiTilde; + case XK_eth: return Qt::Key_AsciiTilde; + case XK_ntilde: return Qt::Key_AsciiTilde; + case XK_ograve: return Qt::Key_AsciiTilde; + case XK_oacute: return Qt::Key_AsciiTilde; + case XK_ocircumflex: return Qt::Key_AsciiTilde; + case XK_otilde: return Qt::Key_AsciiTilde; + case XK_odiaeresis: return Qt::Key_AsciiTilde; + case XK_division: return Qt::Key_AsciiTilde; + case XK_oslash: return Qt::Key_AsciiTilde; + case XK_ooblique: return Qt::Key_AsciiTilde; + case XK_ugrave: return Qt::Key_AsciiTilde; + case XK_uacute: return Qt::Key_AsciiTilde; + case XK_ucircumflex: return Qt::Key_AsciiTilde; + case XK_udiaeresis: return Qt::Key_AsciiTilde; + case XK_yacute: return Qt::Key_AsciiTilde; + case XK_thorn: return Qt::Key_AsciiTilde; + case XK_ydiaeresis: return Qt::Key_AsciiTilde; + + case: XK_Agonek: return Qt::Key_AsciiTilde; + case XK_breve: return Qt::Key_AsciiTilde; + case XK_Lstroke: return Qt::Key_AsciiTilde; + case XK_Lcaron: return Qt::Key_AsciiTilde; + case XK_Sacute: return Qt::Key_AsciiTilde; + case XK_Scaron: return Qt::Key_AsciiTilde; + case XK_Scedilla: return Qt::Key_AsciiTilde; + case XK_Tcaron: return Qt::Key_AsciiTilde; + case XK_Zacute: return Qt::Key_AsciiTilde; + case XK_Zcaron: return Qt::Key_AsciiTilde; + case XK_Zabovedot: return Qt::Key_AsciiTilde; + case XK_aogonek: return Qt::Key_AsciiTilde; + case XK_ogonek: return Qt::Key_AsciiTilde; + case XK_lstroke: return Qt::Key_AsciiTilde; + case XK_lcaron: return Qt::Key_AsciiTilde; + case XK_sacute: return Qt::Key_AsciiTilde; + case XK_caron: return Qt::Key_AsciiTilde; + case XK_scaron: return Qt::Key_AsciiTilde; + case XK_scedilla: return Qt::Key_AsciiTilde; + case XK_tcaron: return Qt::Key_AsciiTilde; + case XK_zacute: return Qt::Key_AsciiTilde; + case XK_doubleacute: return Qt::Key_AsciiTilde; + case XK_zcaron: return Qt::Key_AsciiTilde; + case XK_zabovedot: return Qt::Key_AsciiTilde; + case XK_Racute: return Qt::Key_AsciiTilde; + case XK_Abreve: return Qt::Key_AsciiTilde; + case XK_Lacute: return Qt::Key_AsciiTilde; + case XK_Cacute: return Qt::Key_AsciiTilde; + case XK_Ccaron: return Qt::Key_AsciiTilde; + case XK_Eogonek: return Qt::Key_AsciiTilde; + case XK_Ecaron: return Qt::Key_AsciiTilde; + case XK_Dcaron: return Qt::Key_AsciiTilde; + case XK_Dstroke: return Qt::Key_AsciiTilde; + case XK_Nacute: return Qt::Key_AsciiTilde; + case XK_Ncaron: return Qt::Key_AsciiTilde; + case XK_Odoubleacute: return Qt::Key_AsciiTilde; + case XK_Rcaron: return Qt::Key_AsciiTilde; + case XK_Uring: return Qt::Key_AsciiTilde; + case XK_Udoubleacute: return Qt::Key_AsciiTilde; + case XK_Tcedilla: return Qt::Key_AsciiTilde; + case XK_racute: return Qt::Key_AsciiTilde; + case XK_abreve: return Qt::Key_AsciiTilde; + case XK_lacute: return Qt::Key_AsciiTilde; + case XK_cacute: return Qt::Key_AsciiTilde; + case XK_ccaron: return Qt::Key_AsciiTilde; + case XK_eogonek: return Qt::Key_AsciiTilde; + case XK_ecaron: return Qt::Key_AsciiTilde; + case XK_dcaron: return Qt::Key_AsciiTilde; + case XK_dstroke: return Qt::Key_AsciiTilde; + case XK_nacute: return Qt::Key_AsciiTilde; + case XK_ncaron: return Qt::Key_AsciiTilde; + case XK_odoubleacute: return Qt::Key_AsciiTilde; + case XK_rcaron: return Qt::Key_AsciiTilde; + case XK_uring: return Qt::Key_AsciiTilde; + case XK_udoubleacute: return Qt::Key_AsciiTilde; + case XK_tcedilla: return Qt::Key_AsciiTilde; + case XK_abovedot: return Qt::Key_AsciiTilde; + case XK_Hstroke: return Qt::Key_AsciiTilde; + case XK_Hcircumflex: return Qt::Key_AsciiTilde; + case XK_Iabovedot: return Qt::Key_AsciiTilde; + case XK_Gbreve: return Qt::Key_AsciiTilde; + case XK_Jcircumflex: return Qt::Key_AsciiTilde; + case XK_hstroke: return Qt::Key_AsciiTilde; + case XK_hcircumflex: return Qt::Key_AsciiTilde; + case XK_idotless: return Qt::Key_AsciiTilde; + case XK_gbreve: return Qt::Key_AsciiTilde; + case XK_jcircumflex: return Qt::Key_AsciiTilde; + case XK_Cabovedot: return Qt::Key_AsciiTilde; + case XK_Ccircumflex: return Qt::Key_AsciiTilde; + case XK_Gabovedot: return Qt::Key_AsciiTilde; + case XK_Gcircumflex: return Qt::Key_AsciiTilde; + case XK_Ubreve: return Qt::Key_AsciiTilde; + case XK_Scircumflex: return Qt::Key_AsciiTilde; + case XK_cabovedot: return Qt::Key_AsciiTilde; + case XK_ccircumflex: return Qt::Key_AsciiTilde; + case XK_gabovedot: return Qt::Key_AsciiTilde; + case XK_gcircumflex: return Qt::Key_AsciiTilde; + case XK_ubreve: return Qt::Key_AsciiTilde; + case XK_scircumflex: return Qt::Key_AsciiTilde; + case XK_kra: return Qt::Key_AsciiTilde; + case XK_kappa: return Qt::Key_AsciiTilde; + case XK_Rcedilla: return Qt::Key_AsciiTilde; + case XK_Itilde: return Qt::Key_AsciiTilde; + case XK_Lcedilla: return Qt::Key_AsciiTilde; + case XK_Emacron: return Qt::Key_AsciiTilde; + case XK_Gcedilla: return Qt::Key_AsciiTilde; + case XK_Tslash: return Qt::Key_AsciiTilde; + case XK_rcedilla: return Qt::Key_AsciiTilde; + case XK_itilde: return Qt::Key_AsciiTilde; + case XK_lcedilla: return Qt::Key_AsciiTilde; + case XK_emacron: return Qt::Key_AsciiTilde; + case XK_gcedilla: return Qt::Key_AsciiTilde; + case XK_tslash: return Qt::Key_AsciiTilde; + case XK_ENG: return Qt::Key_AsciiTilde; + case XK_eng: return Qt::Key_AsciiTilde; + case XK_Amacron: return Qt::Key_AsciiTilde; + case XK_Iogonek: return Qt::Key_AsciiTilde; + case XK_Eabovedot: return Qt::Key_AsciiTilde; + case XK_Imacron: return Qt::Key_AsciiTilde; + case XK_Ncedilla: return Qt::Key_AsciiTilde; + case XK_Omacron: return Qt::Key_AsciiTilde; + case XK_Kcedilla: return Qt::Key_AsciiTilde; + case XK_Uogonek: return Qt::Key_AsciiTilde; + case XK_Utilde: return Qt::Key_AsciiTilde; + case XK_Umacron: return Qt::Key_AsciiTilde; + case XK_amacron: return Qt::Key_AsciiTilde; + case XK_iogonek: return Qt::Key_AsciiTilde; + case XK_eabovedot: return Qt::Key_AsciiTilde; + case XK_imacron: return Qt::Key_AsciiTilde; + case XK_ncedilla: return Qt::Key_AsciiTilde; + case XK_omacron: return Qt::Key_AsciiTilde; + case XK_kcedilla: return Qt::Key_AsciiTilde; + case XK_uogonek: return Qt::Key_AsciiTilde; + case XK_utilde: return Qt::Key_AsciiTilde; + case XK_umacron: return Qt::Key_AsciiTilde; + case XK_Wcircumflex: return Qt::Key_AsciiTilde; + case XK_wcircumflex: return Qt::Key_AsciiTilde; + case XK_Ycircumflex: return Qt::Key_AsciiTilde; + case XK_ycircumflex: return Qt::Key_AsciiTilde; + case XK_Babovedot: return Qt::Key_AsciiTilde; + case XK_babovedot: return Qt::Key_AsciiTilde; + case XK_Dabovedot: return Qt::Key_AsciiTilde; + case XK_dabovedot: return Qt::Key_AsciiTilde; + case XK_Fabovedot: return Qt::Key_AsciiTilde; + case XK_fabovedot: return Qt::Key_AsciiTilde; + case XK_Mabovedot: return Qt::Key_AsciiTilde; + case XK_mabovedot: return Qt::Key_AsciiTilde; + case XK_Pabovedot: return Qt::Key_AsciiTilde; + case XK_pabovedot: return Qt::Key_AsciiTilde; + case XK_Sabovedot: return Qt::Key_AsciiTilde; + case XK_sabovedot: return Qt::Key_AsciiTilde; + case XK_Tabovedot: return Qt::Key_AsciiTilde; + case XK_tabovedot: return Qt::Key_AsciiTilde; + case XK_Wgrave: return Qt::Key_AsciiTilde; + case XK_wgrave: return Qt::Key_AsciiTilde; + case XK_Wacute: return Qt::Key_AsciiTilde; + case XK_wacute: return Qt::Key_AsciiTilde; + case XK_Wdiaeresis: return Qt::Key_AsciiTilde; + case XK_wdiaeresis: return Qt::Key_AsciiTilde; + case XK_Ygrave: return Qt::Key_AsciiTilde; + case XK_ygrave: return Qt::Key_AsciiTilde; + case XK_OE: return Qt::Key_AsciiTilde; + case XK_oe: return Qt::Key_AsciiTilde; + case XK_Ydiaeresis: return Qt::Key_AsciiTilde;*/ + default: + qDebug() << "Unknown Key"; + } + qDebug() << " -- Simple Qt Map:" << (Qt::Key)(symbol); + qDebug() << " -- Key Sequence Map:" << QKeySequence(symbol); + qDebug() << " - Not implemented yet"; + return Qt::Key_unknown; +} + +NativeWindowSystem::MouseButton NativeWindowSystem::MouseToQt(int keycode){ + switch(keycode){ + case 1: + return NativeWindowSystem::LeftButton; + case 3: + return NativeWindowSystem::RightButton; + case 2: + return NativeWindowSystem::MidButton; + case 4: + return NativeWindowSystem::WheelUp; + case 5: + return NativeWindowSystem::WheelDown; + case 6: + return NativeWindowSystem::WheelLeft; + case 7: + return NativeWindowSystem::WheelRight; + case 8: + return NativeWindowSystem::BackButton; //Not sure if this is correct yet (1/27/17) + case 9: + return NativeWindowSystem::ForwardButton; //Not sure if this is correct yet (1/27/17) + default: + return NativeWindowSystem::NoButton; + } +} diff --git a/src-qt5/core/libLumina/NativeWindow.cpp b/src-qt5/core/libLumina/NativeWindow.cpp index 97131b52..94d39cb7 100644 --- a/src-qt5/core/libLumina/NativeWindow.cpp +++ b/src-qt5/core/libLumina/NativeWindow.cpp @@ -6,59 +6,96 @@ //=========================================== #include "NativeWindow.h" +#include <QDebug> + // === PUBLIC === NativeWindow::NativeWindow(WId id) : QObject(){ winid = id; - WIN = QWindow::fromWinId(winid); + frameid = 0; + dmgID = 0; } NativeWindow::~NativeWindow(){ hash.clear(); - //WIN->deleteLater(); //This class only deals with Native windows which were created outside the app - they need to be cleaned up outside the app too +} + +void NativeWindow::addFrameWinID(WId fid){ + frameid = fid; +} + +void NativeWindow::addDamageID(unsigned int dmg){ + dmgID = dmg; +} + +bool NativeWindow::isRelatedTo(WId tmp){ + return (relatedTo.contains(tmp) || winid == tmp || frameid == tmp); } WId NativeWindow::id(){ return winid; } -QWindow* NativeWindow::window(){ - return WIN; +WId NativeWindow::frameId(){ + return frameid; +} + +unsigned int NativeWindow::damageId(){ + return dmgID; } QVariant NativeWindow::property(NativeWindow::Property prop){ if(hash.contains(prop)){ return hash.value(prop); } + else if(prop == NativeWindow::RelatedWindows){ return QVariant::fromValue(relatedTo); } return QVariant(); //null variant } -void NativeWindow::setProperty(NativeWindow::Property prop, QVariant val){ - if(prop == NativeWindow::None || hash.value(prop)==val){ return; } - hash.insert(prop, val); +void NativeWindow::setProperty(NativeWindow::Property prop, QVariant val, bool force){ + if(prop == NativeWindow::RelatedWindows){ relatedTo = val.value< QList<WId> >(); } + else if(prop == NativeWindow::None || (!force && hash.value(prop)==val)){ return; } + else{ hash.insert(prop, val); } emit PropertiesChanged(QList<NativeWindow::Property>() << prop, QList<QVariant>() << val); } -void NativeWindow::setProperties(QList<NativeWindow::Property> props, QList<QVariant> vals){ +void NativeWindow::setProperties(QList<NativeWindow::Property> props, QList<QVariant> vals, bool force){ for(int i=0; i<props.length(); i++){ - if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this propertu - if(props[i] == NativeWindow::None || (hash.value(props[i]) == vals[i]) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value + if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property + if(props[i] == NativeWindow::None || (!force && (hash.value(props[i]) == vals[i])) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value hash.insert(props[i], vals[i]); } emit PropertiesChanged(props, vals); } -void NativeWindow::requestProperty(NativeWindow::Property prop, QVariant val){ - if(prop == NativeWindow::None || hash.value(prop)==val ){ return; } +void NativeWindow::requestProperty(NativeWindow::Property prop, QVariant val, bool force){ + if(prop == NativeWindow::None || prop == NativeWindow::RelatedWindows || (!force && hash.value(prop)==val) ){ return; } emit RequestPropertiesChange(winid, QList<NativeWindow::Property>() << prop, QList<QVariant>() << val); } -void NativeWindow::requestProperties(QList<NativeWindow::Property> props, QList<QVariant> vals){ +void NativeWindow::requestProperties(QList<NativeWindow::Property> props, QList<QVariant> vals, bool force){ //Verify/adjust inputs as needed for(int i=0; i<props.length(); i++){ if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property - if(props[i] == NativeWindow::None || hash.value(props[i])==vals[i] ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value + if(props[i] == NativeWindow::None || props[i] == NativeWindow::RelatedWindows || (!force && hash.value(props[i])==vals[i]) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value + /*if( (props[i] == NativeWindow::Visible || props[i] == NativeWindow::Active) && frameid !=0){ + //These particular properties needs to change the frame - not the window itself + emit RequestPropertiesChange(frameid, QList<NativeWindow::Property>() << props[i], QList<QVariant>() << vals[i]); + props.removeAt(i); vals.removeAt(i); i--; + }*/ } emit RequestPropertiesChange(winid, props, vals); } +QRect NativeWindow::geometry(){ + //Calculate the "full" geometry of the window + frame (if any) + QRect geom( hash.value(NativeWindow::GlobalPos).toPoint(), hash.value(NativeWindow::Size).toSize() ); + //Now adjust the window geom by the frame margins + QList<int> frame = hash.value(NativeWindow::FrameExtents).value< QList<int> >(); //Left,Right,Top,Bottom + qDebug() << "Calculate Geometry:" << geom << frame; + if(frame.length()==4){ + geom = geom.adjusted( -frame[0], -frame[2], frame[1], frame[3] ); + } + qDebug() << " - Total:" << geom; + return geom; +} // ==== PUBLIC SLOTS === void NativeWindow::requestClose(){ emit RequestClose(winid); diff --git a/src-qt5/core/libLumina/NativeWindow.h b/src-qt5/core/libLumina/NativeWindow.h index fbdf9e1b..47359b7d 100644 --- a/src-qt5/core/libLumina/NativeWindow.h +++ b/src-qt5/core/libLumina/NativeWindow.h @@ -5,8 +5,8 @@ // See the LICENSE file for full details //=========================================== // This is a container object for setting/announcing changes -// in a native window's properties. -// The WM will usually run the "setProperty" function on this object, +// in a native window's properties. +// The WM will usually run the "setProperty" function on this object, // and any other classes/widgets which watch this window can act appropriatly after-the-fact // Non-WM classes should use the "Request" signals to ask the WM to do something, and listen for changes later //=========================================== @@ -29,10 +29,10 @@ public: enum Action {A_MOVE, A_RESIZE, A_MINIMIZE, A_SHADE, A_STICK, A_MAX_VERT, A_MAX_HORZ, A_FULLSCREEN, A_CHANGE_DESKTOP, A_CLOSE, A_ABOVE, A_BELOW}; enum Property{ /*QVariant Type*/ - None, /*null*/ - MinSize, /*QSize*/ - MaxSize, /*QSize*/ - Size, /*QSize*/ + None=0, /*null*/ + MinSize, /*QSize*/ + MaxSize, /*QSize*/ + Size, /*QSize*/ GlobalPos, /*QPoint*/ Title, /*QString*/ ShortTitle, /*QString*/ @@ -41,31 +41,41 @@ public: Workspace, /*int*/ States, /*QList<NativeWindow::State> : Current state of the window */ WinTypes, /*QList<NativeWindow::Type> : Current type of window (typically does not change)*/ - WinActions, /*QList<NativeWindow::Action> : Current actions that the window allows (Managed/set by the WM)*/ - FrameExtents, /*QList<int> : [Left, Right, Top, Bottom] in pixels */ + WinActions, /*QList<NativeWindow::Action> : Current actions that the window allows (Managed/set by the WM)*/ + FrameExtents, /*QList<int> : [Left, Right, Top, Bottom] in pixels */ + RelatedWindows, /* QList<WId> - better to use the "isRelatedTo(WId)" function instead of reading this directly*/ Active, /*bool*/ Visible /*bool*/ }; static QList<NativeWindow::Property> allProperties(){ - //Return all the available properties (excluding "None") + //Return all the available properties (excluding "None" and "FrameExtents" (WM control only) ) QList<NativeWindow::Property> props; props << MinSize << MaxSize << Size << GlobalPos << Title << ShortTitle << Icon << Name << Workspace \ - << States << WinTypes << WinActions << Active << Visible; + << States << WinTypes << WinActions << RelatedWindows << Active << Visible; return props; }; NativeWindow(WId id); ~NativeWindow(); + void addFrameWinID(WId); + void addDamageID(unsigned int); + bool isRelatedTo(WId); + WId id(); - QWindow* window(); + WId frameId(); + unsigned int damageId(); + + //QWindow* window(); QVariant property(NativeWindow::Property); - void setProperty(NativeWindow::Property, QVariant); - void setProperties(QList<NativeWindow::Property>, QList<QVariant>); - void requestProperty(NativeWindow::Property, QVariant); - void requestProperties(QList<NativeWindow::Property>, QList<QVariant>); + void setProperty(NativeWindow::Property, QVariant, bool force = false); + void setProperties(QList<NativeWindow::Property>, QList<QVariant>, bool force = false); + void requestProperty(NativeWindow::Property, QVariant, bool force = false); + void requestProperties(QList<NativeWindow::Property>, QList<QVariant>, bool force = false); + + QRect geometry(); //this returns the "full" geometry of the window (window + frame) public slots: void requestClose(); //ask the app to close the window (may/not depending on activity) @@ -74,8 +84,10 @@ public slots: private: QHash <NativeWindow::Property, QVariant> hash; - QWindow *WIN; - WId winid; + //QWindow *WIN; + WId winid, frameid; + QList<WId> relatedTo; + unsigned int dmgID; signals: //General Notifications @@ -83,15 +95,23 @@ signals: void RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>); void WindowClosed(WId); void WindowNotResponding(WId); //will be sent out if a window does not respond to a ping request + void VisualChanged(); //Action Requests (not automatically emitted - typically used to ask the WM to do something) //Note: "WId" should be the NativeWindow id() void RequestClose(WId); //Close the window - void RequestKill(WId); //Kill the window/app (usually from being unresponsive) + void RequestKill(WId); //Kill the window/app (usually from being unresponsive) void RequestPing(WId); //Verify that the window is still active (such as not closing after a request - + void RequestReparent(WId, WId, QPoint); //client window, frame window, relative origin point in frame // System Tray Icon Embed/Unembed Requests //void RequestEmbed(WId, QWidget*); //void RequestUnEmbed(WId, QWidget*); }; + +// Declare the enumerations as Qt MetaTypes +Q_DECLARE_METATYPE(NativeWindow::Type); +Q_DECLARE_METATYPE(NativeWindow::Action); +Q_DECLARE_METATYPE(NativeWindow::State); +Q_DECLARE_METATYPE(NativeWindow::Property); + #endif diff --git a/src-qt5/core/libLumina/NativeWindow.pri b/src-qt5/core/libLumina/NativeWindow.pri index a5715287..c2ac0137 100644 --- a/src-qt5/core/libLumina/NativeWindow.pri +++ b/src-qt5/core/libLumina/NativeWindow.pri @@ -1,12 +1,17 @@ # Files QT *= x11extras -LIBS *= -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damage -lxcb-util -lXdamage +LIBS *= -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damage -lxcb-util -lxcb-keysyms -lXdamage SOURCES *= $${PWD}/NativeWindow.cpp \ - $${PWD}/NativeWindowSystem.cpp + $${PWD}/NativeWindowSystem.cpp \ + $${PWD}/NativeKeyToQt.cpp \ + $${PWD}/NativeEventFilter.cpp \ + $${PWD}/NativeEmbedWidget.cpp HEADERS *= $${PWD}/NativeWindow.h \ - $${PWD}/NativeWindowSystem.h + $${PWD}/NativeWindowSystem.h \ + $${PWD}/NativeEventFilter.h \ + $${PWD}/NativeEmbedWidget.h INCLUDEPATH *= $${PWD} diff --git a/src-qt5/core/libLumina/NativeWindowSystem.cpp b/src-qt5/core/libLumina/NativeWindowSystem.cpp index 36a0b7f0..e0f3fe91 100644 --- a/src-qt5/core/libLumina/NativeWindowSystem.cpp +++ b/src-qt5/core/libLumina/NativeWindowSystem.cpp @@ -14,6 +14,10 @@ #include <QDebug> #include <QApplication> #include <QScreen> +#include <QFont> +#include <QFontMetrics> +#include <QKeySequence> + //XCB Library includes #include <xcb/xcb.h> @@ -48,6 +52,22 @@ XCB_EVENT_MASK_FOCUS_CHANGE | \ XCB_EVENT_MASK_ENTER_WINDOW) +#define NORMAL_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_POINTER_MOTION | \ + XCB_EVENT_MASK_BUTTON_MOTION | \ + XCB_EVENT_MASK_EXPOSURE | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ + XCB_EVENT_MASK_ENTER_WINDOW| \ + XCB_EVENT_MASK_PROPERTY_CHANGE) + +inline void registerClientEvents(WId id){ + uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK}; + xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); +} + //Internal XCB private objects class class NativeWindowSystem::p_objects{ public: @@ -58,15 +78,21 @@ public: //Functions for setting up these objects as needed bool init_ATOMS(){ + xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH); + if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){ + qDebug() << "Error with XCB atom initializations"; + return false; + } + QStringList atoms; - atoms << "WM_TAKE_FOCUS" << "WM_DELETE_WINDOW" << "WM_PROTOCOLS" - << "WM_CHANGE_STATE" << "_NET_SYSTEM_TRAY_OPCODE" << "_NET_SYSTEM_TRAY_ORIENTATION" + atoms << "WM_TAKE_FOCUS" << "WM_DELETE_WINDOW" << "WM_PROTOCOLS" << "_NET_WM_WINDOW_OPACITY" + << "WM_CHANGE_STATE" << "_NET_SYSTEM_TRAY_OPCODE" << "_NET_SYSTEM_TRAY_ORIENTATION" << "_XEMBED" << "_NET_SYSTEM_TRAY_VISUAL" << QString("_NET_SYSTEM_TRAY_S%1").arg(QString::number(QX11Info::appScreen())); //Create all the requests for the atoms QList<xcb_intern_atom_reply_t*> reply; for(int i=0; i<atoms.length(); i++){ reply << xcb_intern_atom_reply(QX11Info::connection(), \ - xcb_intern_atom(QX11Info::connection(), 0, atoms[i].length(), atoms[i].toLocal8Bit()), NULL); + xcb_intern_atom(QX11Info::connection(), 0, atoms[i].length(), atoms[i].toLocal8Bit()), NULL); } //Now evaluate all the requests and save the atoms for(int i=0; i<reply.length(); i++){ //NOTE: this will always be the same length as the "atoms" list @@ -81,9 +107,19 @@ public: return (ATOMS.keys().length() == atoms.length()); } + WId getTransientFor(WId win){ + xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for_unchecked(QX11Info::connection(), win); + xcb_window_t trans; + if(1!= xcb_icccm_get_wm_transient_for_reply(QX11Info::connection(), cookie, &trans, NULL) ){ + return win; //error in fetching transient window ID (or none found) + }else{ + return trans; + } +} + bool register_wm(){ uint32_t value_list[1] = {ROOT_WIN_EVENT_MASK}; - xcb_generic_error_t *status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), root_window, XCB_CW_EVENT_MASK, value_list)); + xcb_generic_error_t *status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), root_window, XCB_CW_EVENT_MASK, value_list)); if(status!=0){ return false; } uint32_t params[] = {1}; wm_window = xcb_generate_id(QX11Info::connection()); //need a new ID @@ -97,7 +133,7 @@ public: //Also set this property on the child window (pointing to itself) xcb_ewmh_set_supporting_wm_check(&EWMH, wm_window, wm_window); //Now also setup the root event mask on the wm_window - status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), wm_window, XCB_CW_EVENT_MASK, value_list)); + status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), wm_window, XCB_CW_EVENT_MASK, value_list)); if(status!=0){ return false; } return true; } @@ -144,18 +180,18 @@ public: xcb_visualtype_t *type = xcb_aux_find_visual_by_attrs(root_screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32); if(type!=0){ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, tray_window, \ - ATOMS.value("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &type->visual_id); + ATOMS.value("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &type->visual_id); }else{ qWarning() << " - Could not set TrueColor visual for system tray"; } - + //Finally, send out an X event letting others know that the system tray is up and running xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.window = root_screen->root; event.type = EWMH.MANAGER; //MANAGER atom - event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; + event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; event.data.data32[1] = _NET_SYSTEM_TRAY_S; //_NET_SYSTEM_TRAY_S atom event.data.data32[2] = tray_window; event.data.data32[3] = 0; @@ -175,6 +211,7 @@ public: NativeWindowSystem::NativeWindowSystem() : QObject(){ obj = 0; pingTimer = 0; + screenLocked = false; } NativeWindowSystem::~NativeWindowSystem(){ @@ -200,7 +237,12 @@ bool NativeWindowSystem::start(){ if( !obj->init_ATOMS() ){ return false; } } //Done with private object init bool ok = obj->register_wm(); - if(ok){ ok = obj->start_system_tray(); } + if(ok){ + setRoot_supportedActions(); + ok = obj->start_system_tray(); + }else{ + qWarning() << "Could not register the WM"; + } return ok; } @@ -208,37 +250,30 @@ void NativeWindowSystem::stop(){ } -//Small simplification functions -Qt::Key NativeWindowSystem::KeycodeToQt(int keycode){ - return Qt::Key_unknown; -} - -NativeWindowSystem::MouseButton NativeWindowSystem::MouseToQt(int keycode){ - switch(keycode){ - case 1: - return NativeWindowSystem::LeftButton; - case 3: - return NativeWindowSystem::RightButton; - case 2: - return NativeWindowSystem::MidButton; - case 4: - return NativeWindowSystem::WheelUp; - case 5: - return NativeWindowSystem::WheelDown; - case 6: - return NativeWindowSystem::WheelLeft; - case 7: - return NativeWindowSystem::WheelRight; - case 8: - return NativeWindowSystem::BackButton; //Not sure if this is correct yet (1/27/17) - case 9: - return NativeWindowSystem::ForwardButton; //Not sure if this is correct yet (1/27/17) - default: - return NativeWindowSystem::NoButton; +// === PRIVATE === +NativeWindow* NativeWindowSystem::findWindow(WId id, bool checkRelated){ + //qDebug() << "Find Window:" << id; + for(int i=0; i<NWindows.length(); i++){ + if(id==NWindows[i]->id() || id==NWindows[i]->frameId() ){ return NWindows[i]; } + //if(checkRelated && NWindows[i]->isRelatedTo(id)){ return NWindows[i]; } + //else if(!checkRelated && id==NWindows[i]->id()){ return NWindows[i]; } + } + //Check to see if this is a transient for some other window + if(checkRelated){ + //WId tid = obj->getTransientFor(id); + //if(tid!=id){ return findWindow(tid, checkRelated); } //call it recursively as needed + //qDebug() << " -- Could not find Window!"; } + return 0; +} + +NativeWindow* NativeWindowSystem::findTrayWindow(WId id){ + for(int i=0; i<TWindows.length(); i++){ + if(TWindows[i]->isRelatedTo(id)){ return TWindows[i]; } + } + return 0; } -// === PRIVATE === void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props){ //Put the properties in logical groups as appropriate (some XCB calls return multiple properties) if(props.contains(NativeWindow::Title)){ @@ -255,7 +290,7 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native if(name.isEmpty()){ //_NET_WM_VISIBLE_NAME xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&obj->EWMH, win->id()); - if(cookie.sequence != 0){ + if(cookie.sequence != 0){ xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_visible_name_reply(&obj->EWMH, cookie, &data, NULL) ){ name = QString::fromUtf8(data.strings, data.strings_len); @@ -271,7 +306,7 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native xcb_icccm_get_text_property_reply_wipe(&reply); } } - win->setProperty(NativeWindow::Name, name); + win->setProperty(NativeWindow::Title, name); } //end TITLE property if(props.contains(NativeWindow::ShortTitle)){ @@ -288,7 +323,7 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native if(name.isEmpty()){ //_NET_WM_VISIBLE_ICON_NAME xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&obj->EWMH, win->id()); - if(cookie.sequence != 0){ + if(cookie.sequence != 0){ xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_visible_icon_name_reply(&obj->EWMH, cookie, &data, NULL) ){ name = QString::fromUtf8(data.strings, data.strings_len); @@ -337,7 +372,7 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native uint* dat = iter.data; //dat+=2; //remember the first 2 element offset for(int i=0; i<image.byteCount()/4; ++i, ++dat){ - ((uint*)image.bits())[i] = *dat; + ((uint*)image.bits())[i] = *dat; } icon.addPixmap(QPixmap::fromImage(image)); //layer this pixmap onto the icon //Now see if there are any more icons available @@ -350,7 +385,7 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native win->setProperty(NativeWindow::Icon, icon); } //end ICON property - if(props.contains(NativeWindow::MinSize) || props.contains(NativeWindow::MaxSize) + if(props.contains(NativeWindow::MinSize) || props.contains(NativeWindow::MaxSize) || props.contains(NativeWindow::Size) || props.contains(NativeWindow::GlobalPos) ){ //Try the ICCCM "Normal Hints" structure first (newer spec?) xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_normal_hints_unchecked(QX11Info::connection(), win->id()); @@ -401,41 +436,244 @@ void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< Native }*/ win->setProperty(NativeWindow::Workspace, wkspace); } + if(props.contains(NativeWindow::FrameExtents)){ + //Just assign default values to this - need to automate it later + //win->setProperty(NativeWindow::FrameExtents, QVariant::fromValue<QList<int> >(QList<int>() << 5 << 5 << 5+QFontMetrics(QFont()).height() << 5) ); + } + if(props.contains(NativeWindow::RelatedWindows)){ + WId orig = win->id(); + WId tid = obj->getTransientFor(orig); + QList<WId> list; + while(tid != orig){ + list << tid; + orig = tid; + tid = obj->getTransientFor(orig); + } + win->setProperty(NativeWindow::RelatedWindows, QVariant::fromValue(list)); + } + if(props.contains(NativeWindow::Visible)){ + xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), xcb_get_window_attributes(QX11Info::connection(), win->id()) , NULL); + if(attr != 0){ + win->setProperty(NativeWindow::Visible, attr->map_state == XCB_MAP_STATE_VIEWABLE); + free(attr); + } + } + if(props.contains(NativeWindow::WinTypes)){ + QList< NativeWindow::Type> types; + types << NativeWindow::T_NORMAL; //make this load appropriately later + win->setProperty(NativeWindow::WinTypes, QVariant::fromValue< QList<NativeWindow::Type> >(types) ); + } } +void NativeWindowSystem::ChangeWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props, QList<QVariant> vals){ + if(props.length() == 0 || vals.length()!=props.length() || win ==0 ){ return; } + //qDebug() << "Change Window Properties:" << props << vals; + if(props.contains(NativeWindow::Title)){ + + } + if(props.contains(NativeWindow::ShortTitle)){ + + } + if(props.contains(NativeWindow::Icon)){ + + } + if(props.contains(NativeWindow::Size) || props.contains(NativeWindow::GlobalPos) ){ + xcb_configure_window_value_list_t valList; + valList.x = 0; //Note that this is the relative position - should always be 0,0 relative to the embed widget + valList.y = 0; + QSize sz = win->property(NativeWindow::Size).toSize(); + if(props.contains(NativeWindow::Size)){ + sz = vals[ props.indexOf(NativeWindow::Size) ] .toSize(); + } + valList.width = sz.width(); + valList.height = sz.height(); + /*if(props.contains(NativeWindow::GlobalPos)){ + QPoint pt = vals[ props.indexOf(NativeWindow::GlobalPos) ] .toPoint(); + valList.x = pt.x(); + valList.y = pt.y(); + }else{ + valList.x = win->property(NativeWindow::GlobalPos).toPoint().x(); + valList.y = win->property(NativeWindow::GlobalPos).toPoint().y(); + }*/ + uint16_t mask = 0; + mask = mask | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; + qDebug() << "Configure window Geometry:" << sz; + xcb_configure_window_aux(QX11Info::connection(), win->id(), mask, &valList); + } + if(props.contains(NativeWindow::Name)){ + + } + if(props.contains(NativeWindow::Workspace)){ + int num = vals[ props.indexOf(NativeWindow::Workspace) ].toInt(); + xcb_ewmh_set_wm_desktop(&obj->EWMH, win->id(), (num<0 ? 0xFFFFFFFF : qAbs(num) ) ); + } + if(props.contains(NativeWindow::RelatedWindows)){ + + } + if(props.contains(NativeWindow::Visible)){ + //qDebug() << "Check Window Visibility:" << vals[ props.indexOf(NativeWindow::Visible) ]; + if( vals[ props.indexOf(NativeWindow::Visible) ].toBool() ){ + //qDebug() << " - Map it!"; + xcb_map_window(QX11Info::connection(), win->id()); + }else{ + //qDebug() << " - Unmap it!"; + xcb_unmap_window(QX11Info::connection(), win->id()); + } + } + if(props.contains(NativeWindow::Active)){ + //Only one window can be "Active" at a time - so only do anything if this window wants to be active + if(vals[props.indexOf(NativeWindow::Active)].toBool() ){ + xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), (win->frameId()==0 ?win->id() : win->frameId())); + //Also send the active window a message to take input focus + //Send the window a WM_TAKE_FOCUS message + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = win->id(); + event.type = obj->ATOMS["WM_PROTOCOLS"]; + event.data.data32[0] = obj->ATOMS["WM_TAKE_FOCUS"]; + event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = 0; + + xcb_send_event(QX11Info::connection(), 0, win->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); + xcb_flush(QX11Info::connection()); + } + } + +} // === PUBLIC SLOTS === -//These are the slots which are only used by the desktop system itself or the NativeWindowEventFilter -void NativeWindowSystem::RegisterVirtualRoot(WId){ +//These are the slots which are typically only used by the desktop system itself or the NativeEventFilter +void NativeWindowSystem::RegisterVirtualRoot(WId id){ + //Convert to XCB array + xcb_window_t array[1]; + array[0] = id; + //Set the property + xcb_ewmh_set_virtual_roots(&obj->EWMH, QX11Info::appScreen(), 1, array); +} + +void NativeWindowSystem::setRoot_supportedActions(){ +//NET_WM standards (ICCCM implied - no standard way to list those) + xcb_atom_t list[] = {obj->EWMH._NET_WM_NAME, + obj->EWMH._NET_WM_ICON, + obj->EWMH._NET_WM_ICON_NAME, + obj->EWMH._NET_WM_DESKTOP, + obj->ATOMS["_NET_WM_WINDOW_OPACITY"], + /*_NET_WINDOW_TYPE (and all the various types - 15 in total*/ + obj->EWMH._NET_WM_WINDOW_TYPE, obj->EWMH._NET_WM_WINDOW_TYPE_DESKTOP, obj->EWMH._NET_WM_WINDOW_TYPE_DOCK, + obj->EWMH._NET_WM_WINDOW_TYPE_TOOLBAR, obj->EWMH._NET_WM_WINDOW_TYPE_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_UTILITY, + obj->EWMH._NET_WM_WINDOW_TYPE_SPLASH, obj->EWMH._NET_WM_WINDOW_TYPE_DIALOG, obj->EWMH._NET_WM_WINDOW_TYPE_NORMAL, + obj->EWMH._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_POPUP_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_TOOLTIP, + obj->EWMH._NET_WM_WINDOW_TYPE_NOTIFICATION, obj->EWMH._NET_WM_WINDOW_TYPE_COMBO, obj->EWMH._NET_WM_WINDOW_TYPE_DND, + }; + xcb_ewmh_set_supported(&obj->EWMH, QX11Info::appScreen(), 20,list); +} +void NativeWindowSystem::setRoot_numberOfWorkspaces(QStringList names){ + if(names.isEmpty()){ names << "one"; } + //First set the overall number of workspaces + xcb_ewmh_set_number_of_desktops(&obj->EWMH, QX11Info::appScreen(), names.length()); + //Now set the names for the workspaces + //EWMH LIBRARY BROKEN - appears to be a mismatch in the function header (looking for a single char array, instead of a list of char arrays) + // Ken Moore - 6/27/17 + /* + char *array[ names.length() ]; + for(int i=0; i<names.length(); i++){array[i] = names[i].toUtf8().data(); } //Convert to an array of char arrays + xcb_ewmh_set_desktop_names(&obj->EWMH, QX11Info::appScreen(), names.length(), array); + */ +} + +void NativeWindowSystem::setRoot_currentWorkspace(int num){ + xcb_ewmh_set_current_desktop(&obj->EWMH, QX11Info::appScreen(), num); +} + +void NativeWindowSystem::setRoot_clientList(QList<WId> list, bool stackorder){ + //convert the QList into a generic array + xcb_window_t array[list.length()]; + for(int i=0; i<list.length(); i++){ array[i] = list[i]; } + if(stackorder){ + xcb_ewmh_set_client_list_stacking(&obj->EWMH, QX11Info::appScreen(), list.length(), array); + }else{ + xcb_ewmh_set_client_list(&obj->EWMH, QX11Info::appScreen(), list.length(), array); + } +} + +void NativeWindowSystem::setRoot_desktopGeometry(QRect geom){ + //This one is a combo function + // This will set the "DESKTOP_VIEWPORT" property (point) + // as well as the "DESKTOP_GEOMETRY" property (size) + //Turn the QList into xcb_ewmh_coordinates_t* + xcb_ewmh_coordinates_t array[1]; + array[0].x=geom.x(); array[0].y=geom.y(); + //Now set the property + xcb_ewmh_set_desktop_viewport(&obj->EWMH, QX11Info::appScreen(), 1, array); + xcb_ewmh_set_desktop_geometry(&obj->EWMH, QX11Info::appScreen(), geom.width(), geom.height()); +} + +void NativeWindowSystem::setRoot_desktopWorkarea(QList<QRect> list){ + //Convert to the XCB/EWMH data structures + xcb_ewmh_geometry_t array[list.length()]; + for(int i=0; i<list.length(); i++){ + array[i].x = list[i].x(); array[i].y = list[i].y(); + array[i].width = list[i].width(); array[i].height = list[i].height(); + } + //Now set the property + xcb_ewmh_set_workarea(&obj->EWMH, QX11Info::appScreen(), list.length(), array); +} + +void NativeWindowSystem::setRoot_activeWindow(WId win){ + xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win); + //Also send the active window a message to take input focus + //Send the window a WM_TAKE_FOCUS message + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = win; + event.type = obj->ATOMS["WM_PROTOCOLS"]; + event.data.data32[0] = obj->ATOMS["WM_TAKE_FOCUS"]; + event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = 0; + + xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); + xcb_flush(QX11Info::connection()); +} + +int NativeWindowSystem::currentWorkspace(){ + xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop_unchecked(&obj->EWMH, QX11Info::appScreen()); + uint32_t num = 0; + if(1==xcb_ewmh_get_current_desktop_reply(&obj->EWMH, cookie, &num, NULL) ){ + return num; + }else{ + return 0; + } } //NativeWindowEventFilter interactions void NativeWindowSystem::NewWindowDetected(WId id){ //Make sure this can be managed first - if(findWindow(id) != 0){ return; } //already managed + if(findWindow(id, false) != 0){ qDebug() << "Window Already Managed!!!!"; findWindow(id,false)->setProperty(NativeWindow::Visible, true, true); return; } //already managed xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), id); xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL); if(attr == 0){ return; } //could not get attributes of window if(attr->override_redirect){ free(attr); return; } //window has override redirect set (do not manage) free(attr); - //Register for events from this window - #define NORMAL_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ - XCB_EVENT_MASK_BUTTON_RELEASE | \ - XCB_EVENT_MASK_POINTER_MOTION | \ - XCB_EVENT_MASK_BUTTON_MOTION | \ - XCB_EVENT_MASK_EXPOSURE | \ - XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ - XCB_EVENT_MASK_ENTER_WINDOW) - - uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK}; - xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); //Now go ahead and create/populate the container for this window NativeWindow *win = new NativeWindow(id); + //Register for events from this window + registerClientEvents(win->id()); NWindows << win; UpdateWindowProperties(win, NativeWindow::allProperties()); + qDebug() << "New Window [& associated ID's]:" << win->id() << win->property(NativeWindow::RelatedWindows); + //Now setup the connections with this window + connect(win, SIGNAL(RequestClose(WId)), this, SLOT(RequestClose(WId)) ); + connect(win, SIGNAL(RequestKill(WId)), this, SLOT(RequestKill(WId)) ); + connect(win, SIGNAL(RequestPing(WId)), this, SLOT(RequestPing(WId)) ); + connect(win, SIGNAL(RequestReparent(WId, WId, QPoint)), this, SLOT(RequestReparent(WId, WId, QPoint)) ); + connect(win, SIGNAL(RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>)), this, SLOT(RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>)) ); emit NewWindowAvailable(win); } @@ -457,7 +695,7 @@ void NativeWindowSystem::NewTrayWindowDetected(WId id){ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ XCB_EVENT_MASK_ENTER_WINDOW) - + uint32_t value_list[1] = {TRAY_WIN_EVENT_MASK}; xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list); //Now go ahead and create/populate the container for this window @@ -468,30 +706,66 @@ void NativeWindowSystem::NewTrayWindowDetected(WId id){ } void NativeWindowSystem::WindowCloseDetected(WId id){ - NativeWindow *win = findWindow(id); + NativeWindow *win = findWindow(id, false); + qDebug() << "Got Window Closed" << id << win; + //qDebug() << "Old Window List:" << NWindows.length(); if(win!=0){ NWindows.removeAll(win); + //RequestReparent(id, QX11Info::appRootWindow(), QPoint(0,0)); win->emit WindowClosed(id); - win->deleteLater(); + qDebug() << "Visible Window Closed!!!"; + //win->deleteLater(); }else{ win = findTrayWindow(id); if(win!=0){ TWindows.removeAll(win); win->emit WindowClosed(id); - win->deleteLater(); + win->deleteLater(); } } + //qDebug() << " - Now:" << NWindows.length(); } void NativeWindowSystem::WindowPropertyChanged(WId id, NativeWindow::Property prop){ - //NOTE: This is triggered by the NativeWindowEventFilter - not by changes to the NativeWindow objects themselves - NativeWindow *win = findWindow(id); + //NOTE: This is triggered by the NativeEventFilter - not by changes to the NativeWindow objects themselves + NativeWindow *win = findWindow(id, prop!=NativeWindow::Visible); if(win==0){ win = findTrayWindow(id); } if(win!=0){ UpdateWindowProperties(win, QList<NativeWindow::Property>() << prop); } } +void NativeWindowSystem::WindowPropertyChanged(WId id, NativeWindow::Property prop, QVariant val){ + NativeWindow *win = findWindow(id,prop!=NativeWindow::Visible); + if(win==0){ win = findTrayWindow(id); } + if(win!=0){ + win->setProperty(prop, val); + } +} + +void NativeWindowSystem::WindowPropertiesChanged(WId id, QList<NativeWindow::Property> props, QList<QVariant> vals){ + NativeWindow *win = findWindow(id); + if(win==0){ win = findTrayWindow(id); } + if(win!=0){ + for(int i=0; i<props.length() && i<vals.length(); i++){ win->setProperty(props[i], vals[i]); } + } +} + +void NativeWindowSystem::RequestPropertyChange(WId id, NativeWindow::Property prop, QVariant val){ + //This is just a simplified version of the multiple-property function + RequestPropertiesChange(id, QList<NativeWindow::Property>() << prop, QList<QVariant>() << val); +} + +void NativeWindowSystem::RequestPropertiesChange(WId win, QList<NativeWindow::Property> props, QList<QVariant> vals){ + //Find the window object associated with this id + bool istraywin = false; //just in case we care later if it is a tray window or a regular window + NativeWindow *WIN = findWindow(win); + if(WIN==0){ istraywin = true; WIN = findTrayWindow(win); } + if(WIN==0){ return; } //invalid window ID - no longer available + //Now make any changes as needed + ChangeWindowProperties(WIN, props, vals); +} + void NativeWindowSystem::GotPong(WId id){ if(waitingForPong.contains(id)){ waitingForPong.remove(id); @@ -499,56 +773,40 @@ void NativeWindowSystem::GotPong(WId id){ if(waitingForPong.isEmpty() && pingTimer!=0){ pingTimer->stop(); } } -/*void NativeWindowSystem::NewKeyPress(int keycode, WId win){ +void NativeWindowSystem::NewKeyPress(int keycode, WId win){ emit NewInputEvent(); + if(screenLocked){ return; } + Qt::Key key = KeycodeToQt(keycode); + if(key!=Qt::Key_unknown){ emit KeyPressDetected(win, key); } } void NativeWindowSystem::NewKeyRelease(int keycode, WId win){ emit NewInputEvent(); - //Convert the native button code into a Qt keycode - //Qt::Key key = keycode; //TODO - //emit KeyReleaseDetected( key, win); + if(screenLocked){ return; } + Qt::Key key = KeycodeToQt(keycode); + if(key!=Qt::Key_unknown){ emit KeyReleaseDetected(win, key); } } void NativeWindowSystem::NewMousePress(int buttoncode, WId win){ emit NewInputEvent(); - //Convert the native button code into a Qt mouse button code - Qt::MouseButton button; - switch(buttoncode){ - case 1: - button = Qt::LeftButton ; break; - case 2: - button = Qt::MiddleButton ; break; - case 3: - button = Qt::RightButton ; break; - case 4: - button = Qt::LeftButton ; break; - default: - return; //Unhandled button - } - emit MousePressDetected(button, win); + if(screenLocked){ return; } + emit MousePressDetected(win, MouseToQt(buttoncode)); } void NativeWindowSystem::NewMouseRelease(int buttoncode, WId win){ emit NewInputEvent(); - //Convert the native button code into a Qt mouse button code - Qt::MouseButton button; - switch(buttoncode){ - case 1: - button = Qt::LeftButton ; break; - case 2: - button = Qt::MiddleButton ; break; - case 3: - button = Qt::RightButton ; break; - case 4: - button = Qt::LeftButton ; break; - default: - return; //Unhandled button - } - emit MouseReleaseDetected(button, win); -}*/ + if(screenLocked){ return; } + emit MouseReleaseDetected(win, MouseToQt(buttoncode)); +} void NativeWindowSystem::CheckDamageID(WId win){ + for(int i=0; i<NWindows.length(); i++){ + if(NWindows[i]->damageId() == win || NWindows[i]->id() == win || NWindows[i]->frameId()==win){ + NWindows[i]->emit VisualChanged(); + //qDebug() << "Got DAMAGE Event"; + return; + } + } NativeWindow *WIN = findTrayWindow(win); if(WIN!=0){ UpdateWindowProperties(WIN, QList<NativeWindow::Property>() << NativeWindow::Icon); @@ -557,15 +815,6 @@ void NativeWindowSystem::CheckDamageID(WId win){ // === PRIVATE SLOTS === //These are the slots which are built-in and automatically connected when a new NativeWindow is created -void NativeWindowSystem::RequestPropertiesChange(WId win, QList<NativeWindow::Property> props, QList<QVariant> vals){ - //Find the window object associated with this id - bool istraywin = false; //just in case we care later if it is a tray window or a regular window - NativeWindow *WIN = findWindow(win); - if(WIN==0){ istraywin = true; WIN = findTrayWindow(win); } - if(WIN==0){ return; } //invalid window ID - no longer available - //Now make any changes as needed - -} void NativeWindowSystem::RequestClose(WId win){ //Send the window a WM_DELETE_WINDOW message @@ -598,3 +847,53 @@ void NativeWindowSystem::RequestPing(WId win){ } pingTimer->start(); } + +void NativeWindowSystem::RequestReparent(WId win, WId container, QPoint relorigin){ + NativeWindow *WIN = findWindow(win); + if(WIN==0){ return; } //could not find corresponding window structure +//Reparent the window into the container + xcb_reparent_window(QX11Info::connection(), win, container, relorigin.x(), relorigin.y()); + //xcb_map_window(QX11Info::connection(), win); + + //Now send the embed event to the app + //qDebug() << " - send _XEMBED event"; + xcb_client_message_event_t event; + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = win; + event.type = obj->ATOMS["_XEMBED"]; //_XEMBED + event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; + event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY + event.data.data32[2] = 0; + event.data.data32[3] = container; //WID of the container + event.data.data32[4] = 0; + + xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); + + //Now setup any redirects and return + //this->SelectInput(win, true); //Notify of structure changes + registerClientEvents(win); + //xcb_composite_redirect_window(QX11Info::connection(), win, XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]); + + //Now map the window (will be a transparent child of the container) + xcb_map_window(QX11Info::connection(), win); + xcb_map_window(QX11Info::connection(), container); + //Now create/register the damage handler + // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore) + // -- Retested 6/29/17 (no change) Ken Moore + //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer + //xcb_damage_create(QX11Info::connection(), dmgID, win, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself). + Damage dmgID = XDamageCreate(QX11Info::display(), win, XDamageReportRawRectangles); + WIN->addDamageID( (uint) dmgID); //save this for later + //qDebug() << " - Done"; + //return ( (uint) dmgID ); +} +/* + xcb_reparent_window(QX11Info::connection(), client, parent, relorigin.x(), relorigin.y()); + + //Now ensure that we still get event for these windows + registerClientEvents(client); //make sure we re-do this after reparenting + registerClientEvents(parent); + xcb_map_window(QX11Info::connection(), parent); +}*/ diff --git a/src-qt5/core/libLumina/NativeWindowSystem.h b/src-qt5/core/libLumina/NativeWindowSystem.h index 59e54ca4..97208c2f 100644 --- a/src-qt5/core/libLumina/NativeWindowSystem.h +++ b/src-qt5/core/libLumina/NativeWindowSystem.h @@ -14,6 +14,7 @@ #include "NativeWindow.h" #include <QDateTime> #include <QTimer> +#include <QDebug> class NativeWindowSystem : public QObject{ Q_OBJECT @@ -22,21 +23,11 @@ private: QList<NativeWindow*> TWindows; //Simplifications to find an already-created window object - NativeWindow* findWindow(WId id){ - for(int i=0; i<NWindows.length(); i++){ - if(id==NWindows[i]->id()){ return NWindows[i]; } - } - return 0; - } + NativeWindow* findWindow(WId id, bool checkRelated = true); - NativeWindow* findTrayWindow(WId id){ - for(int i=0; i<TWindows.length(); i++){ - if(id==TWindows[i]->id()){ return TWindows[i]; } - } - return 0; - } + NativeWindow* findTrayWindow(WId id); - //Now define a simple private_objects class so that each implementation + //Now define a simple private_objects class so that each implementation // has a storage container for defining/placing private objects as needed class p_objects; p_objects* obj; @@ -58,12 +49,16 @@ private: } } - // Since some properties may be easier to update in bulk + // Since some properties may be easier to update in bulk // let the native system interaction do them in whatever logical groups are best void UpdateWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props); + void ChangeWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props, QList<QVariant> vals); + + //Generic private variables + bool screenLocked; public: - enum Property{ None, CurrentWorkspace, Workspaces, VirtualRoots, WorkAreas }; + //enum Property{ None, CurrentWorkspace, Workspaces, VirtualRoots, WorkAreas }; enum MouseButton{NoButton, LeftButton, RightButton, MidButton, BackButton, ForwardButton, TaskButton, WheelUp, WheelDown, WheelLeft, WheelRight}; NativeWindowSystem(); @@ -84,11 +79,25 @@ public: public slots: //These are the slots which are typically only used by the desktop system itself or the NativeWindowEventFilter - //RootWindow interactions + //This is called by the lock screen to keep the NWS aware of the current status + // it is **NOT** the function to call for the user to actually lock the session (that is in the screensaver/lockscreen class) + void ScreenLockChanged(bool lock){ + screenLocked = lock; + } + + //Root Window property registrations void RegisterVirtualRoot(WId); + void setRoot_supportedActions(); + void setRoot_numberOfWorkspaces(QStringList names); + void setRoot_currentWorkspace(int); + void setRoot_clientList(QList<WId>, bool stackorder = false); + void setRoot_desktopGeometry(QRect); + void setRoot_desktopWorkarea(QList<QRect>); + void setRoot_activeWindow(WId); + + // - Workspaces + int currentWorkspace(); //void GoToWorkspace(int); - //void RegisterWorkspaces(QStringList); //Names of workspaces, in ascending order - //void RegisterKnownInteractions(); //NativeWindowEventFilter interactions @@ -96,29 +105,33 @@ public slots: void NewTrayWindowDetected(WId); //will automatically create the new NativeWindow object void WindowCloseDetected(WId); //will update the lists and make changes if needed void WindowPropertyChanged(WId, NativeWindow::Property); //will rescan the window and update the object as needed + void WindowPropertyChanged(WId, NativeWindow::Property, QVariant); //will save that property/value to the right object + void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>); + void RequestPropertyChange(WId, NativeWindow::Property, QVariant); + void RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>); void GotPong(WId); -/* void NewKeyPress(int keycode, WId win = 0); + void NewKeyPress(int keycode, WId win = 0); void NewKeyRelease(int keycode, WId win = 0); void NewMousePress(int buttoncode, WId win = 0); - void NewMouseRelease(int buttoncode, WId win = 0);*/ + void NewMouseRelease(int buttoncode, WId win = 0); void CheckDamageID(WId); private slots: //These are the slots which are built-in and automatically connected when a new NativeWindow is created - void RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>); void RequestClose(WId); void RequestKill(WId); void RequestPing(WId); + void RequestReparent(WId, WId, QPoint); //client, parent, relative origin point in parent signals: void NewWindowAvailable(NativeWindow*); void NewTrayWindowAvailable(NativeWindow*); void NewInputEvent(); //a mouse or keypress was detected (lock-state independent); - void KeyPressDetected(Qt::Key, WId); //only emitted if lockstate = false - void KeyReleaseDetected(Qt::Key, WId); //only emitted if lockstate = false - void MousePressDetected(Qt::MouseButton, WId); //only emitted if lockstate = false - void MouseReleaseDetected(Qt::MouseButton, WId); //only emitted if lockstate = false + void KeyPressDetected(WId, Qt::Key); //only emitted if lockstate = false + void KeyReleaseDetected(WId, Qt::Key); //only emitted if lockstate = false + void MousePressDetected(WId, NativeWindowSystem::MouseButton); //only emitted if lockstate = false + void MouseReleaseDetected(WId, NativeWindowSystem::MouseButton); //only emitted if lockstate = false }; diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp index 41c04ee1..9ef6464e 100644 --- a/src-qt5/core/libLumina/RootSubWindow.cpp +++ b/src-qt5/core/libLumina/RootSubWindow.cpp @@ -8,9 +8,12 @@ #include <QDebug> #include <QApplication> #include <QVBoxLayout> -#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QTimer> + +#define WIN_BORDER 5 -#define WIN_BORDER 3 +#include <LIconCache.h> // === PUBLIC === RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){ @@ -19,15 +22,18 @@ RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){ //Create the QWindow and QWidget containers for the window WIN = win; closing = false; - WinWidget = QWidget::createWindowContainer( WIN->window(), this); initWindowFrame(); - LoadProperties( NativeWindow::allProperties() ); //Hookup the signals/slots connect(WIN, SIGNAL(PropertiesChanged(QList<NativeWindow::Property>, QList<QVariant>)), this, SLOT(propertiesChanged(QList<NativeWindow::Property>, QList<QVariant>))); + WinWidget->embedWindow(WIN); + //qDebug() << "[NEW WINDOW]" << WIN->id() << WinWidget->winId() << this->winId(); + activeState = RootSubWindow::Normal; + LoadAllProperties(); } RootSubWindow::~RootSubWindow(){ - + //qDebug() << "Visible Window Destroyed"; + WIN->deleteLater(); } WId RootSubWindow::id(){ @@ -41,41 +47,57 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset //above the frame itself - need to figure out which quadrant it is in (8-directions) if(pt.y() < WIN_BORDER){ //One of the top options - if(pt.x() < WIN_BORDER){ + if(pt.x() < this->width()/5){ if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner return ResizeTopLeft; - }else if(pt.x() > (this->width()-WIN_BORDER)){ + }else if(pt.x() > (this->width()*4.0/5.0)){ if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(pt.y()); } //difference from top-right corner return ResizeTopRight; - }else{ + }else{ if(setoffset){ offset.setX(0); offset.setY(pt.y()); } //difference from top edge (X does not matter) - return ResizeTop; - } + return ResizeTop; + } }else if(pt.y() > (this->height()-WIN_BORDER) ){ //One of the bottom options - if(pt.x() < WIN_BORDER){ + if(pt.x() < this->width()/5){ if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner return ResizeBottomLeft; - }else if(pt.x() > (this->width()-WIN_BORDER)){ + }else if(pt.x() > (this->width()*4.0/5.0)){ if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner return ResizeBottomRight; - }else{ + }else{ if(setoffset){ offset.setX(0); offset.setY(this->height() - pt.y()); } //difference from bottom edge (X does not matter) - return ResizeBottom; - } - }else{ - //One of the side options - if(pt.x() < WIN_BORDER){ + return ResizeBottom; + } + }else if(pt.x() < WIN_BORDER){ + //Left side options + if(pt.y() < this->height()/5){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner + return ResizeTopLeft; + }else if(pt.y() > (this->height()*4.0/5.0)){ + if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner + return ResizeBottomLeft; + }else{ if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter) return ResizeLeft; - }else if(pt.x() > (this->width()-WIN_BORDER) ){ + } + }else if(pt.x() > (this->width()-WIN_BORDER) ){ + //Right side options + if(pt.y() < this->height()/5){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(pt.y()); } //difference from top-right corner + return ResizeTopRight; + }else if(pt.y() > (this->height()*4.0/5.0)){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner + return ResizeBottomRight; + }else{ if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(0); } //difference from right edge (Y does not matter) return ResizeRight; - }else{ - return Normal; } + }else{ + return Normal; } } + //if it gets this far just return normal return Normal; } @@ -112,7 +134,7 @@ void RootSubWindow::setMouseCursor(ModState state, bool override){ break; case ResizeTopLeft: shape = Qt::SizeFDiagCursor; - break; + break; } if(override){ QApplication::setOverrideCursor(QCursor(shape)); @@ -123,31 +145,48 @@ void RootSubWindow::setMouseCursor(ModState state, bool override){ } void RootSubWindow::initWindowFrame(){ + //qDebug() << "Create RootSubWindow Frame"; + this->setContentsMargins(0,0,0,0); mainLayout = new QVBoxLayout(this); - titleBar = new QHBoxLayout(this); + mainLayout->setContentsMargins(0,0,0,0); + titleBar = new QWidget(this); + titleBar->setContentsMargins(0,0,0,0); + titleBarL = new QHBoxLayout(titleBar); + titleBarL->setContentsMargins(0,0,0,0); closeB = new QToolButton(this); maxB = new QToolButton(this); minB = new QToolButton(this); otherB = new QToolButton(this); + anim = new QPropertyAnimation(this); + anim->setTargetObject(this); + anim->setDuration(200); //1/5 second (appx) + connect(anim, SIGNAL(finished()), this, SLOT(animFinished()) ); titleLabel = new QLabel(this); titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); otherM = new QMenu(this); //menu of other actions otherB->setMenu(otherM); otherB->setPopupMode(QToolButton::InstantPopup); otherB->setAutoRaise(true); + WinWidget = new NativeEmbedWidget(this); connect(closeB, SIGNAL(clicked()), this, SLOT(triggerClose()) ); connect(maxB, SIGNAL(clicked()), this, SLOT(toggleMaximize()) ); connect(minB, SIGNAL(clicked()), this, SLOT(toggleMinimize()) ); //Now assemble the frame layout based on the current settings - this->setLayout(mainLayout); - titleBar->addWidget(otherB); - titleBar->addWidget(titleLabel); - titleBar->addWidget(minB); - titleBar->addWidget(maxB); - titleBar->addWidget(closeB); + titleBarL->addWidget(otherB); + titleBarL->addWidget(titleLabel); + titleBarL->addWidget(minB); + titleBarL->addWidget(maxB); + titleBarL->addWidget(closeB); WinWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - mainLayout->addLayout(titleBar); + titleBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + mainLayout->addWidget(titleBar); mainLayout->addWidget(WinWidget); + //Setup the cursors for the buttons + closeB->setCursor(Qt::ArrowCursor); + minB->setCursor(Qt::ArrowCursor); + maxB->setCursor(Qt::ArrowCursor); + otherM->setCursor(Qt::ArrowCursor); + titleLabel->setCursor(Qt::ArrowCursor); //Now all the stylesheet options this->setObjectName("WindowFrame"); closeB->setObjectName("Button_Close"); @@ -155,36 +194,82 @@ void RootSubWindow::initWindowFrame(){ maxB->setObjectName("Button_Maximize"); otherM->setObjectName("Menu_Actions"); titleLabel->setObjectName("Label_Title"); - this->setStyleSheet("QWidget#WindowFrame{background-color: darkblue;} QWidget#Label_Title{background-color: transparent; color: white; }"); + this->setStyleSheet("QFrame#WindowFrame{background-color: rgba(0,0,0,125)} QWidget#Label_Title{background-color: transparent; color: white; } QToolButton{background-color: transparent; border: 1px solid transparent; border-radius: 3px; } QToolButton::hover{background-color: rgba(255,255,255,150); } QToolButton::pressed{ background-color: white; } QToolButton::menu-arrow{ image: none; }"); //And adjust the margins - mainLayout->setContentsMargins(WIN_BORDER,WIN_BORDER,WIN_BORDER,WIN_BORDER); mainLayout->setSpacing(0); - titleBar->setSpacing(1); - titleBar->setContentsMargins(0,0,0,0); + titleBarL->setSpacing(1); + this->setFrameStyle(QFrame::NoFrame); + this->setLineWidth(0); + this->setMidLineWidth(0); + this->setFrameRect(QRect(0,0,0,0)); + + //Setup the timer object to syncronize info + moveTimer = new QTimer(this); + moveTimer->setSingleShot(true); + moveTimer->setInterval(100); //1/10 second + connect(moveTimer, SIGNAL(timeout()), WinWidget, SLOT(resyncWindow()) ); + + //Now load the icons for the button + LIconCache::instance()->loadIcon(closeB, "window-close"); + LIconCache::instance()->loadIcon(maxB, "window-maximize"); + LIconCache::instance()->loadIcon(minB, "window-minimize"); + LIconCache::instance()->loadIcon(otherB, "list"); +} + +void RootSubWindow::enableFrame(bool on){ + //Make the individual frame elements visible as needed + if(on){ this->setContentsMargins(WIN_BORDER,WIN_BORDER,WIN_BORDER,WIN_BORDER); }//default border + else{ this->setContentsMargins(0, 0, 0, 0); } + titleBar->setVisible(on); + //And now calculate/save the frame extents + QList<int> extents; extents << 0 << 0 << 0 << 0; //left, right, top, bottom + if(on){ + extents[0] = WIN_BORDER; + extents[1] = WIN_BORDER; + extents[2] = WIN_BORDER + titleBar->height(); + extents[3] = WIN_BORDER; + } + //qDebug() << "SET FRAME EXTENTS:" << extents; + WIN->requestProperty(NativeWindow::FrameExtents, QVariant::fromValue< QList<int> >(extents) ); //save on raw window itself + WIN->setProperty(NativeWindow::FrameExtents, QVariant::fromValue< QList<int> >(extents) ); //save to structure now } void RootSubWindow::LoadProperties( QList< NativeWindow::Property> list){ QList<QVariant> vals; + //Always ensure that visibility changes are evaluated last + bool addvisible = false; for(int i=0; i<list.length(); i++){ + if(list[i] == NativeWindow::Visible){ list.removeAt(i); i--; addvisible = true; continue; } vals << WIN->property(list[i]); } + //if(addvisible){ list << NativeWindow::Visible; vals << WIN->property(NativeWindow::Visible); } propertiesChanged(list, vals); } // === PUBLIC SLOTS === void RootSubWindow::clientClosed(){ - qDebug() << "Client Closed"; + //qDebug() << "Client Closed"; closing = true; - this->close(); + if(anim->state()!=QAbstractAnimation::Running){ this->close(); } +} + +void RootSubWindow::LoadAllProperties(){ + QList< NativeWindow::Property> list; + list << NativeWindow::WinTypes << NativeWindow::WinActions << NativeWindow::States + << NativeWindow::MinSize << NativeWindow::MaxSize << NativeWindow::Title << NativeWindow::ShortTitle + << NativeWindow::Icon << NativeWindow::Size << NativeWindow::GlobalPos << NativeWindow::Visible << NativeWindow::Active; + LoadProperties(list); + WIN->requestProperty(NativeWindow::Visible, true); } //Button Actions - public so they can be tied to key shortcuts and stuff as well void RootSubWindow::toggleMinimize(){ - + WIN->setProperty(NativeWindow::Visible, false); + QTimer::singleShot(2000, this, SLOT(toggleMaximize()) ); } void RootSubWindow::toggleMaximize(){ - + WIN->setProperty(NativeWindow::Visible, true); } void RootSubWindow::triggerClose(){ @@ -219,26 +304,62 @@ void RootSubWindow::startResizing(){ } - - // === PRIVATE SLOTS === void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList<QVariant> vals){ for(int i=0; i<props.length() && i<vals.length(); i++){ - if(vals[i].isNull()){ return; } //not the same as a default/empty value - the property has just not been set yet - //qDebug() << "Set Window Property:" << props[i] << vals[i]; + if(vals[i].isNull()){ continue; } //not the same as a default/empty value - the property has just not been set yet + //qDebug() << "RootSubWindow: Property Changed:" << props[i] << vals[i]; switch(props[i]){ case NativeWindow::Visible: - if(vals[i].toBool()){ this->show(); } - else{ this->hide(); } + //qDebug() << "Got Visibility Change:" << vals[i] << this->geometry() << WIN->geometry(); + if(vals[i].toBool()){ + if(lastGeom.isNull()){ animResetProp = this->geometry(); } + else{ animResetProp = lastGeom; } + anim->setPropertyName("geometry"); + anim->setStartValue( QRect(animResetProp.toRect().center(), QSize(0,0)) ); + anim->setEndValue(animResetProp); + this->setGeometry( anim->startValue().toRect() ); //ensure the window is the initial geom before it becomes visible + anim->start(); + this->show(); + }else{ + animResetProp = this->geometry(); //hide event - should already be the right geom + lastGeom = this->geometry(); + anim->setPropertyName("geometry"); + anim->setStartValue(this->geometry()); + anim->setEndValue( QRect(this->geometry().center(), QSize(0,0) ) ); + anim->start(); + QTimer::singleShot(anim->duration(), this, SLOT(hide()) ); + } break; case NativeWindow::Title: titleLabel->setText(vals[i].toString()); break; case NativeWindow::Icon: - otherB->setIcon(vals[i].value< QIcon>()); + //qDebug() << "Got Icon Change:" << vals[i]; + if(vals[i].value<QIcon>().isNull() ){ LIconCache::instance()->loadIcon(otherB, "list"); } + else{ otherB->setIcon(vals[i].value<QIcon>()); } + break; + case NativeWindow::GlobalPos: + //qDebug() << "Got Global Pos:" << this->pos() << WinWidget->mapToGlobal(QPoint(0,0)) << WIN->geometry().topLeft() << vals[i].toPoint(); + if(activeState == RootSubWindow::Normal){ + this->move( WIN->geometry().topLeft() ); + } break; case NativeWindow::Size: - WinWidget->resize(vals[i].toSize()); + //qDebug() << " - SIZE CHANGE"; + if(WIN->property(NativeWindow::FrameExtents).isNull() && (i<props.indexOf(NativeWindow::FrameExtents)) ){ + //Frame not loaded yet - push this back until after the frame is set + props << props.takeAt(i); + vals << vals.takeAt(i); + i--; + }else if(anim->state() != QPropertyAnimation::Running ){ + if(vals[i].toSize() != WinWidget->size() && activeState==Normal){ + //qDebug() << "Got Widget Size Change:" << vals[i].toSize() << WinWidget->size(); + WinWidget->resize(vals[i].toSize()); + this->resize( WIN->geometry().size() ); + //qDebug() << " - Size after change:" << WinWidget->size() << this->size() << WIN->geometry(); + } + } break; case NativeWindow::MinSize: WinWidget->setMinimumSize(vals[i].toSize()); @@ -247,19 +368,44 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList WinWidget->setMaximumSize(vals[i].toSize()); break; case NativeWindow::Active: - if(vals[i].toBool()){ WinWidget->setFocus(); } + //if(vals[i].toBool()){ WinWidget->setFocus(); } break; - /*case NativeWindow::WindowFlags: - this->setWindowFlags( val.value< Qt::WindowFlags >() ); + /*case NativeWindow::FrameExtents: + qDebug() << " - FRAME CHANGE"; + if(vals[i].isNull()){ + vals[i] = QVariant::fromValue<QList<int> >( QList<int>() << WinWidget->geometry().x() << this->width()-WinWidget->geometry().x()-WinWidget->geometry().width() << WinWidget->y() << this->height() - WinWidget->y() - WinWidget->geometry().height() ); + WIN->setProperty(NativeWindow::FrameExtents, vals[i]); + } + qDebug() << "Setting Frame Extents:" << vals[i].value<QList<int> >(); + mainLayout->setContentsMargins( vals[i].value< QList<int> >().at(0),vals[i].value< QList<int> >().at(2) - titleLabel->height(),vals[i].value< QList<int> >().at(1),vals[i].value< QList<int> >().at(3)); break;*/ + case NativeWindow::WinTypes: + enableFrame(vals[i].value< QList<NativeWindow::Type> >().contains(NativeWindow::T_NORMAL) ); + break; default: qDebug() << "Window Property Unused:" << props[i] << vals[i]; } } } +void RootSubWindow::animFinished(){ + if(closing){ this->close(); return;} + else if(anim->propertyName()=="geometry"){ + if(!animResetProp.isNull()){ + /*qDebug() << "Animation Finished, Reset Geometry:" << animResetProp; + qDebug() << " - Starting Value:" << anim->startValue(); + qDebug() << " - Ending Value:" << anim->endValue(); + qDebug() << " - Current Value:" << this->geometry();*/ + this->setGeometry( animResetProp.toRect() ); + } + } + animResetProp = QVariant(); //clear the variable +} + // === PROTECTED === void RootSubWindow::mousePressEvent(QMouseEvent *ev){ + activate(); + this->raise(); //qDebug() << "Frame Mouse Press Event"; offset.setX(0); offset.setY(0); if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse @@ -274,11 +420,11 @@ void RootSubWindow::mousePressEvent(QMouseEvent *ev){ activeState = getStateAtPoint(ev->pos(), true); //also have it set the offset variable } setMouseCursor(activeState, true); //this one is an override cursor - + QFrame::mousePressEvent(ev); } void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){ - ev->accept(); + activate(); //make sure this window is "Active" if(activeState == Normal){ setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor }else{ @@ -352,15 +498,15 @@ void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){ default: break; } - this->setGeometry(geom); } + QFrame::mouseMoveEvent(ev); } void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){ //Check for a right-click event //qDebug() << "Frame Mouse Release Event"; - ev->accept(); + QFrame::mouseReleaseEvent(ev); if( (activeState==Normal) && (titleBar->geometry().contains(ev->pos())) && (ev->button()==Qt::RightButton) ){ otherM->popup(ev->globalPos()); return; @@ -368,7 +514,7 @@ void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){ activeState = Normal; QApplication::restoreOverrideCursor(); setMouseCursor( getStateAtPoint(ev->pos()) ); - if(QWidget::mouseGrabber() == this){ this->releaseMouse(); } + if(QFrame::mouseGrabber() == this){ this->releaseMouse(); } } void RootSubWindow::leaveEvent(QEvent *ev){ @@ -377,3 +523,14 @@ void RootSubWindow::leaveEvent(QEvent *ev){ setMouseCursor(Normal); } } + +void RootSubWindow::moveEvent(QMoveEvent *ev){ + //qDebug() << "Got Move Event:" << ev->pos() << WinWidget->geometry(); + QFrame::moveEvent(ev); + if(!closing && anim->state()!=QAbstractAnimation::Running){ + moveTimer->start(); + //WinWidget->resyncWindow(); + //WIN->requestProperty(NativeWindow::GlobalPos, ev->pos() ); + } + +} diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h index 779f783b..de6aba89 100644 --- a/src-qt5/core/libLumina/RootSubWindow.h +++ b/src-qt5/core/libLumina/RootSubWindow.h @@ -18,8 +18,9 @@ #include <QLabel> #include <QToolButton> #include <QMenu> +#include <QPropertyAnimation> #include <NativeWindow.h> - +#include <NativeEmbedWidget.h> class RootSubWindow : public QFrame{ Q_OBJECT @@ -40,19 +41,28 @@ private: void setMouseCursor(ModState, bool override = false); //Update the mouse cursor based on state //Native window embed objects NativeWindow *WIN; - QWidget *WinWidget; + NativeEmbedWidget *WinWidget; bool closing; //Title bar objects - QBoxLayout *titleBar, *mainLayout; + QBoxLayout *titleBarL, *mainLayout; QToolButton *closeB, *maxB, *minB, *otherB; QLabel *titleLabel; QMenu *otherM; //menu of other actions + QWidget *titleBar; + //Other random objects (animations,etc) + QPropertyAnimation *anim; + QVariant animResetProp; + QTimer *moveTimer; + QRect lastGeom; //frame coordinates + void initWindowFrame(); + void enableFrame(bool); void LoadProperties( QList< NativeWindow::Property> list); public slots: void clientClosed(); + void LoadAllProperties(); //Button Actions - public so they can be tied to key shortcuts and stuff as well void toggleMinimize(); @@ -67,6 +77,7 @@ public slots: private slots: void propertiesChanged(QList<NativeWindow::Property>, QList<QVariant>); + void animFinished(); protected: void mousePressEvent(QMouseEvent*); @@ -74,6 +85,9 @@ protected: void mouseReleaseEvent(QMouseEvent*); void leaveEvent(QEvent *ev); + void moveEvent(QMoveEvent *ev); + + }; #endif diff --git a/src-qt5/core/libLumina/RootWindow.cpp b/src-qt5/core/libLumina/RootWindow.cpp index b1f740d3..31faaf50 100644 --- a/src-qt5/core/libLumina/RootWindow.cpp +++ b/src-qt5/core/libLumina/RootWindow.cpp @@ -14,6 +14,7 @@ RootWindow::RootWindow() : QWidget(0, Qt::Window | Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint){ qRegisterMetaType<WId>("WId"); autoResizeTimer = 0; + this->setMouseTracking(true); } RootWindow::~RootWindow(){ @@ -51,7 +52,7 @@ void RootWindow::updateScreenPixmap(screeninfo *info){ QPixmap raw(info->file); //load the image from file //Now apply the proper aspect ratio as needed if(info->scale == RootWindow::Stretch || info->scale == RootWindow::Full || info->scale == RootWindow::Fit){ - Qt::AspectRatioMode armode = Qt::KeepAspectRatio; + Qt::AspectRatioMode armode = Qt::KeepAspectRatio; if(info->scale == RootWindow::Stretch ){ armode = Qt::IgnoreAspectRatio; } else if(info->scale == RootWindow::Full ){ armode = Qt::KeepAspectRatioByExpanding; } if(raw.height()!=info->area.height() && raw.width() !=info->area.width()){ @@ -60,16 +61,16 @@ void RootWindow::updateScreenPixmap(screeninfo *info){ } //Now calculate offset and draw width/height QRect drawRect(0,0, raw.width(), raw.height()); - if(info->scale == RootWindow::Full ){ + if(info->scale == RootWindow::Full ){ drawRect.moveTo( (info->area.width() - raw.width())/2, (info->area.height() - raw.height())/2 ); }else if(info->scale == RootWindow::Fit ){ - drawRect.moveTo( (info->area.width() - raw.width())/2, (info->area.height() - raw.height())/2 ); + drawRect.moveTo( (info->area.width() - raw.width())/2, (info->area.height() - raw.height())/2 ); }else if(info->scale == RootWindow::Center ){ - drawRect.moveTo( (info->area.width() - raw.width())/2, (info->area.height() - raw.height())/2 ); + drawRect.moveTo( (info->area.width() - raw.width())/2, (info->area.height() - raw.height())/2 ); }else if(info->scale == RootWindow::Tile ){ //Draw the entire area - no offset drawRect.setHeight(info->area.height()); - drawRect.setWidth(info->area.width()); + drawRect.setWidth(info->area.width()); }else if(info->scale == RootWindow::BottomLeft ){ drawRect.moveTo( 0 , info->area.height() - raw.height() ); }else if(info->scale == RootWindow::BottomRight ){ @@ -130,7 +131,7 @@ void RootWindow::ResizeRoot(){ //Trigger a repaint and send out any signals this->setGeometry(fullscreen); this->update(); - emit RootResized(); + emit RootResized(fullscreen); if(!valid.isEmpty()){ emit NewScreens(valid); } if(!invalid.isEmpty()){ emit RemovedScreens(invalid); } } @@ -138,7 +139,7 @@ void RootWindow::ResizeRoot(){ void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QString file){ bool found = false; for(int i=0; i<WALLPAPERS.length() && !found; i++){ - if(WALLPAPERS[i].id == id){ + if(WALLPAPERS[i].id == id){ WALLPAPERS[i].scale = scale; WALLPAPERS[i].file = file; updateScreenPixmap(&WALLPAPERS[i]); @@ -168,20 +169,23 @@ void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QStrin void RootWindow::NewWindow(NativeWindow *win){ RootSubWindow *subwin = 0; + qDebug() << "Got New Window:" << win->property(NativeWindow::Title); for(int i=0; i<WINDOWS.length() && subwin==0; i++){ - if(WINDOWS[i]->id() == win->id()){ subwin = WINDOWS[i]; } + if(WINDOWS[i]->id() == win->id()){ subwin = WINDOWS[i]; } } if(subwin==0){ subwin = new RootSubWindow(this, win); connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(CloseWindow(WId)) ); WINDOWS << subwin; } - //subwin->show(); + //win->setProperty(NativeWindow::Visible, true); + //win->requestProperty( NativeWindow::Active, true); + win->requestProperties(QList<NativeWindow::Property>() << NativeWindow::Visible << NativeWindow::Active, QList<QVariant>() << true << true); } void RootWindow::CloseWindow(WId win){ for(int i=0; i<WINDOWS.length(); i++){ - if(WINDOWS[i]->id() == win){ WINDOWS.takeAt(i)->clientClosed(); break; } + if(WINDOWS[i]->id() == win){ qDebug() << "Remove Window From Root List"; WINDOWS.takeAt(i)->clientClosed(); break; } } } diff --git a/src-qt5/core/libLumina/RootWindow.h b/src-qt5/core/libLumina/RootWindow.h index 0ae248b5..a7792752 100644 --- a/src-qt5/core/libLumina/RootWindow.h +++ b/src-qt5/core/libLumina/RootWindow.h @@ -31,7 +31,7 @@ public: RootWindow(); ~RootWindow(); - + void start(); private: @@ -49,7 +49,7 @@ private: //Window Management QList<RootSubWindow*> WINDOWS; - + public slots: void ResizeRoot(); void ChangeWallpaper(QString id, RootWindow::ScaleType scale, QString file); @@ -65,7 +65,7 @@ protected: signals: void RegisterVirtualRoot(WId); - void RootResized(); + void RootResized(QRect); void NewScreens(QStringList); // [screen_id_1, screen_id_2, etc..] void RemovedScreens(QStringList); // [screen_id_1, screen_id_2, etc..] diff --git a/src-qt5/core/libLumina/RootWindow.pri b/src-qt5/core/libLumina/RootWindow.pri index 35e0e770..e4d5f00b 100644 --- a/src-qt5/core/libLumina/RootWindow.pri +++ b/src-qt5/core/libLumina/RootWindow.pri @@ -10,4 +10,5 @@ INCLUDEPATH *= ${PWD} # include other library dependencies include(LUtils.pri) -include(NativeWindow.pri); +include(NativeWindow.pri) +include(LIconCache.pri) |