diff options
author | Ken Moore <ken@ixsystems.com> | 2017-01-27 14:22:46 -0500 |
---|---|---|
committer | Ken Moore <ken@ixsystems.com> | 2017-01-27 14:22:46 -0500 |
commit | 1ee0abd899e1da60b541110642dcc39e6bb44d7b (patch) | |
tree | ec24c8bf3b27485fdd3fd48cff6d7f5bfc8bdebe /src-qt5/core/lumina-desktop-unified/src-events | |
parent | Another large batch of work on the new unified desktop. (diff) | |
download | lumina-1ee0abd899e1da60b541110642dcc39e6bb44d7b.tar.gz lumina-1ee0abd899e1da60b541110642dcc39e6bb44d7b.tar.bz2 lumina-1ee0abd899e1da60b541110642dcc39e6bb44d7b.zip |
Another large update for Lumina 2:
Get the new LShortcutEvents class setup and functional. This is what converts the key presses and mouse clicks into a format that can be scanned for user-defined shortcuts to do things in the session.
NOTES:
The format of the keys.conf files uses "keycodes" (in ascending order") delimited by "+" to make the settings searchable and non-ordered-specific.
Example:
[strict]
22+37+64=logout
This is a shortcut for [L_Ctrl (37), L_Alt (64), Backspace (22)] and it will trigger the start of the logout procedures.
The "strict" section (and it's alternative "desktop" section) determine priority of the shortcut (strict is always evaluated, desktop is only evaluated if the desktop has focus and there is no corresponding "strict" shortcut).
Diffstat (limited to 'src-qt5/core/lumina-desktop-unified/src-events')
5 files changed, 311 insertions, 9 deletions
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} |