diff options
Diffstat (limited to 'src-qt5/core/lumina-desktop-unified/src-WM')
23 files changed, 2287 insertions, 0 deletions
diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/DEPENDENCIES b/src-qt5/core/lumina-desktop-unified/src-WM/DEPENDENCIES new file mode 100644 index 00000000..fa0ce486 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/DEPENDENCIES @@ -0,0 +1,17 @@ +Most dependencies required to build Lumina are listed in the +DEPENDENCIES file in the directory above this one. The following +are dependencies specific to Lumina's window manager. + + +FreeBSD/TrueOS +======================= + + + + +Linux (Debian/Ubuntu) +======================= + +libxcb-screensaver0-dev + + diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/GlobalDefines.h b/src-qt5/core/lumina-desktop-unified/src-WM/GlobalDefines.h new file mode 100644 index 00000000..3ec278ac --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/GlobalDefines.h @@ -0,0 +1,74 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// Global defines and enumerations for the window manager +//=========================================== +#ifndef _LUMINA_WINDOW_MANAGER_GLOBAL_DEFINES_H +#define _LUMINA_WINDOW_MANAGER_GLOBAL_DEFINES_H + +//Qt includes +#include <QObject> +#include <QFrame> +#include <QLabel> +#include <QToolButton> +#include <QMenu> +#include <QHBoxLayout> +#include <QMouseEvent> +#include <QAction> +#include <QPoint> +#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 <QWindow> +#include <QWidget> +#include <QBackingStore> +#include <QPaintEvent> +#include <QPainter> +#include <QSettings> +#include <QHostInfo> +#include <QDesktopWidget> +#include <QStyleOption> +#include <QThread> + +// libLumina includes +#include <LuminaX11.h> +#include <LuminaXDG.h> +#include <LuminaOS.h> +#include <LuminaThemes.h> +#include <LuminaUtils.h> +#include <LuminaSingleApplication.h> + +//XCB Includes +#include <xcb/xcb.h> +#include <xcb/xproto.h> +#include <xcb/damage.h> +#include <xcb/xcb_atom.h> +#include <xcb/xcb_aux.h> //included in libxcb-util.so + +#define ANIMTIME 80 //animation time in milliseconds +//Global flags/structures +namespace LWM{ + //Flags/enumerations + enum WindowAction{MoveResize, Show, Hide, TryClose, Closed, WA_NONE}; + + //Data structures + extern LXCB *SYSTEM; +}; + + + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.cpp new file mode 100644 index 00000000..4cc6d68b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.cpp @@ -0,0 +1,102 @@ +//=========================================== +// 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 WAITMINS 1 +#define DEBUG 1 + +LLockScreen::LLockScreen(QWidget *parent) : QWidget(parent), ui(new Ui::LLockScreen()){ + ui->setupUi(this); + waittime = new QTimer(this); + waittime->setInterval(WAITMINS*60000); //(too many attempts in short time) + 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(); } +} + +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(); +} + +// ================= +// PRIVATE SLOTS +// ================= +void LLockScreen::UpdateLockInfo(){ + QString info; + /*if(triesleft>0 && triesleft<NUMTRIES ){ + if(triesleft==1){info = tr("1 Attempt Left"); } + else{info = QString(tr("%1 Attempts Left")).arg(QString::number(triesleft)); } + }else*/ + 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(); + bool ok = (LUtils::runCmd("lumina-checkpass", QStringList() << pass) == 0); + if(ok){ + emit ScreenUnlocked(); + this->setEnabled(true); + }else{ + triesleft--; + if(triesleft>0){ + this->setEnabled(true); + }else{ + waittime->start(); + refreshtime->start(); + } + ui->line_password->setFocus(); + } + UpdateLockInfo(); + +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.h b/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.h new file mode 100644 index 00000000..040499c1 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/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 "GlobalDefines.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-unified/src-WM/LLockScreen.ui b/src-qt5/core/lumina-desktop-unified/src-WM/LLockScreen.ui new file mode 100644 index 00000000..7f0b45b8 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/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-unified/src-WM/LScreenSaver.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/LScreenSaver.cpp new file mode 100644 index 00000000..0c92784e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LScreenSaver.cpp @@ -0,0 +1,181 @@ +//=========================================== +// 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> + +#define DEBUG 1 + +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); + + LOCKER = new LLockScreen(this); + LOCKER->hide(); + settings = new QSettings("lumina-desktop","lumina-screensaver",this); + SSRunning = SSLocked = updating = false; + this->setObjectName("LSCREENSAVERBASE"); + this->setStyleSheet("LScreenSaver#LSCREENSAVERBASE{ background: grey; }"); + 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(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) ){ starttimer->start(); } //time to SS start + 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 + starttimer->start(); +} + +void LScreenSaver::reloadSettings(){ + settings->sync(); + starttimer->setInterval( settings->value("timedelaymin",10).toInt() * 60000 ); + locktimer->setInterval( settings->value("lockdelaymin",1).toInt() * 60000 ); + hidetimer->setInterval( settings->value("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::ShowScreenSaver(){ + if(DEBUG){ qDebug() << "Showing Screen Saver:" << QDateTime::currentDateTime().toString(); } + SSRunning = true; + updating = true; + //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, settings); + 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 + BASES[i]->setPlugin(settings->value("screenplugin"+QString::number(i+1), settings->value("defaultscreenplugin","random").toString() ).toString() ); + } + //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(); + } + 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::desktop()->screenGeometry(QCursor::pos()).center(); + LOCKER->resize(LOCKER->sizeHint()); + LOCKER->move(ctr - QPoint(LOCKER->width()/2, LOCKER->height()/2) ); + LOCKER->show(); + //Start the timer for hiding the lock screen due to inactivity + UpdateTimers(); +} + +void LScreenSaver::HideScreenSaver(){ + if(DEBUG){ qDebug() << "Hiding Screen Saver:" << QDateTime::currentDateTime().toString(); } + SSRunning = false; + if(cBright>0){ LOS::setScreenBrightness(cBright); } //return to current brightness + if(!SSLocked){ + this->hide(); + emit ClosingScreenSaver(); + } + for(int i=0; i<BASES.length(); i++){ + BASES[i]->hide(); + BASES[i]->stopPainting(); + } + 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; + LOCKER->LoadSystemDetails(); + UpdateTimers(); +} + +void LScreenSaver::SSFinished(){ + if(DEBUG){ qDebug() << "Screensaver Finished:" << QDateTime::currentDateTime().toString(); } + SSLocked = false; + HideLockScreen(); + HideScreenSaver(); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LScreenSaver.h b/src-qt5/core/lumina-desktop-unified/src-WM/LScreenSaver.h new file mode 100644 index 00000000..5119d8b1 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LScreenSaver.h @@ -0,0 +1,59 @@ +//=========================================== +// 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 "GlobalDefines.h" + +#include "SSBaseWidget.h" +#include "LLockScreen.h" + +class LScreenSaver : public QWidget{ + Q_OBJECT +public: + LScreenSaver(); + ~LScreenSaver(); + + bool isLocked(); + +private: + QTimer *starttimer, *locktimer, *hidetimer; + QSettings *settings; + QList<SSBaseWidget*> BASES; + LLockScreen *LOCKER; + int cBright; + bool SSRunning, SSLocked, updating; + + void UpdateTimers(); + +public slots: + void start(); + void reloadSettings(); + void newInputEvent(); + void LockScreenNow(); + +private slots: + void ShowScreenSaver(); + void ShowLockScreen(); + void HideScreenSaver(); + void HideLockScreen(); + + void LockScreen(); + void SSFinished(); + +signals: + void StartingScreenSaver(); + void ClosingScreenSaver(); + +protected: + void mouseMoveEvent(QMouseEvent*){ + QTimer::singleShot(0,this, SLOT(newInputEvent())); + } + +}; + +#endif
\ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.cpp new file mode 100644 index 00000000..84ff2ffd --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.cpp @@ -0,0 +1,474 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LWindow.h" + +LWindowFrame::LWindowFrame(WId client, QWidget *parent) : QFrame(parent, Qt::X11BypassWindowManagerHint){ + activeState = LWindowFrame::Normal; + CID = client; + lastAction = LWM::WA_NONE; + Closing = false; + //qDebug() << "New Window:" << CID << "Frame:" << this->winId(); + this->setMouseTracking(true); //need this to determine mouse location when not clicked + this->setObjectName("LWindowFrame"); + this->setStyleSheet("LWindowFrame#LWindowFrame{ border: 2px solid white; border-radius:3px; } QWidget#TitleBar{background: grey; } QLabel{ color: black; }"); + InitWindow(); //initially create all the child widgets + //LWM::SYSTEM->setupEventsForFrame(this->winId()); + updateAppearance(); //this loads the appearance based on window/theme settings + //QApplication::processEvents(); + //Now set the frame size on this window + SyncSize(); + SyncText(); + this->show(); +} + +LWindowFrame::~LWindowFrame(){ +} + +// ================= +// PRIVATE +// ================= +void LWindowFrame::InitWindow(){ + anim = new QPropertyAnimation(this); //For simple window animations + anim->setTargetObject(this); + anim->setDuration(ANIMTIME); //In milliseconds + connect(anim, SIGNAL(finished()), this, SLOT(finishedAnimation()) ); + titleBar = new QLabel(this); //This is the "container" for all the title buttons/widgets + titleBar->setObjectName("TitleBar"); + titleBar->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + titleBar->setFocusPolicy(Qt::NoFocus); + titleBar->setCursor(Qt::ArrowCursor); + title = new QLabel(this); //Shows the window title/text + title->setObjectName("Title"); + title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + title->setCursor(Qt::ArrowCursor); + title->setFocusPolicy(Qt::NoFocus); + icon = new QLabel(this); //Contains the window icon + icon->setObjectName("Icon"); + icon->setCursor(Qt::ArrowCursor); + icon->setFocusPolicy(Qt::NoFocus); + minB = new QToolButton(this); //Minimize Button + minB->setObjectName("Minimize"); + minB->setCursor(Qt::ArrowCursor); + minB->setFocusPolicy(Qt::NoFocus); + connect(minB, SIGNAL(clicked()), this, SLOT(minClicked()) ); + maxB = new QToolButton(this); //Maximize Button + maxB->setObjectName("Maximize"); + maxB->setCursor(Qt::ArrowCursor); + maxB->setFocusPolicy(Qt::NoFocus); + connect(maxB, SIGNAL(clicked()), this, SLOT(maxClicked()) ); + closeB = new QToolButton(this); + closeB->setObjectName("Close"); + closeB->setCursor(Qt::ArrowCursor); + closeB->setFocusPolicy(Qt::NoFocus); + connect(closeB, SIGNAL(clicked()), this, SLOT(closeClicked()) ); + otherB = new QToolButton(this); //Button to place any other actions + otherB->setObjectName("Options"); + otherB->setCursor(Qt::ArrowCursor); + otherB->setPopupMode(QToolButton::InstantPopup); + otherB->setStyleSheet("QToolButton::menu-indicator{ image: none; }"); + otherB->setFocusPolicy(Qt::NoFocus); + otherM = new QMenu(this); //menu of "other" actions for the window + otherB->setMenu(otherM); + connect(otherM, SIGNAL(triggered(QAction*)), this, SLOT(otherClicked(QAction*)) ); + //Now assemble the titlebar + QHBoxLayout *HL = new QHBoxLayout(this); + HL->setContentsMargins(0,0,0,0); + HL->addWidget(otherB); + HL->addWidget(icon); + HL->addWidget(title); + HL->addWidget(minB); + HL->addWidget(maxB); + HL->addWidget(closeB); + titleBar->setLayout(HL); + QVBoxLayout *VL = new QVBoxLayout(this); + this->setLayout(VL); + //The WinWidget container appears shifted right/down by 1 pixel for some reason + // Adjust the margins to account for this variation + VL->setContentsMargins(1,1,2,2); + VL->setSpacing(0); + //Have the window take the same initial size of the client window + QRect geom = LWM::SYSTEM->WM_Window_Geom(CID); + qDebug() << " - Load Size Hints" << "initial size:" << geom.size(); + icccm_size_hints SH = LWM::SYSTEM->WM_ICCCM_GetNormalHints(CID); + qDebug() << " - - Got Normal Hints"; + if(!SH.isValid()){ SH = LWM::SYSTEM->WM_ICCCM_GetSizeHints(CID); } + qDebug() << " - - Start resizing..."; + if(SH.base_width>geom.width() && SH.base_height>geom.height()){ this->resize(SH.base_width, SH.base_height); } + else if(SH.min_width>geom.width() && SH.min_height>geom.height()){ this->resize(SH.min_width, SH.min_height); } + else if(SH.width>geom.width() && SH.height>geom.height()){ this->resize(SH.width, SH.height); } + else if(geom.isNull()){ this->resize(100,80); } + else{ this->resize( geom.size() ); } + qDebug() << " - done"; + + //Now embed the native window into the frame + WIN = QWindow::fromWinId(CID); + WinWidget = QWidget::createWindowContainer( WIN, this); + WinWidget->setCursor(Qt::ArrowCursor); //this is just a fallback - the window itself will adjust it + //WINBACK = new QBackingStore(WIN); //create a data backup for the widget + + //Now assemble te initial layout for the window (all while still invisible) + /*VL->addWidget(titleBar); + VL->addWidget(WinWidget); + VL->setStretch(1,1);*/ +} + +LWindowFrame::ModState LWindowFrame::getStateAtPoint(QPoint pt, bool setoffset){ + //Note: pt should be in widget-relative coordinates, not global + if(!this->layout()->geometry().contains(pt)){ + //above the frame itself - need to figure out which quadrant it is in (8-directions) + if(pt.y() < 3){ + //One of the top options + if(pt.x() < 3){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner + return ResizeTopLeft; + }else if(pt.x() > (this->width()-3)){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(pt.y()); } //difference from top-right corner + return ResizeTopRight; + }else{ + if(setoffset){ offset.setX(0); offset.setY(pt.y()); } //difference from top edge (X does not matter) + return ResizeTop; + } + }else if(pt.y() > (this->height()-3) ){ + //One of the bottom options + if(pt.x() < 3){ + if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner + return ResizeBottomLeft; + }else if(pt.x() > (this->width()-3)){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner + return ResizeBottomRight; + }else{ + if(setoffset){ offset.setX(0); offset.setY(this->height() - pt.y()); } //difference from bottom edge (X does not matter) + return ResizeBottom; + } + }else{ + //One of the side options + if(pt.x() < 3){ + if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter) + return ResizeLeft; + }else if(pt.x() > (this->width()-3) ){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(0); } //difference from right edge (Y does not matter) + return ResizeRight; + }else{ + return Normal; + } + } + } + return Normal; +} + +void LWindowFrame::setMouseCursor(ModState state, bool override){ + Qt::CursorShape shape; + switch(state){ + case Normal: + shape = Qt::ArrowCursor; + break; + case Move: + shape = Qt::SizeAllCursor; + break; + case ResizeTop: + shape = Qt::SizeVerCursor; + break; + case ResizeTopRight: + shape = Qt::SizeBDiagCursor; + break; + case ResizeRight: + shape = Qt::SizeHorCursor; + break; + case ResizeBottomRight: + shape = Qt::SizeFDiagCursor; + break; + case ResizeBottom: + shape = Qt::SizeVerCursor; + break; + case ResizeBottomLeft: + shape = Qt::SizeBDiagCursor; + break; + case ResizeLeft: + shape = Qt::SizeHorCursor; + break; + case ResizeTopLeft: + shape = Qt::SizeFDiagCursor; + break; + } + if(override){ + QApplication::setOverrideCursor(QCursor(shape)); + }else{ + this->setCursor(shape); + } +} + +// ========================== +// WINDOW INTERACTIONS +//========================== +void LWindowFrame::SyncSize(bool fromwin){ + //sync the window/frame geometries (generally only done before embedding the client window) + int frame = this->frameWidth(); + int TH = titleBar->height(); + //Now load the information about the window and adjust the frame to match + if(fromwin){ lastGeom = LWM::SYSTEM->WM_Window_Geom(CID); } + else{ lastGeom = this->geometry(); } + qDebug() << "Initial Size:" << lastGeom << frame << TH; + //Add in the frame size + lastGeom.moveTop(lastGeom.y()-frame-TH); + lastGeom.setHeight(lastGeom.height()+(2*frame)+TH); + lastGeom.moveLeft(lastGeom.x()-frame); + lastGeom.setWidth( lastGeom.width()+(2*frame)); + QList<unsigned int> margins; + margins << frame << frame << frame+TH << frame; //L/R/Top/Bottom + qDebug() << " - With Frame:" << lastGeom; + //Now adjust for a out-of-bounds location + if(lastGeom.x() < 0){ lastGeom.moveLeft(0); } + if(lastGeom.y() < 0){ lastGeom.moveTop(0); } + qDebug() << " - Adjusted:" << lastGeom; + this->setGeometry(lastGeom); + LWM::SYSTEM->WM_Set_Frame_Extents(CID, margins); +} + +void LWindowFrame::SyncText(){ + QString txt = WIN->title(); + if(txt.isEmpty()){ txt = LWM::SYSTEM->WindowName(CID); } + if(txt.isEmpty()){ txt = LWM::SYSTEM->OldWindowName(CID); } + if(txt.isEmpty()){ txt = LWM::SYSTEM->WindowVisibleName(CID); } + if(txt.isEmpty()){ txt = LWM::SYSTEM->WindowIconName(CID); } + if(txt.isEmpty()){ txt = LWM::SYSTEM->WindowVisibleIconName(CID); } + if(txt.isEmpty()){ txt = LWM::SYSTEM->WM_ICCCM_GetClass(CID); } + title->setText(txt); +} + +// SIMPLE ANIMATIONS +void LWindowFrame::showAnimation(LWM::WindowAction act){ + bool useanimation = (act!=lastAction); + if(anim->state()==QAbstractAnimation::Running){ + qDebug() << "New Animation Event:" << act; + return; + } + //Setup the animation routine + if(act==LWM::Show){ + if(useanimation){ + lastGeom = this->geometry(); + //Expand out from center point + anim->setPropertyName("geometry"); + anim->setStartValue( QRect(lastGeom.center(), QSize(0,0) ) ); + anim->setEndValue( this->geometry() ); + //Fade in gradually + //anim->setPropertyName("windowOpacity"); + //anim->setStartValue( 0.0 ); + //anim->setEndValue( 1.0 ); + }else{ + ShowClient(true); + this->raise(); + this->show(); //just show it right away + } + + }else if(act==LWM::Hide){ + if(useanimation){ + //Collapse in on center point + lastGeom = this->geometry(); + anim->setPropertyName("geometry"); + anim->setStartValue( QRect(this->geometry()) ); + anim->setEndValue( QRect(this->geometry().center(), QSize(0,0) ) ); + }else{ + this->hide(); //just hide it right away + } + }else if(act==LWM::Closed){ + //Need to clean up the container widget first to prevent XCB errors + //qDebug() << "Window Closed:" << WIN->winId() << CID; + if(useanimation){ + //Collapse in on center line + lastGeom = this->geometry(); + anim->setPropertyName("geometry"); + anim->setStartValue( QRect(this->geometry()) ); + anim->setEndValue( QRect(this->geometry().x(), this->geometry().center().y(), this->width(), 0 ) ); + }else{ + CloseAll(); //just hide it right away + } + } + if(useanimation){ + ShowClient(false); + this->show(); + qDebug() << " - Starting Animation:" << act; + lastAction = act; + anim->start(); + }; +} + +void LWindowFrame::ShowClient(bool show){ + if(show && this->layout()->indexOf(WinWidget)<0 && !Closing){ + while(this->layout()->count()>0){ this->layout()->removeItem(0); } + this->layout()->addWidget(titleBar); + this->layout()->setAlignment(titleBar, Qt::AlignTop); + this->layout()->addWidget(WinWidget); + static_cast<QVBoxLayout*>(this->layout())->setStretch(1,1); + LWM::SYSTEM->WM_ShowWindow(CID); + }else if( !show && this->layout()->indexOf(WinWidget)>=0){ + LWM::SYSTEM->WM_HideWindow(CID); + this->layout()->removeWidget(WinWidget); + } +} + +void LWindowFrame::finishedAnimation(){ + //Also set any final values + qDebug() << " - Finished Animation:" << lastAction; + switch(lastAction){ + case LWM::Show: + ShowClient(true); + break; + case LWM::Closed: + case LWM::Hide: + this->lower(); + this->hide(); + LWM::SYSTEM->WM_HideWindow(this->winId()); + default: + break; + } + if(Closing){ + qDebug() << "Emitting finished signal"; + emit Finished(); + } +} + +// ================= +// PUBLIC SLOTS +// ================= +void LWindowFrame::updateAppearance(){ + //Reload any button icons and such + minB->setIcon(LXDG::findIcon("window-suppressed","")); + maxB->setIcon(LXDG::findIcon("view-fullscreen","")); + closeB->setIcon(LXDG::findIcon("application-exit","")); + otherB->setIcon(LXDG::findIcon("configure","")); +} + +void LWindowFrame::windowChanged(LWM::WindowAction act){ + //A window property was changed - update accordingly + switch(act){ + case LWM::Closed: + Closing = true; + case LWM::Hide: + case LWM::Show: + showAnimation(act); + break; + case LWM::MoveResize: + //Re-adjust to the new position/size of the window + SyncSize(true); + break; + default: + break; //do nothing + } +} +// ================= +// PRIVATE SLOTS +// ================= +void LWindowFrame::closeClicked(){ + qDebug() << "Closing Window" << LWM::SYSTEM->WM_ICCCM_GetClass(CID); + //First try the close event to let the client app do cleanup/etc + LWM::SYSTEM->WM_CloseWindow(CID); +} + +void LWindowFrame::minClicked(){ + qDebug() << "Minimize Window"; + windowChanged(LWM::Hide); +} + +void LWindowFrame::maxClicked(){ + if(normalGeom.isNull()){ + qDebug() << "Maximize Window"; + normalGeom = this->geometry(); //save for later + this->showMaximized(); + }else{ + qDebug() << "Restore Window"; + this->showNormal(); + this->setGeometry(normalGeom); + normalGeom = QRect(); //clear it + } +} + +void LWindowFrame::otherClicked(QAction* act){ + QString action = act->whatsThis(); +} + +void LWindowFrame::CloseAll(){ + qDebug() << " - Closing Frame"; + this->hide(); + emit Finished(); +} +// ===================== +// PROTECTED +// ===================== +void LWindowFrame::mousePressEvent(QMouseEvent *ev){ + qDebug() << "Frame Mouse Press Event"; + offset.setX(0); offset.setY(0); + if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse + this->activateWindow(); + LWM::SYSTEM->WM_Set_Active_Window(CID); + if(this->childAt(ev->pos())!=0){ + //Check for any non-left-click event and skip it + if(ev->button()!=Qt::LeftButton){ return; } + activeState = Move; + offset.setX(ev->pos().x()); offset.setY(ev->pos().y()); + }else{ + //Clicked on the frame somewhere + activeState = getStateAtPoint(ev->pos(), true); //also have it set the offset variable + } + setMouseCursor(activeState, true); //this one is an override cursor + +} + +void LWindowFrame::mouseMoveEvent(QMouseEvent *ev){ + ev->accept(); + if(activeState == Normal){ + setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor + + }else{ + //Currently in a modification state + QRect geom = this->geometry(); + switch(activeState){ + case Move: + geom.moveTopLeft(ev->globalPos()-offset); //will not change size + break; + case ResizeTop: + geom.setTop(ev->globalPos().y()-offset.y()); + break; + case ResizeTopRight: + geom.setTopRight(ev->globalPos()-offset); + break; + case ResizeRight: + geom.setRight(ev->globalPos().x()-offset.x()); + break; + case ResizeBottomRight: + geom.setBottomRight(ev->globalPos()-offset); + break; + case ResizeBottom: + geom.setBottom(ev->globalPos().y()-offset.y()); + break; + case ResizeBottomLeft: + geom.setBottomLeft(ev->globalPos()-offset); + break; + case ResizeLeft: + geom.setLeft(ev->globalPos().x()-offset.x()); + break; + case ResizeTopLeft: + geom.setTopLeft(ev->globalPos()-offset); + break; + default: + break; + } + this->setGeometry(geom); + } +} + +void LWindowFrame::mouseReleaseEvent(QMouseEvent *ev){ + //Check for a right-click event + qDebug() << "Frame Mouse Release Event"; + ev->accept(); + if( (activeState==Normal) && (this->childAt(ev->pos())==titleBar) && (ev->button()==Qt::RightButton) ){ + otherM->popup(ev->globalPos()); + return; + } + activeState = Normal; + QApplication::restoreOverrideCursor(); + setMouseCursor( getStateAtPoint(ev->pos()) ); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.h b/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.h new file mode 100644 index 00000000..ceefca83 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LWindow.h @@ -0,0 +1,114 @@ +//=========================================== +// 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_WINDOW_FRAME_H +#define _LUMINA_DESKTOP_WINDOW_FRAME_H + +#include "GlobalDefines.h" + +class LWindowFrame : public QFrame{ + Q_OBJECT +public: + LWindowFrame(WId client, QWidget *parent = 0); //MUST have a valid client window + ~LWindowFrame(); + +private: + void InitWindow(); //Initialize all the internal widgets + + //Window status + enum ModState{Normal, Move, ResizeTop, ResizeTopRight, ResizeRight, ResizeBottomRight, ResizeBottom, ResizeBottomLeft, ResizeLeft, ResizeTopLeft}; + ModState activeState; + QPoint offset; //needed for movement calculations (offset from mouse click to movement point) + //Functions for getting/setting state + ModState getStateAtPoint(QPoint pt, bool setoffset = false); //generally used for mouse location detection + void setMouseCursor(ModState, bool override = false); //Update the mouse cursor based on state + + //General Properties/Modifications + WId CID; //Client ID + QWindow *WIN; //Embedded window container + QWidget *WinWidget; + bool Closing; + LWM::WindowAction lastAction; + //QBackingStore *WINBACK; + void SyncSize(bool fromwin = false); //sync the window/frame geometries + void SyncText(); + + //Window Frame Widgets/Items + QLabel *titleBar, *title, *icon; + QToolButton *minB, *maxB, *closeB, *otherB; + QMenu *otherM; //menu of "other" actions for the window + QRect normalGeom; //used for restoring back to original size after maximization/fullscreen + + //Animations + QPropertyAnimation *anim; //used for appear/disappear animations + QRect lastGeom; //used for appear/disappear animations + void showAnimation(LWM::WindowAction); //sets lastAction + void ShowClient(bool show); + +public slots: + //These slots are generally used for the outside event watcher to prod for changes + void updateAppearance(); //reload the theme and change styling as necessary + void windowChanged(LWM::WindowAction); + +private slots: + void finishedAnimation(); //uses lastAction + void closeClicked(); + void minClicked(); + void maxClicked(); + void otherClicked(QAction*); + + void CloseAll(); + +protected: + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + +signals: + void Finished(); //This means the window is completely finished (with animations and such) and should be removed from any lists + +}; + +class LWindow : public QObject{ + Q_OBJECT +signals: + void Finished(WId client); //ready to be removed +private: + WId CID; + LWindowFrame *FID; + bool needsFrame(QList<LXCB::WINDOWTYPE> list){ + if(list.isEmpty()){ return !LWM::SYSTEM->WM_ICCCM_GetClass(CID).contains("Lumina-DE"); } //assume a normal window (fallback) + return !(list.contains(LXCB::T_DESKTOP) || list.contains(LXCB::T_DOCK) || list.contains(LXCB::T_TOOLBAR) \ + || list.contains(LXCB::T_SPLASH) || list.contains(LXCB::T_DROPDOWN_MENU) \ + || list.contains(LXCB::T_TOOLTIP) || list.contains(LXCB::T_POPUP_MENU) || list.contains(LXCB::T_TOOLTIP) \ + || list.contains(LXCB::T_COMBO) || list.contains(LXCB::T_DND) ); + } +private slots: + void frameclosed(){ + qDebug() << " - Window got frame closed signal"; + //FID->close(); + //delete FID; + emit Finished(CID); + } +public: + LWindow(WId client){ + FID= 0; + CID = client; + if( needsFrame(LWM::SYSTEM->WM_Get_Window_Type(CID)) ){ + FID = new LWindowFrame(CID); + connect(FID, SIGNAL(Finished()), this, SLOT(frameclosed()) ); + } + } + ~LWindow(){ + if(FID!=0){delete FID;} + } + + WId clientID(){ return CID; } + bool hasFrame(){ return FID!=0; } + LWindowFrame* frame(){ return FID; } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.cpp new file mode 100644 index 00000000..14ce6897 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.cpp @@ -0,0 +1,186 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LWindowManager.h" + +#define DEBUG 1 + +LWindowManager::LWindowManager(){ + +} + +LWindowManager::~LWindowManager(){ + +} + +bool LWindowManager::start(){ + //Setup the initial screen/session values + LWM::SYSTEM->WM_Set_Root_Supported(); + LWM::SYSTEM->WM_SetNumber_Desktops(1); + LWM::SYSTEM->WM_Set_Current_Desktop(0); + LWM::SYSTEM->WM_Set_Desktop_Names(QStringList() << "one"); + QRect totgeom; + QList<QPoint> viewports; + QList<QRect> geoms; + for(int i=0; i<QApplication::desktop()->screenCount(); i++){ + geoms << QApplication::desktop()->screen(i)->geometry(); + viewports << QPoint(0,0); + totgeom = QApplication::desktop()->screen(i)->geometry(); + } + LWM::SYSTEM->WM_Set_Desktop_Geometry(totgeom.size()); + LWM::SYSTEM->WM_Set_Desktop_Viewport(viewports); + LWM::SYSTEM->WM_Set_Workarea(geoms); + //Should probably do a quick loop over any existing windows with the root as parent (just in case) + QList<WId> initial = LWM::SYSTEM->WM_RootWindows(); + for(int i=0; i<initial.length(); i++){ + NewWindow(initial[i], false); //These ones did not explicitly request to be mapped + } + RestackWindows(); + return true; +} + +void LWindowManager::stop(){ + for(int i=0; i<WINS.length(); i++){ + if(WINS[i]->hasFrame()){ + LWM::SYSTEM->UnembedWindow(WINS[i]->clientID()); + WINS[i]->frame()->close(); + } + } +} +//=============== +// PUBLIC SLOTS +//=============== +void LWindowManager::NewWindow(WId win, bool requested){ + //Verify that this window can/should be managed first + //if(DEBUG){ qDebug() << "New Window:" << LWM::SYSTEM->WM_ICCCM_GetClass(win); } + QString wclass = LWM::SYSTEM->WM_ICCCM_GetClass(win); + if( wclass.contains("lumina-wm",Qt::CaseInsensitive) ){ return; } //just in case: prevent recursion + else{ + bool ok = (wclass.isEmpty() ? false : LWM::SYSTEM->WM_ManageWindow(win, requested) ); + if(!ok){ + //See if this window is just a transient pointing to some other window + WId tran = LWM::SYSTEM->WM_ICCCM_GetTransientFor(win); + if(tran!=win && tran!=0){ + win = tran; + ok = LWM::SYSTEM->WM_ManageWindow(win); + } + } + if(!ok){ return; } + } + if(DEBUG){ qDebug() << "New Managed Window:" << LWM::SYSTEM->WM_ICCCM_GetClass(win); } + LWM::SYSTEM->WM_Set_Active_Window(win); + LWindow *lwin = new LWindow(win); + connect(lwin, SIGNAL(Finished(WId)), this, SLOT(FinishedWindow(WId)) ); + WINS << lwin; + if(lwin->hasFrame()){ + lwin->frame()->windowChanged(LWM::Show); //Make sure to show it right away + }else{ + LWM::SYSTEM->WM_ShowWindow(win); //just map the window right now + } +} + +void LWindowManager::ClosedWindow(WId win){ + for(int i=0; i<WINS.length(); i++){ + if(WINS[i]->clientID()==win){ + qDebug() << " - Closed Window"; + if(WINS[i]->hasFrame()){ WINS[i]->frame()->windowChanged(LWM::Closed); } //do any animations/cleanup + else{ FinishedWindow(win); } + break; + } + } +} + +void LWindowManager::ModifyWindow(WId win, LWM::WindowAction act){ + for(int i=0; i<WINS.length(); i++){ + if(WINS[i]->clientID()==win){ + if(WINS[i]->hasFrame()){ WINS[i]->frame()->windowChanged(act); } + return; + } + } + //If it gets this far - it is an unmanaged window + if(act==LWM::Show){ + NewWindow(win); + } + RestackWindows(); +} + +void LWindowManager::RestackWindows(){ + Stack_Desktop.clear(); Stack_Below.clear(); Stack_Normal.clear(); Stack_Above.clear(); Stack_Fullscreen.clear(); + QList<WId> currwins; + int cwork = LWM::SYSTEM->WM_Get_Current_Desktop(); + int winwork = -1; + QList<LXCB::WINDOWSTATE> states; + QList<LXCB::WINDOWTYPE> types; + for(int i=0; i<WINS.length(); i++){ + //Only show windows on the current desktop + winwork = LWM::SYSTEM->WM_Get_Desktop(WINS[i]->clientID()); + states = LWM::SYSTEM->WM_Get_Window_States(WINS[i]->clientID()); + types = LWM::SYSTEM->WM_Get_Window_Type(WINS[i]->clientID()); + WId id = WINS[i]->clientID(); + if(WINS[i]->hasFrame()){ id = WINS[i]->frame()->winId(); } + if(winwork<0 || winwork == cwork || states.contains(LXCB::S_STICKY) ){ + //Now check the state/type and put it in the proper stack + currwins << WINS[i]->clientID(); //add this to the overall "age" list + //Now add it to the proper stack + if(types.contains(LXCB::T_DESKTOP)){ Stack_Desktop << id; } + else if(states.contains(LXCB::S_BELOW)){ Stack_Below << id; } + else if(types.contains(LXCB::T_DOCK) || states.contains(LXCB::S_ABOVE) ){ Stack_Above << id; } + else if(states.contains(LXCB::S_FULLSCREEN)){ Stack_Fullscreen << id; } + else{ Stack_Normal << id; } + } + } + //Active Window management + WId active = LWM::SYSTEM->WM_Get_Active_Window(); + if(Stack_Desktop.contains(active)){ Stack_Desktop.removeAll(active); Stack_Desktop << active; } + else if(Stack_Below.contains(active)){ Stack_Below.removeAll(active); Stack_Below << active; } + else if(Stack_Normal.contains(active)){ Stack_Normal.removeAll(active); Stack_Normal << active; } + else if(Stack_Above.contains(active)){ Stack_Above.removeAll(active); Stack_Above << active; } + else if(Stack_Fullscreen.contains(active)){ Stack_Fullscreen.removeAll(active); Stack_Fullscreen << active; } + + //Now set the root properties for these lists + LWM::SYSTEM->WM_Set_Client_List(currwins, false); //age-ordered version + LWM::SYSTEM->WM_Set_Client_List(QList<WId>() << Stack_Desktop << Stack_Below << Stack_Normal << Stack_Above << Stack_Fullscreen, true); //stacking order version + //Now re-paint (in order) the windows + RepaintWindows(); +} + +void LWindowManager::RepaintWindows(){ + //Go through all the current windows (in stacking order) and re-paint them + for(int i=0; i<Stack_Desktop.length(); i++){ + LWM::SYSTEM->WM_ShowWindow(Stack_Desktop[i]); + } + for(int i=0; i<Stack_Below.length(); i++){ + LWM::SYSTEM->WM_ShowWindow(Stack_Below[i]); + } + for(int i=0; i<Stack_Normal.length(); i++){ + LWM::SYSTEM->WM_ShowWindow(Stack_Normal[i]); + } + for(int i=0; i<Stack_Above.length(); i++){ + LWM::SYSTEM->WM_ShowWindow(Stack_Above[i]); + } + for(int i=0; i<Stack_Fullscreen.length(); i++){ + LWM::SYSTEM->WM_ShowWindow(Stack_Fullscreen[i]); + } +} + +//================= +// PRIVATE SLOTS +//================= +void LWindowManager::FinishedWindow(WId win){ + for(int i=0; i<WINS.length(); i++){ + if(WINS[i]->clientID() == win){ + qDebug() << " - Finished Window"; + if(win == LWM::SYSTEM->WM_Get_Active_Window()){ + if(i==0 && WINS.length()>1){ LWM::SYSTEM->WM_Set_Active_Window(WINS[i+1]->clientID()); } + else if(i>0){ LWM::SYSTEM->WM_Set_Active_Window(WINS[i-1]->clientID()); } + else{ LWM::SYSTEM->WM_Set_Active_Window( QX11Info::appRootWindow()); } + } + delete WINS.takeAt(i); break; + } + } + //Now update the list of clients + RestackWindows(); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.h b/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.h new file mode 100644 index 00000000..203efa1b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LWindowManager.h @@ -0,0 +1,40 @@ +//=========================================== +// 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_WINDOW_MANAGER_MAIN_CLASS_H +#define _LUMINA_DESKTOP_WINDOW_MANAGER_MAIN_CLASS_H + +#include "GlobalDefines.h" +#include "LWindow.h" + +class LWindowManager : public QObject{ + Q_OBJECT +public: + LWindowManager(); + ~LWindowManager(); + + bool start(); + void stop(); + +private: + QList<LWindow*> WINS; + QList<WId> Stack_Desktop, Stack_Below, Stack_Normal, Stack_Above, Stack_Fullscreen; +public slots: + void NewWindow(WId win, bool requested = true); + void ClosedWindow(WId win); + void ModifyWindow(WId win, LWM::WindowAction act); + + void RestackWindows(); + void RepaintWindows(); + +private slots: + void FinishedWindow(WId win); //This is used for LWindow connections/animations + +signals: + void NewFullScreenWindows(QList<WId>); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.cpp new file mode 100644 index 00000000..abbe5a5a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.cpp @@ -0,0 +1,204 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LXcbEventFilter.h" + +//================================================== +// NOTE: All the XCB interactions and atoms are accessed via: +// LWM::SYSTEM->EWMH.(atom name) +// LWM::SYSTEM->(do something) +// (LWM::SYSTEM is the global XCB structure) +//================================================== + +#include <LuminaX11.h> +#include <QDebug> + +//#include <xcb/screensaver.h> + +#define DEBUG 1 +// Also keep the root window/screen around for use in the filters +namespace L_XCB{ + xcb_screen_t *root_screen; + xcb_window_t root; +} + +//Constructor for the Event Filter wrapper +EventFilter::EventFilter() : QObject(){ + EF = new XCBEventFilter(this); + L_XCB::root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen()); + L_XCB::root = L_XCB::root_screen->root; + SSLocked = false; + WMFlag = 0; +} + +void EventFilter::start(){ + if(DEBUG){ qDebug() << " - Install event filter..."; } + QCoreApplication::instance()->installNativeEventFilter(EF); + if(DEBUG){ qDebug() << " - Run request check..."; } + if(!LWM::SYSTEM->setupEventsForRoot()){ + qCritical() << "[ERROR] Unable to setup WM event retrieval. Is another WM running?"; + exit(1); + } + if(DEBUG){ qDebug() << " - Create WM ID Window"; } + WMFlag = LWM::SYSTEM->WM_CreateWindow(); + LWM::SYSTEM->setupEventsForRoot(WMFlag); + LWM::SYSTEM->WM_Set_Supporting_WM(WMFlag); + QCoreApplication::instance()->flush(); +} + +//Constructor for the XCB event filter +XCBEventFilter::XCBEventFilter(EventFilter *parent) : QAbstractNativeEventFilter(){ + obj = parent; + InitAtoms(); +} + +//This function format taken directly from the Qt5.3 documentation +bool XCBEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE +{ + //if(stopping){ return false; } //don't do any parsing + //qDebug() << "New Event"; + bool stopevent = false; + if(eventType=="xcb_generic_event_t"){ + //Convert to known event type (for X11 systems) + xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message); + //Now parse the event and emit signals as necessary + switch( ev->response_type & ~0x80){ +//============================== +// INTERACTIVITY EVENTS +//============================== + case XCB_KEY_PRESS: + //This is a keyboard key press + //qDebug() << "Key Press Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent( ((xcb_key_press_event_t *) ev)->root ); //use the main "root" window - not the child widget + break; + case XCB_KEY_RELEASE: + //This is a keyboard key release + //qDebug() << "Key Release Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent( ((xcb_key_release_event_t *) ev)->root ); //use the main "root" window - not the child widget + break; + case XCB_BUTTON_PRESS: + //This is a mouse button press + //qDebug() << "Button Press Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent( ((xcb_button_press_event_t *) ev)->root ); //use the main "root" window - not the child widget + if(!stopevent){ + //Activate the window right now if needed + if(LWM::SYSTEM->WM_Get_Active_Window()!=((xcb_button_press_event_t *) ev)->root){ + LWM::SYSTEM->WM_Set_Active_Window( ((xcb_button_press_event_t *) ev)->root); + } + } + break; + case XCB_BUTTON_RELEASE: + //This is a mouse button release + //qDebug() << "Button Release Event"; + //xcb_button_release_event_t *tmp = (xcb_button_release_event_t *)ev; + stopevent = BlockInputEvent( ((xcb_button_release_event_t *) ev)->root ); //use the main "root" window - not the child widget + break; + case XCB_MOTION_NOTIFY: + //This is a mouse movement event + //qDebug() << "Motion Notify Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent( ((xcb_motion_notify_event_t *) ev)->root ); //use the main "root" window - not the child widget); + break; + case XCB_ENTER_NOTIFY: + //This is a mouse movement event when mouse goes over a new window + //qDebug() << "Enter Notify Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent( ((xcb_enter_notify_event_t *) ev)->root ); + break; + case XCB_LEAVE_NOTIFY: + //This is a mouse movement event when mouse goes leaves a window + //qDebug() << "Leave Notify Event"; + obj->emit NewInputEvent(); + stopevent = BlockInputEvent(); + break; +//============================== + case XCB_EXPOSE: + //qDebug() << "Expose Notify Event:"; + //qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window; + break; +//============================== + case XCB_MAP_NOTIFY: + break; //This is just a notification that a window was mapped - nothing needs to change here + case XCB_MAP_REQUEST: + qDebug() << "Window Map Request Event"; + obj->emit ModifyWindow( ((xcb_map_request_event_t *) ev)->window, LWM::Show); + break; +//============================== + case XCB_CREATE_NOTIFY: + qDebug() << "Window Create Event"; + break; +//============================== + case XCB_UNMAP_NOTIFY: + qDebug() << "Window Unmap Event"; + obj->emit ModifyWindow( ((xcb_unmap_notify_event_t *)ev)->window, LWM::Hide); + break; +//============================== + case XCB_DESTROY_NOTIFY: + qDebug() << "Window Closed Event"; + obj->emit WindowClosed( ((xcb_destroy_notify_event_t *) ev)->window ); + break; +//============================== + case XCB_FOCUS_IN: + //qDebug() << "Focus In Event:"; + break; +//============================== + case XCB_FOCUS_OUT: + //qDebug() << "Focus Out Event:"; + break; +//============================== + case XCB_PROPERTY_NOTIFY: + //qDebug() << "Property Notify Event:"; + //qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window; + break; +//============================== + case XCB_CLIENT_MESSAGE: + //qDebug() << "Client Message Event"; + //qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window; + break; +//============================== + case XCB_CONFIGURE_NOTIFY: + //qDebug() << "Configure Notify Event"; + break; +//============================== + case XCB_CONFIGURE_REQUEST: + //qDebug() << "Configure Request Event"; + break; +//============================== + case XCB_SELECTION_CLEAR: + //qDebug() << "Selection Clear Event"; + break; +//============================== + case 85: //not sure what event this is - but it seems to come up very often (just hide the notice) + case 0: + case XCB_GE_GENERIC: + break; //generic event - don't do anything special + default: + qDebug() << "Default Event:" << (ev->response_type & ~0x80); +//============================== + } + } + return false; + //never stop event handling (this will not impact the X events themselves - just the internal screensaver/WM/widgets) +} + +bool XCBEventFilter::BlockInputEvent(WId win){ + //Checks the current state of the WM and sets the stop flag as needed + // - Always let the screensaver know about the event first (need to reset timers and such) + obj->emit NewInputEvent(); + // - Check the state of the screensaver + if(obj->SSLocked){ qDebug() << "SS Locked"; return true; } + // - Check the state of any fullscreen apps + else if( win!=0 && !obj->FS_WINS.isEmpty()){ + if(!obj->FS_WINS.contains(win) ){ + //If this event is for an app underneath a fullscreen window - stop it + if(obj->FS_WINS.length() == QApplication::desktop()->screenCount()){ qDebug() << "Screens Covered"; return true; } //all screens covered right now + } + } + return false; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.h b/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.h new file mode 100644 index 00000000..b68eedf5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/LXcbEventFilter.h @@ -0,0 +1,130 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class provides the XCB ->Xlib conversion necessary for Qt5 usage +//=========================================== +#ifndef _LUMINA_DESKTOP_XCB_FILTER_H +#define _LUMINA_DESKTOP_XCB_FILTER_H + +#include "GlobalDefines.h" + + +/* +List of XCB response types (since almost impossible to find good docs on XCB) +switch (xcb_generic_event_t*->response_type & ~0x80) +case values: +XCB_KEY_[PRESS | RELEASE] +XCB_BUTTON_[PRESS | RELEASE] +XCB_MOTION_NOTIFY +XCB_ENTER_NOTIFY +XCB_LEAVE_NOTIFY +XCB_FOCUS_[IN | OUT] +XCB_KEYMAP_NOTIFY +XCB_EXPOSE +XCB_GRAPHICS_EXPOSURE +XCB_VISIBILITY_NOTIFY +XCB_CREATE_NOTIFY +XCB_DESTROY_NOTIFY +XCB_UNMAP_NOTIFY +XCB_MAP_[NOTIFY | REQUEST] +XCB_REPARENT_NOTIFY +XCB_CONFIGURE_[NOTIFY | REQUEST] +XCB_GRAVITY_NOTIFY +XCB_RESIZE_REQUEST +XCB_CIRCULATE_[NOTIFY | REQUEST] +XCB_PROPERTY_NOTIFY +XCB_SELECTION_[CLEAR | REQUEST | NOTIFY] +XCB_COLORMAP_NOTIFY +XCB_CLIENT_MESSAGE +*/ + +//SYSTEM TRAY STANDARD DEFINITIONS +//#define SYSTEM_TRAY_REQUEST_DOCK 0 +//#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +//#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + + +class EventFilter : public QObject{ + Q_OBJECT +private: + QAbstractNativeEventFilter* EF; + WId WMFlag; //used to flag a running WM process + +public: + EventFilter(); + ~EventFilter(){} + + void start(); + + //Public variables for the event filter to use/check + QList<WId> FS_WINS; //Full-screen windows (1 per monitor) - used for hiding non-app events as needed + bool SSLocked; + +public slots: + void StartedSS(){ SSLocked = true; } + void StoppedSS(){ SSLocked = false; } + void FullScreenChanged(QList<WId> fslist){ FS_WINS = fslist; } + +signals: + void NewInputEvent(); + void NewManagedWindow(WId); + void WindowClosed(WId); + void ModifyWindow(WId win, LWM::WindowAction); +}; + +class XCBEventFilter : public QAbstractNativeEventFilter{ +public: + XCBEventFilter(EventFilter *parent); + ~XCBEventFilter(){} + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *); + +private: + EventFilter *obj; + QList<xcb_atom_t> WinNotifyAtoms, SysNotifyAtoms; + + void InitAtoms(){ + //Initialize any special atoms that we need to save/use regularly + //NOTE: All the EWMH atoms are already saved globally in LWM::SYSTEM->EWMH + WinNotifyAtoms.clear(); + WinNotifyAtoms << LWM::SYSTEM->EWMH._NET_WM_NAME \ + << LWM::SYSTEM->EWMH._NET_WM_VISIBLE_NAME \ + << LWM::SYSTEM->EWMH._NET_WM_ICON_NAME \ + << LWM::SYSTEM->EWMH._NET_WM_VISIBLE_ICON_NAME \ + << LWM::SYSTEM->EWMH._NET_WM_ICON \ + << LWM::SYSTEM->EWMH._NET_WM_ICON_GEOMETRY; + + SysNotifyAtoms.clear(); + SysNotifyAtoms << LWM::SYSTEM->EWMH._NET_CLIENT_LIST \ + << LWM::SYSTEM->EWMH._NET_CLIENT_LIST_STACKING \ + << LWM::SYSTEM->EWMH._NET_CURRENT_DESKTOP \ + << LWM::SYSTEM->EWMH._NET_WM_STATE \ + << LWM::SYSTEM->EWMH._NET_ACTIVE_WINDOW \ + << LWM::SYSTEM->EWMH._NET_WM_ICON \ + << LWM::SYSTEM->EWMH._NET_WM_ICON_GEOMETRY; + + } + + bool BlockInputEvent(WId win = 0); //Checks the current state of the WM and sets the stop flag as needed + + //Longer Event handling functions + //bool ParseKeyPressEvent(); + //bool ParseKeyReleaseEvent(); + //bool ParseButtonPressEvent(); + //bool ParseButtonReleaseEvent(); + //bool ParseMotionEvent(); + //bool ParsePropertyEvent(); + //bool ParseClientMessageEvent(); + //bool ParseDestroyEvent(); + //bool ParseConfigureEvent(); + //bool ParseKeySelectionClearEvent(); + + + + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.cpp new file mode 100644 index 00000000..83b82ff8 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.cpp @@ -0,0 +1,83 @@ +//=========================================== +// 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" + +#define DEBUG 1 + +static QStringList validPlugs; +// ======== +// PUBLIC +// ======== +SSBaseWidget::SSBaseWidget(QWidget *parent, QSettings *set) : QWidget(parent){ + if(validPlugs.isEmpty()){ validPlugs << "none"; } //add more later + settings = set; //needed to pass along for plugins to read any special options/settings + this->setObjectName("LuminaBaseSSWidget"); + ANIM = 0; + this->setMouseTracking(true); +} + +SSBaseWidget::~SSBaseWidget(){ + if(ANIM!=0){ this->stopPainting(); } +} + +void SSBaseWidget::setPlugin(QString plug){ + plug = plug.toLower(); + if(validPlugs.contains(plug) || plug=="random"){ plugType = plug; } + else{ plugType = "none"; } +} + +// ============= +// PUBLIC SLOTS +// ============= +void SSBaseWidget::startPainting(){ + cplug = plugType; + //free up any old animation instance + if(ANIM!=0){ + ANIM->stop(); ANIM->clear(); + delete ANIM; ANIM = 0; + } + //If a random plugin - grab one of the known plugins + if(cplug=="random"){ + QStringList valid = BaseAnimGroup::KnownAnimations(); + if(valid.isEmpty()){ cplug = "none"; } //no known plugins + else{ cplug = valid[ qrand()%valid.length() ]; } //grab a random plugin + } + if(DEBUG){ qDebug() << " - Screen Saver:" << cplug; } + //Now list all the various plugins and start them appropriately + QString style; + if(cplug=="none"){ + style = "background: transparent;"; //show the underlying black parent widget + }else{ + style = "background: black;"; + } + this->setStyleSheet("QWidget#LuminaBaseSSWidget{ "+style+"}"); + this->repaint(); + //If not a stylesheet-based plugin - set it here + if(cplug!="none"){ + ANIM = BaseAnimGroup::NewAnimation(cplug, this, settings); + connect(ANIM, SIGNAL(finished()), this, SLOT(startPainting()) ); //repeat the plugin as needed + ANIM->LoadAnimations(); + } + //Now start the animation(s) + if(ANIM!=0){ + //if(DEBUG){ qDebug() << " - Starting SS Plugin:" << cplug << ANIM->animationCount() << ANIM->duration() << ANIM->loopCount(); } + if(ANIM->animationCount()>0){ + if(DEBUG){ qDebug() << " - Starting SS Plugin:" << cplug << ANIM->animationCount() << ANIM->duration() << ANIM->loopCount(); } + ANIM->start(); + } + } +} + +void SSBaseWidget::stopPainting(){ + if(ANIM!=0){ + ANIM->stop(); + ANIM->clear(); + delete ANIM; + ANIM = 0; + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.h b/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.h new file mode 100644 index 00000000..a6574679 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/SSBaseWidget.h @@ -0,0 +1,55 @@ +//=========================================== +// 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 "GlobalDefines.h" +#include "animations/BaseAnimGroup.h" + +class SSBaseWidget : public QWidget{ + Q_OBJECT +public: + SSBaseWidget(QWidget *parent, QSettings *set); + ~SSBaseWidget(); + + void setPlugin(QString); + +public slots: + void startPainting(); + void stopPainting(); + +private: + QString plugType, cplug; //type of custom painting to do + BaseAnimGroup *ANIM; + QSettings *settings; + +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(); + } + void paintEvent(QPaintEvent*){ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.cpp new file mode 100644 index 00000000..4a7c6e02 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.cpp @@ -0,0 +1,62 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "WMSession.h" + +#define DEBUG 1 +// ========== +// PUBLIC +// ========== +WMSession::WMSession(){ + if(DEBUG){ qDebug() << "Creating Event Filter..."; } + EFILTER = new EventFilter(); + if(DEBUG){ qDebug() << "Creating Screen Saver..."; } + SS = new LScreenSaver(); + if(DEBUG){ qDebug() << "Creating Window Manager..."; } + WM = new LWindowManager(); + EVThread = new QThread(); + EFILTER->moveToThread(EVThread); + //Setup connections + connect(EFILTER, SIGNAL(NewInputEvent()), SS, SLOT(newInputEvent()) ); + connect(EFILTER, SIGNAL(NewManagedWindow(WId)), WM, SLOT(NewWindow(WId)) ); + connect(EFILTER, SIGNAL(WindowClosed(WId)), WM, SLOT(ClosedWindow(WId)) ); + connect(EFILTER, SIGNAL(ModifyWindow(WId, LWM::WindowAction)), WM, SLOT(ModifyWindow(WId,LWM::WindowAction)) ); + connect(SS, SIGNAL(StartingScreenSaver()), EFILTER, SLOT(StartedSS()) ); + connect(SS, SIGNAL(ClosingScreenSaver()), EFILTER, SLOT(StoppedSS()) ); + connect(WM, SIGNAL(NewFullScreenWindows(QList<WId>)), EFILTER, SLOT(FullScreenChanged(QList<WId>)) ); +} + +WMSession::~WMSession(){ +} + +void WMSession::start(bool SSONLY){ + //Get the screensaver initialized/ready + if(DEBUG){ qDebug() << "Starting Screen Saver..."; } + SS->start(); + if(SSONLY){ return; } + //Now start pulling/filtering events + if(DEBUG){ qDebug() << "Starting Window Manager..."; } + WM->start(); + if(DEBUG){ qDebug() << "Starting Event Filter..."; } + EVThread->start(); + EFILTER->start(); + if(DEBUG){ qDebug() << "Done Starting WM session..."; } +} + +// ========== +// Public Slots +// ========== +void WMSession::reloadIcons(){ + +} + +void WMSession::newInputsAvailable(QStringList inputs){ + for(int i=0; i<inputs.length(); i++){ + if(inputs[i]=="--lock-now" || inputs[i]=="--test-ss"){ + QTimer::singleShot(0,SS, SLOT(LockScreenNow()) ); + } + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.h b/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.h new file mode 100644 index 00000000..5b511326 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/WMSession.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_WINDOW_MANAGER_SESSION_H +#define _LUMINA_DESKTOP_WINDOW_MANAGER_SESSION_H + +#include "GlobalDefines.h" + +#include "LScreenSaver.h" +#include "LXcbEventFilter.h" +#include "LWindowManager.h" + +class WMSession : public QObject{ + Q_OBJECT +public: + WMSession(); + ~WMSession(); + + void start(bool SSONLY = false); + +private: + //XCB Event Watcher + EventFilter *EFILTER; + //ScreenSaver + LScreenSaver *SS; + //Window Manager + LWindowManager *WM; + + QThread *EVThread; //X Event thread + +public slots: + void reloadIcons(); + void newInputsAvailable(QStringList); + +private slots: + +}; + +#endif
\ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.cpp new file mode 100644 index 00000000..1e55dc76 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.cpp @@ -0,0 +1,27 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, 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" + +//============================== +// PLUGIN LOADING/LISTING +//============================== +BaseAnimGroup* BaseAnimGroup::NewAnimation(QString type, QWidget *parent, QSettings *set){ + //This is where we place all the known plugin ID's, and load the associated subclass + if(type == "sample"){ + return (new SampleAnimation(parent, set)); + }else{ + //Unknown screensaver, return a blank animation group + return (new BaseAnimGroup(parent, set)); + } +} + +QStringList BaseAnimGroup::KnownAnimations(){ + return (QStringList() << "sample"); +}
\ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.h b/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.h new file mode 100644 index 00000000..dd7269d4 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/animations/BaseAnimGroup.h @@ -0,0 +1,37 @@ +//=========================================== +// 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 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 "GlobalDefines.h" + +class BaseAnimGroup : public QParallelAnimationGroup{ + Q_OBJECT +public: + QWidget *canvas; + QSettings *settings; + + virtual void LoadAnimations(){} //This is the main function which needs to be subclassed + + BaseAnimGroup(QWidget *parent, QSettings *set){ + canvas = parent; + settings = set; + } + ~BaseAnimGroup(){} + + //============================== + // PLUGIN LOADING/LISTING (Change in the .cpp file) + //============================== + static BaseAnimGroup* NewAnimation(QString type, QWidget *parent, QSettings *set); + static QStringList KnownAnimations(); + +}; + +#endif
\ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/animations/SampleAnimation.h b/src-qt5/core/lumina-desktop-unified/src-WM/animations/SampleAnimation.h new file mode 100644 index 00000000..e0f11ba5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/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 "GlobalDefines.h" +#include "BaseAnimGroup.h" + +class SampleAnimation : public BaseAnimGroup{ + Q_OBJECT +private: + QWidget *ball; + +public: + SampleAnimation(QWidget *parent, QSettings *set) : BaseAnimGroup(parent, set){} + ~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-unified/src-WM/animations/animations.pri b/src-qt5/core/lumina-desktop-unified/src-WM/animations/animations.pri new file mode 100644 index 00000000..5473d4e1 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/animations/animations.pri @@ -0,0 +1,6 @@ +SOURCES += $$PWD/BaseAnimGroup.cpp + +HEADERS += $$PWD/BaseAnimGroup.h \ + $$PWD/SampleAnimation.h + +FORMS +=
\ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/lumina-wm.pro b/src-qt5/core/lumina-desktop-unified/src-WM/lumina-wm.pro new file mode 100644 index 00000000..928f8744 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/lumina-wm.pro @@ -0,0 +1,107 @@ +include("$${PWD}/../../OS-detect.pri") + +QT += core gui network +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets x11extras + +TARGET = lumina-wm +target.path = $${L_BINDIR} + +LIBS += -lLuminaUtils -lxcb -lxcb-damage -lxcb-composite -lxcb-screensaver -lxcb-util + +DEPENDPATH += ../libLumina + +SOURCES += main.cpp \ + WMSession.cpp \ + LScreenSaver.cpp \ + SSBaseWidget.cpp \ + LLockScreen.cpp \ + LXcbEventFilter.cpp \ + LWindow.cpp \ + LWindowManager.cpp + + +HEADERS += GlobalDefines.h \ + WMSession.h \ + LScreenSaver.h \ + SSBaseWidget.h \ + LLockScreen.h \ + LXcbEventFilter.h \ + LWindow.h \ + LWindowManager.h + +FORMS += LLockScreen.ui + +#Now add in all the screensaver animation plugins +include(animations/animations.pri) + +TRANSLATIONS = i18n/lumina-wm_af.ts \ + i18n/lumina-wm_ar.ts \ + i18n/lumina-wm_az.ts \ + i18n/lumina-wm_bg.ts \ + i18n/lumina-wm_bn.ts \ + i18n/lumina-wm_bs.ts \ + i18n/lumina-wm_ca.ts \ + i18n/lumina-wm_cs.ts \ + i18n/lumina-wm_cy.ts \ + i18n/lumina-wm_da.ts \ + i18n/lumina-wm_de.ts \ + i18n/lumina-wm_el.ts \ + i18n/lumina-wm_en_GB.ts \ + i18n/lumina-wm_en_ZA.ts \ + i18n/lumina-wm_es.ts \ + i18n/lumina-wm_et.ts \ + i18n/lumina-wm_eu.ts \ + i18n/lumina-wm_fa.ts \ + i18n/lumina-wm_fi.ts \ + i18n/lumina-wm_fr.ts \ + i18n/lumina-wm_fr_CA.ts \ + i18n/lumina-wm_gl.ts \ + i18n/lumina-wm_he.ts \ + i18n/lumina-wm_hi.ts \ + i18n/lumina-wm_hr.ts \ + i18n/lumina-wm_hu.ts \ + i18n/lumina-wm_id.ts \ + i18n/lumina-wm_is.ts \ + i18n/lumina-wm_it.ts \ + i18n/lumina-wm_ja.ts \ + i18n/lumina-wm_ka.ts \ + i18n/lumina-wm_ko.ts \ + i18n/lumina-wm_lt.ts \ + i18n/lumina-wm_lv.ts \ + i18n/lumina-wm_mk.ts \ + i18n/lumina-wm_mn.ts \ + i18n/lumina-wm_ms.ts \ + i18n/lumina-wm_mt.ts \ + i18n/lumina-wm_nb.ts \ + i18n/lumina-wm_nl.ts \ + i18n/lumina-wm_pa.ts \ + i18n/lumina-wm_pl.ts \ + i18n/lumina-wm_pt.ts \ + i18n/lumina-wm_pt_BR.ts \ + i18n/lumina-wm_ro.ts \ + i18n/lumina-wm_ru.ts \ + i18n/lumina-wm_sk.ts \ + i18n/lumina-wm_sl.ts \ + i18n/lumina-wm_sr.ts \ + i18n/lumina-wm_sv.ts \ + i18n/lumina-wm_sw.ts \ + i18n/lumina-wm_ta.ts \ + i18n/lumina-wm_tg.ts \ + i18n/lumina-wm_th.ts \ + i18n/lumina-wm_tr.ts \ + i18n/lumina-wm_uk.ts \ + i18n/lumina-wm_uz.ts \ + i18n/lumina-wm_vi.ts \ + i18n/lumina-wm_zh_CN.ts \ + i18n/lumina-wm_zh_HK.ts \ + i18n/lumina-wm_zh_TW.ts \ + i18n/lumina-wm_zu.ts + +dotrans.path=$${L_SHAREDIR}/lumina-desktop/i18n/ +dotrans.extra=cd i18n && $${LRELEASE} -nounfinished *.ts && cp *.qm $(INSTALL_ROOT)$${L_SHAREDIR}/lumina-desktop/i18n/ + +INSTALLS += target + +WITH_I18N{ + INSTALLS += dotrans +} diff --git a/src-qt5/core/lumina-desktop-unified/src-WM/main.cpp b/src-qt5/core/lumina-desktop-unified/src-WM/main.cpp new file mode 100644 index 00000000..02e48b7b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-WM/main.cpp @@ -0,0 +1,56 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== + +#include "GlobalDefines.h" +//Initialize any global structures here +LXCB *LWM::SYSTEM = 0; + +//Local includes +#include "WMSession.h" +#include "LWindow.h" +#include <QDialog> + + +//#define DEBUG 0 +int main(int argc, char ** argv) +{ + qDebug() << "Starting lumina-wm..."; + LTHEME::LoadCustomEnvSettings(); + LSingleApplication a(argc, argv, "lumina-wm"); + if(!a.isPrimaryProcess()){ return 0; } //Inputs forwarded on to the primary already + LuminaThemeEngine themes(&a); + + //Setup the global structures + LWM::SYSTEM = new LXCB(); + if( a.inputlist.contains("--test-win") ){ + //Simple override to test out the window class + qDebug() << "Starting window test..."; + QLabel dlg(0, Qt::Window | Qt::BypassWindowManagerHint); //this test should be ignored by the current WM + dlg.setText("Sample Window"); + dlg.setWindowTitle("Test"); + dlg.resize(200,100); + dlg.setStyleSheet("background: rgba(255,255,255,100); color: black;"); + dlg.move(100,100); + dlg.show(); + //dlg.move(100,100); + qDebug() << " - Loading window frame..."; + LWindow win(dlg.winId()); //have it wrap around the dialog + qDebug() << " - Show frame..."; + win.frame()->windowChanged(LWM::Show); + qDebug() << " - Start event loop..."; + a.setQuitOnLastWindowClosed(true); + return a.exec(); + } + WMSession w; + w.start(a.inputlist.contains("--test-ss")); + QObject::connect(&themes, SIGNAL(updateIcons()), &w, SLOT(reloadIcons()) ); + QObject::connect(&a, SIGNAL(InputsAvailable(QStringList)), &w, SLOT(newInputsAvailable(QStringList)) ); + if(!a.inputlist.isEmpty()){ w.newInputsAvailable(a.inputlist); } + int retCode = a.exec(); + + return retCode; +} |