diff options
33 files changed, 887 insertions, 380 deletions
diff --git a/src-qt5/core/lumina-desktop-unified/defaults/desktop/panels.conf b/src-qt5/core/lumina-desktop-unified/defaults/desktop/panels.conf new file mode 100644 index 00000000..07cf4635 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/defaults/desktop/panels.conf @@ -0,0 +1,21 @@ +[General] + +[templates] +windows/anchor="bottom" +windows/align="center" +windows/length_percent=100 +windows/plugins="" +windows/background="rgba(0,0,0,120)" + +[default] +active_ids="initial" + +[session] + +[initial] +anchor="bottom" +align="center" +length_percent=100 +width_percent=2.1 +plugins="" +background="rgba(0,0,0,120)" diff --git a/src-qt5/core/lumina-desktop-unified/defaults/desktop/screensaver.conf b/src-qt5/core/lumina-desktop-unified/defaults/desktop/screensaver.conf index b4bfec59..98e6f20a 100644 --- a/src-qt5/core/lumina-desktop-unified/defaults/desktop/screensaver.conf +++ b/src-qt5/core/lumina-desktop-unified/defaults/desktop/screensaver.conf @@ -1,3 +1,2 @@ [General] default_plugin="random" -plugin_VGA-0="random" diff --git a/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro b/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro index 07781770..96d84a71 100644 --- a/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro +++ b/src-qt5/core/lumina-desktop-unified/lumina-desktop.pro @@ -10,7 +10,7 @@ lessThan(QT_MINOR_VERSION, 7){ } QT *= core gui network widgets x11extras multimedia multimediawidgets concurrent svg quick qml - +CONFIG += c++11 TARGET = lumina-desktop-unified target.path = $${L_BINDIR} @@ -24,6 +24,7 @@ include(../libLumina/ExternalProcess.pri) include(../libLumina/XDGMime.pri) include(../../src-cpp/plugins-base.pri) +include(../../src-cpp/framework-OSInterface.pri) #include all the main individual source groups include(src-events/events.pri) diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp index eefe6d7e..bfded781 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.cpp @@ -84,10 +84,6 @@ void DesktopManager::updateWallpaper(QString screen_id, int wkspace){ RootDesktopObject::instance()->ChangeWallpaper(screen_id,QUrl::fromLocalFile(wpaper).toString() ); } -void DesktopManager::updatePanels(QString panel_id){ - -} - void DesktopManager::updatePlugins(QString plugin_id){ } @@ -103,13 +99,13 @@ void DesktopManager::settingsChanged(DesktopSettings::File type){ case DesktopSettings::Desktop: QTimer::singleShot(0, this, SLOT(updateDesktopSettings()) ); case DesktopSettings::Panels: - QTimer::singleShot(0, this, SLOT(updatePanelSettings()) ); + QTimer::singleShot(1, this, SLOT(updatePanelSettings()) ); case DesktopSettings::Plugins: - QTimer::singleShot(0, this, SLOT(updatePluginSettings()) ); + QTimer::singleShot(2, this, SLOT(updatePluginSettings()) ); case DesktopSettings::ContextMenu: - QTimer::singleShot(0, this, SLOT(updateMenuSettings()) ); + QTimer::singleShot(3, this, SLOT(updateMenuSettings()) ); case DesktopSettings::Animation: - QTimer::singleShot(0, this, SLOT(updateAnimationSettings()) ); + QTimer::singleShot(4, this, SLOT(updateAnimationSettings()) ); default: break; //Do nothing - not a settings change we care about here @@ -148,14 +144,28 @@ void DesktopManager::syncTrayWindowList(){ // === PRIVATE SLOTS === void DesktopManager::updateDesktopSettings(){ qDebug() << "Update Desktop Settings..."; - QList<QScreen*> scrns= QApplication::screens(); + QList<QScreen*> scrns = QGuiApplication::screens(); int wkspace = Lumina::NWS->currentWorkspace(); for(int i=0; i<scrns.length(); i++){ updateWallpaper(scrns[i]->name(), wkspace); } } void DesktopManager::updatePanelSettings(){ - + QList<QScreen*> scrns = QGuiApplication::screens(); + int primary = QApplication::desktop()->primaryScreen(); + for(int i=0; i<scrns.length(); i++){ + ScreenObject *sObj = RootDesktopObject::instance()->screen(scrns[i]->name()); + if(sObj == 0){ continue; } //screen is not managed directly - skip it + QStringList ids = DesktopSettings::instance()->value(DesktopSettings::Panels, scrns[i]->name().replace("-","_")+"/active_ids", QStringList()).toStringList(); + if(ids.isEmpty() && (scrns.length()==1 || i==primary)){ + //Also look for the "default" panel id's for the primary/default screen + ids = DesktopSettings::instance()->value(DesktopSettings::Panels, "default/active_ids", QStringList()).toStringList(); + } + sObj->setPanels(ids); + } + //Now do the global-session panels + QStringList ids = DesktopSettings::instance()->value(DesktopSettings::Panels, "session/active_ids", QStringList()).toStringList(); + RootDesktopObject::instance()->setPanels(ids); //put the new ones in place } void DesktopManager::updatePluginSettings(){ diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h index d4a0cf79..da42e477 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/DesktopManager.h @@ -23,7 +23,6 @@ public: private: void updateWallpaper(QString screen_id, int wkspace); - void updatePanels(QString panel_id); void updatePlugins(QString plugin_id); public slots: diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp new file mode 100644 index 00000000..a74d2585 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.cpp @@ -0,0 +1,65 @@ +//=========================================== +// Lumina-desktop source code +// Copyright (c) 2018, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include <global-objects.h> +#include "QMLImageProvider.h" + +QMLImageProvider::QMLImageProvider(QQmlImageProviderBase::ImageType type) : QQuickImageProvider(type, 0){ + +} + +QMLImageProvider::~QMLImageProvider(){ + +} + +/*QMLImageProvider* QMLImageProvider::instance(){ + static QMLImageProvider *_prov = 0; + if(_prov==0){ _prov = new QMLImageProvider(); } + return _prov; +}*/ + +QImage QMLImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize){ + NativeWindowObject *win = Lumina::NWS->findWindow( id.section(":",1,1).toInt(), false); + if(win==0){ win = Lumina::NWS->findTrayWindow(id.section(":",1,1).toInt()); } + + if(!id.startsWith("image:")){ qDebug() << "Request Image:" << id << win << requestedSize; } + + QImage img(requestedSize,QImage::Format_RGB32); + if(win==0){ img.fill("black"); } //invalid window ID (should never happen) + else if(id.startsWith("image:")){ img = Lumina::NWS->GetWindowImage(win); } + else if(id.startsWith("icon:")){ + QIcon ico = win->property(NativeWindowObject::Icon).value<QIcon>(); + QList<QSize> sizes = ico.availableSizes(); + QSize sz(0,0); + //Just grab the largest size currently available + for(int i=0; i<sizes.length(); i++){ + if(sz.width()<sizes[i].width() && sz.height()<sizes[i].height()){ sz = sizes[i]; } + } + qDebug() << "Icon Sizes:" <<sizes; + img = ico.pixmap(sz).toImage(); + } + //qDebug() << "Got Window Image:" << img.size(); + if(img.size().isNull()){ + if(requestedSize.isValid()){ img = QImage(requestedSize,QImage::Format_RGB32); } + else{ img = QImage(QSize(64,64), QImage::Format_RGB32); } + img.fill("black"); + } + //qDebug() << "Final Window Image:" << img.size(); + if(size!=0){ + size->setHeight(img.height()); + size->setWidth( img.width() ); + } + if(requestedSize.isValid() && !requestedSize.isNull() && img.size()!=requestedSize){ + img = img.scaled(requestedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + return img; +} + +QPixmap QMLImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize){ + qDebug() << "Pixmap Requested:" << id; + QImage img = requestImage(id, size, requestedSize); + return QPixmap::fromImage(img); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h new file mode 100644 index 00000000..8719176e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/QMLImageProvider.h @@ -0,0 +1,26 @@ +//=========================================== +// Lumina-desktop source code +// Copyright (c) 2018, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_QML_IMAGE_PROVIDER_H +#define _LUMINA_DESKTOP_QML_IMAGE_PROVIDER_H + +#include <QQuickImageProvider> +#include <QImage> +#include <QPixmap> +#include <QSize> + +class QMLImageProvider : public QQuickImageProvider{ +public: + QMLImageProvider(QQmlImageProviderBase::ImageType); + ~QMLImageProvider(); + + //static QMLImageProvider* instance(); + + virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp index 0cfa4e6b..2ceff4a0 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/RootWindow.cpp @@ -5,6 +5,8 @@ // See the LICENSE file for full details //=========================================== #include "RootWindow.h" +#include "QMLImageProvider.h" +#include <QQmlImageProviderBase> RootWindow::RootWindow() : QObject(){ root_win = QWindow::fromWinId( QX11Info::appRootWindow() ); // @@ -16,6 +18,8 @@ RootWindow::RootWindow() : QObject(){ //Now setup the QQuickView root_view->setResizeMode(QQuickView::SizeRootObjectToView); root_view->engine()->rootContext()->setContextProperty("RootObject", root_obj); + root_view->engine()->addImageProvider("native_window", new QMLImageProvider(QQmlImageProviderBase::Image) ); + //root_view->engine()->addImageProvider("native_window_icon", new QMLImageProvider(QQmlImageProviderBase::Pixmap) ); RootDesktopObject::RegisterType(); //make sure object classes are registered with the QML subsystems } diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri b/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri index f4a6882d..ee5ada62 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/desktop.pri @@ -1,10 +1,12 @@ QT *= gui widgets qml quick SOURCES *= $${PWD}/RootWindow.cpp \ - $${PWD}/Desktopmanager.cpp + $${PWD}/Desktopmanager.cpp \ + $${PWD}/QMLImageProvider.cpp HEADERS *= $${PWD}/RootWindow.h \ - $${PWD}/DesktopManager.h + $${PWD}/DesktopManager.h \ + $${PWD}/QMLImageProvider.h #update the includepath so we can just #include as needed without paths INCLUDEPATH *= $${PWD} diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp index e9049cdd..9bb78dd0 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.cpp @@ -21,7 +21,11 @@ void NativeWindowObject::RegisterType(){ NativeWindowObject::NativeWindowObject(WId id) : QObject(){ winid = id; frameid = 0; - dmgID = 0; + dmgID = dmg = 0; + geomTimer = new QTimer(this); + geomTimer->setSingleShot(true); + geomTimer->setInterval(50); //1/20 second + connect(geomTimer, SIGNAL(timeout()), this, SLOT(sendNewGeom()) ); } NativeWindowObject::~NativeWindowObject(){ @@ -62,17 +66,9 @@ void NativeWindowObject::setProperty(NativeWindowObject::Property prop, QVariant if(prop == NativeWindowObject::RelatedWindows){ relatedTo = val.value< QList<WId> >(); } else if(prop == NativeWindowObject::None || (!force && hash.value(prop)==val)){ return; } else if(prop == NativeWindowObject::WinImage){ - //special case - QImage is passed in, but QString is passed out (needed for QML) - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - val.value<QImage>().save(&buffer, "PNG"); - QString img("data:image/png;base64,"); - img.append(QString::fromLatin1(ba.toBase64().data())); - qDebug() << "Image Data Header:" << img.section(",",0,0); - hash.insert(prop, img); //save the string instead - } - else{ hash.insert(prop, val); } + //special case - This should never be actually set in the property hash + // it is loaded dynamically by the QMLImageProvider instead (prevent flickering/caching image) + } else{ hash.insert(prop, val); } emitSinglePropChanged(prop); emit PropertiesChanged(QList<NativeWindowObject::Property>() << prop, QList<QVariant>() << val); } @@ -83,15 +79,8 @@ void NativeWindowObject::setProperties(QList<NativeWindowObject::Property> props if(props[i] == NativeWindowObject::None || (!force && (hash.value(props[i]) == vals[i])) ){ props.removeAt(i); vals.removeAt(i); i--; continue; //Invalid property or identical value }else if(props[i] == NativeWindowObject::WinImage){ - //special case - QImage is passed in, but QString is passed out (needed for QML) - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - vals[i].value<QImage>().save(&buffer, "PNG"); - QString img("data:image/png;base64,"); - img.append(QString::fromLatin1(ba.toBase64().data())); - qDebug() << "Image Data Header:" << img.section(",",0,0); - hash.insert(props[i], img); //save the string instead + //special case - This should never be actually set in the property hash + // it is loaded dynamically by the QMLImageProvider instead (prevent flickering/caching image) }else{ hash.insert(props[i], vals[i]); } @@ -141,9 +130,16 @@ QRect NativeWindowObject::geometry(){ return geom; } +void NativeWindowObject::setGeometryNow(QRect geom){ + updateGeometry(geom.x(), geom.y(), geom.width(), geom.height(), true); +} + // QML ACCESS FUNCTIONS (shortcuts for particular properties in a format QML can use) QString NativeWindowObject::winImage(){ - return this->property(NativeWindowObject::WinImage).toString(); + //Need to alternate something on the end to ensure that QML knows to fetch the new image (non-cached only) + if(dmg==0){ dmg = 1; } + else{ dmg = 0; } + return "image://native_window/image:"+QString::number(winid)+":"+QString::number(dmg); } QString NativeWindowObject::name(){ @@ -161,8 +157,11 @@ QString NativeWindowObject::shortTitle(){ return tmp; } -QIcon NativeWindowObject::icon(){ - return this->property(NativeWindowObject::Name).value<QIcon>(); +QString NativeWindowObject::icon(){ + if(icodmg==0){ icodmg=1; } + else{ icodmg = 0; } + qDebug() << "Window Icon:" << icodmg << this->property(NativeWindowObject::Icon).value<QIcon>().availableSizes(); + return "image://native_window/icon:"+QString::number(winid)+":"+QString::number(icodmg); } //QML Button states @@ -257,6 +256,23 @@ QRect NativeWindowObject::imageGeometry(){ return geom; } +void NativeWindowObject::updateGeometry(int x, int y, int width, int height, bool now){ + // Full frame+window geometry - go ahead and pull it apart and only update the interior window geom + QList<int> fgeom = this->property(NativeWindowObject::FrameExtents).value<QList<int> >(); + if(fgeom.isEmpty()){ fgeom << 0<<0<<0<<0; } //just in case (left/right/top/bottom) + QPoint pos(x+fgeom[0], y+fgeom[2]); + QSize sz(width-fgeom[0]-fgeom[1], height-fgeom[2]-fgeom[3]); + if(!now){ + newgeom = QRect(pos, sz); + //qDebug() << "Update Geometry:" << fgeom << QRect(x,y,width,height) << pos << sz; + //requestProperties(QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size, QList<QVariant>() << pos << sz); + if(!geomTimer->isActive()){ geomTimer->start(); } + }else{ + requestProperties(QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size , QList<QVariant>() << pos << sz ); + setProperties(QList<NativeWindowObject::Property>() << NativeWindowObject::GlobalPos << NativeWindowObject::Size , QList<QVariant>() << pos << sz ); + } +} + // ==== PUBLIC SLOTS === void NativeWindowObject::toggleVisibility(){ setProperty(NativeWindowObject::Visible, !property(NativeWindowObject::Visible).toBool() ); @@ -281,7 +297,9 @@ void NativeWindowObject::emitSinglePropChanged(NativeWindowObject::Property prop case NativeWindowObject::Name: emit nameChanged(); break; case NativeWindowObject::Title: - emit titleChanged(); break; + emit titleChanged(); + if(this->property(NativeWindowObject::ShortTitle).toString().isEmpty()){ emit shortTitleChanged(); } + break; case NativeWindowObject::ShortTitle: emit shortTitleChanged(); break; case NativeWindowObject::Icon: @@ -297,3 +315,10 @@ void NativeWindowObject::emitSinglePropChanged(NativeWindowObject::Property prop break; //do nothing otherwise } } + +void NativeWindowObject::sendNewGeom(){ + QList<NativeWindowObject::Property> props; props << NativeWindowObject::GlobalPos << NativeWindowObject::Size; + QList<QVariant> vals; vals << newgeom.topLeft() << newgeom.size(); + requestProperties(props, vals); + setProperties(props,vals); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h index e4f9d41e..6a63813e 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/NativeWindowObject.h @@ -21,7 +21,7 @@ class NativeWindowObject : public QObject{ Q_PROPERTY( QString name READ name NOTIFY nameChanged) Q_PROPERTY( QString title READ title NOTIFY titleChanged) Q_PROPERTY( QString shortTitle READ shortTitle NOTIFY shortTitleChanged) - Q_PROPERTY( QIcon icon READ icon NOTIFY iconChanged) + Q_PROPERTY( QString icon READ icon NOTIFY iconChanged) Q_PROPERTY( bool sticky READ isSticky NOTIFY stickyChanged) //Button/Titlebar visibility Q_PROPERTY( bool showCloseButton READ showCloseButton NOTIFY winTypeChanged) @@ -38,6 +38,8 @@ public: enum State{ S_MODAL, S_STICKY, S_MAX_VERT, S_MAX_HORZ, S_SHADED, S_SKIP_TASKBAR, S_SKIP_PAGER, S_HIDDEN, S_FULLSCREEN, S_ABOVE, S_BELOW, S_ATTENTION }; enum Type{T_DESKTOP, T_DOCK, T_TOOLBAR, T_MENU, T_UTILITY, T_SPLASH, T_DIALOG, T_DROPDOWN_MENU, T_POPUP_MENU, T_TOOLTIP, T_NOTIFICATION, T_COMBO, T_DND, T_NORMAL }; 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 Location { TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT }; + Q_ENUM(Location) enum Property{ /*QVariant Type*/ None=0, /*null*/ @@ -90,13 +92,14 @@ public: void requestProperties(QList<NativeWindowObject::Property>, QList<QVariant>, bool force = false); Q_INVOKABLE QRect geometry(); //this returns the "full" geometry of the window (window + frame) + void setGeometryNow(QRect geom); // QML ACCESS FUNCTIONS (shortcuts for particular properties in a format QML can use) Q_INVOKABLE QString winImage(); Q_INVOKABLE QString name(); Q_INVOKABLE QString title(); Q_INVOKABLE QString shortTitle(); - Q_INVOKABLE QIcon icon(); + Q_INVOKABLE QString icon(); //QML Button states Q_INVOKABLE bool showCloseButton(); Q_INVOKABLE bool showMaxButton(); @@ -111,6 +114,7 @@ public: //QML Geometry reporting Q_INVOKABLE QRect frameGeometry(); Q_INVOKABLE QRect imageGeometry(); + Q_INVOKABLE void updateGeometry(int x, int y, int width, int height, bool now = false); //For QML to change the current window position public slots: Q_INVOKABLE void toggleVisibility(); @@ -123,17 +127,22 @@ private: //QWindow *WIN; WId winid, frameid; QList<WId> relatedTo; - unsigned int dmgID; + unsigned int dmgID, dmg, icodmg; + //Collation/Delay for window resize events + QTimer *geomTimer; + QRect newgeom; void emitSinglePropChanged(NativeWindowObject::Property); +private slots: + void sendNewGeom(); + signals: //General Notifications void PropertiesChanged(QList<NativeWindowObject::Property>, QList<QVariant>); void RequestPropertiesChange(WId, QList<NativeWindowObject::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 NativeWindowObject id() diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp index 471da58f..9054f528 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.cpp @@ -5,6 +5,8 @@ // See the LICENSE file for full details //=========================================== #include "PanelObject.h" +#include <global-objects.h> + #include <QQmlEngine> #include <QDebug> @@ -42,3 +44,41 @@ void PanelObject::setGeometry( QRect newgeom ){ emit geomChanged(); } } + +void PanelObject::syncWithSettings(QRect parent_geom){ + //Read off all the settings + //qDebug() << "Sync Panel Settings:" << panel_id << parent_geom; + QString anchor = DesktopSettings::instance()->value(DesktopSettings::Panels, panel_id+"/anchor", "bottom").toString().toLower(); + QString align = DesktopSettings::instance()->value(DesktopSettings::Panels, panel_id+"/align", "center").toString().toLower(); + double length = DesktopSettings::instance()->value(DesktopSettings::Panels, panel_id+"/length_percent", 100).toDouble()/100.0; + double width = DesktopSettings::instance()->value(DesktopSettings::Panels, panel_id+"/width_font_percent", 2.1).toDouble(); + width = qRound(width * QApplication::fontMetrics().height() ); + this->setBackground( DesktopSettings::instance()->value(DesktopSettings::Panels, panel_id+"/background", "rgba(0,0,0,120)").toString() ); + // qDebug() << "Update Panel:" << panel_id << anchor+"/"+align << length << width; + //Now calculate the geometry of the panel + QRect newgeom; + //Figure out the size of the panel + if(anchor=="top" || anchor=="bottom"){ newgeom.setWidth( parent_geom.width()*length ); newgeom.setHeight(width); } + else{ newgeom.setWidth(width); newgeom.setHeight(parent_geom.height()*length); } + //qDebug() << " - Size:" << newgeom; + //Now figure out the location of the panel + if(align=="left" || align=="top"){ + if(anchor=="top" || anchor=="left"){ newgeom.moveTopLeft(QPoint(0,0)); } + else if(anchor=="right"){ newgeom.moveTopRight(QPoint(parent_geom.width(), 0)); } + else{ newgeom.moveBottomLeft(QPoint(0, parent_geom.height()) ); } //bottom by default + + }else if(align=="right" || align=="bottom"){ + if(anchor=="top"){ newgeom.moveTopRight(QPoint(parent_geom.width(),0)); } + else if(anchor=="left"){ newgeom.moveBottomLeft(QPoint(0, parent_geom.height())); } + else if(anchor=="right"){ newgeom.moveBottomRight(QPoint(parent_geom.width(), parent_geom.height())); } + else{ newgeom.moveBottomRight(QPoint(parent_geom.width(), parent_geom.height()) ); } + + }else{ //center + if(anchor=="top"){ newgeom.moveTopLeft(QPoint( (parent_geom.width()-newgeom.width())/2,0)); } + else if(anchor=="left"){ newgeom.moveTopLeft(QPoint(0, (parent_geom.height()-newgeom.height())/2 )); } + else if(anchor=="right"){ newgeom.moveTopRight(QPoint(parent_geom.width(), (parent_geom.height()-newgeom.height())/2 )); } + else{ newgeom.moveBottomLeft(QPoint( (parent_geom.width()-newgeom.width())/2, parent_geom.height()) ); } + } + //qDebug() << " - Calculated Geometry:" << newgeom; + this->setGeometry(newgeom); //Note: This is in parent-relative coordinates (not global) +} diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h index a788fa07..8cf59dee 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/PanelObject.h @@ -40,6 +40,7 @@ public: public slots: void setBackground(QString fileOrColor); void setGeometry(QRect newgeom); + void syncWithSettings(QRect parent_geom); signals: void backgroundChanged(); diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp index 39dc30c1..07d4e463 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.cpp @@ -14,6 +14,7 @@ // === PUBLIC === RootDesktopObject::RootDesktopObject(QObject *parent) : QObject(parent){ updateScreens(); //make sure the internal list is updated right away + connect(this, SIGNAL(changePanels(QStringList)), this, SLOT(setPanels(QStringList)) ); } RootDesktopObject::~RootDesktopObject(){ @@ -87,6 +88,41 @@ void RootDesktopObject::setPanels(QList<PanelObject*> list){ emit panelsChanged(); } +void RootDesktopObject::setPanels(QStringList ids){ + //Make this thread-safe for object creation + if(this->thread() != QThread::currentThread()){ + //use internal signal/slot combo to change threads + this->emit changePanels(ids); + return; + } + + //Get the current bounding rectangle for the session + QRect total; + for(int i=0; i<s_objects.length(); i++){ + total = total.united(s_objects[i]->geometry()); + } + //First update/remove any current panel objects + bool change = false; + for(int i=0; i<panel_objects.length(); i++){ + if(ids.contains(panel_objects[i]->name()) ){ + ids.removeAll(panel_objects[i]->name()); //already handled + panel_objects[i]->syncWithSettings(total); + }else{ + panel_objects.takeAt(i)->deleteLater(); + i--; + change = true; //list changed + } + } + //Now create any new panel objects as needed + for(int i=0; i<ids.length(); i++){ + PanelObject *tmp = new PanelObject(ids[i], this); + tmp->syncWithSettings(total); + panel_objects << tmp; + change = true; //list changed + } + if(change){ emit panelsChanged(); } +} + void RootDesktopObject::setWindows(QList<NativeWindowObject*> list){ window_objects = list; emit windowsChanged(); diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h index a4236596..ad0e538b 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/RootDesktopObject.h @@ -10,6 +10,7 @@ #define _LUMINA_DESKTOP_QML_BACKEND_ROOT_DESKTOP_OBJECT_H #include <global-includes.h> #include <ScreenObject.h> +#include <QThread> class RootDesktopObject : public QObject{ Q_OBJECT @@ -53,6 +54,9 @@ public slots: QString CurrentWallpaper(QString screen); void setPanels(QList<PanelObject*> list); + void setPanels(QStringList ids); + QList<PanelObject*> panelObjectList(){ return panel_objects; } + void setWindows(QList<NativeWindowObject*> list); private slots: @@ -66,5 +70,8 @@ signals: void mouseMoved(); void lockScreen(); void launchApplication(QString); + + //Internal signals for thread-safety + void changePanels(QStringList); }; #endif diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp index 1b22c450..c754906d 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.cpp @@ -10,6 +10,7 @@ ScreenObject::ScreenObject(QScreen *scrn, QObject *parent) : QObject(parent){ bg_screen = scrn; + connect(this, SIGNAL(changePanels(QStringList)), this, SLOT(setPanels(QStringList)) ); } void ScreenObject::RegisterType(){ @@ -40,6 +41,37 @@ void ScreenObject::setPanels(QList<PanelObject*> list){ emit panelsChanged(); } +void ScreenObject::setPanels(QStringList ids){ + //Make this thread-safe for object creation + if(this->thread() != QThread::currentThread()){ + //use internal signal/slot combo to change threads + this->emit changePanels(ids); + return; + } + + //First update/remove any current panel objects + bool change = false; + for(int i=0; i<panel_objects.length(); i++){ + if(ids.contains(panel_objects[i]->name()) ){ + ids.removeAll(panel_objects[i]->name()); //already handled + panel_objects[i]->syncWithSettings(bg_screen->geometry()); + }else{ + panel_objects.takeAt(i)->deleteLater(); + i--; + change = true; //list changed + } + } + //Now create any new panel objects as needed + for(int i=0; i<ids.length(); i++){ + PanelObject *tmp = new PanelObject(ids[i], this); + tmp->syncWithSettings(bg_screen->geometry()); + panel_objects << tmp; + change = true; //list changed + } + if(change){ emit panelsChanged(); } +} + + //QML Read Functions QStringList ScreenObject::panels(){ //qDebug() << "Request Panels:" << panel_objects.length(); diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h index 1afff6d2..250c9403 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-cpp/ScreenObject.h @@ -11,6 +11,7 @@ #include <QObject> #include <QString> #include <QScreen> +#include <QThread> #include "PanelObject.h" @@ -42,16 +43,24 @@ public: Q_INVOKABLE int height(); Q_INVOKABLE QStringList panels(); Q_INVOKABLE PanelObject* panel(QString id); + Q_INVOKABLE QRect geometry(){ return bg_screen->geometry(); } void setPanels(QList<PanelObject*> list); + QList<PanelObject*> panelObjectList(){ return panel_objects; } + public slots: + void setPanels(QStringList ids); void setBackground(QString fileOrColor); signals: void backgroundChanged(); void geomChanged(); void panelsChanged(); + + //Internal signals for thread-safety + void changePanels(QStringList); + }; #endif diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml index 92263689..2150e37c 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/NativeWindow.qml @@ -5,6 +5,7 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import Lumina.Backend.NativeWindowObject 2.0 +import Lumina.Backend.RootDesktopObject 2.0 Rectangle { property NativeWindowObject object @@ -14,94 +15,119 @@ Rectangle { id: windowFrame border.width: 5 - //border.color: palette.window - color: palette.window + border.color: palette.highlight + radius: 5 + color: "transparent" //palette.window x: object.frameGeometry.x y: object.frameGeometry.y width: object.frameGeometry.width height: object.frameGeometry.height + onXChanged: { + windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height) + } + onYChanged: { + windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height) + } + MouseArea { id: resizeArea anchors.fill: parent - property int positionX: 0 - property int positionY: 0 - property int newWidth: 0 - property int newHeight: 0 - - onPositionChanged: { - var globalP = windowFrame.mapToGlobal(mouse.x, mouse.y) - if(positionY < windowFrame.y + 15 ) { - /*if(positionX < windowFrame.x + 15) { - console.log("Top Left"); - //Top Left - newWidth = windowFrame.width + (windowFrame.x - mouse.x) - newHeight = windowFrame.height + (windowFrame.y - mouse.y) - windowFrame.x = mouse.x - windowFrame.y = mouse.y - }else if(positionX > windowFrame.x + windowFrame.width - 15) { - console.log("Top Right"); - //Top Right - newX = positionX - mouse.x - newY = positionY - mouse.y - newWidth = windowFrame.width - (positionX - mouse.x) - newHeight = windowFrame.height + (windowFrame.y - mouse.y) - windowFrame.y = mouse.y - }else{*/ - //Top - windowFrame.height -= 1 - windowFrame.y = globalP.y - // } + drag.target: undefined + property int resizeDirection: NativeWindowObject.TOP_LEFT + property int positionX: -1 + property int positionY: -1 + + onPressed: { + //NOTE: This is only triggered for resize events + var globalP = windowFrame.mapToItem(rootCanvas, mouse.x, mouse.y) + positionX = globalP.x + positionY = globalP.y + if(positionY <= windowFrame.y + 10 ) { + if(positionX <= windowFrame.x + 10) + resizeDirection = NativeWindowObject.TOP_LEFT + else if(positionX >= windowFrame.x + windowFrame.width - 10) + resizeDirection = NativeWindowObject.TOP_RIGHT + else + resizeDirection = NativeWindowObject.TOP + }else if(positionY >= windowFrame.y + windowFrame.height - 10) { + if(positionX <= windowFrame.x + 10) + resizeDirection = NativeWindowObject.BOTTOM_LEFT + else if(positionX >= windowFrame.x + windowFrame.width - 10) + resizeDirection = NativeWindowObject.BOTTOM_RIGHT + else + resizeDirection = NativeWindowObject.BOTTOM + }else if(positionX <= windowFrame.x + 10) { + resizeDirection = NativeWindowObject.LEFT + }else if(positionX >= windowFrame.x + windowFrame.width - 10) { + resizeDirection = NativeWindowObject.RIGHT } -/*else if(mouse.x < windowFrame.x + 15) { - if(mouse.y > windowFrame.y + windowFrame.height - 15) { - //Bottom Left - newX = positionX - mouse.x - newWidth = windowFrame.width - newX - newHeight = windowFrame.height - newY - }else{ - //Left - } - }else if(mouse.y > windowFrame.y + windowFrame.height - 15) { - if(mouse.x > windowFrame.x + windowFrame.width - 15) { - //Bottom Right - }else{ - //Bottom - } - }else if(mouse.x > windowFrame.x + windowFrame.width - 15) { - //Right - } else { - console.log("Cursor error"); - }*/ + //console.log("Initial X: ", positionX, "Initial Y: ", positionY); + //console.log("Initial X Frame: ", windowFrame.x, "Initial Y Frame: ", windowFrame.y); } - } - - MouseArea { - id: dragArea - anchors.fill: titleBar - drag.target: windowFrame - drag.axis: Drag.XAndYAxis - onClicked: { console.log("dragArea"); } - //released: { function(); } - } - states: [ - State { - when: resizeArea.drag.held - PropertyChanges { target: Window; color:"red" } - }, - State { - when: dragArea.drag.held - AnchorChanges { target: windowFrame; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined } + onReleased: { + positionX = -1 + positionY = -1 + //windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height) } - ] - + onPositionChanged: { + //NOTE: This is only triggered for resize events + if(positionX != -1 && positionY != -1) { + var globalP = windowFrame.mapToItem(rootCanvas, mouse.x, mouse.y) + /*console.log("Global P: ", globalP); + console.log("Position X: ", positionX, "Position Y: ", positionY) + console.log("Old Position : ", windowFrame.x, " , ", windowFrame.y) + console.log(resizeDirection);*/ + if(resizeDirection == NativeWindowObject.TOP_LEFT) { + windowFrame.height -= globalP.y - positionY + windowFrame.width -= globalP.x - positionX + windowFrame.y = globalP.y + windowFrame.x = globalP.x + }else if(resizeDirection == NativeWindowObject.TOP_RIGHT) { + //console.log("TOP RIGHT Old Height: ", windowFrame.height, "Old Width: ", windowFrame.width) + windowFrame.height -= globalP.y - positionY + windowFrame.width += globalP.x - positionX + //console.log("New Height: ", windowFrame.height, "New Width: ", windowFrame.width) + windowFrame.y = globalP.y + //console.log("New Position : ", windowFrame.x, " , ", windowFrame.y) + }else if(resizeDirection == NativeWindowObject.TOP) { + windowFrame.height -= globalP.y - positionY + windowFrame.y = globalP.y + } else if(resizeDirection == NativeWindowObject.RIGHT) { + windowFrame.width += globalP.x - positionX + } else if(resizeDirection == NativeWindowObject.BOTTOM_RIGHT) { + windowFrame.height += globalP.y - positionY + windowFrame.width += globalP.x - positionX + } else if(resizeDirection == NativeWindowObject.BOTTOM) { + windowFrame.height += globalP.y - positionY + } else if(resizeDirection == NativeWindowObject.BOTTOM_LEFT) { + windowFrame.width -= globalP.x - positionX + windowFrame.height += globalP.y - positionY + windowFrame.x = globalP.x + } else if(resizeDirection == NativeWindowObject.LEFT) { + windowFrame.width -= globalP.x - positionX + windowFrame.x = globalP.x + } + //Set a miniumum width and height as 80x50 + if(windowFrame.width < 80) { + windowFrame.width = 80 + } + if(windowFrame.height < 50) { + windowFrame.height = 50 + } + positionY = globalP.y + positionX = globalP.x + } + windowFrame.object.updateGeometry(windowFrame.x, windowFrame.y, windowFrame.width, windowFrame.height) + } + } Rectangle { id: titleBar - border.width: 2 - color: palette.window + border.width: 0 + color: palette.window height: 25 anchors.top: windowFrame.top anchors.right: windowFrame.right @@ -109,48 +135,51 @@ Rectangle { anchors.margins: windowFrame.border.width width: parent.width - RowLayout { - anchors.fill: titleBar - spacing: 0 + MouseArea { + id: dragArea + anchors.fill: parent + drag.target: windowFrame + drag.axis: Drag.XAndYAxis + //acceptedButtons: Qt.RightButton + //onClicked: contextMenu.open() + //released: { function(); } + } - Button { - id: otherButton - anchors.left: parent.left - Layout.fillHeight: true - iconSource: windowFrame.object.icon - } + ToolButton { + id: otherButton + anchors.left: parent.left + height: parent.height + iconSource: windowFrame.object.icon + } - Text { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - color: palette.windowText - text: windowFrame.object.shortTitle - - MouseArea { - acceptedButtons: Qt.RightButton - anchors.fill: parent - //onClicked: contextMenu.open() - } - } + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: palette.windowText + text: windowFrame.object.shortTitle + fontSizeMode: Text.Fit + } - Button { + RowLayout { + spacing: 0 + anchors.right: parent.right + height: parent.height + + ToolButton { id: minButton Layout.fillHeight: true iconName: "window-minimize" onClicked: { windowFrame.object.toggleVisibility() } } - Button { + ToolButton { id: maxButton Layout.fillHeight: true iconName: "window-maximize" //onClicked: { windowFrame.object.toggleMaximize() } } - Button { + ToolButton { id: closeButton Layout.fillHeight: true iconName: "document-close" @@ -161,6 +190,7 @@ Rectangle { Image { id: frameContents + cache: false source: windowFrame.object.winImage anchors.top: titleBar.bottom anchors.bottom: parent.bottom @@ -173,11 +203,10 @@ Rectangle { height: parent.height MouseArea { - width: parent.width; + width: parent.width height: parent.height anchors.fill: frameContents onClicked: { console.log(parent.mapToGlobal(mouse.x, mouse.y)); } - } } } diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml index 846b5b55..556da5ec 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Panel.qml @@ -10,15 +10,13 @@ import QtQuick.Controls 1 import Lumina.Backend.PanelObject 2.0 -AnimatedImage { +Rectangle { //C++ backend object - property string screen_id + property string panel_id property PanelObject object //Normal geometries/placements - asynchronous: true - clip: true - source: object.background + color: object.background x: object.x y: object.y width: object.width diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml index 9122ce5b..f48f7751 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/RootDesktop.qml @@ -66,4 +66,14 @@ Rectangle { z: 100+index } } + + //Setup the Panels + Repeater{ + model: RootObject.panels + QML.Panel{ + panel_id: modelData + object: RootObject.panel(panel_id) + z: 10100+index + } + } } diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml index 3b83653a..82e7c89d 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-qml/Screen.qml @@ -9,6 +9,9 @@ import QtQuick.Window 2.2 import QtQuick.Controls 1 import Lumina.Backend.ScreenObject 2.0 +import Lumina.Backend.PanelObject 2.0 + +import "." as QML AnimatedImage { //C++ backend object @@ -23,4 +26,15 @@ AnimatedImage { y: object.y width: object.width height: object.height + + //Setup the Panels + Repeater{ + model: object.panels + QML.Panel{ + panel_id: modelData + object: parent.object.panel(panel_id) + z: 10000+index + } + } + } diff --git a/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.cpp b/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.cpp index 30a6a47d..063c1337 100644 --- a/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.cpp @@ -10,7 +10,7 @@ #include "NativeWindowSystem.h" #include <global-objects.h> -#define DISABLE_COMPOSITING 1 +#define DISABLE_COMPOSITING 0 //XCB Library includes #include <xcb/xcb.h> @@ -523,7 +523,7 @@ void NativeWindowSystem::ChangeWindowProperties(NativeWindowObject* win, QList< } if(props.contains(NativeWindowObject::Size) || props.contains(NativeWindowObject::GlobalPos) ){ - /*xcb_configure_window_value_list_t valList; + 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(NativeWindowObject::Size).toSize(); @@ -543,7 +543,7 @@ void NativeWindowSystem::ChangeWindowProperties(NativeWindowObject* win, QList< 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);*/ + xcb_configure_window_aux(QX11Info::connection(), win->id(), mask, &valList); } if(props.contains(NativeWindowObject::Name)){ @@ -628,9 +628,9 @@ void NativeWindowSystem::SetupNewWindow(NativeWindowObject *win){ registerClientEvents(win->id()); } -void NativeWindowSystem::UpdateWindowImage(NativeWindowObject* win){ +QImage NativeWindowSystem::GetWindowImage(NativeWindowObject* win){ QImage img; - qDebug() << "Update Window Image:" << win->name(); + //qDebug() << "Update Window Image:" << win->name(); QRect geom(QPoint(0,0), win->property(NativeWindowObject::Size).toSize()); if(DISABLE_COMPOSITING){ QList<QScreen*> screens = static_cast<QApplication*>( QApplication::instance() )->screens(); @@ -641,12 +641,12 @@ void NativeWindowSystem::UpdateWindowImage(NativeWindowObject* win){ //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){ qDebug() << "Got blank pixmap!"; return; } + if(pix==0){ return QImage(); } //Convert this pixmap into a QImage //xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, 0, 0, this->width(), this->height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP); 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){ qDebug() << "Got blank image!"; return; } + if(ximg == 0){ return QImage(); } QImage tmp(ximg->data, ximg->width, ximg->height, ximg->stride, QImage::Format_ARGB32_Premultiplied); img = tmp.copy(); //detach this image from the XCB data structures before we clean them up, otherwise the QImage will try to clean it up a second time on window close and crash xcb_image_destroy(ximg); @@ -654,7 +654,8 @@ void NativeWindowSystem::UpdateWindowImage(NativeWindowObject* win){ //Cleanup the XCB data structures xcb_free_pixmap(QX11Info::connection(), pix); } - win->setProperty(NativeWindowObject::WinImage, QVariant::fromValue<QImage>(img) ); + return img; + //win->setProperty(NativeWindowObject::WinImage, QVariant::fromValue<QImage>(img) ); } // === PUBLIC SLOTS === @@ -783,15 +784,19 @@ void NativeWindowSystem::NewWindowDetected(WId id){ registerClientEvents(win->id()); NWindows << win; UpdateWindowProperties(win, NativeWindowObject::allProperties()); - win->setProperty(NativeWindowObject::FrameExtents, QVariant::fromValue<QList<int> >( QList<int>() << 5 << 5 << 30 << 5 )); + if(win->showWindowFrame()){ + win->setProperty(NativeWindowObject::FrameExtents, QVariant::fromValue<QList<int> >( QList<int>() << 5 << 5 << 30 << 5 )); + } qDebug() << "New Window [& associated ID's]:" << win->id() << win->property(NativeWindowObject::Name).toString(); SetupNewWindow(win); + CheckWindowPosition(id, true); //first time placement //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<NativeWindowObject::Property>, QList<QVariant>)), this, SLOT(RequestPropertiesChange(WId, QList<NativeWindowObject::Property>, QList<QVariant>)) ); + connect(win, SIGNAL(VerifyNewGeometry(WId)), this, SLOT(CheckWindowPosition(WId)) ); xcb_map_window(QX11Info::connection(), win->id()); emit NewWindowAvailable(win); } @@ -942,12 +947,12 @@ void NativeWindowSystem::NewMouseRelease(int buttoncode, WId win){ } void NativeWindowSystem::CheckDamageID(WId win){ - qDebug() << "Got Damage Event:" << win; + //qDebug() << "Got Damage Event:" << win; for(int i=0; i<NWindows.length(); i++){ if(NWindows[i]->damageId() == win || NWindows[i]->id() == win || NWindows[i]->frameId()==win){ - qDebug() << " - Found window"; - UpdateWindowImage(NWindows[i]); - NWindows[i]->emit VisualChanged(); + //qDebug() << " - Found window"; + //UpdateWindowImage(NWindows[i]); + NWindows[i]->emit winImageChanged(); return; } } diff --git a/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.h b/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.h index c50babff..5810fc36 100644 --- a/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.h +++ b/src-qt5/core/lumina-desktop-unified/src-events/NativeWindowSystem.h @@ -23,11 +23,6 @@ private: QList<NativeWindowObject*> NWindows; QList<NativeWindowObject*> TWindows; - //Simplifications to find an already-created window object - NativeWindowObject* findWindow(WId id, bool checkRelated = true); - - NativeWindowObject* findTrayWindow(WId id); - //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; @@ -51,14 +46,6 @@ private: } } - // 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(NativeWindowObject* win, QList< NativeWindowObject::Property > props); - void ChangeWindowProperties(NativeWindowObject* win, QList< NativeWindowObject::Property > props, QList<QVariant> vals); - - void SetupNewWindow(NativeWindowObject *win); - void UpdateWindowImage(NativeWindowObject *win); - //Generic private variables bool screenLocked; @@ -66,6 +53,18 @@ public: //enum Property{ None, CurrentWorkspace, Workspaces, VirtualRoots, WorkAreas }; enum MouseButton{NoButton, LeftButton, RightButton, MidButton, BackButton, ForwardButton, TaskButton, WheelUp, WheelDown, WheelLeft, WheelRight}; + //Simplifications to find an already-created window object + NativeWindowObject* findWindow(WId id, bool checkRelated = true); + + NativeWindowObject* findTrayWindow(WId id); + // 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(NativeWindowObject* win, QList< NativeWindowObject::Property > props); + void ChangeWindowProperties(NativeWindowObject* win, QList< NativeWindowObject::Property > props, QList<QVariant> vals); + + void SetupNewWindow(NativeWindowObject *win); + QImage GetWindowImage(NativeWindowObject *win); + NativeWindowSystem(); ~NativeWindowSystem(); @@ -130,6 +129,12 @@ private slots: void RequestPing(WId); void RequestReparent(WId, WId, QPoint); //client, parent, relative origin point in parent + //Window-mgmt functions (see Window-mgmt.cpp for details) + void ArrangeWindows(WId primary, QString type); + void TileWindows(WId primary, QString type); + void CheckWindowPosition(WId id, bool newwindow = false); + void arrangeWindows(NativeWindowObject *primary, QString type, bool primaryonly = false); + signals: void NewWindowAvailable(NativeWindowObject*); void WindowClosed(); diff --git a/src-qt5/core/libLumina/obsolete/RootWindow-mgmt.cpp b/src-qt5/core/lumina-desktop-unified/src-events/Window-mgmt.cpp index 24ea639b..0b45c208 100644 --- a/src-qt5/core/libLumina/obsolete/RootWindow-mgmt.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-events/Window-mgmt.cpp @@ -4,28 +4,38 @@ // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== -#include "RootWindow.h" +#include "NativeWindowSystem.h" +inline QScreen* screenUnderMouse(){ + QPoint pos = QCursor::pos(); + QList<QScreen*> scrns = QGuiApplication::screens(); + for(int i=0; i<scrns.length(); i++){ + if(scrns[i]->geometry().contains(pos)){ return scrns[i]; } + } + return 0; +} + //Primary/private function -void RootWindow::arrangeWindows(RootSubWindow *primary, QString type, bool primaryonly){ +void NativeWindowSystem::arrangeWindows(NativeWindowObject *primary, QString type, bool primaryonly){ if(type.isEmpty()){ type = "center"; } if(primary==0){ //Get the currently active window and treat that as the primary - for(int i=0; i<WINDOWS.length(); i++){ - if(WINDOWS[i]->nativeWindow()->property(NativeWindow::Active).toBool()){ primary = WINDOWS[i]; } + for(int i=0; i<NWindows.length(); i++){ + if(NWindows[i]->property(NativeWindowObject::Active).toBool()){ primary = NWindows[i]; } } - if(primary==0 && !WINDOWS.isEmpty()){ primary = WINDOWS[0]; } //just use the first one in the list + if(primary==0 && !NWindows.isEmpty()){ primary = NWindows[0]; } //just use the first one in the list } //Now get the current screen that the mouse cursor is over (and valid area) QScreen *screen = screenUnderMouse(); + if(screen==0){ return; } //should never happen (theoretically) QRect desktopArea = screen->availableGeometry(); //qDebug() << "Arrange Windows:" << primary->geometry() << type << primaryonly << desktopArea; //Now start filtering out all the windows that need to be ignored - int wkspace = primary->nativeWindow()->property(NativeWindow::Workspace).toInt(); - QList<RootSubWindow*> winlist = WINDOWS; + int wkspace = primary->property(NativeWindowObject::Workspace).toInt(); + QList<NativeWindowObject*> winlist = NWindows; for(int i=0; i<winlist.length(); i++){ - if(winlist[i]->nativeWindow()->property(NativeWindow::Workspace).toInt()!=wkspace - || !winlist[i]->nativeWindow()->property(NativeWindow::Visible).toBool() + if(winlist[i]->property(NativeWindowObject::Workspace).toInt()!=wkspace + || !winlist[i]->property(NativeWindowObject::Visible).toBool() || desktopArea.intersected(winlist[i]->geometry()).isNull() ){ //window is outside of the desired area or invisible - ignore it winlist.removeAt(i); @@ -45,17 +55,17 @@ void RootWindow::arrangeWindows(RootSubWindow *primary, QString type, bool prima //Now apply the proper placement routine if(type=="center"){ QPoint ct = desktopArea.center(); - winlist[i]->setGeometry( ct.x()-(geom.width()/2), ct.y()-(geom.height()/2), geom.width(), geom.height()); + winlist[i]->setGeometryNow( QRect( ct.x()-(geom.width()/2), ct.y()-(geom.height()/2), geom.width(), geom.height()) ); }else if(type=="snap"){ }else if(type=="single_max"){ - winlist[i]->setGeometry( desktopArea.x(), desktopArea.y(), desktopArea.width(), desktopArea.height()); + winlist[i]->setGeometryNow( QRect( desktopArea.x(), desktopArea.y(), desktopArea.width(), desktopArea.height()) ); }else if(type=="under-mouse"){ QPoint ct = QCursor::pos(); geom = QRect(ct.x()-(geom.width()/2), ct.y()-(geom.height()/2), geom.width(), geom.height() ); //Now verify that the top of the window is still contained within the desktop area if(geom.y() < desktopArea.y() ){ geom.moveTop(desktopArea.y()); } - winlist[i]->setGeometry(geom); + winlist[i]->setGeometryNow(geom); } //qDebug() << " - New Geometry:" << winlist[i]->geometry(); @@ -65,31 +75,31 @@ void RootWindow::arrangeWindows(RootSubWindow *primary, QString type, bool prima // ================ // Public slots for starting the arrangement routine(s) above // ================ -void RootWindow::ArrangeWindows(WId primary, QString type){ - RootSubWindow* win = windowForId(primary); +void NativeWindowSystem::ArrangeWindows(WId primary, QString type){ + NativeWindowObject* win = findWindow(primary); if(type.isEmpty()){ type = "center"; } //grab the default arrangement format arrangeWindows(win, type); } -void RootWindow::TileWindows(WId primary, QString type){ - RootSubWindow* win = windowForId(primary); +void NativeWindowSystem::TileWindows(WId primary, QString type){ + NativeWindowObject* win = findWindow(primary); if(type.isEmpty()){ type = "single_max"; } //grab the default arrangement format for tiling arrangeWindows(win, type); } -void RootWindow::CheckWindowPosition(WId id, bool newwindow){ +void NativeWindowSystem::CheckWindowPosition(WId id, bool newwindow){ //used after a "drop" to validate/snap/re-arrange window(s) as needed // if "newwindow" is true, then this is the first-placement routine for a window before it initially appears - RootSubWindow* win = windowForId(id); + NativeWindowObject* win = findWindow(id); if(win==0){ return; } //invalid window - QRect geom = win->nativeWindow()->geometry(); + QRect geom = win->geometry(); bool changed = false; //Make sure it is on the screen (quick check) if(geom.x() < 0){ changed = true; geom.moveLeft(0); } if(geom.y() < 0){ changed = true; geom.moveTop(0); } if(geom.width() < 20){ changed = true; geom.setWidth(100); } if(geom.height() < 20){ changed = true; geom.setHeight(100); } - if(changed){ win->setGeometry(geom); } + if(changed){ win->setGeometryNow(geom); } //Now run it through the window arrangement routine arrangeWindows(win, newwindow ?"center" : "snap", true); } diff --git a/src-qt5/core/lumina-desktop-unified/src-events/events.pri b/src-qt5/core/lumina-desktop-unified/src-events/events.pri index 3f89fdf2..3f586e8b 100644 --- a/src-qt5/core/lumina-desktop-unified/src-events/events.pri +++ b/src-qt5/core/lumina-desktop-unified/src-events/events.pri @@ -5,7 +5,8 @@ LIBS *= -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damag SOURCES *= $${PWD}/LShortcutEvents.cpp \ $${PWD}/NativeEventFilter.cpp \ $${PWD}/NativeKeyToQt.cpp \ - $${PWD}/NativeWindowSystem.cpp + $${PWD}/NativeWindowSystem.cpp \ + $${PWD}/Window-mgmt.cpp HEADERS *= $${PWD}/LShortcutEvents.h \ diff --git a/src-qt5/core/lumina-desktop/panel-plugins/battery/LBattery.cpp b/src-qt5/core/lumina-desktop/panel-plugins/battery/LBattery.cpp index 69ea5faa..1870eefb 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/battery/LBattery.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/battery/LBattery.cpp @@ -78,19 +78,20 @@ void LBattery::updateBattery(bool force){ break; } } - if(icon<iconOld && icon==0){ - //Play some audio warning chime when - bool playaudio = sessionsettings->value("PlayBatteryLowAudio",true).toBool(); - if( playaudio ){ QString sfile = LSession::handle()->sessionSettings()->value("audiofiles/batterylow", LOS::LuminaShare()+"low-battery.ogg").toString(); + if(icon<iconOld && icon==0){ + //Play some audio warning chime when + bool playaudio = sessionsettings->value("PlayBatteryLowAudio",true).toBool(); + if( playaudio ){ + QString sfile = LSession::handle()->sessionSettings()->value("audiofiles/batterylow", LOS::LuminaShare()+"low-battery.ogg").toString(); LSession::handle()->playAudioFile(sfile); - } + } + } - if(icon==0){ label->setStyleSheet("QLabel{ background: red;}"); } - else if(icon==14 && charge>98){ label->setStyleSheet("QLabel{ background: green;}"); } - else{ label->setStyleSheet("QLabel{ background: transparent;}"); } - iconOld = icon; + if(icon==0){ label->setStyleSheet("QLabel{ background: red;}"); } + else if(icon==14 && charge>98){ label->setStyleSheet("QLabel{ background: green;}"); } + else{ label->setStyleSheet("QLabel{ background: transparent;}"); } + iconOld = icon; - } //Now update the display QString tt; //Make sure the tooltip can be properly translated as necessary (Ken Moore 5/9/14) diff --git a/src-qt5/desktop-utils/lumina-archiver/TarBackend.cpp b/src-qt5/desktop-utils/lumina-archiver/TarBackend.cpp index 91e233d0..714cfe45 100644 --- a/src-qt5/desktop-utils/lumina-archiver/TarBackend.cpp +++ b/src-qt5/desktop-utils/lumina-archiver/TarBackend.cpp @@ -114,7 +114,10 @@ void Backend::startAdd(QStringList paths, bool absolutePaths){ paths[i] = paths[i].section(parent,1,-1); if(paths[i].startsWith("/")){ paths[i].remove(0,1); } } - args << "-C" << parent; } + args << "-C" << parent; + }else{ + args << "-C" << "/"; + } args << paths; if(QFile::exists(filepath)){ //append to existing args.replaceInStrings(filepath, tmpfilepath); diff --git a/src-qt5/desktop-utils/lumina-notify/README.md b/src-qt5/desktop-utils/lumina-notify/README.md deleted file mode 100644 index 4772a743..00000000 --- a/src-qt5/desktop-utils/lumina-notify/README.md +++ /dev/null @@ -1,41 +0,0 @@ -### lumina-notify - -This is a simple application for launching QDialogs from shell. - -Use: - -lumina-notify arg1 arg2 arg3 arg4 - -* arg1 = Message Text -* arg2 = Button Text (Accept Role) -* arg3 = Button Text (Reject Role) -* arg4 = Window Title - -For multiple word arguments encapsulate them with "s - -*** -Example usage in shell script - -~~~~ -#!/bin/csh -set a=`./lumina-notify "Did Jar Jar do anything wrong?" Yes No "Question"` -if ($a == 1) then -set b=`./lumina-notify "Are you sure?" Yes No "Are you Sure?"` -if ($b == 1) then -./lumina-notify "Please stop being a hater." Ok Ok "Haters gunna hate" -else -set c=`./lumina-notify "Thank you for changing your mind" OK OK "You're Awesome"` -endif -else -./lumina-notify "High Five for Darth Jar Jar" Sure OK "Respect" -endif` -~~~~ - -*** - -### Library Dependencies - -1. Qt 5.0+ (specific modules listed below) - * core - * gui - * widgets diff --git a/src-qt5/desktop-utils/lumina-notify/lumina-notify.pro b/src-qt5/desktop-utils/lumina-notify/lumina-notify.pro deleted file mode 100644 index 7aa09583..00000000 --- a/src-qt5/desktop-utils/lumina-notify/lumina-notify.pro +++ /dev/null @@ -1,19 +0,0 @@ -#------------------------------------------------- -# Created by q5sys -# Released under MIT License 2017-03-08 -#------------------------------------------------- -include($${PWD}/../../OS-detect.pri) - -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = lumina-notify -target.path = $${L_BINDIR} -TEMPLATE = app - -DEFINES += QT_DEPRECATED_WARNINGS - -SOURCES += main.cpp - -INSTALLS += target desktop diff --git a/src-qt5/desktop-utils/lumina-notify/main.cpp b/src-qt5/desktop-utils/lumina-notify/main.cpp deleted file mode 100644 index 23f30b95..00000000 --- a/src-qt5/desktop-utils/lumina-notify/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//------------------------------------------------- -// Created by q5sys (JT) -// Released under MIT License 2017-03-08 -// A Simple GUI Dialog Program -//------------------------------------------------- - -#include <QApplication> -#include <QMessageBox> -#include <QDebug> -#include <QTextStream> - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - a.setAttribute(Qt::AA_UseHighDpiPixmaps); - int answer; - QMessageBox *messageBox = new QMessageBox; - messageBox->setText(argv[1]); - QPushButton *pushButtonOk = messageBox->addButton(argv[2], QMessageBox::AcceptRole); - QPushButton *pushButtonNo = messageBox->addButton(argv[3], QMessageBox::RejectRole); - messageBox->QDialog::setWindowTitle(argv[4]); - messageBox->show(); - if(messageBox->exec() == QMessageBox::AcceptRole){ answer = 0; QTextStream cout(stdout); cout << answer;} - else { answer = 1; QTextStream cout(stdout); cout << answer;} -} diff --git a/src-qt5/src-cpp/framework-OSInterface-template.cpp b/src-qt5/src-cpp/framework-OSInterface-template.cpp index 972e02e0..7921511f 100644 --- a/src-qt5/src-cpp/framework-OSInterface-template.cpp +++ b/src-qt5/src-cpp/framework-OSInterface-template.cpp @@ -5,23 +5,6 @@ // See the LICENSE file for full details //=========================================== #include <framework-OSInterface.h> -#include <QNetworkConfiguration> -#include <QNetworkInterface> - -//Start/stop interface watchers/notifications -void OSInterface::start(){ - setupMediaWatcher(); //will create/connect the filesystem watcher automatically - setupNetworkManager(); -} - -void OSInterface::stop(){ - if(isRunning()){ - watcher->deleteLater(); - watcher = 0; - } -} - -bool OSInterface::isRunning(){ return (watcher!=0); } //status of the object - whether it has been started yet // = Battery = bool OSInterface::batteryAvailable(){ return false; } @@ -30,39 +13,27 @@ bool OSInterface::batteryCharging(){ return false; } double OSInterface::batterySecondsLeft(){ return -1; } // = Volume = -bool OSInterface::volumeAvailable(){ return false; } +bool OSInterface::volumeSupported(){ return false; } int OSInterface::volume(){ return -1; } void OSInterface::setVolume(int){} // = Network Information = -bool OSInterface::networkAvailable(){ - if(INFO.contains("netaccess/available")){ return INFO.value("netaccess/available").toBool(); } - return false; -} - -QString OSInterface::networkType(){ - if(INFO.contains("netaccess/type")){ return INFO.value("netaccess/type").toString(); } //"wifi", "wired", or "cell" +QString OSInterface::networkTypeFromDeviceName(QString name){ + //Return options: wifi, wired, cell, cell-2G, cell-3G, cell-4G return ""; } -float OSInterface::networkStrength(){ return -1; } //percentage. ("wired" type should always be 100%) - -QString OSInterface::networkHostname(){ - return QHostInfo::localHostName(); -} - -QHostAddress OSInterface::networkAddress(){ - QString addr; - if(INFO.contains("netaccess/address")){ addr = INFO.value("netaccess/address").toString(); } - return QHostAddress(addr); +float OSInterface::networkStrength(){ + //QString device = INFO.value("netaccess/devicename"); + return -1; //percentage. ("wired" type should always be 100%) } -// = Network Modification = // = Media Shortcuts = QStringList OSInterface::mediaDirectories(){ return QStringList() << "/media"; } //directory where XDG shortcuts are placed for interacting with media (local/remote) QStringList OSInterface::mediaShortcuts(){ return autoHandledMediaFiles(); } //List of currently-available XDG shortcut file paths // = Updates = +bool OSInterface::updatesSupported(){ return false; } bool OSInterface::updatesAvailable(){ return false; } QString OSInterface::updateDetails(){ return QString(); } //Information about any available updates bool OSInterface::updatesRunning(){ return false; } @@ -71,6 +42,7 @@ bool OSInterface::updatesFinished(){ return false; } QString OSInterface::updateResults(){ return QString(); } //Information about any finished update void OSInterface::startUpdates(){} bool OSInterface::updateOnlyOnReboot(){ return false; } //Should the startUpdates function be called only when rebooting the system? +bool OSInterface::updateCausesReboot(){ return false; } QDateTime OSInterface::lastUpdate(){ return QDateTime(); } //The date/time of the previous updates QString OSInterface::lastUpdateResults(){ return QString(); } //Information about the previously-finished update @@ -83,68 +55,39 @@ bool OSInterface::canSuspend(){ return false; } void OSInterface::startSuspend(){} // = Screen Brightness = +bool OSInterface::brightnessSupported(){ return false; } int OSInterface::brightness(){ return -1; } //percentage: 0-100 with -1 for errors void OSInterface::setBrightness(int){} // = System Status Monitoring +bool OSInterface::cpuSupported(){ return false; } QList<int> OSInterface::cpuPercentage(){ return QList<int>(); } // (one per CPU) percentage: 0-100 with empty list for errors QStringList OSInterface::cpuTemperatures(){ return QStringList(); } // (one per CPU) Temperature of CPU ("50C" for example) + +bool OSInterface::memorySupported(){ return false; } int OSInterface::memoryUsedPercentage(){ return -1; } //percentage: 0-100 with -1 for errors QString OSInterface::memoryTotal(){ return QString(); } //human-readable form - does not tend to change within a session QStringList OSInterface::diskIO(){ return QStringList(); } //Returns list of current read/write stats for each device + +bool OSInterface::diskSupported(){ return false; } int OSInterface::fileSystemPercentage(QString dir){ return -1; } //percentage of capacity used: 0-100 with -1 for errors QString OSInterface::fileSystemCapacity(QString dir){ return QString(); } //human-readable form - total capacity // = OS-Specific Utilities = -bool OSInterface::hasControlPanel(){ return false; } QString OSInterface::controlPanelShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop") -bool OSInterface::hasAudioMixer(){ return false; } QString OSInterface::audioMixerShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop") -bool OSInterface::hasAppStore(){ return false; } QString OSInterface::appStoreShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop") +QString OSInterface::networkManagerUtility(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop") -//FileSystemWatcher slots -void OSInterface::watcherFileChanged(QString){} -void OSInterface::watcherDirChanged(QString dir){ - if(handleMediaDirChange(dir)){ return; } +//FileSystemWatcher slots (optional - re-implement only if needed/used by this OS) +void OSInterface::watcherFileChanged(QString){} //any additional parsing for files that are watched +void OSInterface::watcherDirChanged(QString dir){ //any additional parsing for watched directories + if(handleMediaDirChange(dir)){ return; } //auto-handled media directories } -//IO Device slots +//IO Device slots (optional - implement only if needed/used by this OS) void OSInterface::iodeviceReadyRead(){} void OSInterface::iodeviceAboutToClose(){} -//NetworkAccessManager slots -void OSInterface::netAccessChanged(QNetworkAccessManager::NetworkAccessibility stat){ - INFO.insert("netaccess/available", stat== QNetworkAccessManager::Accessible); - //Update all the other network status info at the same time - QNetworkConfiguration active = netman->activeConfiguration(); - //Type of connection - QString type; - switch(active.bearerTypeFamily()){ - case QNetworkConfiguration::BearerEthernet: type="wired"; break; - case QNetworkConfiguration::BearerWLAN: type="wifi"; break; - case QNetworkConfiguration::Bearer2G: type="cell-2G"; break; - case QNetworkConfiguration::Bearer3G: type="cell-3G"; break; - case QNetworkConfiguration::Bearer4G: type="cell-4G"; break; - default: type=""; - } - INFO.insert("netaccess/type", type); - qDebug() << "Detected Device Status:" << active.identifier() << type << stat; - QNetworkInterface iface = QNetworkInterface::interfaceFromName(active.name()); - qDebug() << " - Configuration: Name:" << active.name() << active.bearerTypeName() << active.identifier(); - qDebug() << " - Interface: MAC Address:" << iface.hardwareAddress() << "Name:" << iface.name() << iface.humanReadableName() << iface.isValid(); - QList<QNetworkAddressEntry> addressList = iface.addressEntries(); - QStringList address; - //NOTE: There are often 2 addresses, IPv4 and IPv6 - for(int i=0; i<addressList.length(); i++){ - address << addressList[i].ip().toString(); - } - qDebug() << " - IP Address:" << address; - qDebug() << " - Hostname:" << networkHostname(); - INFO.insert("netaccess/address", address.join(", ")); - emit networkStatusChanged(); -} - void OSInterface::netRequestFinished(QNetworkReply*){} void OSInterface::netSslErrors(QNetworkReply*, const QList<QSslError>&){} -void OSInterface::timerUpdate(){} diff --git a/src-qt5/src-cpp/framework-OSInterface.h b/src-qt5/src-cpp/framework-OSInterface.h index a173ad5a..f8345bac 100644 --- a/src-qt5/src-cpp/framework-OSInterface.h +++ b/src-qt5/src-cpp/framework-OSInterface.h @@ -18,6 +18,9 @@ #include <QVariant> #include <QHash> #include <QTimer> +#include <QFile> +#include <QDir> +#include <QVariant> #include <QIODevice> #include <QFileSystemWatcher> @@ -26,6 +29,11 @@ #include <QSslError> #include <QHostInfo> #include <QHostAddress> +#include <QNetworkConfiguration> +#include <QNetworkInterface> + +//Lumina Utils class +#include <LUtils.h> class OSInterface : public QObject{ Q_OBJECT @@ -69,22 +77,30 @@ public: Q_INVOKABLE float batteryCharge(); Q_INVOKABLE bool batteryCharging(); Q_INVOKABLE double batterySecondsLeft(); + // = Volume = - Q_INVOKABLE bool volumeAvailable(); + Q_INVOKABLE bool volumeSupported(); Q_INVOKABLE int volume(); Q_INVOKABLE void setVolume(int); + // = Network Information = Q_INVOKABLE bool networkAvailable(); Q_INVOKABLE QString networkType(); //"wifi", "wired", "cell", "cell-2G", "cell-3G", "cell-4G" + Q_INVOKABLE QString networkTypeFromDeviceName(QString name); //wifi, wired, cell, cell-2G, cell-3G, cell-4G Q_INVOKABLE float networkStrength(); //percentage. ("wired" type should always be 100%) Q_INVOKABLE QString networkHostname(); Q_INVOKABLE QHostAddress networkAddress(); + // = Network Modification = + Q_INVOKABLE bool hasNetworkManager(); + Q_INVOKABLE QString networkManagerUtility(); //binary name or *.desktop filename (if registered on the system) // = Media Shortcuts = Q_INVOKABLE QStringList mediaDirectories(); //directory where XDG shortcuts are placed for interacting with media (local/remote) Q_INVOKABLE QStringList mediaShortcuts(); //List of currently-available XDG shortcut file paths + // = Updates = + Q_INVOKABLE bool updatesSupported(); //is thie subsystem supported for the OS? Q_INVOKABLE bool updatesAvailable(); Q_INVOKABLE QString updateDetails(); //Information about any available updates Q_INVOKABLE bool updatesRunning(); @@ -93,8 +109,10 @@ public: Q_INVOKABLE QString updateResults(); //Information about any finished update Q_INVOKABLE void startUpdates(); Q_INVOKABLE bool updateOnlyOnReboot(); //Should the startUpdates function be called only when rebooting the system? + Q_INVOKABLE bool updateCausesReboot(); //Does the update power-cycle the system? Q_INVOKABLE QDateTime lastUpdate(); //The date/time of the previous updates Q_INVOKABLE QString lastUpdateResults(); //Information about the previously-finished update + // = System Power = Q_INVOKABLE bool canReboot(); Q_INVOKABLE void startReboot(); @@ -102,17 +120,26 @@ public: Q_INVOKABLE void startShutdown(); Q_INVOKABLE bool canSuspend(); Q_INVOKABLE void startSuspend(); + // = Screen Brightness = + Q_INVOKABLE bool brightnessSupported(); //is this subsystem available for the OS? Q_INVOKABLE int brightness(); //percentage: 0-100 with -1 for errors Q_INVOKABLE void setBrightness(int); + // = System Status Monitoring + Q_INVOKABLE bool cpuSupported(); //is this subsystem available for the OS? Q_INVOKABLE QList<int> cpuPercentage(); // (one per CPU) percentage: 0-100 with -1 for errors Q_INVOKABLE QStringList cpuTemperatures(); // (one per CPU) Temperature of CPU ("50C" for example) + + Q_INVOKABLE bool memorySupported(); //is this subsystem available for the OS? Q_INVOKABLE int memoryUsedPercentage(); //percentage: 0-100 with -1 for errors Q_INVOKABLE QString memoryTotal(); //human-readable form - does not tend to change within a session Q_INVOKABLE QStringList diskIO(); //Returns list of current read/write stats for each device + + Q_INVOKABLE bool diskSupported(); //is this subsystem available for the OS? Q_INVOKABLE int fileSystemPercentage(QString dir); //percentage of capacity used: 0-100 with -1 for errors Q_INVOKABLE QString fileSystemCapacity(QString dir); //human-readable form - total capacity + // = OS-Specific Utilities = Q_INVOKABLE bool hasControlPanel(); Q_INVOKABLE QString controlPanelShortcut(); //relative *.desktop shortcut name (Example: "some_utility.desktop") @@ -121,10 +148,14 @@ public: Q_INVOKABLE bool hasAppStore(); Q_INVOKABLE QString appStoreShortcut(); //relative *.desktop shortcut name (Example: "some_utility.desktop") + + // QML-Accessible properties/functions + private slots: // ================ // SEMI-VIRTUAL FUNCTIONS - NEED TO BE DEFINED IN THE OS-SPECIFIC FILES // ================ + //FileSystemWatcher slots void watcherFileChanged(QString); void watcherDirChanged(QString); @@ -136,7 +167,13 @@ private slots: void netRequestFinished(QNetworkReply*); void netSslErrors(QNetworkReply*, const QList<QSslError>&); //Timer slots - void timerUpdate(); + void BatteryTimerUpdate(); + void UpdateTimerUpdate(); + void BrightnessTimerUpdate(); + void VolumeTimerUpdate(); + void CpuTimerUpdate(); + void MemTimerUpdate(); + void DiskTimerUpdate(); signals: void batteryChargeChanged(); @@ -152,6 +189,7 @@ signals: private: //Internal persistant data storage, OS-specific usage implementation QHash< QString, QVariant> INFO; + bool _started; // ============ // Internal possibilities for watching the system (OS-Specific usage/implementation) @@ -163,14 +201,16 @@ private: //Network Access Manager (check network connectivity, etc) QNetworkAccessManager *netman; //Timer for regular probes/updates - QTimer *timer; + QTimer *batteryTimer, *updateTimer, *brightnessTimer, *volumeTimer, *cpuTimer, *memTimer, *diskTimer; // Internal implifications for connecting the various watcher objects to their respective slots // (OS-agnostic - defined in the "OSInterface_private.cpp" file) void connectWatcher(); //setup the internal connections *only* void connectIodevice(); //setup the internal connections *only* void connectNetman(); //setup the internal connections *only* - void connectTimer(); //setup the internal connections *only* + + //Internal simplification routines + bool verifyAppOrBin(QString chk); // External Media Management (if system uses *.desktop shortcuts only) void setupMediaWatcher(); @@ -180,6 +220,24 @@ private: // Qt-based NetworkAccessManager usage void setupNetworkManager(); + // Timer-based monitors + void setupBatteryMonitor(int update_ms, int delay_ms); + void setupUpdateMonitor(int update_ms, int delay_ms); + void setupBrightnessMonitor(int update_ms, int delay_ms); + void setupVolumeMonitor(int update_ms, int delay_ms); + void setupCpuMonitor(int update_ms, int delay_ms); + void setupMemoryMonitor(int update_ms, int delay_ms); + void setupDiskMonitor(int update_ms, int delay_ms); + + // Timer-based monitor update routines (NOTE: these are all run in a separate thread!!) + void syncBatteryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncUpdateInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncBrightnessInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncVolumeInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncCpuInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncMemoryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + void syncDiskInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer); + public: OSInterface(QObject *parent = 0); ~OSInterface(); diff --git a/src-qt5/src-cpp/framework-OSInterface_private.cpp b/src-qt5/src-cpp/framework-OSInterface_private.cpp index d15d9be8..d633fe9a 100644 --- a/src-qt5/src-cpp/framework-OSInterface_private.cpp +++ b/src-qt5/src-cpp/framework-OSInterface_private.cpp @@ -7,9 +7,7 @@ // Internal, OS-agnostic functionality for managing the object itself //=========================================== #include <framework-OSInterface.h> -#include <QFile> -#include <QDir> -#include <QVariant> +#include <QtConcurrent> OSInterface::OSInterface(QObject *parent) : QObject(parent){ watcher = 0; @@ -40,6 +38,33 @@ OSInterface* OSInterface::instance(){ return m_os_object; } +//Start/stop interface systems +void OSInterface::start(){ + if(!mediaDirectories().isEmpty()){ setupMediaWatcher(); }//will create/connect the filesystem watcher automatically + setupNetworkManager(); //will create/connect the network monitor automatically + if(batteryAvailable()){ setupBatteryMonitor(30000, 1); } //30 second updates, 1 ms init delay + if(brightnessSupported()){ setupBrightnessMonitor(60000, 1); } //1 minute updates, 1 ms init delay + if(volumeSupported()){ setupVolumeMonitor(60000, 2); } //1 minute updates, 2 ms init delay + if(updatesSupported()){ setupUpdateMonitor(12*60*60*1000, 5*60*1000); } //12-hour updates, 5 minute delay + if(cpuSupported()){ setupCpuMonitor(2000, 20); } //2 second updates, 20 ms init delay + if(memorySupported()){ setupMemoryMonitor(2000, 21); } //2 second updates, 21 ms init delay + if(diskSupported()){ setupDiskMonitor(60000, 25); } //1 minute updates, 25 ms init delay +} + +void OSInterface::stop(){ + if(watcher!=0){ watcher->deleteLater(); watcher=0; } + if(batteryTimer!=0){ batteryTimer->stop(); disconnect(batteryTimer); } + if(brightnessTimer!=0){ brightnessTimer->stop(); disconnect(brightnessTimer); } + if(volumeTimer!=0){ volumeTimer->stop(); disconnect(volumeTimer); } + if(updateTimer!=0){ updateTimer->stop(); disconnect(updateTimer); } + if(cpuTimer!=0){ cpuTimer->stop(); disconnect(cpuTimer); } + if(memTimer!=0){ memTimer->stop(); disconnect(memTimer); } + if(diskTimer!=0){ diskTimer->stop(); disconnect(diskTimer); } + if(netman!=0){ disconnect(netman); netman->deleteLater(); netman = 0; } +} + +bool OSInterface::isRunning(){ return _started; } //status of the object - whether it has been started yet + void OSInterface::connectWatcher(){ if(watcher==0){ return; } connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherFileChanged(QString)) ); @@ -58,11 +83,36 @@ void OSInterface::connectNetman(){ connect(netman, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(netSslErrors(QNetworkReply*, const QList<QSslError>&)) ); } -void OSInterface::connectTimer(){ - if(timer==0){ return; } - connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()) ); +bool OSInterface::verifyAppOrBin(QString chk){ + bool valid = !chk.isEmpty(); + if(valid && chk.endsWith(".desktop")){ + chk = LUtils::AppToAbsolute(chk); + valid = QFile::exists(chk); + }else if(valid){ + valid = LUtils::isValidBinary(chk); + } + return valid; +} + +// =========================== +// OS SPECIFIC EXISTANCE CHECKS +// =========================== +bool OSInterface::hasControlPanel(){ + return verifyAppOrBin(controlPanelShortcut()); +} + +bool OSInterface::hasAudioMixer(){ + return verifyAppOrBin(audioMixerShortcut()); +} + +bool OSInterface::hasAppStore(){ + return verifyAppOrBin(appStoreShortcut()); } +// ======================== +// MEDIA DIRECTORIES +// ======================== + // External Media Management (if system uses *.desktop shortcuts) void OSInterface::setupMediaWatcher(){ //Create/connect the watcher if needed @@ -99,6 +149,9 @@ QStringList OSInterface::autoHandledMediaFiles(){ return files; } +// ============================= +// NETWORK INTERFACE FUNCTIONS +// ============================= // Qt-based NetworkAccessManager usage void OSInterface::setupNetworkManager(){ if(netman==0){ @@ -108,3 +161,179 @@ void OSInterface::setupNetworkManager(){ //Load the initial state of the network accessibility netAccessChanged(netman->networkAccessible()); } + +bool OSInterface::networkAvailable(){ + if(INFO.contains("netaccess/available")){ return INFO.value("netaccess/available").toBool(); } + return false; +} + +QString OSInterface::networkType(){ + if(INFO.contains("netaccess/type")){ return INFO.value("netaccess/type").toString(); } //"wifi", "wired", or "cell" + return ""; +} + +QString OSInterface::networkHostname(){ + return QHostInfo::localHostName(); +} + +QHostAddress OSInterface::networkAddress(){ + QString addr; + if(INFO.contains("netaccess/address")){ addr = INFO.value("netaccess/address").toString(); } + return QHostAddress(addr); +} + +bool OSInterface::hasNetworkManager(){ + return verifyAppOrBin(networkManagerUtility()); +} + +//NetworkAccessManager slots +void OSInterface::netAccessChanged(QNetworkAccessManager::NetworkAccessibility stat){ + INFO.insert("netaccess/available", stat== QNetworkAccessManager::Accessible); + //Update all the other network status info at the same time + QNetworkConfiguration active = netman->activeConfiguration(); + //Type of connection + QString type; + switch(active.bearerTypeFamily()){ + case QNetworkConfiguration::BearerEthernet: type="wired"; break; + case QNetworkConfiguration::BearerWLAN: type="wifi"; break; + case QNetworkConfiguration::Bearer2G: type="cell-2G"; break; + case QNetworkConfiguration::Bearer3G: type="cell-3G"; break; + case QNetworkConfiguration::Bearer4G: type="cell-4G"; break; + default: type=networkTypeFromDeviceName(active.name()); //could not be auto-determined - run the OS-specific routine + } + INFO.insert("netaccess/type", type); + //qDebug() << "Detected Device Status:" << active.identifier() << type << stat; + QNetworkInterface iface = QNetworkInterface::interfaceFromName(active.name()); + //qDebug() << " - Configuration: Name:" << active.name() << active.bearerTypeName() << active.identifier(); + //qDebug() << " - Interface: MAC Address:" << iface.hardwareAddress() << "Name:" << iface.name() << iface.humanReadableName() << iface.isValid(); + QList<QNetworkAddressEntry> addressList = iface.addressEntries(); + QStringList address; + //NOTE: There are often 2 addresses, IPv4 and IPv6 + for(int i=0; i<addressList.length(); i++){ + address << addressList[i].ip().toString(); + } + //qDebug() << " - IP Address:" << address; + //qDebug() << " - Hostname:" << networkHostname(); + INFO.insert("netaccess/address", address.join(", ")); + emit networkStatusChanged(); +} + + +// ======================== +// TIMER-BASED MONITORS +// ======================== +//Timer slots +void OSInterface::BatteryTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncBatteryInfo, this, &INFO, batteryTimer); +} + +void OSInterface::UpdateTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncUpdateInfo, this, &INFO, updateTimer); +} + +void OSInterface::BrightnessTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncBrightnessInfo, this, &INFO, brightnessTimer); +} + +void OSInterface::VolumeTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncVolumeInfo, this, &INFO, volumeTimer); +} + +void OSInterface::CpuTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncCpuInfo, this, &INFO, cpuTimer); +} + +void OSInterface::MemTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncMemoryInfo, this, &INFO, memTimer); +} + +void OSInterface::DiskTimerUpdate(){ + QtConcurrent::run(this, &OSInterface::syncDiskInfo, this, &INFO, diskTimer); +} + +// Timer Setup functions +void OSInterface::setupBatteryMonitor(int update_ms, int delay_ms){ + batteryTimer = new QTimer(this); + batteryTimer->setSingleShot(true); + batteryTimer->setInterval(update_ms); + connect(batteryTimer, SIGNAL(timeout()), this, SLOT(BatteryTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(BatteryTimerUpdate()) ); +} +void OSInterface::setupUpdateMonitor(int update_ms, int delay_ms){ + updateTimer = new QTimer(this); + updateTimer->setSingleShot(true); + updateTimer->setInterval(update_ms); + connect(updateTimer, SIGNAL(timeout()), this, SLOT(UpdateTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(UpdateTimerUpdate()) ); +} +void OSInterface::setupBrightnessMonitor(int update_ms, int delay_ms){ + brightnessTimer = new QTimer(this); + brightnessTimer->setSingleShot(true); + brightnessTimer->setInterval(update_ms); + connect(brightnessTimer, SIGNAL(timeout()), this, SLOT(BrightnessTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(BrightnessTimerUpdate()) ); +} +void OSInterface::setupVolumeMonitor(int update_ms, int delay_ms){ + volumeTimer = new QTimer(this); + volumeTimer->setSingleShot(true); + volumeTimer->setInterval(update_ms); + connect(volumeTimer, SIGNAL(timeout()), this, SLOT(VolumeTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(VolumeTimerUpdate()) ); +} +void OSInterface::setupCpuMonitor(int update_ms, int delay_ms){ + cpuTimer = new QTimer(this); + cpuTimer->setSingleShot(true); + cpuTimer->setInterval(update_ms); + connect(cpuTimer, SIGNAL(timeout()), this, SLOT(CpuTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(CpuTimerUpdate()) ); +} +void OSInterface::setupMemoryMonitor(int update_ms, int delay_ms){ + memTimer = new QTimer(this); + memTimer->setSingleShot(true); + memTimer->setInterval(update_ms); + connect(memTimer, SIGNAL(timeout()), this, SLOT(MemTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(MemTimerUpdate()) ); +} +void OSInterface::setupDiskMonitor(int update_ms, int delay_ms){ + diskTimer = new QTimer(this); + diskTimer->setSingleShot(true); + diskTimer->setInterval(update_ms); + connect(diskTimer, SIGNAL(timeout()), this, SLOT(DiskTimerUpdate()) ); + QTimer::singleShot(delay_ms, this, SLOT(DiskTimerUpdate()) ); +} + +// Timer-based monitor update routines (NOTE: these are all run in a separate thread!!) +void OSInterface::syncBatteryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncUpdateInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncBrightnessInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncVolumeInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncCpuInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncMemoryInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} + +void OSInterface::syncDiskInfo(OSInterface *os, QHash<QString, QVariant> *hash, QTimer *timer){ + + QTimer::singleShot(0, timer, SLOT(start())); +} |