1 //===========================================
2 //  Lumina desktop source code
3 //  Copyright (c) 2017, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "LShortcutEvents.h"
8 #include "global-objects.h"
9 
10 // === PUBLIC ===
LShortcutEvents()11 LShortcutEvents::LShortcutEvents(){
12   WIN = 0;
13   clearTimer = 0;
14   evaluated = false;
15 }
16 
~LShortcutEvents()17 LShortcutEvents::~LShortcutEvents(){
18 
19 }
20 
start()21 void LShortcutEvents::start(){
22   if(clearTimer==0){
23     clearTimer = new QTimer(this);
24     clearTimer->setInterval(2000); //2 seconds
25     clearTimer->setSingleShot(true);
26     connect(clearTimer, SIGNAL(timeout()), this, SLOT(clearKeys()) );
27   }
28 }
29 
stop()30 void LShortcutEvents::stop(){
31   clearKeys();
32 }
33 
34 // === PRIVATE ===
CheckKeySequence(WId win)35 void LShortcutEvents::CheckKeySequence(WId win){
36   //Get the keyboard modifiers
37   QString shortcut = keylistToString();
38  //Now see if there is a match for this shortcut
39   //  "strict" actions (operate even if a non-desktop window is active/focused)
40   QString action = DesktopSettings::instance()->value(DesktopSettings::Keys, "strict/"+shortcut, "").toString();
41   qDebug() << "Strict Action:" << "strict/"+shortcut << action;
42   if(action.isEmpty() && win==0){
43     //"loose" actions (operating on the desktop or root window itself)
44     action = DesktopSettings::instance()->value(DesktopSettings::Keys, "desktop/"+shortcut, "").toString();
45     qDebug() << "Desktop Action:" << "desktop/"+shortcut << action;
46   }
47   if(!action.isEmpty()){
48     //Found a valid action - go ahead and evaluate it
49     evaluateShortcutAction(action);
50   }
51 }
52 
CheckMouseSequence(WId win,NativeWindowSystem::MouseButton button,bool release)53 void LShortcutEvents::CheckMouseSequence(WId win, NativeWindowSystem::MouseButton button, bool release){
54   if(release && (button == NativeWindowSystem::WheelUp || button == NativeWindowSystem::WheelDown || button == NativeWindowSystem::WheelLeft || button == NativeWindowSystem::WheelRight)){
55     return; //skip mouse release events for wheel actions (always come in pairs of press/release)
56   }else if(keylist.isEmpty() || button == NativeWindowSystem::NoButton){ return; } //Never overwrite mouse clicks themselves - just combinations with key presses
57   //Get the keyboard modifiers
58   QString shortcut = keylistToString();
59   //Add the mouse button to the shortcut
60   switch(button){
61     case NativeWindowSystem::LeftButton:
62       shortcut.append("+LeftMouse");
63       break;
64     case NativeWindowSystem::RightButton:
65       shortcut.append("+RightMouse");
66       break;
67     case NativeWindowSystem::MidButton:
68       shortcut.append("+MiddleMouse");
69       break;
70     case NativeWindowSystem::BackButton:
71       shortcut.append("+BackMouse");
72       break;
73     case NativeWindowSystem::ForwardButton:
74       shortcut.append("+ForwardMouse");
75       break;
76     case NativeWindowSystem::TaskButton:
77       shortcut.append("+TaskMouse");
78       break;
79     case NativeWindowSystem::WheelUp:
80       shortcut.append("+WheelUp");
81       break;
82     case NativeWindowSystem::WheelDown:
83       shortcut.append("+WheelDown");
84       break;
85     case NativeWindowSystem::WheelLeft:
86       shortcut.append("+WheelLeft");
87       break;
88     case NativeWindowSystem::WheelRight:
89       shortcut.append("+WheelRight");
90       break;
91     default:
92       shortcut.clear();
93   }
94   if(shortcut.isEmpty()){ return; }
95   //Now see if there is a match for this shortcut
96   //  "strict" actions (operate even if a non-desktop window is active/focused)
97   QString action = DesktopSettings::instance()->value(DesktopSettings::Keys, "strict/"+shortcut, "").toString();
98   if(action.isEmpty() && win==0){
99     //"loose" actions (operating on the desktop or root window itself)
100     action = DesktopSettings::instance()->value(DesktopSettings::Keys, "desktop/"+shortcut, "").toString();
101   }
102   if(!action.isEmpty()){
103     //Found a valid action - go ahead and evaluate it
104     evaluateShortcutAction(action);
105   }
106 }
107 
keylistToString()108 QString LShortcutEvents::keylistToString(){
109   if(keylist.isEmpty()){ return ""; }
110   QString shortcut;
111   QList<int> keys; int ckey = 0;
112   for(int i=0; i<keylist.length(); i++){
113     if(i == keylist.length()-1){ ckey+=keylist[i]; } //always treat the last key as a non-modifier
114     else if(keylist[i] == Qt::Key_Control){ ckey+=Qt::CTRL; } //use the modifier form of the key
115     else if(keylist[i] == Qt::Key_Alt){ ckey += Qt::ALT; }
116     else if(keylist[i] == Qt::Key_Shift){ ckey += Qt::SHIFT; }
117     else if(keylist[i] == Qt::Key_Meta){ ckey += Qt::META; }
118     else{ ckey+= keylist[i]; keys << ckey; ckey = 0; } //non-modifier - need to finish current mod+key combo and start a new one
119   }
120   if(ckey!=0){ keys << ckey; } //add in the last one as well
121   if(keys.length() < 1){ return  ""; }
122   QKeySequence seq;
123   switch(keys.length()){
124     case 1:
125       seq = QKeySequence(keys[0]); break;
126     case 2:
127       seq = QKeySequence(keys[0], keys[1]); break;
128     case 3:
129       seq = QKeySequence(keys[0], keys[1], keys[2]); break;
130     default:
131       seq = QKeySequence(keys[0],keys[1], keys[2], keys[3]); break;
132   }
133   /*qDebug() << "KeyList to String:";
134   qDebug() << "  keys:" << seq;
135   qDebug() << "  string:" << seq.toString();*/
136   return seq.toString();
137 }
138 
evaluateShortcutAction(QString action)139 void LShortcutEvents::evaluateShortcutAction(QString action){
140   qDebug() << "Found Shortcut Action:" << action;
141   evaluated = true;
142   if(action.startsWith("Exec:")){
143     emit LaunchApplication(action.section(":",1,-1));
144     return;
145   }else if(action.startsWith("Launch:")){
146     emit LaunchStandardApplication(action.section(":",1,-1));
147   }
148   //Specific Internal actions
149   action = action.toLower();
150   //Power Options
151   if(action=="logout"){ emit StartLogout(); }
152   else if(action=="reboot"){ emit StartReboot(); }
153   else if(action=="shutdown"){ emit StartShutdown(); }
154   else if(action=="show_leave_options"){ emit OpenLeaveDialog(); }
155   else if(action=="lockscreen"){ emit LockSession(); }
156 
157 }
158 
159 // === PUBLIC SLOTS ===
KeyPress(WId window,Qt::Key key)160 void LShortcutEvents::KeyPress(WId window, Qt::Key key){
161   if(window!=WIN){ keylist.clear(); WIN = window; }
162   /*if(!keylist.contains(key)){
163     //Put it in the list in ascending order
164     bool found = false;
165     for(int i=0; i<keylist.length() && !found; i++){
166       if(keylist[i]>key){ keylist.insert(i,key); found = true; }
167     }
168     if(!found){ keylist << key;  }
169     evaluated = false;
170   }*/
171   if(!keylist.isEmpty()){
172     if( keylist.last()!=key ){ keylist << key; }
173   }else{
174     keylist << key;
175   }
176   //Evaluate the key sequence only when the first one is released
177   clearTimer->start(); //will "restart" if already running
178 }
179 
KeyRelease(WId window,Qt::Key key)180 void LShortcutEvents::KeyRelease(WId window, Qt::Key key){
181   if(window!=WIN){ keylist.clear(); return; }
182   if(!evaluated){ CheckKeySequence(WIN); } //run this "before" removing the key from the list
183   for(int i=keylist.length()-1; i>=0; i--){
184     if(keylist[i] == key){ keylist.removeAt(i); break; }
185   }
186   clearTimer->start(); //will "restart" if already running
187 }
188 
MousePress(WId window,NativeWindowSystem::MouseButton button)189 void LShortcutEvents::MousePress(WId window, NativeWindowSystem::MouseButton button){
190   //We do not provide shortcuts for combinations of mouse buttons - just mouse+keyboard combinations
191   CheckMouseSequence(window, button, false);
192   clearTimer->start(); //will "restart" if already running
193 }
194 
MouseRelease(WId window,NativeWindowSystem::MouseButton button)195 void LShortcutEvents::MouseRelease(WId window, NativeWindowSystem::MouseButton button){
196   //We do not provide shortcuts for combinations of mouse buttons - just mouse+keyboard combinations
197   CheckMouseSequence(window, button, true);
198   clearTimer->start(); //will "restart" if already running
199 }
200 
clearKeys()201 void LShortcutEvents::clearKeys(){
202   keylist.clear();
203   WIN = 0;
204   if(clearTimer!=0){ clearTimer->stop(); }
205 }
206