aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src-qt5/core/libLumina/DesktopSettings.cpp2
-rw-r--r--src-qt5/core/lumina-desktop-unified/LSession.cpp3
-rw-r--r--src-qt5/core/lumina-desktop-unified/global-includes.h1
-rw-r--r--src-qt5/core/lumina-desktop-unified/global-objects.h2
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.cpp185
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.h72
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.cpp49
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.h10
-rw-r--r--src-qt5/core/lumina-desktop-unified/src-events/events.pri4
9 files changed, 318 insertions, 10 deletions
diff --git a/src-qt5/core/libLumina/DesktopSettings.cpp b/src-qt5/core/libLumina/DesktopSettings.cpp
index bfd149ca..cd85df5e 100644
--- a/src-qt5/core/libLumina/DesktopSettings.cpp
+++ b/src-qt5/core/libLumina/DesktopSettings.cpp
@@ -164,7 +164,7 @@ void DesktopSettings::locateFiles(){
//run at start - finds the locations of the various files (based on RunMode)
QString userdir;
QStringList systemdirs;
- userdir = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop";
+ userdir = QString(getenv("XDG_CONFIG_HOME"));
systemdirs << QString(getenv("XDG_CONFIG_DIRS")).split(":") << QString(getenv("XDG_DATA_DIRS")).split(":");
//Load all the user-level files for this run mode
QList< DesktopSettings::File > tmp;
diff --git a/src-qt5/core/lumina-desktop-unified/LSession.cpp b/src-qt5/core/lumina-desktop-unified/LSession.cpp
index 57b30a2e..156029ee 100644
--- a/src-qt5/core/lumina-desktop-unified/LSession.cpp
+++ b/src-qt5/core/lumina-desktop-unified/LSession.cpp
@@ -20,6 +20,7 @@ DesktopSettings* Lumina::SETTINGS = 0;
QThread* Lumina::EVThread = 0;
RootWindow* Lumina::ROOTWIN = 0;
XDGDesktopList* Lumina::APPLIST = 0;
+LShortcutEvents* Lumina::SHORTCUTS = 0;
LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lumina-desktop"){
//Initialize the global objects to null pointers
@@ -48,6 +49,7 @@ LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lu
Lumina::EVThread->start();
Lumina::ROOTWIN = new RootWindow();
Lumina::APPLIST = new XDGDesktopList(0, true); //keep this list up to date
+ Lumina::SHORTCUTS = new LShortcutEvents(); //this can be moved to it's own thread eventually as well
//Setup the various connections between the global classes
connect(Lumina::ROOTWIN, SIGNAL(RegisterVirtualRoot(WId)), Lumina::EFILTER, SLOT(RegisterVirtualRoot(WId)) );
@@ -160,6 +162,7 @@ void LSession::setupSession(){
Lumina::SS->start();
if(DEBUG){ qDebug() << " - Init Finished:" << timer->elapsed(); delete timer;}
+ Lumina::SHORTCUTS->start(); //Startup the shortcut handler now
//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
//launchStartupApps();
//QTimer::singleShot(500, this, SLOT(launchStartupApps()) );
diff --git a/src-qt5/core/lumina-desktop-unified/global-includes.h b/src-qt5/core/lumina-desktop-unified/global-includes.h
index 04c4c27c..ab810ada 100644
--- a/src-qt5/core/lumina-desktop-unified/global-includes.h
+++ b/src-qt5/core/lumina-desktop-unified/global-includes.h
@@ -64,6 +64,7 @@
namespace Lumina{
//Flags/enumerations
enum WindowAction{MoveResize, Show, Hide, TryClose, Closed, WA_NONE};
+ enum MouseButton{NoButton, LeftButton, RightButton, MidButton, BackButton, ForwardButton, TaskButton, WheelUp, WheelDown, WheelLeft, WheelRight};
};
#endif
diff --git a/src-qt5/core/lumina-desktop-unified/global-objects.h b/src-qt5/core/lumina-desktop-unified/global-objects.h
index 66bfd122..2f298e27 100644
--- a/src-qt5/core/lumina-desktop-unified/global-objects.h
+++ b/src-qt5/core/lumina-desktop-unified/global-objects.h
@@ -21,6 +21,7 @@
//#ifndef USE_WAYLAND
#include "src-events/LXcbEventFilter.h"
//#endif
+#include "src-events/LShortcutEvents.h"
#include "src-screensaver/LScreenSaver.h"
//#include "src-WM/LWindowManager.h"
@@ -32,6 +33,7 @@
namespace Lumina{
//Data structures and objects
extern EventFilter *EFILTER; //Native Event Watcher
+ extern LShortcutEvents *SHORTCUTS; //Keyboard/mouse shortcut events
extern DesktopSettings *SETTINGS; //All Settings files
//ScreenSaver
extern LScreenSaver *SS;
diff --git a/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.cpp b/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.cpp
new file mode 100644
index 00000000..b09a1a5b
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.cpp
@@ -0,0 +1,185 @@
+//===========================================
+// Lumina desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "LShortcutEvents.h"
+#include "global-objects.h"
+
+// === PUBLIC ===
+LShortcutEvents::LShortcutEvents(){
+ WIN = 0;
+ clearTimer = 0;
+ evaluated = false;
+}
+
+LShortcutEvents::~LShortcutEvents(){
+
+}
+
+void LShortcutEvents::start(){
+ if(clearTimer==0){
+ clearTimer = new QTimer(this);
+ clearTimer->setInterval(2000); //2 seconds
+ clearTimer->setSingleShot(true);
+ connect(clearTimer, SIGNAL(timeout()), this, SLOT(clearKeys()) );
+ }
+ //Now connect this object to the global EFILTER object signals
+ connect(Lumina::EFILTER, SIGNAL(KeyPressed(WId, int)), this, SLOT(KeyPress(WId, int)) );
+ connect(Lumina::EFILTER, SIGNAL(KeyReleased(WId, int)), this, SLOT(KeyRelease(WId, int)) );
+ connect(Lumina::EFILTER, SIGNAL(MousePressed(WId, Lumina::MouseButton)), this, SLOT(MousePress(WId, Lumina::MouseButton)) );
+ connect(Lumina::EFILTER, SIGNAL(MouseReleased(WId, Lumina::MouseButton)), this, SLOT(MouseRelease(WId, Lumina::MouseButton)) );
+}
+
+void LShortcutEvents::stop(){
+ disconnect(Lumina::EFILTER, SIGNAL(KeyPressed(WId, int)), this, SLOT(KeyPress(WId, int)) );
+ disconnect(Lumina::EFILTER, SIGNAL(KeyReleased(WId, int)), this, SLOT(KeyRelease(WId, int)) );
+ disconnect(Lumina::EFILTER, SIGNAL(MousePressed(WId, Lumina::MouseButton)), this, SLOT(MousePress(WId, Lumina::MouseButton)) );
+ disconnect(Lumina::EFILTER, SIGNAL(MouseReleased(WId, Lumina::MouseButton)), this, SLOT(MouseRelease(WId, Lumina::MouseButton)) );
+ clearKeys();
+}
+
+// === PRIVATE ===
+void LShortcutEvents::CheckKeySequence(WId win){
+ //Get the keyboard modifiers
+ QString shortcut = keylistToString();
+ //Now see if there is a match for this shortcut
+ // "strict" actions (operate even if a non-desktop window is active/focused)
+ QString action = Lumina::SETTINGS->value(DesktopSettings::Keys, "strict/"+shortcut, "").toString();
+ qDebug() << "Strict Action:" << "strict/"+shortcut << action;
+ if(action.isEmpty() && win==0){
+ //"loose" actions (operating on the desktop or root window itself)
+ action = Lumina::SETTINGS->value(DesktopSettings::Keys, "desktop/"+shortcut, "").toString();
+ qDebug() << "Desktop Action:" << "desktop/"+shortcut << action;
+ }
+ if(!action.isEmpty()){
+ //Found a valid action - go ahead and evaluate it
+ evaluateShortcutAction(action);
+ }
+}
+
+void LShortcutEvents::CheckMouseSequence(WId win, Lumina::MouseButton button, bool release){
+ if(release && (button == Lumina::WheelUp || button == Lumina::WheelDown || button == Lumina::WheelLeft || button == Lumina::WheelRight)){
+ return; //skip mouse release events for wheel actions (always come in pairs of press/release)
+ }else if(keylist.isEmpty() || button == Lumina::NoButton){ return; } //Never overwrite mouse clicks themselves - just combinations with key presses
+ //Get the keyboard modifiers
+ QString shortcut = keylistToString();
+ //Add the mouse button to the shortcut
+ switch(button){
+ case Lumina::LeftButton:
+ shortcut.append("+LeftMouse");
+ break;
+ case Lumina::RightButton:
+ shortcut.append("+RightMouse");
+ break;
+ case Lumina::MidButton:
+ shortcut.append("+MiddleMouse");
+ break;
+ case Lumina::BackButton:
+ shortcut.append("+BackMouse");
+ break;
+ case Lumina::ForwardButton:
+ shortcut.append("+ForwardMouse");
+ break;
+ case Lumina::TaskButton:
+ shortcut.append("+TaskMouse");
+ break;
+ case Lumina::WheelUp:
+ shortcut.append("+WheelUp");
+ break;
+ case Lumina::WheelDown:
+ shortcut.append("+WheelDown");
+ break;
+ case Lumina::WheelLeft:
+ shortcut.append("+WheelLeft");
+ break;
+ case Lumina::WheelRight:
+ shortcut.append("+WheelRight");
+ break;
+ default:
+ shortcut.clear();
+ }
+ if(shortcut.isEmpty()){ return; }
+ //Now see if there is a match for this shortcut
+ // "strict" actions (operate even if a non-desktop window is active/focused)
+ QString action = Lumina::SETTINGS->value(DesktopSettings::Keys, "strict/"+shortcut, "").toString();
+ if(action.isEmpty() && win==0){
+ //"loose" actions (operating on the desktop or root window itself)
+ action = Lumina::SETTINGS->value(DesktopSettings::Keys, "desktop/"+shortcut, "").toString();
+ }
+ if(!action.isEmpty()){
+ //Found a valid action - go ahead and evaluate it
+ evaluateShortcutAction(action);
+ }
+}
+
+QString LShortcutEvents::keylistToString(){
+ QString shortcut;
+ for(int i=0; i<keylist.length(); i++){
+ if(i>0){ shortcut.append("+"); }
+ shortcut.append( QString::number(keylist[i]) );
+ }
+ /*qDebug() << "KeyList to String:";
+ qDebug() << " keys:" << keylist;
+ qDebug() << " string:" << shortcut;*/
+ return shortcut;
+}
+
+void LShortcutEvents::evaluateShortcutAction(QString action){
+ qDebug() << "Found Shortcut Action:" << action;
+ evaluated = true;
+ if(action.startsWith("Exec=")){
+ emit LaunchApplication(action.section("=",1,-1));
+ return;
+ }
+ //Specific Internal actions
+ action = action.toLower();
+ //Power Options
+ if(action=="logout"){ emit StartLogout(); }
+ else if(action=="reboot"){ emit StartReboot(); }
+ else if(action=="shutdown"){ emit StartShutdown(); }
+ else if(action=="show_leave_options"){ emit OpenLeaveDialog(); }
+
+}
+
+// === PUBLIC SLOTS ===
+void LShortcutEvents::KeyPress(WId window, int key){
+ if(window!=WIN){ keylist.clear(); WIN = window; }
+ if(!keylist.contains(key)){
+ //Put it in the list in ascending order
+ bool found = false;
+ for(int i=0; i<keylist.length() && !found; i++){
+ if(keylist[i]>key){ keylist.insert(i,key); found = true; }
+ }
+ if(!found){ keylist << key; }
+ evaluated = false;
+ }
+ //Evaluate the key sequence only when the first one is released
+ clearTimer->start(); //will "restart" if already running
+}
+
+void LShortcutEvents::KeyRelease(WId window, int key){
+ if(window!=WIN){ keylist.clear(); return; }
+ if(!evaluated){ CheckKeySequence(WIN); } //run this "before" removing the key from the list
+ keylist.removeAll(key);
+ clearTimer->start(); //will "restart" if already running
+}
+
+void LShortcutEvents::MousePress(WId window, Lumina::MouseButton button){
+ //We do not provide shortcuts for combinations of mouse buttons - just mouse+keyboard combinations
+ CheckMouseSequence(window, button, false);
+ clearTimer->start(); //will "restart" if already running
+}
+
+void LShortcutEvents::MouseRelease(WId window, Lumina::MouseButton button){
+ //We do not provide shortcuts for combinations of mouse buttons - just mouse+keyboard combinations
+ CheckMouseSequence(window, button, true);
+ clearTimer->start(); //will "restart" if already running
+}
+
+void LShortcutEvents::clearKeys(){
+ keylist.clear();
+ WIN = 0;
+ if(clearTimer!=0){ clearTimer->stop(); }
+}
diff --git a/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.h b/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.h
new file mode 100644
index 00000000..d1c3b4e0
--- /dev/null
+++ b/src-qt5/core/lumina-desktop-unified/src-events/LShortcutEvents.h
@@ -0,0 +1,72 @@
+//===========================================
+// Lumina desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// Class for managing key presses and sending out signals
+// when a shortcut combination is pressed
+//===========================================
+#ifndef _LUMINA_KEY_SEQUENCE_DETECTION_H
+#define _LUMINA_KEY_SEQUENCE_DETECTION_H
+
+#include "../global-includes.h"
+
+class LShortcutEvents : public QObject{
+ Q_OBJECT
+public:
+ LShortcutEvents();
+ ~LShortcutEvents();
+
+ void start();
+ void stop();
+
+private:
+ QList<int> keylist; //keys currently held down (NOTE: QKeySequence has a max of 4 keys for combinations)
+ WId WIN; //current window being acted on by the keys
+ QTimer *clearTimer; //used to clear the internal keylist every once in a while if no events come in.
+ bool evaluated;
+
+ //Actual check functions
+ void CheckKeySequence(WId win);
+ void CheckMouseSequence(WId win, Lumina::MouseButton, bool release);
+ QString keylistToString();
+ void evaluateShortcutAction(QString action);
+
+public slots:
+ void KeyPress(WId window, int key);
+ void KeyRelease(WId window, int key);
+ void MousePress(WId window, Lumina::MouseButton);
+ void MouseRelease(WId window, Lumina::MouseButton);
+ void clearKeys();
+
+signals:
+ // Power Options
+ void OpenLeaveDialog();
+ void StartLogout();
+ void StartReboot(); //assumes startUpdates==true
+ void StartShutdown(); //assumes startUpdates==true
+
+ // Session Options
+ void ChangeWorkspace(int); // +/- 1 from current
+ void LockSession();
+
+ //Active Window Options
+ void ActiveWindowMoveToWorkspace(int); //number of workspace
+ void ActiveWindowTakeToWorkspace(int); //number of workspace
+ void ActiveWindowKill();
+ void ActiveWindowClose();
+ void ActiveWindowMinMaxToggle();
+ void ActiveWindowFullscreenToggle();
+ void ActiveWindowStartMove();
+ void ActiveWindowStopMove();
+ void ActiveWindowStartResize();
+ void ActiveWindowStopResize();
+
+
+ //General Utility Launch
+ void LaunchApplication(QString exec);
+
+};
+
+#endif
diff --git a/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.cpp b/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.cpp
index ced2cadb..04ebd4d3 100644
--- a/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.cpp
+++ b/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.cpp
@@ -21,6 +21,7 @@
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+#include <xcb/xcb_keysyms.h>
#define DEBUG 0
@@ -126,22 +127,22 @@ bool XCBEventFilter::nativeEventFilter(const QByteArray &eventType, void *messag
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
+ if(!stopevent){ obj->emit KeyPressed( InputWindow(((xcb_key_press_event_t *) ev)->root), ((xcb_key_press_event_t *) ev)->detail ); }
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
+ if(!stopevent){ obj->emit KeyReleased( InputWindow(((xcb_key_release_event_t *) ev)->root), ((xcb_key_release_event_t *) ev)->detail ); }
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
+ obj->emit MousePressed( InputWindow(((xcb_button_press_event_t *) ev)->root), MouseKey(((xcb_key_press_event_t *) ev)->detail) );
if(obj->XCB->WM_Get_Active_Window()!=((xcb_button_press_event_t *) ev)->root){
obj->XCB->WM_Set_Active_Window( ((xcb_button_press_event_t *) ev)->root);
}
@@ -152,23 +153,21 @@ bool XCBEventFilter::nativeEventFilter(const QByteArray &eventType, void *messag
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
+ if(!stopevent){ obj->emit MouseReleased( InputWindow(((xcb_button_release_event_t *) ev)->root), MouseKey(((xcb_key_press_event_t *) ev)->detail) ); }
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();
+ //qDebug() << "Enter Notify Event";
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();
+ //qDebug() << "Leave Notify Event";
stopevent = BlockInputEvent();
break;
//==============================
@@ -319,6 +318,40 @@ bool XCBEventFilter::BlockInputEvent(WId){
return false;
}
+WId XCBEventFilter::InputWindow(WId win){
+ //check window and see if it is a desktop/root window (return 0) or an external app window
+ if(win == L_XCB::root){ return 0; }
+ QString cl = obj->XCB->WindowClass(win);
+ qDebug() << "Got Input Event on window:" << cl;
+ if(cl.toLower()=="lumina-desktop"){ return 0; }
+ return win; //external app window
+}
+
+Lumina::MouseButton XCBEventFilter::MouseKey(int keycode){
+ switch(keycode){
+ case 1:
+ return Lumina::LeftButton;
+ case 3:
+ return Lumina::RightButton;
+ case 2:
+ return Lumina::MidButton;
+ case 4:
+ return Lumina::WheelUp;
+ case 5:
+ return Lumina::WheelDown;
+ case 6:
+ return Lumina::WheelLeft;
+ case 7:
+ return Lumina::WheelRight;
+ case 8:
+ return Lumina::BackButton; //Not sure if this is correct yet (1/27/17)
+ case 9:
+ return Lumina::ForwardButton; //Not sure if this is correct yet (1/27/17)
+ default:
+ return Lumina::NoButton;
+ }
+}
+
//System Tray functions
void XCBEventFilter::addTrayApp(WId win){
if(SystemTrayID==0){ return; }
diff --git a/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.h b/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.h
index bd235658..dc88dcc0 100644
--- a/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.h
+++ b/src-qt5/core/lumina-desktop-unified/src-events/LXcbEventFilter.h
@@ -70,10 +70,15 @@ signals:
void WindowClosed(WId);
void ModifyWindow(WId win, Lumina::WindowAction);
- //System Tray Signals
+ // System Tray Signals
void Tray_AppAdded(WId); //new tray app registered
void Tray_AppClosed(WId); //tray app de-registered
void Tray_AppUpdated(WId); //tray app appearance changed (damage event)
+ // Shortcut Signals
+ void KeyPressed(WId, int);
+ void KeyReleased(WId, int);
+ void MousePressed(WId, Lumina::MouseButton);
+ void MouseReleased(WId, Lumina::MouseButton);
};
class XCBEventFilter : public QAbstractNativeEventFilter{
@@ -95,6 +100,9 @@ private:
void InitAtoms();
bool BlockInputEvent(WId win = 0); //Checks the current state of the system to see if the event should be stopped
+ WId InputWindow(WId win = 0); //Checks the window ID and determines if this is an external window or returns 0 (for desktop/root windows)
+ Lumina::MouseButton MouseKey(int keycode); //convert the keycode into the mouse button code
+
//System Tray Variables
WId SystemTrayID;
diff --git a/src-qt5/core/lumina-desktop-unified/src-events/events.pri b/src-qt5/core/lumina-desktop-unified/src-events/events.pri
index b3ca9847..9eec91ca 100644
--- a/src-qt5/core/lumina-desktop-unified/src-events/events.pri
+++ b/src-qt5/core/lumina-desktop-unified/src-events/events.pri
@@ -2,5 +2,9 @@ SOURCES *= $${PWD}/LXcbEventFilter.cpp
HEADERS *= $${PWD}/LXcbEventFilter.h
+#Shortcut event files
+SOURCES *= $${PWD}/LShortcutEvents.cpp
+HEADERS *= $${PWD}/LShortcutEvents.h
+
#update the includepath so we can just (#include <LXcbEventFilter.h>) as needed without paths
INCLUDEPATH *= ${PWD}
bgstack15