diff options
69 files changed, 2554 insertions, 182 deletions
@@ -11,6 +11,7 @@ https://lumina-desktop.org/ **Lumina Project Info: Table of Contents** - [Description](#description) +- [Operating Systems and Distributions](#osdistros) - [Latest Versions](#latestversions) - [Translations](#translations) - [How to file bug reports or feature requests](#filebugs) @@ -23,10 +24,26 @@ Description <a name="description"></a> The Lumina Desktop Environment is a lightweight system interface that is designed for use on any Unix-like operating system. It takes a plugin-based approach, allowing the entire interface to be assembled/arranged by each individual user as desired, with a system-wide default layout which can be setup by the system administrator. This allows every system (or user session) to be designed to maximize the individual user's productivity. + +Operating Systems and Distributions <a name="osdistros"></a> +---- Supported operating systems include (but are not limited to): * BSD: TrueOS, FreeBSD, OpenBSD, NetBSD, DragonflyBSD, GNU/kFreeBSD - * Linux: Gentoo, Debian, "Generic" Linux (including Arch, Ubuntu, and more) + * Linux: Void, Gentoo, Debian, "Generic" Linux (including Arch, Ubuntu, and more) + +Known OS distributions that provide the Lumina desktop out-of-box: + + * [Project Trident (Void Linux)](https://project-trident.org) + +Some operating systems package the Lumina desktop under the name `lumina` and some use `lumina-desktop`. + +Operating system repos with a `lumina` package + +[![Packaging status](https://repology.org/badge/vertical-allrepos/lumina.svg)](https://repology.org/project/lumina/versions) + +Operating system repos with a `lumina-desktop` package +[![Packaging status](https://repology.org/badge/vertical-allrepos/lumina-desktop.svg)](https://repology.org/project/lumina-desktop/versions) Latest Versions <a name="latestversions"></a> ---- diff --git a/src-qt5/OS-detect.pri b/src-qt5/OS-detect.pri index 2214016d..b61a137e 100644 --- a/src-qt5/OS-detect.pri +++ b/src-qt5/OS-detect.pri @@ -30,8 +30,7 @@ isEmpty(OS){ QMAKE_LIBDIR = $${PWD}/core/libLumina $$[QT_INSTALL_LIBS] $$LIBPREFIX/qt5 $$LIBPREFIX #Setup the default values for build settings (if not explicitly set previously) - isEmpty(PREFIX){ PREFIX=/usr/local } - isEmpty(LIBPREFIX){ LIBPREFIX=$${PREFIX}/lib } + #message("General Settings: PREFIX=$${PREFIX}, LIBPREFIX=$${LIBPREFIX}") #Now go through and setup any known OS build settings # which are different from the defaults @@ -41,21 +40,25 @@ isEmpty(OS){ LIBS += -L/usr/local/lib -L/usr/lib }else : freebsd-*{ + isEmpty(PREFIX){ PREFIX=/usr/local } OS = FreeBSD LIBS += -L/usr/local/lib -L/usr/lib #Use the defaults for everything else }else : openbsd-*{ + isEmpty(PREFIX){ PREFIX=/usr/local } OS = OpenBSD LIBS += -L/usr/local/lib -L/usr/lib #Use the defaults for everything else }else : netbsd-*{ + isEmpty(PREFIX){ PREFIX=/usr/local } OS = NetBSD LIBS += -L/usr/local/lib -L/usr/lib #Use the defaults for everything else }else : linux-*{ + isEmpty(PREFIX){ PREFIX=/usr } L_SESSDIR=/usr/share/xsessions OS=Linux LIBS += -L/usr/local/lib -L/usr/lib -L/lib @@ -87,6 +90,7 @@ isEmpty(OS){ isEmpty(L_MANDIR){ L_MANDIR = $${PREFIX}/share/man } } }else{ + isEmpty(PREFIX){ PREFIX=/usr/local } OS="Unknown"; } @@ -96,6 +100,7 @@ isEmpty(OS){ # Setup the dirs needed to find/load libraries INCLUDEPATH +=$${PREFIX}/include + isEmpty(LIBPREFIX){ LIBPREFIX=$${PREFIX}/lib } # If the detailed install variables are not set - create them from the general vars isEmpty(L_BINDIR){ L_BINDIR = $${PREFIX}/bin } diff --git a/src-qt5/core-utils/lumina-xconfig/MainUI.cpp b/src-qt5/core-utils/lumina-xconfig/MainUI.cpp index 9abc30b2..98a8be0a 100644 --- a/src-qt5/core-utils/lumina-xconfig/MainUI.cpp +++ b/src-qt5/core-utils/lumina-xconfig/MainUI.cpp @@ -414,7 +414,7 @@ void MainUI::SaveSettings(bool quiet){ } void MainUI::RestartFluxbox(){ - QProcess::startDetached("killall fluxbox"); + QProcess::startDetached("pkill fluxbox"); } void MainUI::removeProfile(){ diff --git a/src-qt5/core/core.pro b/src-qt5/core/core.pro index 1ae58167..452d2fb7 100644 --- a/src-qt5/core/core.pro +++ b/src-qt5/core/core.pro @@ -8,9 +8,10 @@ SUBDIRS+= lumina-desktop \ lumina-session \ lumina-open \ lumina-info \ + lumina-pingcursor \ $${PWD}/../../icon-theme \ - lumina-theme-engine -# lumina-checkpass + lumina-theme-engine \ + lumina-checkpass # lumina-desktop-unified #Also install any special menu scripts diff --git a/src-qt5/core/libLumina/LuminaX11.cpp b/src-qt5/core/libLumina/LuminaX11.cpp index 1df5a9f0..6a71f629 100644 --- a/src-qt5/core/libLumina/LuminaX11.cpp +++ b/src-qt5/core/libLumina/LuminaX11.cpp @@ -27,6 +27,7 @@ #include <xcb/xcb_aux.h> #include <xcb/composite.h> #include <xcb/damage.h> +#include <xcb/dpms.h> //XLib includes #include <X11/extensions/Xdamage.h> @@ -184,6 +185,17 @@ void LXCB::SetCurrentWorkspace(int number){ xcb_ewmh_request_change_showing_desktop(&EWMH, QX11Info::appScreen(), number); } +//Display Power Management System (DPMS) +// ===== enableDPMS() ===== +void LXCB::enableDPMS(){ + xcb_dpms_enable(QX11Info::connection()); +} + +// ===== disableDPMS() ===== +void LXCB::disableDPMS(){ + xcb_dpms_disable(QX11Info::connection()); +} + // === WindowClass() === QString LXCB::WindowClass(WId win){ if(DEBUG){ qDebug() << "XCB: WindowClass()" << win; } @@ -462,12 +474,12 @@ int LXCB::WindowIsFullscreen(WId win){ int i = 0; for(it = screens.constBegin(); it != screens.constEnd(); ++it, ++i) { QRect sgeom = (*it)->availableGeometry(); - qDebug() << " -- Check Window Geom:" << sgeom << geom << this->WindowClass(win); + //qDebug() << " -- Check Window Geom:" << sgeom << geom << this->WindowClass(win); if( sgeom.contains(geom.center()) ){ //Allow a 1 pixel variation in "full-screen" detection - qDebug() << " -- Found Screen:" << i; + //qDebug() << " -- Found Screen:" << i; if( geom.width() >= (sgeom.width()-1) && geom.height()>=(sgeom.height()-1) ){ - qDebug() << " -- Is Fullscreen!"; + //qDebug() << " -- Is Fullscreen!"; //fullS = true; fscreen = i; } diff --git a/src-qt5/core/libLumina/LuminaX11.h b/src-qt5/core/libLumina/LuminaX11.h index 453d3443..a2c0c58a 100644 --- a/src-qt5/core/libLumina/LuminaX11.h +++ b/src-qt5/core/libLumina/LuminaX11.h @@ -90,7 +90,7 @@ public: //XCB Library replacement for LX11 (Qt5 uses XCB instead of XLib) class LXCB{ - + public: enum WINDOWVISIBILITY {IGNORE, INVISIBLE, VISIBLE, ACTIVE, ATTENTION}; //note that this in order of priority enum ICCCM_STATE {WITHDRAWN, NORMAL, ICONIC}; @@ -123,6 +123,9 @@ public: bool CheckDisableXinerama(); //returns true if Xinerama was initially set but now disabled void RegisterVirtualRoots(QList<WId> roots); void SetCurrentWorkspace(int); + //Display Power Management System (DPMS) + void enableDPMS(); + void disableDPMS(); //Window Information QString WindowClass(WId); diff --git a/src-qt5/core/libLumina/LuminaX11.pri b/src-qt5/core/libLumina/LuminaX11.pri index 0e472dd4..e22b83ad 100644 --- a/src-qt5/core/libLumina/LuminaX11.pri +++ b/src-qt5/core/libLumina/LuminaX11.pri @@ -1,7 +1,7 @@ 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-dpms -lXdamage #LUtils Files SOURCES *= $${PWD}/LuminaX11.cpp diff --git a/src-qt5/core/lumina-checkpass/main.c b/src-qt5/core/lumina-checkpass/main.c index 70caf396..f7d55c7d 100644 --- a/src-qt5/core/lumina-checkpass/main.c +++ b/src-qt5/core/lumina-checkpass/main.c @@ -22,7 +22,18 @@ //PAM/security libraries #include <sys/types.h> #include <security/pam_appl.h> -#include <security/openpam.h> + +//Found this little snippet from SDDM - nice alternative to using the entire openpam library from FreeBSD +static int PAM_conv( + int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *ctx) +{ + return 0; +} +//----- + void showUsage(){ puts("lumina-checkpass: Simple user-level check for password validity (for screen unlockers and such)."); @@ -74,12 +85,17 @@ int main(int argc, char** argv){ if( getuid() != pwd->pw_uid ){ return 1; } //Current UID does not match currently logged-in user UID //Create the non-interactive PAM structures pam_handle_t *pamh; - struct pam_conv pamc = { openpam_nullconv, NULL }; + struct pam_conv pamc = { &PAM_conv, 0 }; //Place the user-supplied password into the structure +#ifdef __linux__ int ret = pam_start( "system", cUser, &pamc, &pamh); - if(ret != PAM_SUCCESS){ return 1; } //could not init PAM +#else + int ret = pam_start( "system-auth", cUser, &pamc, &pamh); +#endif + if(ret != PAM_SUCCESS){ puts("Could not initialize PAM"); return 1; } //could not init PAM //char* cPassword = argv[1]; ret = pam_set_item(pamh, PAM_AUTHTOK, pass); + if(ret != PAM_SUCCESS){ puts("Could not set conversation structure"); } //Authenticate with PAM ret = pam_authenticate(pamh,0); //this can be true without verifying password if pam_self.so is used in the auth procedures (common) if( ret == PAM_SUCCESS ){ ret = pam_acct_mgmt(pamh,0); } //Check for valid, unexpired account and verify access restrictions diff --git a/src-qt5/core/lumina-desktop-unified/src-screensaver/LLockScreen.h b/src-qt5/core/lumina-desktop-unified/src-screensaver/LLockScreen.h index 5470128a..147b0db8 100644 --- a/src-qt5/core/lumina-desktop-unified/src-screensaver/LLockScreen.h +++ b/src-qt5/core/lumina-desktop-unified/src-screensaver/LLockScreen.h @@ -7,7 +7,7 @@ #ifndef _LUMINA_DESKTOP_LOCK_SCREEN_WIDGET_H #define _LUMINA_DESKTOP_LOCK_SCREEN_WIDGET_H -#include "global-includes.h" +#include "globals.h" namespace Ui{ class LLockScreen; diff --git a/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.cpp b/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.cpp index bd812744..cffde639 100644 --- a/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.cpp @@ -17,8 +17,10 @@ LScreenSaver::LScreenSaver() : QWidget(0,Qt::BypassWindowManagerHint | Qt::Windo locktimer->setSingleShot(true); hidetimer = new QTimer(this); hidetimer->setSingleShot(true); - mouseCheckTimer = new QTimer(this); - mouseCheckTimer->setInterval(10000); //10 seconds - fallback timer for mouse movement detection + checkTimer = new QTimer(this); + checkTimer->setInterval(1500); //1.5 seconds + + lastTimeCode = 0; LOCKER = new LLockScreen(this); LOCKER->hide(); @@ -29,7 +31,7 @@ LScreenSaver::LScreenSaver() : QWidget(0,Qt::BypassWindowManagerHint | Qt::Windo connect(starttimer, SIGNAL(timeout()), this, SLOT(ShowScreenSaver()) ); connect(locktimer, SIGNAL(timeout()), this, SLOT(LockScreen()) ); connect(hidetimer, SIGNAL(timeout()), this, SLOT(HideLockScreen()) ); - connect(mouseCheckTimer, SIGNAL(timeout()), this, SLOT(checkMousePosition()) ); + connect(checkTimer, SIGNAL(timeout()), this, SLOT(checkInputEvents()) ); connect(LOCKER, SIGNAL(ScreenUnlocked()), this, SLOT(SSFinished()) ); connect(LOCKER, SIGNAL(InputDetected()), this, SLOT(newInputEvent()) ); } @@ -59,7 +61,7 @@ void LScreenSaver::UpdateTimers(){ void LScreenSaver::start(){ reloadSettings(); //setup all the initial time frames starttimer->start(); - mouseCheckTimer->start(); + checkTimer->start(); } void LScreenSaver::reloadSettings(){ @@ -80,7 +82,6 @@ void LScreenSaver::newInputEvent(){ //Only running, not locked HideScreenSaver(); } - lastMousePos = QCursor::pos(); //update the internal point UpdateTimers(); } @@ -92,11 +93,23 @@ void LScreenSaver::LockScreenNow(){ // =========== // PRIVATE SLOTS // =========== -void LScreenSaver::checkMousePosition(){ +void LScreenSaver::checkInputEvents(){ + //Check mouse position first QPoint pos = QCursor::pos(); + bool change = false; if(pos != lastMousePos){ - newInputEvent(); //this will update the internal position automatically + lastMousePos = QCursor::pos(); //update the internal point + change = true; + } + //Check the last keyboard input timestamp + unsigned int timecode = LSession::instance()->XCB->WM_Get_User_Time( LSession::instance()->XCB->WM_Get_Active_Window() ); + //unsigned int timecode = 0; //Not done yet - read the _NET_WM_USER_TIME number on the active window + if(timecode > lastTimeCode){ + lastTimeCode = timecode; + change = true; } + //If there was an input event detected, update timers and such + if(change){ newInputEvent(); } } void LScreenSaver::ShowScreenSaver(){ diff --git a/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.h b/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.h index eec42481..71121e4e 100644 --- a/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.h +++ b/src-qt5/core/lumina-desktop-unified/src-screensaver/LScreenSaver.h @@ -21,10 +21,11 @@ public: bool isLocked(); private: - QTimer *starttimer, *locktimer, *hidetimer, *mouseCheckTimer; + QTimer *starttimer, *locktimer, *hidetimer, *CheckTimer; QList<SSBaseWidget*> BASES; LLockScreen *LOCKER; QPoint lastMousePos; + unsigned int lastTimeCode; int cBright; bool SSRunning, SSLocked, updating; @@ -37,7 +38,7 @@ public slots: void LockScreenNow(); private slots: - void checkMousePosition(); + void checkInputEvents(); void ShowScreenSaver(); void ShowLockScreen(); void HideScreenSaver(); diff --git a/src-qt5/core/lumina-desktop/Globals.h b/src-qt5/core/lumina-desktop/Globals.h index f7821466..3da9b3ae 100644 --- a/src-qt5/core/lumina-desktop/Globals.h +++ b/src-qt5/core/lumina-desktop/Globals.h @@ -45,37 +45,7 @@ public: } return QString::fromLocal8Bit(name,count); } - /*//Shutdown the system -#ifdef __linux - static void shutdown(){ system("(shutdown -h now) &"); } -#else // #ifdef __linux - static void shutdown(){ system("(shutdown -p now) &"); } -#endif // #ifdef __linux - //Restart the system - static void restart(){ system("(shutdown -r now) &"); } - //Determine if there is battery support - static bool hasBattery(){ - int val = LUtils::getCmdOutput("apm -l").join("").toInt(); - return (val >= 0 && val <= 100); - } - - //Get the current battery charge percentage - static int batteryCharge(){ - int charge = LUtils::getCmdOutput("apm -l").join("").toInt(); - if(charge > 100){ charge = -1; } //invalid charge - return charge; - } - - //Get the current battery charge percentage - static bool batteryIsCharging(){ - return (LUtils::getCmdOutput("apm -a").join("").simplified() == "1"); - } - - //Get the amount of time remaining for the battery - static int batterySecondsLeft(){ - return LUtils::getCmdOutput("apm -t").join("").toInt(); - }*/ }; #endif diff --git a/src-qt5/core/lumina-desktop/LDesktop.cpp b/src-qt5/core/lumina-desktop/LDesktop.cpp index 6ca48a28..9d77af61 100644 --- a/src-qt5/core/lumina-desktop/LDesktop.cpp +++ b/src-qt5/core/lumina-desktop/LDesktop.cpp @@ -109,7 +109,8 @@ void LDesktop::UpdateGeometry(){ } void LDesktop::SystemLock(){ - QProcess::startDetached("xscreensaver-command -lock"); + QTimer::singleShot(30,LSession::handle(), SLOT(LockScreen()) ); + //QProcess::startDetached("xscreensaver-command -lock"); } void LDesktop::SystemLogout(){ diff --git a/src-qt5/core/lumina-desktop/LSession.cpp b/src-qt5/core/lumina-desktop/LSession.cpp index a49bbc96..996b3acf 100644 --- a/src-qt5/core/lumina-desktop/LSession.cpp +++ b/src-qt5/core/lumina-desktop/LSession.cpp @@ -21,10 +21,7 @@ #include <LIconCache.h> #include <unistd.h> //for usleep() usage - -#ifndef DEBUG #define DEBUG 0 -#endif XCBEventFilter *evFilter = 0; LIconCache *ICONS = 0; @@ -67,6 +64,7 @@ LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lu currTranslator=0; mediaObj=0; sessionsettings=0; + ScreenSaver=0; //Setup the event filter for Qt5 evFilter = new XCBEventFilter(this); this->installNativeEventFilter( evFilter ); @@ -90,6 +88,7 @@ LSession::~LSession(){ //delete WM; settingsmenu->deleteLater(); appmenu->deleteLater(); + if(ScreenSaver!=0){ ScreenSaver->deleteLater(); } delete currTranslator; if(mediaObj!=0){delete mediaObj;} } @@ -162,8 +161,8 @@ void LSession::setupSession(){ splash.showScreen("init"); qDebug() << "Initializing Session"; if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); } - QTime* timer = 0; - if(DEBUG){ timer = new QTime(); timer->start(); qDebug() << " - Init srand:" << timer->elapsed();} + QElapsedTimer* timer = 0; + if(DEBUG){ timer = new QElapsedTimer(); timer->start(); qDebug() << " - Init srand:" << timer->elapsed();} //Setup the QSettings default paths splash.showScreen("settings"); @@ -238,6 +237,9 @@ void LSession::setupSession(){ connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherChange(QString)) ); connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange(QString)) ); connect(this, SIGNAL(aboutToQuit()), this, SLOT(SessionEnding()) ); + if(DEBUG){ qDebug() << " - Start screensaver:" << timer->elapsed(); } + ScreenSaver = new LScreenSaver(); + ScreenSaver->start(); //if(DEBUG){ qDebug() << " - Process Events (4x):" << timer->elapsed();} //for(int i=0; i<4; i++){ LSession::processEvents(); } //Again, just a few event loops here so thing can settle before we close the splash screen if(DEBUG){ qDebug() << " - Launch Startup Apps:" << timer->elapsed();} @@ -339,6 +341,8 @@ void LSession::NewCommunication(QStringList list){ screensChanged(); }else if(list[i]=="--show-start"){ emit StartButtonActivated(); + }else if(list[i]=="--lock"){ + QTimer::singleShot(10, ScreenSaver, SLOT(LockScreenNow()) ); }else if(list[i]=="--logout"){ QTimer::singleShot(1000, this, SLOT(StartLogout()));} } } @@ -395,6 +399,10 @@ void LSession::StartReboot(bool skipupdates){ QCoreApplication::exit(0); } +void LSession::LockScreen(){ + QTimer::singleShot(10, ScreenSaver, SLOT(LockScreenNow()) ); +} + void LSession::reloadIconTheme(){ //Wait a moment for things to settle before sending out the signal to the interfaces QApplication::processEvents(); @@ -561,7 +569,7 @@ void LSession::updateDesktops(){ //Make sure fluxbox also gets prompted to re-load screen config if the number of screens changes in the middle of a session if(!firstrun && xchange){ qDebug() << "Update WM"; - //QProcess::startDetached("killall fluxbox"); + //QProcess::startDetached("pkill fluxbox"); xchange = false; } @@ -719,6 +727,10 @@ QSettings* LSession::DesktopPluginSettings(){ return DPlugSettings; } +LScreenSaver* LSession::screenSaver(){ + return ScreenSaver; +} + WId LSession::activeWindow(){ //Check the last active window pointer first WId active = XCB->ActiveWindow(); diff --git a/src-qt5/core/lumina-desktop/LSession.h b/src-qt5/core/lumina-desktop/LSession.h index ee17660d..0cd9f1d2 100644 --- a/src-qt5/core/lumina-desktop/LSession.h +++ b/src-qt5/core/lumina-desktop/LSession.h @@ -30,6 +30,7 @@ #include "LDesktop.h" //#include "WMProcess.h" //#include "BootSplash.h" +#include <LScreenSaver.h> #include <LuminaX11.h> #include <LuminaSingleApplication.h> @@ -97,6 +98,7 @@ public: QSettings* sessionSettings(); QSettings* DesktopPluginSettings(); + LScreenSaver* screenSaver(); //Keep track of which non-desktop window should be treated as active WId activeWindow(); //This will return the last active window if a desktop element is currently active @@ -125,6 +127,7 @@ private: QMediaPlayer *mediaObj; QSettings *sessionsettings, *DPlugSettings; bool cleansession; + LScreenSaver *ScreenSaver; //QList<QRect> savedScreens; //System Tray Variables @@ -151,6 +154,7 @@ public slots: void StartLogout(); void StartShutdown(bool skipupdates = false); void StartReboot(bool skipupdates = false); + void LockScreen(); void reloadIconTheme(); diff --git a/src-qt5/core/lumina-desktop/LWinInfo.cpp b/src-qt5/core/lumina-desktop/LWinInfo.cpp index 6a6cea0b..6adde234 100644 --- a/src-qt5/core/lumina-desktop/LWinInfo.cpp +++ b/src-qt5/core/lumina-desktop/LWinInfo.cpp @@ -30,15 +30,18 @@ QIcon LWinInfo::icon(bool &noicon){ noicon = false; QIcon ico = LSession::handle()->XCB->WindowIcon(window); //Check for a null icon, and supply one if necessary - if(ico.isNull()){ ico = LXDG::findIcon( this->Class().toLower(),""); } - if(ico.isNull()){ico = LXDG::findIcon("preferences-system-windows",""); noicon=true;} + if(ico.isNull()){ + QString cls = this->Class(); + ico = LXDG::findIcon( cls, cls.toLower()); + } + if(ico.isNull()){ico = LXDG::findIcon("window","preferences-system-windows"); noicon=true;} return ico; } - + QString LWinInfo::Class(){ return LSession::handle()->XCB->WindowClass(window); } - + LXCB::WINDOWVISIBILITY LWinInfo::status(bool update){ if(window==0){ return LXCB::IGNORE; } if(update || cstate == LXCB::IGNORE){ diff --git a/src-qt5/core/lumina-desktop/SystemWindow.cpp b/src-qt5/core/lumina-desktop/SystemWindow.cpp index bfad961d..6a4172d3 100644 --- a/src-qt5/core/lumina-desktop/SystemWindow.cpp +++ b/src-qt5/core/lumina-desktop/SystemWindow.cpp @@ -105,7 +105,8 @@ void SystemWindow::sysSuspend(){ this->hide(); LSession::processEvents(); //Make sure to lock the system first (otherwise anybody can access it again) - LUtils::runCmd("xscreensaver-command -lock"); + LSession::handle()->LockScreen(); + //LUtils::runCmd("xscreensaver-command -lock"); //Now suspend the system LOS::systemSuspend(); } @@ -114,5 +115,6 @@ void SystemWindow::sysLock(){ this->hide(); LSession::processEvents(); qDebug() << "Locking the desktop..."; - QProcess::startDetached("xscreensaver-command -lock"); + QTimer::singleShot(30,LSession::handle(), SLOT(LockScreen()) ); + //QProcess::startDetached("xscreensaver-command -lock"); } diff --git a/src-qt5/core/lumina-desktop/defaults/compton.conf b/src-qt5/core/lumina-desktop/defaults/compton.conf index a14fee29..b5561e51 100644 --- a/src-qt5/core/lumina-desktop/defaults/compton.conf +++ b/src-qt5/core/lumina-desktop/defaults/compton.conf @@ -15,7 +15,8 @@ shadow-exclude = [ "class_g = 'Conky'", "class_g ?= 'Notify-osd'", "class_g = 'Cairo-clock'", - "_GTK_FRAME_EXTENTS@:c" + "_GTK_FRAME_EXTENTS@:c", + "name = 'lumina-pingcursor'" ]; # shadow-exclude = "n:e:Notification"; # shadow-exclude-reg = "x10+0+0"; diff --git a/src-qt5/core/lumina-desktop/extrafiles/Lumina-DE.png b/src-qt5/core/lumina-desktop/extrafiles/Lumina-DE.png Binary files differnew file mode 100644 index 00000000..ce88a252 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/Lumina-DE.png diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/Fireflies.json b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Fireflies.json new file mode 100644 index 00000000..c09de308 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Fireflies.json @@ -0,0 +1,26 @@ +{ + "name" : { + "default" : "Fireflies" + }, + "description" : { + "default" : "Dancing balls of light on the screen" + }, + "author" : { + "name" : "Ken Moore", + "email" : "ken@ixsystems.com", + "website" : "https://github.com/beanpole135", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20171010", + "version" : "1.0" + }, + "qml" : { + "exec" : "qml_scripts/Fireflies.qml", + "additional_files" : ["qml_scripts/Firefly.qml"] + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/Grav.json b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Grav.json new file mode 100644 index 00000000..c75ae170 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Grav.json @@ -0,0 +1,26 @@ +{ + "disabled" : true, + "name" : { + "default" : "Grav" + }, + "description" : { + "default" : "Simulates a solar system, with a single star and planets erratically orbiting that star" + }, + "author" : { + "name" : "Zackary Welch", + "email" : "zwelch@ixsystems.com", + "website" : "https://github.com/ZackaryWelch", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20171101", + "version" : "1.0" + }, + "qml" : { + "exec" : "qml_scripts/Grav.qml" + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/Matrix.json b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Matrix.json new file mode 100644 index 00000000..8c9a351f --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Matrix.json @@ -0,0 +1,26 @@ +{ + "disabled" : true, + "name" : { + "default" : "Matrix" + }, + "description" : { + "default" : "Erratic falling columns of various characters with a set color" + }, + "author" : { + "name" : "Zackary Welch", + "email" : "zwelch@ixsystems.com", + "website" : "https://github.com/ZackaryWelch", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20180103", + "version" : "1.0" + }, + "qml" : { + "exec" : "qml_scripts/Matrix.qml" + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/README.md b/src-qt5/core/lumina-desktop/extrafiles/screensavers/README.md new file mode 100644 index 00000000..d9093b44 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/README.md @@ -0,0 +1,79 @@ +## Screensaver Format +The screensaver system for the Lumina desktop allows for the creation and use of scripts written in the QML language, with a number of screensavers and other examples installed out-of-box. There are only a couple warnings/caveats to consider when developing a new screensaver: + +1. The root object in your QML script will be automatically sized to fit the screen as needed. Avoid trying to hard-code specific screen dimensions within your script as it will not work properly. +2. A JSON manifest file must be created (format listed below) and placed into one of the screensaver plugin directories for it to be recognized as a valid screensaver by the desktop. + + +### JSON Manifest +The manifest file contains all the information needed to actually validate/launch the screensaver, as well as additional information about the author and/or the screensaver itself. + +Example JSON manifest file (sample.json): +``` +{ + "name" : { + "default" : "sample", + "en_US" : "US English localization of the name", + "en" : "Generic english localization of the name" + }, + + "description" : { + "default" : "sample screensaver", + "en_US" : "US English Localization of the description" + }, + + "author" : { + "name" : "Me", + "email" : "Me@myself.net", + "website" : "http://mywebsite.net", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20171010", + "date_updated" : "20171011", + "version" : "1.0" + }, + + "qml" : { + "exec" : "absolute/or/relative/path/to/script.qml", + "additional_files" : ["file/which/must/exist.png"], + "qt_min_version" : "5.0", + "qt_max_version" : "6.0" + } +} +``` + +Details of the individual items in the manifest: +* NOTE: for locale codes, both long and short version are acceptable: + Example 1: If the current locale is "en_GB", but the JSON manifest lists translations for "en_US" and "en", then the "en" translation will be used. + Example 2: If the current locale is "en_GB", but neither "en_GB" nor "en" translations exist, then the "default" version will be used. + +* **name** : (required) This is the official name of the screensaver to show to users + * *default* : (required) Non-translated name of the screensaver + * *[locale]* : (optional) Translated name for specific [locale] +* **description** : (required) This is a short description of the screensaver to show to users + * *default* : (required) Non-translated description of the screensaver + * *[locale]* : (optional) Translated description for specific [locale] +* **author** : (all optional) Additional information about the author(s) of the screensaver + * *name* : Name of the author + * *email* : Email to contact the author (useful for licensing questions and such) + * *website* : Personal website for the author (github/facebook/twitter profile, etc) + * *company* : Company for which the author is creating this screensaver + * *company_website* : Website for the company +* **meta** : (all optional) Additional information about the screensaver itself + * *license* : License the screensaver is released under + * *license_url* : Website which contains the full text of the license + * *copyright* : Copyright notice for this screensaver + * *date_created* : (yyyyMMdd) Date the screensaver was initially created + * *date_updated* : (yyyyMMdd) Date the screensaver was last updated + * *version* : Current version of the screensaver (typically updated every time "date_updated" is changed) +* **qml** : (required) Information about launching the screensaver and checking validity + * *exec* : (required) Absolute or relative path to the QML script (relative to the directory which contains the JSON manifest) + * *additional_files* : (optional) Array of paths for other files/scripts which must exist for the screensaver to work properly. + * *qt_min_version* : (optional) Minimum version of the Qt libraries that this screensaver supports + * *qt_max_version* : (optional) Maximum version of the Qt libraries that this screensaver supports diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/Video.json b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Video.json new file mode 100644 index 00000000..8bd86157 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Video.json @@ -0,0 +1,26 @@ +{ + "disabled" : true, + "name" : { + "default" : "Video" + }, + "description" : { + "default" : "Play a single video or a list of videos in a loop" + }, + "author" : { + "name" : "Zackary Welch", + "email" : "zwelch@ixsystems.com", + "website" : "https://github.com/ZackaryWelch", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20171025", + "version" : "1.0" + }, + "qml" : { + "exec" : "qml_scripts/Video.qml" + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/Warp.json b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Warp.json new file mode 100644 index 00000000..888df01f --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/Warp.json @@ -0,0 +1,25 @@ +{ + "name" : { + "default" : "Warp" + }, + "description" : { + "default" : "Warp trail through the stars" + }, + "author" : { + "name" : "Ken Moore", + "email" : "ken@ixsystems.com", + "website" : "https://github.com/beanpole135", + "company" : "iXsystems", + "company_website" : "http://ixsystems.com" + }, + "meta" : { + "license" : "3-clause BSD", + "license_url" : "https://github.com/trueos/lumina/blob/master/LICENSE", + "copyright" : "Copyright (c) 2017, Ken Moore (ken@ixsystems.com)", + "date_created" : "20171012", + "version" : "1.0" + }, + "qml" : { + "exec" : "qml_scripts/Warp.qml" + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Fireflies.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Fireflies.qml new file mode 100644 index 00000000..d8dcc1ed --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Fireflies.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 +import QtGraphicalEffects 1.0 +import "." as QML + +Rectangle { + id : canvas + anchors.fill: parent + color: "black" + + Repeater { + model: Math.round(Math.random()*canvas.width/10)+100 + QML.Firefly { + parent: canvas + x: Math.round(Math.random()*canvas.width) + y: Math.round(Math.random()*canvas.height) + } + } //end of Repeater + +} //end of canvas rectangle diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Firefly.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Firefly.qml new file mode 100644 index 00000000..7b65d8ec --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Firefly.qml @@ -0,0 +1,63 @@ +import QtQuick 2.0 +import QtQuick.Window 2.2 +import QtGraphicalEffects 1.0 + +Item { + + RectangularGlow { + anchors.fill: fly + glowRadius: Math.round(fly.radius /2) + spread: 0.5 + color: Qt.rgba(1,1,1,0.3) + cornerRadius: fly.radius + glowRadius + } + + Rectangle { + id: fly + width: Math.round(Math.random()*canvas.width/200)+2 + height: width + x: parent.x + y: parent.y + color: Qt.rgba(Math.random(),Math.random(),0,0.5) + radius: Math.floor(width/2) + property int jitterX: Math.round(Math.random()*100)+10 + property int jitterY: Math.round(Math.random()*100)+10 + + Behavior on color { + ColorAnimation { + duration: 500 + } + } + Behavior on x { + SmoothedAnimation { + velocity: 10+Math.random()*canvas.width/100 + } + } + Behavior on y { + SmoothedAnimation { + velocity: 10+Math.random()*canvas.height/100 + } + } + + } + + + + Timer { + interval: 5 + repeat: true + running: true + property bool starting: true + onTriggered: { + if(starting){ interval = Math.round(Math.random()*1000)+500; starting = false; } + if ( (fly.x+fly.jitterX)>parent.width || (fly.x+fly.jitterX)<0 ){ fly.jitterX = 0-fly.jitterX } + fly.x = fly.x+fly.jitterX + if( (fly.y+fly.jitterY)>parent.height || (fly.y+fly.jitterY)<0 ){ fly.jitterY = 0-fly.jitterY } + fly.y = fly.y+fly.jitterY + fly.jitterX = (Math.round(Math.random())*2 - 1) *fly.jitterX + fly.jitterY = (Math.round(Math.random())*2 - 1) *fly.jitterY + fly.color = Qt.rgba(Math.random(),Math.random(),Math.random(),0.5) + + } + } //end of timer +} //end of item diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Grav.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Grav.qml new file mode 100644 index 00000000..d1e5d3c9 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Grav.qml @@ -0,0 +1,132 @@ +import QtQuick 2.7 +import QtGraphicalEffects 1.0 + +Rectangle { + id : canvas + anchors.fill: parent + width: Screen.width + height: Screen.height + color: "black" + + //TODO Add orbital trails option + //TODO Fix jitteryness and start position + //TODO Make orbits more extreme + + //Between 5 and 15 planets, read from settings + property int planets: Math.round(( Math.random() * 10 ) + 5 ) + property int cx: Math.round(width/2) + property int cy: Math.round(height/2) + + //Create planets + Repeater { + id: planetRepeater + model: planets + + Rectangle { + id : index + parent: canvas + + //Creates random distance for elipse + property double c: Math.random() * 250 + property double b: Math.random() * 150 + c + property double a: Math.sqrt(b*b+c*c) + //Random angle of rotation + property double th: Math.random() * Math.PI + property var path: [] + + //Calculates starting position + x: Math.round(cx + a * Math.cos(th)) + y: Math.round(cy + b * Math.sin(th)) + + //Planet size between 14 and 32 pixels + width: Math.round(1.75 * (((Math.random() * 10) + 8 ))) + height: width + + //Make each rectangle look circular + radius: width / 2 + + //Give each planet a random color, semi-transparent + color: Qt.rgba(Math.random(), Math.random(), Math.random(), 0.5) + + /*Timer { + //Each planet updates between 1ms and 51ms (smaller times=faster) + interval: Math.round(Math.random() * 50 ) + 1 + repeat: true + running: true + property int time: 0 + + onTriggered: { + //Parametric equation that calculates the position of the general ellipse. Completes a loop ever 314 cycles. Credit to + x = cx+a*Math.cos(2*Math.PI*(time/314.0))*Math.cos(th) - b*Math.sin(2*Math.PI*(time/314.0))*Math.sin(th) + y = cy+a*Math.cos(2*Math.PI*(time/314.0))*Math.sin(th) + b*Math.sin(2*Math.PI*(time/314.0))*Math.cos(th) + time++; + + //Move a planet 80 pixels away from the sun if the planet is too close + if(x > cx && Math.abs(cx-x) < 80) { + x+=80 + }else if(x < cx && Math.abs(cx-x) < 80) { + x-=80 + } + + if(y > cy && Math.abs(cy-y) < 80) { + y+=80 + }else if(y < cy && Math.abs(cy-y) < 80) { + y-=80 + } + } + }*/ + + Component.onCompleted: { + pahtX[0] = x + pahtY[0] = y + for(int i = 1; i <= 200; i++) { + pathX[i] = cx+a*Math.cos(2*Math.PI*(i/200.0)*Math.cos(th) - b*Math.sin(2*Math.PI*(i/200.0)*Math.sin(th) + pathY[i] = cy+a*Math.cos(2*Math.PI*(i/200.0)*Math.sin(th) + b*Math.sin(2*Math.PI*(i/200.0)*Math.cos(th) + } + } + } + } + + //Create the star + Rectangle{ + id: star + parent: canvas + + //Centers in star in the center of the canvas, with an offset to center the animation + x: cx - 30 + y: cy - 30 + + width: 60 + height: width + + //Create the wobble animation + SequentialAnimation on height { + loops: Animation.Infinite + PropertyAnimation { duration: 2000; to: 90 } + PropertyAnimation { duration: 2000; to: 60 } + } + + SequentialAnimation on width { + loops: Animation.Infinite + PropertyAnimation { duration: 2000; to: 90 } + PropertyAnimation { duration: 2000; to: 60 } + } + + color: "black" + radius: width / 2 + + //Creates a radial gradient to make the star look cool + RadialGradient { + anchors.fill: parent + gradient: Gradient { + GradientStop { position:0 ;color: Qt.rgba(0,0,0,0)} + GradientStop { position:0.18 ;color: Qt.rgba(0,0,0,0)} + GradientStop { position:0.2 ;color: Qt.rgba(0.32,0.47,0.30,0.13)} + GradientStop { position:0.3 ;color: Qt.rgba(0.62,0.92,0.58,0.25)} + GradientStop { position:0.4 ;color: Qt.rgba(1.00,0.93,0.59,0.51)} + GradientStop { position:0.5 ;color: Qt.rgba(0,0,0,0)} + } + } + + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Matrix.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Matrix.qml new file mode 100644 index 00000000..d4031201 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Matrix.qml @@ -0,0 +1,71 @@ +import QtQuick 2.0 +import QtMultimedia 5.7 +import QtQuick.Window 2.2 + +Rectangle { + width: Window.width + height: Window.height + color: "black" + + Row{ + id: masterRow + anchors.left: parent.left + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + spacing: 5 + Repeater { + id: cR + model: Window.width / 15 + 1 + Column { + id: masterColumn + width: 10 + Text { + id: column + color: "#ff4d4d" + font.pixelSize: 10 + transform: Rotation { origin.x: 0; origin.y: 0; angle: 90 } + Timer { + interval: 50 + repeat: true + running: true + onTriggered: { + if(Math.random() < 0.95) { + var bottom = column.text.charAt(column.text.length-1) + var newString = bottom+column.text.substring(0, column.text.length-1) + column.text = newString + interval = 50 + }else{ + interval = 1000 + } + } + } + Component.onCompleted: { + var str = " " + var numberChar = Math.random() * 100 + (Window.height * 0.1); + if(Math.random() < 0.80) { + while(str.length < numberChar) { + if(Math.random() < 0.5) { + var charCount = Math.random() * 8 + 10 + var segStr = "" + while(segStr.length < charCount) { + var randChar = String.fromCharCode(0x30A0 + Math.random() * (0x30FF-0x30A0+1)); + segStr += randChar + } + str += segStr + }else{ + var charCount = Math.random() * 6 + 14 + var segStr = "" + while(segStr.length < charCount) { + segStr += " " + } + str += segStr + } + } + } + column.text = str + } + } + } + } + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Video.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Video.qml new file mode 100644 index 00000000..9948537b --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Video.qml @@ -0,0 +1,48 @@ +import QtQuick 2.0 +import QtMultimedia 5.7 +import QtQuick.Window 2.2 +import Qt.labs.folderlistmodel 2.1 + +Rectangle { + width: Screen.width + height: Screen.height + color: "black" + + FolderListModel { + id: folderModel + folder: "/usr/local/videos" + } + + Repeater { + model: folderModel + Component { + Item { + Component.onCompleted: { playlist.addItem(fileURL) } + } + } + } + + Playlist { + id: playlist + playbackMode: Playlist.Random + PlaylistItem { source: "/" } + onError: { console.log("ERROR") } + } + + MediaPlayer { + id: player + autoPlay: true + playlist: playlist + } + + VideoOutput { + id: videoOutput + source: player + anchors.fill: parent + } + + Component.onCompleted: { + playlist.shuffle() + console.log(playlist.itemCount) + } +} diff --git a/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Warp.qml b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Warp.qml new file mode 100644 index 00000000..1cf9bc37 --- /dev/null +++ b/src-qt5/core/lumina-desktop/extrafiles/screensavers/qml_scripts/Warp.qml @@ -0,0 +1,64 @@ +import QtQuick 2.0 +import QtGraphicalEffects 1.0 + +Rectangle { + id : canvas + anchors.fill: parent + color: "black" + + function getStarColor(num){ + if(num < 1) { return "white" } + if(num < 2) { return "mistyrose" } + return "lightblue" + } + + // CREATE STARFIELD + Repeater { + model: Math.round(Math.random()*canvas.width/10)+500 + Rectangle { + parent: canvas + x: Math.round(Math.random()*canvas.width) + y: Math.round(Math.random()*canvas.height) + width: Math.round(Math.random()*3)+3 + height: width + radius: width/2 + color: getStarColor( (index%3) ) + + } + } //end of Repeater + + // NOW CREATE THE WARP EFFECT + ZoomBlur { + id: blur + anchors.fill: canvas + source: canvas + samples: 24 + length: canvas.width / 20 + horizontalOffset: 0 + verticalOffset: 0 + + Behavior on horizontalOffset{ + NumberAnimation{ + duration: 3000 + } + } + Behavior on verticalOffset{ + NumberAnimation{ + duration: 3000 + } + } + } //end of zoom blur + + Timer { + interval: 5 + repeat: true + running: true + property bool starting: true + onTriggered: { + if(starting){ interval = 3010; starting = false; } + blur.horizontalOffset = (Math.random()*canvas.width/4) - (canvas.width/8) + blur.verticalOffset = (Math.random()*canvas.height/4) - (canvas.height/8) + } + } //end of timer + +} //end of canvas rectangle diff --git a/src-qt5/core/lumina-desktop/fluxboxconf/fluxbox-keys b/src-qt5/core/lumina-desktop/fluxboxconf/fluxbox-keys index 9726d5a4..92f58e71 100644 --- a/src-qt5/core/lumina-desktop/fluxboxconf/fluxbox-keys +++ b/src-qt5/core/lumina-desktop/fluxboxconf/fluxbox-keys @@ -144,3 +144,4 @@ Print :Exec lumina-screenshot Mod1 Home :Exec lumina-open -brightnessup Mod1 End :Exec lumina-open -brightnessdown Pause :Exec xscreensaver-command -lock +Control Mod1 space :Exec lumina-pingcursor diff --git a/src-qt5/core/lumina-desktop/global-includes.h b/src-qt5/core/lumina-desktop/global-includes.h new file mode 100644 index 00000000..be33a45b --- /dev/null +++ b/src-qt5/core/lumina-desktop/global-includes.h @@ -0,0 +1,85 @@ +//=========================================== +// Lumina-desktop source code +// Copyright (c) 2015-2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// Global defines and enumerations for external includes +//=========================================== +#ifndef _LUMINA_EXTERNAL_GLOBAL_DEFINES_H +#define _LUMINA_EXTERNAL_GLOBAL_DEFINES_H + +//Qt includes +#include <QObject> +#include <QFrame> +#include <QLabel> +#include <QToolButton> +#include <QMenu> +#include <QHBoxLayout> +#include <QMouseEvent> +#include <QAction> +#include <QPoint> +#include <QTemporaryFile> +#include <QFile> +#include <QDir> +#include <QString> +#include <QTextStream> +#include <QUrl> +#include <QDebug> +#include <QStringList> +#include <QAbstractNativeEventFilter> +#include <QList> +#include <QX11Info> +#include <QCoreApplication> +#include <QPropertyAnimation> +#include <QAnimationGroup> +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QGraphicsOpacityEffect> +#include <QWindow> +#include <QWidget> +#include <QWidgetAction> +#include <QBackingStore> +#include <QPaintEvent> +#include <QPainter> +#include <QSettings> +#include <QHostInfo> +#include <QDesktopWidget> +#include <QStyleOption> +#include <QThread> +#include <QMediaObject> +#include <QMediaPlayer> +#include <QVideoWidget> +#include <QMediaPlaylist> +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonDocument> +#include <QQuickView> +#include <QQmlContext> +#include <QQmlEngine> +#include <QQuickImageProvider> +#include <QtConcurrent> + +// libLumina includes +#include <LuminaX11.h> +#include <LuminaXDG.h> +#include <LuminaOS.h> +#include <LuminaThemes.h> +#include <LUtils.h> +#include <LDesktopUtils.h> +#include <LuminaSingleApplication.h> +#include <DesktopSettings.h> +#include <ExternalProcess.h> +#include <XDGMime.h> +#include <LIconCache.h> +#include <LFileInfo.h> +#include <framework-OSInterface.h> + +// C++ Backend classes for QML interface +//#include <NativeWindowObject.h> +//#include <ScreenObject.h> + +//Setup any global defines (no classes or global objects: use "global-objects.h" for that) + + +#endif diff --git a/src-qt5/core/lumina-desktop/lumina-desktop.pro b/src-qt5/core/lumina-desktop/lumina-desktop.pro index cc4b63ae..678a8526 100644 --- a/src-qt5/core/lumina-desktop/lumina-desktop.pro +++ b/src-qt5/core/lumina-desktop/lumina-desktop.pro @@ -1,8 +1,14 @@ include($${PWD}/../../OS-detect.pri) -QT += core gui network -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets x11extras multimedia concurrent svg - +lessThan(QT_MAJOR_VERSION, 5) { + message("[ERROR] Qt 5.11+ is required to use the Lumina Desktop!") + exit +} +lessThan(QT_MINOR_VERSION, 11){ + message("[ERROR] Qt 5.11+ is required to use the Lumina Desktop!") + exit +} +QT += core gui network widgets x11extras multimedia multimediawidgets concurrent svg quick qml TARGET = lumina-desktop @@ -17,6 +23,7 @@ include(../libLumina/LuminaSingleApplication.pri) include(../libLumina/LuminaThemes.pri) include(../libLumina/ExternalProcess.pri) include(../libLumina/LIconCache.pri) +include(../libLumina/DesktopSettings.pri) TEMPLATE = app @@ -37,6 +44,7 @@ SOURCES += main.cpp \ HEADERS += Globals.h \ + global-includes.h \ WMProcess.h \ LXcbEventFilter.h \ LSession.h \ @@ -60,6 +68,10 @@ FORMS += SystemWindow.ui \ BootSplash.ui +#include the individual desktop components +include(../../src-cpp/plugins-base.pri) +include(src-screensaver/screensaver.pri) + #Now include all the files for the various plugins include(panel-plugins/panel-plugins.pri) include(desktop-plugins/desktop-plugins.pri) @@ -84,6 +96,9 @@ defaults.path = $${L_SHAREDIR}/lumina-desktop/ conf.path = $${L_ETCDIR} +extrafiles.path = $${L_SHAREDIR}/lumina-desktop +extrafiles.files = extrafiles/* + #Now do any OS-specific defaults (if available) #First see if there is a known OS override first !isEmpty(DEFAULT_SETTINGS){ @@ -173,7 +188,7 @@ dotrans.extra=cd $$PWD/i18n && $${LRELEASE} -nounfinished *.ts && cp *.qm $(INST manpage.path=$${L_MANDIR}/man1/ manpage.extra="$${MAN_ZIP} $$PWD/lumina-desktop.1 > $(INSTALL_ROOT)$${L_MANDIR}/man1/lumina-desktop.1.gz" -INSTALLS += target desktop icons defaults conf fluxconf manpage +INSTALLS += target desktop icons defaults conf fluxconf manpage extrafiles WITH_I18N{ INSTALLS += dotrans diff --git a/src-qt5/core/lumina-desktop/main.cpp b/src-qt5/core/lumina-desktop/main.cpp index 826d697c..c8a51462 100644 --- a/src-qt5/core/lumina-desktop/main.cpp +++ b/src-qt5/core/lumina-desktop/main.cpp @@ -84,8 +84,8 @@ int main(int argc, char ** argv) return 787; //return special restart code }*/ //Setup the log file - QTime *timer=0; - if(DEBUG){ timer = new QTime(); timer->start(); } + QElapsedTimer *timer=0; + if(DEBUG){ timer = new QElapsedTimer(); timer->start(); } if(DEBUG){ qDebug() << "Session Setup:" << timer->elapsed(); } a.setupSession(); if(DEBUG){ qDebug() << "Exec Time:" << timer->elapsed(); delete timer;} diff --git a/src-qt5/core/lumina-desktop/panel-plugins/systemdashboard/SysMenuQuick.cpp b/src-qt5/core/lumina-desktop/panel-plugins/systemdashboard/SysMenuQuick.cpp index 56ad0788..83aaceea 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/systemdashboard/SysMenuQuick.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/systemdashboard/SysMenuQuick.cpp @@ -92,22 +92,11 @@ void LSysMenuQuick::UpdateMenu(){ if(hasBat){ ui->group_battery->setVisible(true); val = LOS::batteryCharge(); - if(LOS::batteryIsCharging()){ - if(val < 15){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-low","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 30){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-caution","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 50){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-040","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 70){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-060","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 90){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-080","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else{ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging","").pixmap(ui->label_bat_icon->maximumSize()) ); } + bool charging = LOS::batteryIsCharging(); + ui->label_bat_icon->setPixmap( LXDG::findIcon(LSession::batteryIconName(val,charging), "").pixmap(ui->label_bat_icon->maximumSize()) ); + if(charging){ ui->label_bat_text->setText( QString("%1%\n(%2)").arg(QString::number(val), tr("connected")) ); }else{ - if(val < 1){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-missing","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 15){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-low","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 30){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-caution","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 50){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-040","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 70){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-060","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else if(val < 90){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-080","").pixmap(ui->label_bat_icon->maximumSize()) ); } - else{ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-100","").pixmap(ui->label_bat_icon->maximumSize()) ); } ui->label_bat_text->setText( QString("%1%\n(%2)").arg(QString::number(val), getRemainingTime()) ); } }else{ diff --git a/src-qt5/core/lumina-desktop/panel-plugins/systemstart/StartMenu.cpp b/src-qt5/core/lumina-desktop/panel-plugins/systemstart/StartMenu.cpp index 57c17d47..38f5246f 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/systemstart/StartMenu.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/systemstart/StartMenu.cpp @@ -578,7 +578,8 @@ void StartMenu::on_tool_launch_deskinfo_clicked(){ //Logout Buttons void StartMenu::on_tool_lock_clicked(){ //QProcess::startDetached("xscreensaver-command -lock"); - LaunchItem("xscreensaver-command -lock",false); + QTimer::singleShot(30,LSession::handle(), SLOT(LockScreen()) ); + //LaunchItem("xscreensaver-command -lock",false); } void StartMenu::on_tool_logout_clicked(){ diff --git a/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp b/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp index 8f867261..67964c3f 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp @@ -21,6 +21,7 @@ LTaskButton::LTaskButton(QWidget *parent, bool smallDisplay) : LTBWidget(parent) this->setFocusPolicy(Qt::NoFocus); this->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); winMenu->setContextMenuPolicy(Qt::CustomContextMenu); + LWINLIST = 0; connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) ); connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); connect(winMenu, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) ); @@ -84,8 +85,8 @@ LWinInfo LTaskButton::currentWindow(){ void LTaskButton::UpdateButton(){ if(winMenu->isVisible()){ return; } //skip this if the window menu is currently visible for now - bool statusOnly = (WINLIST.length() == LWINLIST.length()); - LWINLIST = WINLIST; + bool statusOnly = (WINLIST.length() == LWINLIST); + LWINLIST = WINLIST.length(); winMenu->clear(); LXCB::WINDOWVISIBILITY showstate = LXCB::IGNORE; @@ -95,7 +96,7 @@ void LTaskButton::UpdateButton(){ i--; continue; } - if(i==0 && !statusOnly){ + if(i==0 && (!statusOnly || noicon) ){ //Update the button visuals from the first window this->setIcon(WINLIST[i].icon(noicon)); cname = WINLIST[i].Class(); @@ -171,7 +172,7 @@ void LTaskButton::buttonClicked(){ if(WINLIST.length() > 1){ winMenu->popup(QCursor::pos()); }else{ - triggerWindow(); + triggerWindow(); } } @@ -189,7 +190,7 @@ void LTaskButton::maximizeWindow(){ LWinInfo win = currentWindow(); LSession::handle()->XCB->MaximizeWindow(win.windowID()); //LSession::handle()->adjustWindowGeom(win.windowID(), true); //run this for now until the WM works properly - cWin = LWinInfo(); //clear the current + cWin = LWinInfo(); //clear the current } void LTaskButton::minimizeWindow(){ @@ -197,7 +198,7 @@ void LTaskButton::minimizeWindow(){ if(winMenu->isVisible()){ winMenu->hide(); } LWinInfo win = currentWindow(); LSession::handle()->XCB->MinimizeWindow(win.windowID()); - cWin = LWinInfo(); //clear the current + cWin = LWinInfo(); //clear the current QTimer::singleShot(100, this, SLOT(UpdateButton()) ); //make sure to update this button if losing active status } @@ -244,7 +245,7 @@ void LTaskButton::winClicked(QAction* act){ //Get the window from the action if(act->data().toInt() < WINLIST.length()){ cWin = WINLIST[act->data().toInt()]; - cstate = cWin.status(); + cstate = cWin.status(); }else{ cWin = LWinInfo(); } //clear it //Now trigger the window triggerWindow(); diff --git a/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.h b/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.h index ff551998..3062bb74 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.h +++ b/src-qt5/core/lumina-desktop/panel-plugins/taskmanager/LTaskButton.h @@ -42,7 +42,7 @@ public: private: QList<LWinInfo> WINLIST; - QList<LWinInfo> LWINLIST; + int LWINLIST; QMenu *actMenu; // action menu (custom context menu) QMenu *winMenu; // window menu (if more than 1) LWinInfo cWin; diff --git a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/LUserButton.cpp b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/LUserButton.cpp index acb27135..c1cf2907 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/LUserButton.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/LUserButton.cpp @@ -23,7 +23,7 @@ LUserButtonPlugin::LUserButtonPlugin(QWidget *parent, QString id, bool horizonta mact = new QWidgetAction(this); mact->setDefaultWidget(usermenu); menu->addAction(mact); - + button->setMenu(menu); connect(menu, SIGNAL(aboutToHide()), this, SLOT(updateButtonVisuals()) ); //Setup the global shortcut handling for opening the start menu diff --git a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp index 8d7dab7a..dc89525c 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp @@ -33,7 +33,7 @@ UserItemWidget::UserItemWidget(QWidget *parent, QString itemPath, QString type, name->setText( tr("Go Back") ); }else{ icon->setPixmap( LXDG::findIcon("folder","").pixmap(32,32) ); - name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); + name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); } }else{ actButton->setVisible(false); @@ -46,7 +46,7 @@ UserItemWidget::UserItemWidget(QWidget *parent, QString itemPath, QString type, }else{ icon->setPixmap( LXDG::findMimeIcon(itemPath.section("/",-1)).pixmap(32,32) ); } - name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); + name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); } icon->setWhatsThis(itemPath); if(!goback){ this->setWhatsThis(name->text()); } @@ -77,7 +77,7 @@ UserItemWidget::UserItemWidget(QWidget *parent, XDGDesktop *item) : QFrame(paren } //Now fill it appropriately icon->setPixmap( LXDG::findIcon(item->icon,"preferences-system-windows-actions").pixmap(32,32) ); - name->setText( this->fontMetrics().elidedText(item->name, Qt::ElideRight, TEXTCUTOFF) ); + name->setText( this->fontMetrics().elidedText(item->name, Qt::ElideRight, TEXTCUTOFF) ); this->setWhatsThis(name->text()); icon->setWhatsThis(item->filePath); //Now setup the buttons appropriately @@ -85,7 +85,7 @@ UserItemWidget::UserItemWidget(QWidget *parent, XDGDesktop *item) : QFrame(paren setupActions(item); } -UserItemWidget::~UserItemWidget(){ +UserItemWidget::~UserItemWidget(){ delete button; delete icon; delete name; @@ -98,7 +98,7 @@ void UserItemWidget::createWidget(){ menuopen = false; menureset = new QTimer(this); menureset->setSingleShot(true); - menureset->setInterval(1000); //1 second + menureset->setInterval(1000); //1 second this->setContentsMargins(0,0,0,0); button = new QToolButton(this); button->setIconSize( QSize(14,14) ); @@ -125,7 +125,7 @@ void UserItemWidget::createWidget(){ void UserItemWidget::setupButton(bool disable){ //if(isDirectory){ qDebug() << "Directory Entry:" << isShortcut << linkPath << icon->whatsThis(); } - + if(disable){ button->setVisible(false); }else if(isShortcut && !linkPath.isEmpty()){ //Favorite Item - can always remove this @@ -141,7 +141,7 @@ void UserItemWidget::setupButton(bool disable){ }else if(!isShortcut){// if( !QFile::exists( QDir::homePath()+"/Desktop/"+icon->whatsThis().section("/",-1) ) && !LUtils::isFavorite(icon->whatsThis() ) ){ //This file does not have a shortcut yet -- allow the user to add it button->setWhatsThis("add"); - button->setIcon( LXDG::findIcon("bookmark-toolbar","") ); + button->setIcon( LXDG::findIcon("bookmark-toolbar","") ); button->setToolTip(tr("Create Shortcut")); connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); }else{ @@ -161,7 +161,7 @@ void UserItemWidget::setupActions(XDGDesktop *app){ QAction *act = new QAction(LXDG::findIcon(app->actions[i].icon, app->icon), app->actions[i].name, this); act->setToolTip(app->actions[i].ID); act->setWhatsThis(app->actions[i].ID); - actButton->menu()->addAction(act); + actButton->menu()->addAction(act); } connect(actButton->menu(), SIGNAL(triggered(QAction*)), this, SLOT(actionClicked(QAction*)) ); connect(actButton->menu(), SIGNAL(aboutToShow()), this, SLOT(actionMenuOpen()) ); @@ -171,22 +171,22 @@ void UserItemWidget::setupActions(XDGDesktop *app){ void UserItemWidget::buttonClicked(){ button->setVisible(false); - if(button->whatsThis()=="add"){ + if(button->whatsThis()=="add"){ LDesktopUtils::addFavorite(icon->whatsThis()); //QFile::link(icon->whatsThis(), QDir::homePath()+"/.lumina/favorites/"+icon->whatsThis().section("/",-1) ); - emit NewShortcut(); - }else if(button->whatsThis()=="remove"){ - if(linkPath.isEmpty()){ + emit NewShortcut(); + }else if(button->whatsThis()=="remove"){ + if(linkPath.isEmpty()){ //This is a desktop file if(isDirectory){ QProcess::startDetached("rm -r \""+icon->whatsThis()+"\""); }else{ - QFile::remove(icon->whatsThis()); - } + QFile::remove(icon->whatsThis()); + } //Don't emit the RemovedShortcut signal here - the automatic ~/Desktop watcher will see the change when finished - }else{ + }else{ LDesktopUtils::removeFavorite(icon->whatsThis()); //This is a favorite - emit RemovedShortcut(); + emit RemovedShortcut(); } } } diff --git a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp index a0ba8996..9fdc7169 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp +++ b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp @@ -15,7 +15,7 @@ UserWidget::UserWidget(QWidget* parent) : QTabWidget(parent), ui(new Ui::UserWid if(parent!=0){ parent->setMouseTracking(true); } this->setMouseTracking(true); sysapps = LSession::handle()->applicationMenu()->currentAppHash(); //get the raw info - + //Connect the signals/slots connect(ui->tool_desktopsettings, SIGNAL(clicked()), this, SLOT(openDeskSettings()) ); connect(ui->tool_config_screensaver, SIGNAL(clicked()), this, SLOT(openScreenSaverConfig()) ); @@ -28,13 +28,13 @@ UserWidget::UserWidget(QWidget* parent) : QTabWidget(parent), ui(new Ui::UserWid connect(ui->tool_home_browse, SIGNAL(clicked()), this, SLOT(slotOpenDir()) ); connect(ui->tool_home_search, SIGNAL(clicked()), this, SLOT(slotOpenSearch()) ); connect(ui->tool_config_about, SIGNAL(clicked()), this, SLOT(openLuminaInfo()) ); - + //Setup the special buttons connect(ui->tool_app_store, SIGNAL(clicked()), this, SLOT(openStore()) ); connect(ui->tool_controlpanel, SIGNAL(clicked()), this, SLOT(openControlPanel()) ); //connect(ui->tool_qtconfig, SIGNAL(clicked()), this, SLOT(openQtConfig()) ); - - lastUpdate = QDateTime(); //make sure it refreshes + + lastUpdate = QDateTime(); //make sure it refreshes connect(LSession::handle()->applicationMenu(), SIGNAL(AppMenuUpdated()), this, SLOT(UpdateMenu()) ); connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(updateFavItems()) ); @@ -68,7 +68,7 @@ void UserWidget::SortScrollArea(QScrollArea *area){ for(int i=0; i<lay->count(); i++){ items << lay->itemAt(i)->widget()->whatsThis().toLower(); } - + items.sort(); //qDebug() << " - Sorted Items:" << items; for(int i=0; i<items.length(); i++){ @@ -84,7 +84,7 @@ void UserWidget::SortScrollArea(QScrollArea *area){ } } } - + } QIcon UserWidget::rotateIcon(QIcon ico){ @@ -119,13 +119,13 @@ void UserWidget::UpdateAll(){ ui->tool_fav_dirs->setIcon( LXDG::findIcon("folder","") ); ui->tool_fav_files->setIcon( LXDG::findIcon("document-multiple","") ); ui->tool_desktopsettings->setIcon( LXDG::findIcon("preferences-desktop","") ); - ui->tool_config_screensaver->setIcon( LXDG::findIcon("preferences-desktop-screensaver","") ); + ui->tool_config_screensaver->setIcon( LXDG::findIcon("preferences-desktop-screensaver","") ); ui->tool_config_screensettings->setIcon( LXDG::findIcon("preferences-other","") ); ui->tool_home_gohome->setIcon( LXDG::findIcon("go-home","") ); ui->tool_home_browse->setIcon( LXDG::findIcon("document-open","") ); ui->tool_home_search->setIcon( LXDG::findIcon("system-search","") ); ui->tool_config_about->setIcon( LXDG::findIcon("lumina","") ); - + //Setup the special buttons QString APPSTORE = LOS::AppStoreShortcut(); if(QFile::exists(APPSTORE) && !APPSTORE.isEmpty()){ diff --git a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.h b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.h index 8b03c489..c112ac0f 100644 --- a/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.h +++ b/src-qt5/core/lumina-desktop/panel-plugins/userbutton/UserWidget.h @@ -24,8 +24,6 @@ #include <LuminaOS.h> #include "UserItemWidget.h" -#define SSAVER QString("xscreensaver-demo") - namespace Ui{ class UserWidget; }; @@ -81,7 +79,8 @@ private slots: LaunchItem("lumina-config", false); } void openScreenSaverConfig(){ - LaunchItem(SSAVER, false); + LaunchItem("lumina-config",false); + //LaunchItem(SSAVER, false); } void openScreenConfig(){ LaunchItem("lumina-xconfig",false); @@ -89,7 +88,7 @@ private slots: void openLuminaInfo(){ LaunchItem("lumina-info",false); } - + protected: void mouseMoveEvent( QMouseEvent *event); diff --git a/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.cpp b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.cpp new file mode 100644 index 00000000..4253a115 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.cpp @@ -0,0 +1,112 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LLockScreen.h" +#include "ui_LLockScreen.h" + +#include <unistd.h> + +#define NUMTRIES 3 +#define DEBUG 1 + +LLockScreen::LLockScreen(QWidget *parent) : QWidget(parent), ui(new Ui::LLockScreen()){ + ui->setupUi(this); + waittime = new QTimer(this); + waittime->setSingleShot(true); + refreshtime = new QTimer(this); //timer to update the wait time display + refreshtime->setInterval(6000); //6 seconds (1/10 second) + + connect(ui->tool_unlock, SIGNAL(clicked()), this, SLOT(TryUnlock()) ); + connect(ui->line_password, SIGNAL(returnPressed()), this, SLOT(TryUnlock()) ); + connect(ui->line_password, SIGNAL(textEdited(QString)), this, SIGNAL(InputDetected()) ); + connect(ui->line_password, SIGNAL(cursorPositionChanged(int,int)), this, SIGNAL(InputDetected()) ); + connect(waittime, SIGNAL(timeout()), this, SLOT(aboutToShow()) ); + connect(refreshtime, SIGNAL(timeout()), this, SLOT(UpdateLockInfo()) ); +} + +LLockScreen::~LLockScreen(){ + +} + +void LLockScreen::LoadSystemDetails(){ + //Run every time the screen is initially locked + QString user = QString(getlogin()); + ui->label_username->setText( QString(tr("Locked by: %1")).arg(user) ); + ui->label_hostname->setText( QHostInfo::localHostName() ); + ui->tool_unlock->setIcon( LXDG::findIcon("document-decrypt","") ); + attempts = 0; +} + +void LLockScreen::aboutToHide(){ + //auto-hide timeout - clear display + ui->line_password->clear(); + ui->line_password->clearFocus(); + if(refreshtime->isActive()){ refreshtime->stop(); } + ui->line_password->releaseKeyboard(); +} + +void LLockScreen::aboutToShow(){ + if(!waittime->isActive()){ + ui->label_info->clear(); + this->setEnabled(true); + triesleft = NUMTRIES; //back to initial number of tries + if(refreshtime->isActive()){ refreshtime->stop(); } + }else{ + if(!refreshtime->isActive()){ refreshtime->start(); } + } + UpdateLockInfo(); + ui->line_password->clear(); + ui->line_password->setFocus(); + ui->line_password->grabKeyboard(); +} + +// ================= +// PRIVATE SLOTS +// ================= +void LLockScreen::UpdateLockInfo(){ + QString info; + if(waittime->isActive()){ + info = tr("Too Many Failures")+"\n"+ QString(tr("Wait %1 Minutes")).arg( QString::number(qRound(waittime->remainingTime()/6000.0)/10.0) ); + }else if(attempts>0){ info.append("\n"+QString(tr("Failed Attempts: %1")).arg(QString::number(attempts)) ); } + ui->label_info->setText(info); +} + +void LLockScreen::TryUnlock(){ + attempts++; + this->setEnabled(false); + QString pass = ui->line_password->text(); + ui->line_password->clear(); + //Create a temporary file for the password, then pass that file descriptor to lumina-checkpass + QTemporaryFile *TF = new QTemporaryFile(".XXXXXXXXXX"); + TF->setAutoRemove(true); + bool ok = false; + if( TF->open() ){ + QTextStream in(TF); + in << pass.toUtf8()+"\0"; //make sure it is null-terminated + TF->close(); + if(DEBUG){ qDebug() << "Trying to unlock session:" << getlogin(); } + LUtils::runCommand(ok, "lumina-checkpass",QStringList() << "-f" << TF->fileName() ); + if(DEBUG){ qDebug() << " - Success:" << ok; } + ok = true; //bypass for the moment + } + delete TF; //ensure the temporary file is removed **right now** for security purposes + if(ok){ + emit ScreenUnlocked(); + this->setEnabled(true); + }else{ + triesleft--; + if(triesleft>0){ + this->setEnabled(true); + }else{ + waittime->setInterval( (attempts/NUMTRIES)*60000); + waittime->start(); + refreshtime->start(); + } + ui->line_password->setFocus(); + } + UpdateLockInfo(); + +} diff --git a/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.h b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.h new file mode 100644 index 00000000..79892714 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.h @@ -0,0 +1,42 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_LOCK_SCREEN_WIDGET_H +#define _LUMINA_DESKTOP_LOCK_SCREEN_WIDGET_H + +#include "global-includes.h" + +namespace Ui{ + class LLockScreen; +}; + +class LLockScreen : public QWidget{ + Q_OBJECT +public: + LLockScreen(QWidget *parent = 0); + ~LLockScreen(); + + void LoadSystemDetails(); //Run right after the screen is initially locked + +public slots: + void aboutToHide(); //auto-hide timeout (can happen multiple times per lock) + void aboutToShow(); //about to be re-shown (can happen multiple times per lock) + +private: + Ui::LLockScreen *ui; + int triesleft, attempts; + QTimer *waittime; + QTimer *refreshtime; + +private slots: + void UpdateLockInfo(); + void TryUnlock(); + +signals: + void ScreenUnlocked(); + void InputDetected(); +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.ui b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.ui new file mode 100644 index 00000000..7f0b45b8 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/LLockScreen.ui @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LLockScreen</class> + <widget class="QWidget" name="LLockScreen"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>289</width> + <height>188</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QFrame" name="frame_unlock"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_hostname"> + <property name="font"> + <font> + <weight>50</weight> + <italic>true</italic> + <bold>false</bold> + <underline>true</underline> + </font> + </property> + <property name="text"> + <string notr="true">hostname</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_username"> + <property name="text"> + <string notr="true">Locked by username</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_info"> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string notr="true"/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLineEdit" name="line_password"> + <property name="inputMask"> + <string notr="true"/> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="placeholderText"> + <string>Password</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QToolButton" name="tool_unlock"> + <property name="focusPolicy"> + <enum>Qt::NoFocus</enum> + </property> + <property name="text"> + <string>Unlock Session</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.cpp b/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.cpp new file mode 100644 index 00000000..6ccd8420 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.cpp @@ -0,0 +1,231 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LScreenSaver.h" +#include <QScreen> +#include <QApplication> + +#include <LSession.h> +#define DEBUG 0 + +LScreenSaver::LScreenSaver() : QWidget(0,Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint){ + starttimer = new QTimer(this); + starttimer->setSingleShot(true); + locktimer = new QTimer(this); + locktimer->setSingleShot(true); + hidetimer = new QTimer(this); + hidetimer->setSingleShot(true); + checkTimer = new QTimer(this); + checkTimer->setInterval(1500); //1.5 seconds + + lastTimeCode = 0; + + LOCKER = new LLockScreen(this); + LOCKER->hide(); + SSRunning = SSLocked = updating = false; + this->setObjectName("LSCREENSAVERBASE"); + this->setStyleSheet("LScreenSaver#LSCREENSAVERBASE{ background: darkgrey; }"); + this->setMouseTracking(true); + connect(starttimer, SIGNAL(timeout()), this, SLOT(ShowScreenSaver()) ); + connect(locktimer, SIGNAL(timeout()), this, SLOT(LockScreen()) ); + connect(hidetimer, SIGNAL(timeout()), this, SLOT(HideLockScreen()) ); + connect(checkTimer, SIGNAL(timeout()), this, SLOT(checkInputEvents()) ); + connect(LOCKER, SIGNAL(ScreenUnlocked()), this, SLOT(SSFinished()) ); + connect(LOCKER, SIGNAL(InputDetected()), this, SLOT(newInputEvent()) ); +} + +LScreenSaver::~LScreenSaver(){ + +} + +bool LScreenSaver::isLocked(){ + return SSLocked; +} + +void LScreenSaver::UpdateTimers(){ + //This is generally used for programmatic changes + if(starttimer->isActive()){ starttimer->stop();} + if(locktimer->isActive()){ locktimer->stop(); } + if(hidetimer->isActive()){ hidetimer->stop(); } + + if(!SSRunning && !SSLocked && (starttimer->interval() > 1000) ){ + //time to SS start + WId active = LSession::handle()->XCB->WM_Get_Active_Window(); + bool skip = (LSession::handle()->XCB->WindowName(active) != "Lumina Desktop Environment") && LSession::handle()->XCB->WindowIsFullscreen(active); + if(!skip){ starttimer->start(); } //do not start if current window is fullscreen (videos, movies, etc) + } + else if( SSRunning && !SSLocked && (locktimer->interval() > 1000 ) ){ locktimer->start(); } //time to lock + else if( !SSRunning && SSLocked ){ hidetimer->start(); } //time to hide lock screen +} + +// =========== +// PUBLIC SLOTS +// =========== +void LScreenSaver::start(){ + reloadSettings(); //setup all the initial time frames + LSession::handle()->XCB->disableDPMS(); //disable this initially - this only gets turned on when the screensaver activates + starttimer->start(); + checkTimer->start(); +} + +void LScreenSaver::reloadSettings(){ + starttimer->setInterval( DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "timedelaymin",10).toInt() * 60000 ); + locktimer->setInterval( DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "lockdelaymin",1).toInt() * 60000 ); + hidetimer->setInterval( DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "hidesecs",15).toInt() * 1000 ); +} + +void LScreenSaver::newInputEvent(){ + if(updating){ return; } //in the middle of making changes which could cause an event + if(DEBUG){ qDebug() << "New Input Event"; } + if(SSRunning && SSLocked){ + //Running and locked + // Hide the running setting, and display the lock screen + HideScreenSaver(); + ShowLockScreen(); + }else if(SSRunning){ + //Only running, not locked + HideScreenSaver(); + } + UpdateTimers(); +} + +void LScreenSaver::LockScreenNow(){ + ShowScreenSaver(); + LockScreen(); +} + +// =========== +// PRIVATE SLOTS +// =========== +void LScreenSaver::checkInputEvents(){ + //Check mouse position first + QPoint pos = QCursor::pos(); + bool change = false; + if(pos != lastMousePos){ + lastMousePos = QCursor::pos(); //update the internal point + change = true; + } + //Check the last keyboard input timestamp + WId active = LSession::handle()->XCB->WM_Get_Active_Window(); + if(active != lastActiveWindow){ + lastActiveWindow = active; + change = true; + } + unsigned int timecode = LSession::handle()->XCB->WM_Get_User_Time(active); + if(timecode > lastTimeCode){ + lastTimeCode = timecode; + change = true; + } + //If there was an input event detected (or a window is currently full-screen), update timers and such + if(change){ newInputEvent(); } +} + +void LScreenSaver::ShowScreenSaver(){ + if(DEBUG){ qDebug() << "Showing Screen Saver:" << QDateTime::currentDateTime().toString(); } + checkInputEvents(); //update all the internal times to now + //QApplication::setOverrideCursor(QCursor::BlankCursor); + SSRunning = true; + updating = true; + LSession::handle()->XCB->enableDPMS(); + //Now remove any current Base widgets (prevent any lingering painting between sessions) + for(int i=0; i<BASES.length(); i++){ + if(DEBUG){ qDebug() << " - Removing SS Base"; } + delete BASES.takeAt(i); i--; + } + //Now go through and create/show all the various widgets + QList<QScreen*> SCREENS = QApplication::screens(); + QRect bounds; + //cBright = LOS::ScreenBrightness(); + //if(cBright>0){ LOS::setScreenBrightness(cBright/2); } //cut to half while the screensaver is active + for(int i=0; i<SCREENS.length(); i++){ + bounds = bounds.united(SCREENS[i]->geometry()); + if(DEBUG){ qDebug() << " - New SS Base:" << i; } + BASES << new SSBaseWidget(this); + connect(BASES[i], SIGNAL(InputDetected()), this, SLOT(newInputEvent()) ); + + //Setup the geometry of the base to match the screen + BASES[i]->setGeometry(SCREENS[i]->geometry()); //match this screen geometry + QString plug = DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "plugin_"+SCREENS[i]->name(), "").toString(); + if(plug.isEmpty()){ plug = DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "default_plugin","random").toString(); } + BASES[i]->setPlugin(plug); + } + //Now set the overall parent widget geometry and show everything + this->setGeometry(bounds); //overall background widget + if(!this->isActiveWindow()){ + this->raise(); + this->show(); + this->activateWindow(); + } + for(int i=0; i<BASES.length(); i++){ + BASES[i]->show(); + BASES[i]->startPainting(); + } + this->grabKeyboard(); + updating = false; + UpdateTimers(); +} + +void LScreenSaver::ShowLockScreen(){ + if(DEBUG){ qDebug() << "Showing Lock Screen:" << QDateTime::currentDateTime().toString(); } + LOCKER->aboutToShow(); + //Move the screen locker to the appropriate spot + QPoint ctr = QApplication::screenAt(QCursor::pos())->geometry().center(); + LOCKER->resize(LOCKER->sizeHint()); + LOCKER->move(ctr - QPoint(LOCKER->width()/2, LOCKER->height()/2) ); + LOCKER->show(); + LOCKER->activateWindow(); + //Start the timer for hiding the lock screen due to inactivity + UpdateTimers(); +} + +void LScreenSaver::HideScreenSaver(){ + //QApplication::restoreOverrideCursor(); + if(DEBUG){ qDebug() << "Hiding Screen Saver:" << QDateTime::currentDateTime().toString(); } + SSRunning = false; + LSession::handle()->XCB->disableDPMS(); + //if(cBright>0){ LOS::setScreenBrightness(cBright); } //return to current brightness + this->releaseKeyboard(); + if(!SSLocked){ + this->hide(); + emit ClosingScreenSaver(); + emit LockStatusChanged(false); + } + if(DEBUG){ qDebug() << "Stop ScreenSavers"; } + for(int i=0; i<BASES.length(); i++){ + BASES[i]->stopPainting(); + BASES[i]->hide(); + BASES.takeAt(i)->deleteLater(); + i--; + } + UpdateTimers(); +} + +void LScreenSaver::HideLockScreen(){ + if(DEBUG){ qDebug() << "Hiding Lock Screen:" << QDateTime::currentDateTime().toString(); } + //Leave the Locked flag set (still locked, just not visible) + LOCKER->aboutToHide(); + LOCKER->hide(); + this->repaint(); + if(SSLocked){ ShowScreenSaver(); } + UpdateTimers(); +} + +void LScreenSaver::LockScreen(){ + if(SSLocked){ return; } + if(DEBUG){ qDebug() << "Locking Screen:" << QDateTime::currentDateTime().toString(); } + SSLocked = true; + emit LockStatusChanged(true); + LOCKER->LoadSystemDetails(); + UpdateTimers(); +} + +void LScreenSaver::SSFinished(){ + if(DEBUG){ qDebug() << "Screensaver Finished:" << QDateTime::currentDateTime().toString(); } + SSLocked = false; + emit LockStatusChanged(false); + HideLockScreen(); + HideScreenSaver(); +} diff --git a/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.h b/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.h new file mode 100644 index 00000000..5d154e64 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/LScreenSaver.h @@ -0,0 +1,63 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_H + +#include "global-includes.h" + +#include "SSBaseWidget.h" +#include "LLockScreen.h" + +class LScreenSaver : public QWidget{ + Q_OBJECT +public: + LScreenSaver(); + ~LScreenSaver(); + + bool isLocked(); + +private: + QTimer *starttimer, *locktimer, *hidetimer, *checkTimer; + QList<SSBaseWidget*> BASES; + LLockScreen *LOCKER; + QPoint lastMousePos; + unsigned int lastTimeCode; + WId lastActiveWindow; + int cBright; + bool SSRunning, SSLocked, updating; + + void UpdateTimers(); + +public slots: + void start(); + void reloadSettings(); + void newInputEvent(); + void LockScreenNow(); + +private slots: + void checkInputEvents(); + void ShowScreenSaver(); + void ShowLockScreen(); + void HideScreenSaver(); + void HideLockScreen(); + + void LockScreen(); + void SSFinished(); + +signals: + void StartingScreenSaver(); + void ClosingScreenSaver(); + void LockStatusChanged(bool locked); + +protected: + void mouseMoveEvent(QMouseEvent*){ + QTimer::singleShot(0,this, SLOT(newInputEvent())); + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.cpp b/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.cpp new file mode 100644 index 00000000..840eb12a --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.cpp @@ -0,0 +1,64 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== + +#include "SSBaseWidget.h" + +//Relative directory to search along the XDG paths for screensavers +#define REL_DIR QString("/lumina-desktop/screensavers") + +#define DEBUG 1 + +// ======== +// PUBLIC +// ======== +SSBaseWidget::SSBaseWidget(QWidget *parent) : QQuickView(parent->windowHandle()){ + this->setObjectName("LuminaBaseSSWidget"); + this->setResizeMode(QQuickView::SizeRootObjectToView); + this->setColor(QColor("black")); //default color for the view + this->setCursor(Qt::BlankCursor); + plugType="none"; + restartTimer = new QTimer(this); + restartTimer->setInterval( DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, "globals/plugin_time_seconds", 120).toInt() * 1000); + restartTimer->setSingleShot(true); + connect(restartTimer, SIGNAL(timeout()), this, SLOT(startPainting()) ); +} + +SSBaseWidget::~SSBaseWidget(){ +} + +void SSBaseWidget::setPlugin(QString plug){ + plugType = plug.toLower(); +} + +// ============= +// PUBLIC SLOTS +// ============= +void SSBaseWidget::startPainting(){ + //free up any old animation instance + stopPainting(); + //If a random plugin - grab one of the known plugins + if(plugType=="random"){ + QList<SSPlugin> valid = PluginSystem::findAllPlugins<SSPlugin>(REL_DIR); + if(!valid.isEmpty()){ cplug = valid[ qrand()%valid.length() ]; } //grab a random plugin + }else if(plugType!="none"){ + cplug = PluginSystem::findPlugin<SSPlugin>(plugType, REL_DIR); + } + if(DEBUG){ qDebug() << " - Screen Saver:" << plugType << cplug.scriptURL() << cplug.isValid(); } + if(cplug.isValid()){ + this->setSource( cplug.scriptURL() ); + if(plugType=="random"){ restartTimer->start(); } + } + +} + +void SSBaseWidget::stopPainting(){ + if(!this->source().isEmpty()){ + this->setSource(QUrl()); + cplug = SSPlugin(); //empty structure + } + if(restartTimer->isActive()){ restartTimer->stop(); } +} diff --git a/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.h b/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.h new file mode 100644 index 00000000..e0a03d17 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/SSBaseWidget.h @@ -0,0 +1,50 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the widget which provides the screensaver painting/plugin functionality +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_BASE_WIDGET_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_BASE_WIDGET_H + +#include "global-includes.h" +#include <plugins-base.h> +#include <plugins-screensaver.h> + +class SSBaseWidget : public QQuickView{ + Q_OBJECT +public: + SSBaseWidget(QWidget *parent); + ~SSBaseWidget(); + + void setPlugin(QString); + +public slots: + void startPainting(); + void stopPainting(); + +private: + QString plugType; + SSPlugin cplug; + QTimer *restartTimer; + +private slots: + +signals: + void InputDetected(); //just in case no event handling setup at the WM level + +protected: + void mouseMoveEvent(QMouseEvent *ev){ + ev->accept(); + emit InputDetected(); + } + void keyPressEvent(QKeyEvent *ev){ + ev->accept(); + emit InputDetected(); + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.cpp b/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.cpp new file mode 100644 index 00000000..9b095fe4 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.cpp @@ -0,0 +1,51 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015-2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "BaseAnimGroup.h" + +//Include all the known subclasses here, then add a unique ID for it to the functions at the bottom +//#include "SampleAnimation.h" +#include "Fireflies.h" +#include "Grav.h" +#include "SampleAnimation.h" +#include "Text.h" +#include "ImageSlideshow.h" +#include "VideoSlideshow.h" + + +QVariant BaseAnimGroup::readSetting(QString variable, QVariant defaultvalue){ + return DesktopSettings::instance()->value(DesktopSettings::ScreenSaver, + "Animations/"+animPlugin+"/"+variable, defaultvalue); +} + +//============================== +// PLUGIN LOADING/LISTING +//============================== +BaseAnimGroup* BaseAnimGroup::NewAnimation(QString type, QWidget *parent){ + //This is where we place all the known plugin ID's, and load the associated subclass + BaseAnimGroup *anim = 0; + if(type=="fireflies"){ + anim = new FirefliesAnimation(parent); + }else if(type == "grav") { + anim = new GravAnimation(parent); + }else if(type == "text") { + anim = new TextAnimation(parent); + }else if(type == "imageSlideshow") { + anim = new ImageAnimation(parent); + }else if(type == "videoSlideshow") { + anim = new VideoAnimation(parent); + }else { + //Unknown screensaver, return a blank animation group + anim = new BaseAnimGroup(parent); + } + //tag the animation with the type it is and return it + if(anim!=0){ anim->animPlugin = type; } + return anim; +} + +QStringList BaseAnimGroup::KnownAnimations(){ + return (QStringList() << "none" << "grav" << "text" << "imageSlideshow" << "videoSlideshow" << "fireflies"); +} diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.h new file mode 100644 index 00000000..92e038ed --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/BaseAnimGroup.h @@ -0,0 +1,40 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015-2017, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the container which provides the screensaver animations +// and should be subclassed for each of the various animation types +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_BASE_ANIMATION_GROUP_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_BASE_ANIMATION_GROUP_H + +#include "global-includes.h" + +class BaseAnimGroup : public QParallelAnimationGroup{ + Q_OBJECT +public: + QWidget *canvas; + QString animPlugin; + + virtual void LoadAnimations(){} //This is the main function which needs to be subclassed + + BaseAnimGroup(QWidget *parent){ + canvas = parent; + canvas->setCursor( QCursor(Qt::BlankCursor) ); + } + ~BaseAnimGroup(){} + + QVariant readSetting(QString variable, QVariant defaultvalue = QVariant()); + + + //============================== + // PLUGIN LOADING/LISTING (Change in the .cpp file) + //============================== + static BaseAnimGroup* NewAnimation(QString type, QWidget *parent); + static QStringList KnownAnimations(); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/Fireflies.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/Fireflies.h new file mode 100644 index 00000000..dfc12e79 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/Fireflies.h @@ -0,0 +1,122 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the sample plugin for a ScreenSaver animation +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_FIREFLIES_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_FIREFLIES_ANIMATION_H + +#include "global-includes.h" +#include "BaseAnimGroup.h" +#include <QSequentialAnimationGroup> +#include <QGraphicsOpacityEffect> + +class Firefly : public QSequentialAnimationGroup{ + Q_OBJECT +private: + QWidget *fly; + QPropertyAnimation *movement, *flash; + int maxX, maxY; //maximum jitter in X/Y directions + QSize range; +private slots: + void LoopChanged(){ + //Adjust the movement animation a bit + movement->setStartValue(movement->endValue()); //start at the previous end point + QPoint pt = movement->startValue().toPoint(); + QPoint diff( (qrand()% maxX) - (maxX/2), (qrand()% maxY) - (maxY/2) ); + //Need to ensure it stays in the current box + if( (pt.x()+diff.x()) < 0 || (pt.x()+diff.x())>range.width()){ pt.setX(pt.x() - diff.x()); } //reverse the direction - otherwise will go out of bounds + else{ pt.setX( pt.x() + diff.x() ); } + if( (pt.y()+diff.y()) < 0 || (pt.y()+diff.y())>range.height()){ pt.setY(pt.y() - diff.y()); } //reverse the direction - otherwise will go out of bounds + else{ pt.setY( pt.y() + diff.y() ); } + movement->setEndValue(pt); + movement->setDuration( qrand() %500 + 1000); //between 1000->1500 ms animations for movements + //Adjust the flash duration/size a bit + flash->setDuration(qrand() %200 + 500); //500-700 ms + int sz = qrand()%4 + 4; //6-10 pixel square + //flash->setKeyValueAt(0.5, (qrand()%50) /100.0); + //fly->resize(sz,sz); + flash->setKeyValueAt(0.5, QSize(sz,sz)); //half-way point for the flash + + fly->show(); + } + void stopped(){ fly->hide(); } + +public: + Firefly(QWidget *parent) : QSequentialAnimationGroup(parent){ + fly = new QWidget(parent); + range = parent->size(); + maxX = range.width()/4; maxY = range.height()/4; + QString B = QString::number(qrand()%70); + QString RY = QString::number(qrand()%200+50); + QString style = "background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(245, 245, 143, 200), stop:0.83871 rgba(%1, %1, %2, 140), stop:0.99 rgba(0, 0, 0, 255), stop:1 transparent);"; + fly->setStyleSheet(style.arg(RY, B) ); + //setup the movement animation + movement = new QPropertyAnimation(fly); + movement->setTargetObject(fly); + movement->setPropertyName("pos"); + movement->setEndValue( QPoint( qrand() % range.width(), qrand()%range.height()) ); //on anim start, this will become the starting point + //setup the flashing animation + /*QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(parent); + fly->setGraphicsEffect(eff); + flash = new QPropertyAnimation(eff, "opacity");*/ + flash = new QPropertyAnimation(this); + flash->setTargetObject(fly); + flash->setPropertyName("size"); + flash->setStartValue(QSize(0,0)); + flash->setEndValue(flash->startValue()); + //fly->setProperty("opacity",0); + //flash->setPropertyName("opacity"); + //flash->setStartValue(0); + //flash->setEndValue(0); + //now setup the order of the animations + this->setLoopCount(100); //do this 100 times + //Roughly half the number of fireflies with start with movement/flash + if(qrand()%2 == 1){ + this->addAnimation(movement); + this->addAnimation(flash); + }else{ + this->addAnimation(flash); + this->addAnimation(movement); + } + //Start up this firefly + LoopChanged(); //load initial values + + fly->setGeometry( QRect(movement->startValue().toPoint(), flash->startValue().toSize()) ); + connect(this, SIGNAL(currentLoopChanged(int)), this, SLOT(LoopChanged()) ); + connect(this, SIGNAL(finished()), this, SLOT(stopped()) ); + } + ~Firefly(){} + +}; + +class FirefliesAnimation : public BaseAnimGroup{ + Q_OBJECT +private: + QList<Firefly*> fireflies; + +public: + FirefliesAnimation(QWidget *parent) : BaseAnimGroup(parent){} + ~FirefliesAnimation(){ + this->stop(); + //while(fireflies.length()>0){ fireflies.takeAt(0)->deleteLater(); } + } + + void LoadAnimations(){ + while(fireflies.length()>0){ fireflies.takeAt(0)->deleteLater(); } + canvas->setStyleSheet("background: black;"); + int number = readSetting("number",qrand()%30 + 50).toInt(); + for(int i=0; i<number; i++){ + if(fireflies.length()>number){ continue; } + Firefly *tmp = new Firefly(canvas); + this->addAnimation(tmp); + fireflies << tmp; + } + + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/Grav.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/Grav.h new file mode 100644 index 00000000..df75ad67 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/Grav.h @@ -0,0 +1,195 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_GRAV_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_GRAV_ANIMATION_H + +//PI is equal to 2*pi +#define PI 6.2832 +#include "global-includes.h" +#include "BaseAnimGroup.h" +#include <QtMath> +#include <QMatrix> + +class Grav: public QParallelAnimationGroup{ + Q_OBJECT +private: + QWidget *planet; + QPropertyAnimation *orbit; + QSize range; + //QList<QPoint> path; + double radius; + + void setupLoop(QPoint start, QPoint *ref){ + //orbit->setStartValue(start); + + //Used to find overall speed. Distance from the planet to the sun + radius = qSqrt( (qPow(start.x()-ref->x(),2) + qPow(start.y()-ref->y(),2) )); + + //Number of frames in animation. Increase for smother motion + double step = 300.0; + + //Random values that give the eliptical pattern to the orbit. Between 0.4 and 2.3 + double xrand = 0.4; //(qrand()%10+4)/10.0; + double yrand = 0.4; //(qrand()%10+4)/10.0; + + double theta = 1.5707963; + //double theta = aTan((start.x() - ref->x())/(start.y() - ref->y())); + QMatrix rotation = QMatrix(qCos(theta), qSin(theta), -qSin(theta), qCos(theta), -ref->x(), -ref->y()); + qDebug() << rotation; + //qDebug() << "Starting Point" << start; + //qDebug() << "Angle" << theta; + //qDebug() << "Distance" << radius; + //qDebug() << "Center" << *ref; + + QPoint firstP = (QPoint(ref->x() + xrand*start.x()*(qCos(0/step) -qSin(0/step)), ref->y() + yrand*start.y()*(qCos(0/step) -qSin(0/step)))); + QPoint rotFP = rotation.map(firstP); + qDebug() << "First Point" << firstP; + qDebug() << "Rotation by Matrix" << rotFP; + QPoint lastP = (QPoint(ref->x() + xrand*start.x()*(qCos(PI/step) -qSin(PI/step)), ref->y() + yrand*start.y()*(qCos(PI/step) -qSin(PI/step)))); + orbit->setKeyValueAt(0, firstP); + orbit->setKeyValueAt(1, lastP); + //path.push_back(firstP); + + //Loops through all steps and creates all the points of the orbit + for(int i=1; i<step; i++) { + //Calculates the new point, including gravitational pull and eccentricity. Goes from 0 to 2PI in steps. + double newX = ref->x() + xrand*start.x()*(qCos((PI*i)/step) - qSin((PI*i)/step)); + double newY = ref->y() + yrand*start.y()*(qSin((PI*i)/step) + qCos((PI*i)/step)); + + //Creates a new point and creates a key as part of the animation + QPoint newLoc = (QPoint(newX, newY)); + orbit->setKeyValueAt(i/step, newLoc); + //path.push_back(newLoc); + } + //Sets the time for a full orbit. Increasing makes the orbit slower. + //path.push_back(lastP); + } +private slots: + /*void LoopChanged(int loop){ + //Adjust the orbit animation a bit + if(loop >= 0) { + orbit->setStartValue(orbit->endValue()); //start at the previous end point + orbit->setEndValue(path.at(loop%1000)); + orbit->setDuration(10); + } + }*/ + void stopped(){ planet->hide();} + +public: + Grav(QWidget *parent) : QParallelAnimationGroup(parent){ + planet = new QWidget(parent); + range = parent->size(); + QPoint center = QRect(QPoint(0,0), parent->size()).center();; + + //Creates a random planet size. Between 12 and 45 pixels + int planet_radius = qRound(1.75* ((qrand()%20)+7) ); + + //Creates a random color in RGB, then creates a circular gradient + QString color = "rgba(" + QString::number(qrand() % 256) + ", " + QString::number(qrand() % 256) + ", " + QString::number(qrand() % 256); + QString style = "background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 " + color+ + ", 255)" + " , stop:0.83871 " + color + ", 140)" + " , stop:0.99 rgba(0, 0, 0, 255), stop:1 transparent);"; + planet->setStyleSheet(style); + + //setup the orbit animation + orbit = new QPropertyAnimation(planet); + orbit->setPropertyName("pos"); + orbit->setTargetObject(planet); + + //Creates the random position of the planet, making sure it isn't too close to the sun + QRect invalid = QRect(center+QPoint(-50,-50), center+QPoint(50,50)); + QPoint tmp = center; + while(invalid.contains(tmp)){ + int randwidth = qrand()%(range.width() - 2*planet_radius) + planet_radius; + int randheight = qrand()%(range.height()- 2*planet_radius) + planet_radius; + tmp = QPoint(randwidth, randheight); + } + + //Creates all frames for the animation + setupLoop(tmp, ¢er); + this->addAnimation(orbit); + planet->show(); + + //Ensures the screensaver will not stop until the user wishes to login or it times out + this->setLoopCount(3); //number of orbits + orbit->setDuration( qrand() %1000 + 19000); //20 second orbits + //orbit->setEndValue(path.at(0)); + //LoopChanged(0); //load initial values + + //Sets the initial size and location of the planet + planet->setGeometry(QRect(orbit->startValue().toPoint(), QSize(planet_radius, planet_radius))); + //connect(this, SIGNAL(currentLoopChanged(int)), this, SLOT(LoopChanged(int)) ); + connect(this, SIGNAL(finished()), this, SLOT(stopped()) ); + } + ~Grav(){} + +}; + +class GravAnimation : public BaseAnimGroup{ + Q_OBJECT +private: + QList<Grav*> planets; + QWidget *sun; + QPropertyAnimation *wobble; + +private slots: + void checkFinished(){ + int running = 0; + for(int i=0; i<this->animationCount(); i++){ + if(this->animationAt(i)->state()==QAbstractAnimation::Running){ running++; } + } + if(running<=1){ wobble->stop(); emit wobble->finished();} + } + +public: + GravAnimation(QWidget *parent) : BaseAnimGroup(parent){} + ~GravAnimation(){ + sun->deleteLater(); + while(planets.length()>0){ planets.takeAt(0)->deleteLater(); } + } + + void LoadAnimations(){ + //Creates the sun, which is a thin shell with a gradient from green to yellow + sun = new QWidget(canvas); + QPoint center = QRect(QPoint(0,0), canvas->size()).center(); + QString sunstyle = QStringLiteral("background-color:qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, ") + + QStringLiteral("stop:0 rgba(0, 0, 0, 0), stop:0.38 rgba(0, 0, 0, 0), stop:0.4 rgba(82, 121, 76, 33), stop:0.5 rgba(159, 235, 148, 64), ") + + QStringLiteral("stop:0.6 rgba(255, 238, 150, 129), stop:0.7 rgba(0, 0, 0, 0));"); + sun->setStyleSheet(sunstyle); + + //Creates the sun's pulsing animation + wobble = new QPropertyAnimation(sun); + wobble->setPropertyName("geometry"); + wobble->setTargetObject(sun); + QRect initgeom = QRect(center-QPoint(30,30), QSize(60, 60)); + wobble->setStartValue(initgeom); + wobble->setKeyValueAt(0, initgeom ); //starting point + wobble->setKeyValueAt(1, initgeom ); //starting point + wobble->setKeyValueAt(0.5, QRect(center-QPoint(45,45), QSize(90, 90))); //starting point + wobble->setDuration(2000); + wobble->setLoopCount(-1); + this->addAnimation(wobble); + sun->show(); + sun->setGeometry(initgeom); + + //Gives the screensaver a black background + //canvas->setStyleSheet("background: black;"); + + //Pulls number of planets from settings, with 10 as default + int number = readSetting("planets/number",qrand()%5+3).toInt(); + + //Loops through all planets and sets up the animations, then adds them to the base group and vector, which + //qDebug() << "Starting planets"; + for(int i=0; i<number; i++){ + Grav *tmp = new Grav(canvas); + this->addAnimation(tmp); + connect(tmp, SIGNAL(finished()), this, SLOT(checkFinished())); + planets << tmp; + } + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/ImageSlideshow.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/ImageSlideshow.h new file mode 100644 index 00000000..81bc2b35 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/ImageSlideshow.h @@ -0,0 +1,165 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_IMAGESLIDESHOW_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_IMAGESLIDESHOW_ANIMATION_H + +#include "global-includes.h" +#include "BaseAnimGroup.h" + +class ImageSlideshow: public QParallelAnimationGroup{ + Q_OBJECT +private: + QLabel *image; + QPropertyAnimation *bounce, *fading; + QPixmap pixmap; + QStringList imageFiles; + QString imagePath, scriptPath, curpixmap; + QSize screenSize; + bool animate, scriptLoad; + +private: + void setupAnimation() { + //Choose between starting from top or bottom at random + if(qrand() % 2) { + bounce->setKeyValueAt(0, QPoint(0,screenSize.height()-image->height())); + bounce->setKeyValueAt(0.25, QPoint((screenSize.width()-image->width())/2,0)); + bounce->setKeyValueAt(0.5, QPoint(screenSize.width()-image->width(),screenSize.height()-image->height())); + bounce->setKeyValueAt(0.75, QPoint((screenSize.width()-image->width())/2,0)); + bounce->setKeyValueAt(1, QPoint(0,screenSize.height()-image->height())); + }else{ + bounce->setKeyValueAt(0, QPoint(0,0)); + bounce->setKeyValueAt(0.25, QPoint((screenSize.width()-image->width())/2,screenSize.height()-image->height())); + bounce->setKeyValueAt(0.5, QPoint(screenSize.width()-image->width(),0)); + bounce->setKeyValueAt(0.75, QPoint((screenSize.width()-image->width())/2,screenSize.height()-image->height())); + bounce->setKeyValueAt(1, QPoint(0,0)); + } + } + + void chooseImage() { + /*if(scriptLoad){ + QProcess process; + process.start("/home/zwelch/test.sh"); + process.waitForFinished(1000); + QByteArray output = process.readAllStandardOutput(); + //qDebug() << output; + //pixmap.load(randomFile); + }else{*/ + //File Load + QString randomFile = curpixmap; + if(imageFiles.size()>1 || curpixmap.isEmpty()){ + while(curpixmap==randomFile){ randomFile = imagePath+imageFiles[qrand() % imageFiles.size()]; } + } + if(curpixmap!=randomFile){ + curpixmap = randomFile; //save this for later + //no need to load the new file or change the label + pixmap.load(randomFile); + //If the image is larger than the screen, then shrink the image down to 3/4 it's size (so there's still some bounce) + //Scale the pixmap to keep the aspect ratio instead of resizing the label itself + if(pixmap.width() >= (screenSize.width()-10) || pixmap.height() >= (screenSize.height()-10) ){ + pixmap = pixmap.scaled(screenSize*(3.0/4.0), Qt::KeepAspectRatio); + } + //Set pixmap to the image label + image->setPixmap(pixmap); + image->resize(pixmap.size()); + } + //} + + } + +private slots: + void LoopChanged(){ + //Load a new random image. Resize the label based on the image's size + chooseImage(); + setupAnimation(); + } + void stopped(){ image->hide();} + +public: + ImageSlideshow(QWidget *parent, QString path, bool animate, bool scriptLoad, QString scriptPath) : QParallelAnimationGroup(parent){ + imagePath = path; + image = new QLabel(parent); + screenSize = parent->size(); + this->animate = animate; + this->scriptLoad = scriptLoad; + this->scriptPath = scriptPath; + + //Generate the list of files in the directory + imageFiles = QDir(imagePath).entryList(QDir::Files); + //Ensure all the files are actually images + for(int i=0; i<imageFiles.length(); i++){ + if(QImageReader::imageFormat(imagePath+"/"+imageFiles[i]).isEmpty()){ imageFiles.removeAt(i); i--; } + } + if(imageFiles.empty()){ + qDebug() << "Current image file path has no files."; + image->setText("No image files found:\n"+imagePath); + }else{ + //Change some default settings for the image. If scaledContents is false, the image will be cut off if resized + image->setScaledContents(true); + image->setAlignment(Qt::AlignHCenter); + //Load a random initial image + chooseImage(); + } + + //Create the animation that moves the image across the screen + bounce = new QPropertyAnimation(image, "pos", parent); + + //Add the animation that fades the image in and out + QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(parent); + image->setGraphicsEffect(eff); + fading = new QPropertyAnimation(eff,"opacity"); + fading->setKeyValueAt(0, 0); + fading->setKeyValueAt(0.20, 1); + fading->setKeyValueAt(0.80, 1); + fading->setKeyValueAt(1, 0); + this->addAnimation(fading); + + setupAnimation(); + image->show(); + //Only add the animation if set in the configuration file + if(animate) + this->addAnimation(bounce); + else + //If no animation, center the image in the middle of the screen + image->move(QPoint((parent->width()-image->width())/2,(parent->height()-image->height())/2)); + + //Loop through 15 times for a total for 2 minutes + this->setLoopCount(15); + bounce->setDuration(8000); + fading->setDuration(8000); + + connect(this, SIGNAL(currentLoopChanged(int)), this, SLOT(LoopChanged()) ); + connect(this, SIGNAL(finished()), this, SLOT(stopped()) ); + } + ~ImageSlideshow(){} + +}; + +class ImageAnimation: public BaseAnimGroup{ + Q_OBJECT +public: + ImageAnimation(QWidget *parent) : BaseAnimGroup(parent){} + ~ImageAnimation(){ + this->stop(); + } + + void LoadAnimations(){ + canvas->setStyleSheet("background: black;"); + //Load the path of the images from the configuration file (default /usr/local/backgrounds/) + QString imagePath = readSetting("path", LOS::LuminaShare()+"../wallpapers/").toString(); + //Load whether to animate the image (default true) + bool animate = readSetting("animate", true).toBool(); + bool scriptLoad = readSetting("scriptLoad", true).toBool(); + QString scriptPath; + if(scriptLoad){ + scriptPath = readSetting("scriptPath", "/usr/local/backgrounds/script.sh").toString(); + } + ImageSlideshow *tmp = new ImageSlideshow(canvas, imagePath, animate, scriptLoad, scriptPath); + this->addAnimation(tmp); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/SampleAnimation.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/SampleAnimation.h new file mode 100644 index 00000000..c7a8b237 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/SampleAnimation.h @@ -0,0 +1,45 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the sample plugin for a ScreenSaver animation +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_SAMPLE_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_SAMPLE_ANIMATION_H + +#include "global-includes.h" +#include "BaseAnimGroup.h" + +class SampleAnimation : public BaseAnimGroup{ + Q_OBJECT +private: + QWidget *ball; + +public: + SampleAnimation(QWidget *parent) : BaseAnimGroup(parent){} + ~SampleAnimation(){ this->stop(); delete ball; } + + void LoadAnimations(){ + //qDebug() << "Loading Sample Animation"; + ball = new QWidget(canvas); + //This creates a red "ball" on the widget which is going to expand/contract in the center of the screen + ball->setStyleSheet("background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.341, fy:0.796, stop:0.00531915 rgba(107, 10, 10, 255), stop:0.521277 rgba(170, 10, 10, 255), stop:0.957447 rgba(200, 0, 0, 255), stop:0.994681 rgba(0, 0, 0, 225), stop:1 rgba(255, 255, 255, 0));"); + //Now setup the movements + QPropertyAnimation *move = new QPropertyAnimation(ball,"geometry"); + QPoint ctr(canvas->width()/2, canvas->height()/2); + QRect initgeom(ctr-QPoint(12,12), QSize(24,24) ); + move->setKeyValueAt(0, initgeom ); //starting point + move->setKeyValueAt(1, initgeom ); //ending point (same as start for continuity) + int size = canvas->width(); + if(size > canvas->height()){ size = canvas->height(); } + move->setKeyValueAt(0.5, QRect(ctr-QPoint(size/2, size/2), QSize(size,size))); //touch the edge of the screen + move->setDuration(10000); //10 seconds + this->addAnimation(move); + this->setLoopCount(10); //repeat 10 times + ball->show(); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/Text.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/Text.h new file mode 100644 index 00000000..bdde5ba2 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/Text.h @@ -0,0 +1,100 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_TEXT_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_TEXT_ANIMATION_H + +#include "global-includes.h" +#include "BaseAnimGroup.h" +#include <QParallelAnimationGroup> +#include <QtMath> + +#include <unistd.h> + +class Text: public QParallelAnimationGroup{ + Q_OBJECT +private: + QLabel *text; + QPropertyAnimation *movement; + QSize range; + QPoint v; + bool bounce; + +private slots: + void LoopChanged(){ + movement->setStartValue(movement->endValue()); + QPoint currLoc = movement->startValue().toPoint(); + bounce = !(currLoc.y() < 100 or currLoc.y() > range.height()-100 or currLoc.x() > range.width()-100 or currLoc.x() < 100); + if((currLoc.y() < 10 or currLoc.y() > range.height()-40) and !bounce) { + v.setY((v.y() * -1) + (qrand() % 20 - 10)); + }else if((currLoc.x() > range.width()-10 or currLoc.x() < 10) and !bounce) { + v.setX((v.x() * -1) + (qrand() % 20 - 10)); + } + currLoc.setX(currLoc.x() + v.x()); + currLoc.setY(currLoc.y() + v.y()); + movement->setEndValue(currLoc); + } + void stopped(){ qDebug() << "Text Stopped"; text->hide();} + +public: + Text(QWidget *parent, QString display) : QParallelAnimationGroup(parent){ + text = new QLabel(parent); + range = parent->size(); + QPoint center = QRect( QPoint(0,0), parent->size()).center(); + + QString color = "rgba(" + QString::number(qrand() % 206 + 50) + ", " + QString::number(qrand() % 206 + 50) + ", " + QString::number(qrand() % 206 + 50); + text->setStyleSheet("QLabel {background-color: transparent; color: " + color + "); }"); + text->setFont(QFont("Courier", 24, QFont::Bold)); + text->setText(display); + QFontMetrics metrics(text->font()); + text->setMinimumSize(QSize( metrics.horizontalAdvance(text->text())+10, metrics.height()*text->text().count("\n") +10)); + + movement = new QPropertyAnimation(text); + movement->setPropertyName("pos"); + movement->setTargetObject(text); + + this->addAnimation(movement); + text->show(); + v.setX((qrand() % 100 + 50) * qPow(-1, qrand() % 2)); + v.setY((qrand() % 100 + 50) * qPow(-1, qrand() % 2)); + movement->setStartValue(center); + //Ensures the screensaver will not stop until the user wishes to login or it times out + this->setLoopCount(200); //number of wall bounces + movement->setDuration(200); + movement->setEndValue(QPoint(qrand() % (int)range.height(), qrand() % range.width())); + LoopChanged(); //load initial values + + connect(this, SIGNAL(currentLoopChanged(int)), this, SLOT(LoopChanged()) ); + connect(this, SIGNAL(finished()), this, SLOT(stopped()) ); + } + ~Text(){} + +}; + +class TextAnimation : public BaseAnimGroup{ + Q_OBJECT +public: + TextAnimation(QWidget *parent) : BaseAnimGroup(parent){} + ~TextAnimation(){ + this->stop(); + } + + void LoadAnimations(){ + canvas->setStyleSheet("background: black;"); + //Read off the text that needs to be displayed + QString textToShow = readSetting("text", "").toString(); + if(textToShow.isEmpty()){ + char hname[300]; + gethostname(hname, 300); + textToShow = QString::fromLocal8Bit(hname); + } + // Now create the animation + Text *tmp = new Text(canvas, textToShow); + this->addAnimation(tmp); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/VideoSlideshow.h b/src-qt5/core/lumina-desktop/src-screensaver/animations/VideoSlideshow.h new file mode 100644 index 00000000..358b4bfb --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/VideoSlideshow.h @@ -0,0 +1,85 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SCREEN_SAVER_VIDEOSLIDESHOW_ANIMATION_H +#define _LUMINA_DESKTOP_SCREEN_SAVER_VIDEOSLIDESHOW_ANIMATION_H + +#include "global-includes.h" +#include "BaseAnimGroup.h" + +class VideoAnimation: public BaseAnimGroup{ + Q_OBJECT +private: + QString videoPath, singleVideo; + QVideoWidget *videoWidget; + QMediaPlayer *video; + QStringList videoFiles; + bool multiple; + +private slots: + +public: + VideoAnimation(QWidget *parent) : BaseAnimGroup(parent){} + + ~VideoAnimation(){ + this->stop(); + } + + void LoadAnimations(){ + canvas->setStyleSheet("background: black;"); + + //Load the path of the videos from the configuration file (default /usr/local/videos/) + videoPath = readSetting("path","/usr/local/videos").toString(); + singleVideo = readSetting("videoLocation","").toString(); + multiple = readSetting("multiple",true).toBool(); + if(!videoPath.endsWith("/")){ videoPath.append("/"); } + + //Set whether to copy videos on two monitors or play different videos + //multimonitor = settings->value("videoSlideshow/multimonitor",true).toBool(); + + //Set up the VideoWidget + video = new QMediaPlayer(canvas, QMediaPlayer::VideoSurface); + videoWidget = new QVideoWidget(canvas); + video->setVideoOutput(videoWidget); + videoWidget->setGeometry(QRect(QPoint(0,0), canvas->size())); + + //Generate the list of files in the directory + videoFiles = QDir(videoPath).entryList(QDir::Files); + if(videoFiles.empty()){ + qDebug() << "Current video file path has no files:" << videoPath; + return; + } + + if(singleVideo.isNull()) + singleVideo = videoPath+videoFiles[0]; + + //Loading a random file from a directory + QDesktopWidget *dw = new QDesktopWidget(); + QMediaPlaylist *playlist = new QMediaPlaylist(); + if(multiple) { + for(int i = 0; i < videoFiles.size(); i++){ + playlist->addMedia(QUrl::fromLocalFile(videoPath+videoFiles[i])); + } + playlist->shuffle(); + }else{ + playlist->addMedia(QUrl::fromLocalFile(singleVideo)); + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + } + videoWidget->show(); + if(multiple) + video->setPlaylist(playlist); + + //Only play sound for one monitor to prevent messed up audio + if(dw->screenNumber(canvas) == 0) + video->setVolume(100); + else + video->setVolume(0); + + video->play(); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop/src-screensaver/animations/animations.pri b/src-qt5/core/lumina-desktop/src-screensaver/animations/animations.pri new file mode 100644 index 00000000..fdc75717 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/animations/animations.pri @@ -0,0 +1,10 @@ +SOURCES += $$PWD/BaseAnimGroup.cpp + +HEADERS += $$PWD/BaseAnimGroup.h \ + $$PWD/SampleAnimation.h \ + $${PWD}/Fireflies.h \ + $${PWD}/Grav.h \ + $${PWD}/ImageSlideshow.h \ + $${PWD}/VideoSlideshow.h \ + $${PWD}/Text.h +#FORMS += diff --git a/src-qt5/core/lumina-desktop/src-screensaver/screensaver.pri b/src-qt5/core/lumina-desktop/src-screensaver/screensaver.pri new file mode 100644 index 00000000..92cc7bd2 --- /dev/null +++ b/src-qt5/core/lumina-desktop/src-screensaver/screensaver.pri @@ -0,0 +1,15 @@ +SOURCES *= $${PWD}/LLockScreen.cpp \ + $${PWD}/LScreenSaver.cpp \ + $${PWD}/SSBaseWidget.cpp + +HEADERS *= $${PWD}/LLockScreen.h \ + $${PWD}/LScreenSaver.h \ + $${PWD}/SSBaseWidget.h + +FORMS *= $${PWD}/LLockScreen.ui + +#update the includepath so we can just (#include <LScreenSaver.h>) as needed without paths +INCLUDEPATH *= $${PWD} + +#Now include all the screensaver animations/options +#include(animations/animations.pri) diff --git a/src-qt5/core/lumina-pingcursor/lumina-pingcursor.pro b/src-qt5/core/lumina-pingcursor/lumina-pingcursor.pro new file mode 100644 index 00000000..5609a35b --- /dev/null +++ b/src-qt5/core/lumina-pingcursor/lumina-pingcursor.pro @@ -0,0 +1,14 @@ +include("$${PWD}/../../OS-detect.pri") + +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + + +TARGET = lumina-pingcursor +target.path = $${L_BINDIR} + +TEMPLATE = app + +SOURCES += main.cpp + +INSTALLS += target diff --git a/src-qt5/core/lumina-pingcursor/main.cpp b/src-qt5/core/lumina-pingcursor/main.cpp new file mode 100644 index 00000000..6e332398 --- /dev/null +++ b/src-qt5/core/lumina-pingcursor/main.cpp @@ -0,0 +1,44 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2020, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== + +#include <QApplication> +#include <QPixmap> +#include <QLabel> +#include <QDateTime> +#include <QPoint> +#include <QScreen> +#include <QDesktopWidget> +#include <QPropertyAnimation> +#include <QThread> + +int main(int argc, char **argv){ + //Setup the application + QApplication App(argc, argv); + App.setAttribute(Qt::AA_UseHighDpiPixmaps); + //Display the OSD + QLabel splash(0, Qt::Window | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::NoDropShadowWindowHint); + splash.setWindowTitle(""); + splash.resize(100,100); + QWidget overlay( &splash ); + overlay.setStyleSheet("margin: 0px; border: none; background: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(0, 0, 0, 0), stop:0.52 rgba(0, 0, 0, 0), stop:0.565 rgba(82, 121, 76, 33), stop:0.65 rgba(159, 235, 148, 64), stop:0.721925 rgba(255, 238, 150, 129), stop:0.77 rgba(130, 205, 47, 204), stop:0.89 rgba(100, 128, 255, 64), stop:1 rgba(0, 0, 0, 0));"); + overlay.setGeometry(QRect(0,0,100,100)); + //Make sure it is centered on the current screen + QPoint center = QCursor::pos(); + splash.move(center.x()-(splash.size().width()/2), center.y()-(splash.size().height()/2)); + splash.setPixmap(QApplication::screens().at(0)->grabWindow(QApplication::desktop()->winId(), splash.x(), splash.y(), 100,100) ); + splash.show(); + QPropertyAnimation anim(&splash,"windowOpacity"); + anim.setDuration(1200); + anim.setStartValue(1); + anim.setKeyValueAt(0.30, 0); anim.setKeyValueAt(0.31, 1); + anim.setKeyValueAt(0.60, 0); anim.setKeyValueAt(0.61, 1); + anim.setEndValue(0); + anim.start(); + while(anim.state() != QAbstractAnimation::Stopped){ QThread::msleep(20); App.processEvents(); } //60 fps + splash.hide(); + return 0; +} diff --git a/src-qt5/core/lumina-session/session.cpp b/src-qt5/core/lumina-session/session.cpp index 6115c365..cba855c9 100644 --- a/src-qt5/core/lumina-session/session.cpp +++ b/src-qt5/core/lumina-session/session.cpp @@ -100,7 +100,7 @@ void LSession::procFinished(){ //Note about compton: It does not like running multiple sessions under the *same user* // (even on different displays). Run a blanket killall on it when closing down so that // any other Lumina sessions will automatically restart compton on that specific display - QProcess::execute("killall compton"); + QProcess::execute("pkill compton"); //More OS's have pkill instead of killall QCoreApplication::exit(0); }else{ //Make sure we restart the process as needed @@ -151,10 +151,16 @@ void LSession::setupCompositor(bool force){ //Compositing manager QSettings settings("lumina-desktop","sessionsettings"); if(settings.value("enableCompositing",false).toBool() || force){ - if(LUtils::isValidBinary("compton")){ + if(LUtils::isValidBinary("picom")){ + //New name for compton - removed the "-d" startup option (finally reads DISPLAY instead) (May 2020) + //Always use the GLX backend for picom - the xrender and hybrid backends cause lots of flickering + startProcess("compositing","picom --backend glx --config "+set, QStringList() << set); + + }else if(LUtils::isValidBinary("compton")){ QString disp = getenv("DISPLAY"); //Always use the GLX backend for compton - the xrender and hybrid backends cause lots of flickering startProcess("compositing","compton --backend glx -d "+disp+" --config "+set, QStringList() << set); + }else if(LUtils::isValidBinary("xcompmgr") && !settings.value("compositingWithGpuAccelOnly",true).toBool() ){ startProcess("compositing","xcompmgr"); } @@ -196,12 +202,12 @@ void LSession::start(bool unified){ startProcess("wm", WM); } //Compositing manager - setupCompositor(); + setupCompositor(true); //Desktop Next LSingleApplication::removeLocks("lumina-desktop"); startProcess("runtime","lumina-desktop"); //ScreenSaver - if(LUtils::isValidBinary("xscreensaver")){ startProcess("screensaver","xscreensaver -no-splash"); } + //if(LUtils::isValidBinary("xscreensaver")){ startProcess("screensaver","xscreensaver -no-splash"); } }else{ //unified process setupCompositor(true); //required for Lumina 2 diff --git a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp index 9ffe39f8..b03647f4 100644 --- a/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp +++ b/src-qt5/core/lumina-theme-engine/src/lthemeengine/lthemeengine.cpp @@ -194,6 +194,10 @@ bool lthemeengine::setCursorTheme(QString cursorname){ else{ info << "[Icon Theme]" << newval; } //entire section missing from file } //Now save the file + if( !QFile::exists(QDir::homePath()+"/.icons/default") ){ + //Need to create the directory first + QDir().mkpath(QDir::homePath()+"/.icons/default"); + } QFile file(QDir::homePath()+"/.icons/default/index.theme"); bool ok = false; if( file.open(QIODevice::WriteOnly | QIODevice::Truncate) ){ diff --git a/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp index 0aedb1a0..ed3ca2c3 100644 --- a/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp +++ b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp @@ -34,13 +34,17 @@ PlainTextEditor::PlainTextEditor(QSettings *set, QWidget *parent) : QPlainTextEd //this->setObjectName("PlainTextEditor"); //this->setStyleSheet("QPlainTextEdit#PlainTextEditor{ }"); SYNTAX = new Custom_Syntax(settings, this->document()); + FTIMER = new QTimer(this); + FTIMER->setInterval(1000); + FTIMER->setSingleShot(true); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(LNW_updateWidth()) ); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(LNW_highlightLine()) ); connect(this, SIGNAL(updateRequest(const QRect&, int)), this, SLOT(LNW_update(const QRect&, int)) ); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(checkMatchChar()) ); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorMoved()) ); connect(this, SIGNAL(textChanged()), this, SLOT(textChanged()) ); - connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(fileChanged()) ); + connect(watcher, SIGNAL(fileChanged(const QString&)), FTIMER, SLOT(start()) ); + connect(FTIMER, SIGNAL(timeout()), this, SLOT(fileChanged())); LNW_updateWidth(); LNW_highlightLine(); } diff --git a/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.h b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.h index 8f174d4b..8f2d7e98 100644 --- a/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.h +++ b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.h @@ -55,6 +55,7 @@ private: QString lastSaveContents; QFileSystemWatcher *watcher; QList<Word*> wordList; + QTimer *FTIMER; //Syntax Highlighting class Custom_Syntax *SYNTAX; //Hunspell *hunspell; diff --git a/src-qt5/desktop-utils/lumina-textedit/syntaxSupport.h b/src-qt5/desktop-utils/lumina-textedit/syntaxSupport.h index f7321869..4ce2424c 100644 --- a/src-qt5/desktop-utils/lumina-textedit/syntaxSupport.h +++ b/src-qt5/desktop-utils/lumina-textedit/syntaxSupport.h @@ -85,62 +85,42 @@ public: void setupDocument(QPlainTextEdit *edit){ syntax.SetupDocument(edit); } //simple redirect for the function in the currently-loaded rules protected: + void highlightBlock(const QString &text){ + QTextCharFormat defaultFormat; //qDebug() << "Highlight Block:" << text; - //Now look for any multi-line patterns (starting/continuing/ending) + //Now finish any multi-line patterns int start = 0; int splitactive = previousBlockState(); if(splitactive>syntax.rules.length()-1){ splitactive = -1; } //just in case - while(start>=0 && start<=text.length()-1){ - //qDebug() << "split check:" << start << splitactive; - if(splitactive>=0){ - //Find the end of the current rule - int end = syntax.rules[splitactive].endPattern.indexIn(text, start); - if(end==-1){ - //qDebug() << "Highlight to end of line:" << text << start; - //rule did not finish - apply to all - if(start>0){ setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format); } - else{ setFormat(start, text.length()-start, syntax.rules[splitactive].format); } - break; //stop looking for more multi-line patterns - }else{ - //Found end point within the same line - //qDebug() << "Highlight to particular point:" << text << start << end; - int len = end-start+syntax.rules[splitactive].endPattern.matchedLength(); - if(start>0){ start--; len++; } //need to include the first character as well - setFormat(start, len , syntax.rules[splitactive].format); - start+=len; //move pointer to the end of handled range - splitactive = -1; //done with this rule - } - } //end check for end match - //Look for the start of any new split rules - //qDebug() << "Loop over multi-line rules"; - for(int i=0; i<syntax.rules.length() && splitactive<0; i++){ - //qDebug() << "Check Rule:" << i << syntax.rules[i].startPattern << syntax.rules[i].endPattern; - if(syntax.rules[i].startPattern.isEmpty()){ continue; } - //qDebug() << "Look for start of split rule:" << syntax.rules[i].startPattern << splitactive; - int newstart = syntax.rules[i].startPattern.indexIn(text,start); - if(newstart>=start){ - //qDebug() << "Got Start of split rule:" << start << newstart << text; - splitactive = i; - start = newstart+1; - if(start>=text.length()-1){ - //qDebug() << "Special case: start now greater than line length"; - //Need to apply highlighting to this section too - start matches the end of the line - setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format); - } - } + //qDebug() << "split check:" << start << splitactive; + if(splitactive>=0){ + //Find the end of the current rule + int end = syntax.rules[splitactive].endPattern.indexIn(text, start); + if(end==-1){ + //qDebug() << "Highlight to end of line:" << text << start; + //rule did not finish - apply to all + if(start>0){ setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format); } + else{ setFormat(start, text.length()-start, syntax.rules[splitactive].format); } + }else{ + //Found end point within the same line + //qDebug() << "Highlight to particular point:" << text << start << end; + int len = end-start+syntax.rules[splitactive].endPattern.matchedLength(); + if(start>0){ start--; len++; } //need to include the first character as well + setFormat(start, len , syntax.rules[splitactive].format); + start+=len; //move pointer to the end of handled range + splitactive = -1; //done with this rule } - if(splitactive<0){ break; } //no other rules found - go ahead and exit the loop - } //end scan over line length and multi-line formats + } //end check for end of pre-existing multi-line block match + setCurrentBlockState(splitactive); //tag this block as continuing as well - setCurrentBlockState(splitactive); - //Do all the single-line patterns - for(int i=0; i<syntax.rules.length(); i++){ - if(syntax.rules[i].pattern.isEmpty()){ continue; } //not a single-line rule + //Do all the single-line patterns + for(int i=0; i<syntax.rules.length() && splitactive<0; i++){ + if(syntax.rules[i].pattern.isEmpty()){ continue; } //not a single-line rule QRegExp patt(syntax.rules[i].pattern); //need a copy of the rule's pattern (will be changing it below) int index = patt.indexIn(text); - if(splitactive>=0 || index<start){ continue; } //skip this one - falls within a multi-line pattern above + if( index<start ){ continue; } //skip this one - falls within a multi-line pattern above while(index>=0){ int len = patt.matchedLength(); if(format(index)==currentBlock().charFormat()){ setFormat(index, len, syntax.rules[i].format); } //only apply highlighting if not within a section already @@ -148,10 +128,37 @@ protected: } }//end loop over normal (single-line) patterns + //Look for the start of any new split rules + //qDebug() << "Loop over multi-line rules"; + for(int i=0; i<syntax.rules.length() && splitactive<0; i++){ + //qDebug() << "Check Rule:" << i << syntax.rules[i].startPattern << syntax.rules[i].endPattern; + if(syntax.rules[i].startPattern.isEmpty()){ continue; } + //qDebug() << "Look for start of split rule:" << syntax.rules[i].startPattern << splitactive; + int newstart = syntax.rules[i].startPattern.indexIn(text,start); + if(newstart>=start && (format(newstart) == defaultFormat) ){ //only start multi-line formatting if it is not already contained in a single-line formatting + //qDebug() << "Got Start of split rule:" << start << newstart << text << (format(newstart) != defaultFormat); + splitactive = i; + start = newstart+1; + int end = syntax.rules[splitactive].endPattern.indexIn(text, start); + if(end>0){ //end of multi-line comment in the same block + setFormat(start-1, end-start+1, syntax.rules[splitactive].format); + start = end+1; + }else{ + setCurrentBlockState(splitactive); + } + if(start>=text.length()-1 || splitactive>=0){ + //qDebug() << "Special case: start now greater than line length"; + //Need to apply highlighting to this section too - start matches the end of the line + setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format); + break; //this goes to the end of the text block + } + } + } + //Now go through and apply any document-wide formatting rules - QTextCharFormat fmt; - fmt.setBackground( QColor( settings->value("colors/bracket-missing").toString() ) ); - int max = syntax.char_limit(); + QTextCharFormat fmt; + fmt.setBackground( QColor( settings->value("colors/bracket-missing").toString() ) ); + int max = syntax.char_limit(); if(max >= 0 && ( (text.length()+(text.count("\t")*(syntax.tab_length()-1)) )> max) ) { //Line longer than it should be - highlight the offending characters //Need to be careful about where tabs show up in the line @@ -159,7 +166,7 @@ protected: for(int i=0; i<text.length() and len<=max; i++){ len += (text[i]=='\t') ? syntax.tab_length() : 1; if(len>max) - setFormat(i, text.length()-i, fmt); + setFormat(i, text.length()-i, fmt); } } if(syntax.highlight_excess_whitespace()){ diff --git a/src-qt5/desktop-utils/lumina-textedit/syntax_rules/sh.syntax b/src-qt5/desktop-utils/lumina-textedit/syntax_rules/sh.syntax index f2256731..6ca832e9 100644 --- a/src-qt5/desktop-utils/lumina-textedit/syntax_rules/sh.syntax +++ b/src-qt5/desktop-utils/lumina-textedit/syntax_rules/sh.syntax @@ -9,7 +9,7 @@ "meta": { "name": "Shell", "file_suffix": ["sh"], - "first_line_match":["#!/bin/sh", "#!/sbin/openrc-run"] + "first_line_match":["#!/bin/sh", "#!/bin/bash","#!/bin/env bash","#!/sbin/openrc-run"] }, "format": { "line_wrap": false, diff --git a/src-qt5/src-cpp/plugins-screensaver.cpp b/src-qt5/src-cpp/plugins-screensaver.cpp index 6370b068..09779bc5 100644 --- a/src-qt5/src-cpp/plugins-screensaver.cpp +++ b/src-qt5/src-cpp/plugins-screensaver.cpp @@ -19,6 +19,7 @@ SSPlugin::~SSPlugin(){ bool SSPlugin::isValid(){ if(data.isEmpty()){ return false; } + if( data.value("disabled").toBool(false) ){ return false; } //Skip any plugin with "disabled" = true bool ok = data.contains("name") && data.contains("qml") && data.contains("description"); ok &= containsDefault("name"); ok &= containsDefault("description"); |