aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--src/FbTk/KeyUtil.cc16
-rw-r--r--src/Keys.cc251
-rw-r--r--src/Keys.hh11
-rw-r--r--src/fluxbox.cc13
5 files changed, 113 insertions, 189 deletions
diff --git a/ChangeLog b/ChangeLog
index 6367429..0ef21fb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
1 (Format: Year/Month/Day) 1 (Format: Year/Month/Day)
2Changes for 1.0rc3: 2Changes for 1.0rc3:
3*06/12/19:
4 * Little simplification of data structure for keybindings (Mark)
5 Side effects:
6 - "Mod4 a b" now behaves like "Mod4 a None b" -- in fact, "None" is now
7 completely obsolete
8 - You can press "Escape" to cancel any Emacs-style keychain in progress
9 (unless it's bound to something else)
10 - If there is a conflict between bindings, the first one in the file wins
11 - Fixes handling of keychains like "Mod4 a Mod1 b"
12 - Should fix some issues with "None" modifier
13 Keys.cc/hh FbTk/KeyUtil.cc fluxbox.cc
3*06/12/18: 14*06/12/18:
4 * Rotate items in toolbar the same way as tabs (Mark) 15 * Rotate items in toolbar the same way as tabs (Mark)
5 Toolbar.cc 16 Toolbar.cc
diff --git a/src/FbTk/KeyUtil.cc b/src/FbTk/KeyUtil.cc
index 24e43c8..de9f947 100644
--- a/src/FbTk/KeyUtil.cc
+++ b/src/FbTk/KeyUtil.cc
@@ -124,42 +124,42 @@ void KeyUtil::grabKey(unsigned int key, unsigned int mod) {
124 124
125 XGrabKey(display, key, mod, 125 XGrabKey(display, key, mod,
126 root, True, 126 root, True,
127 GrabModeAsync, GrabModeSync); 127 GrabModeAsync, GrabModeAsync);
128 128
129 // Grab with numlock, capslock and scrlock 129 // Grab with numlock, capslock and scrlock
130 130
131 //numlock 131 //numlock
132 XGrabKey(display, key, mod|nummod, 132 XGrabKey(display, key, mod|nummod,
133 root, True, 133 root, True,
134 GrabModeAsync, GrabModeSync); 134 GrabModeAsync, GrabModeAsync);
135 //scrolllock 135 //scrolllock
136 XGrabKey(display, key, mod|scrollmod, 136 XGrabKey(display, key, mod|scrollmod,
137 root, True, 137 root, True,
138 GrabModeAsync, GrabModeSync); 138 GrabModeAsync, GrabModeAsync);
139 //capslock 139 //capslock
140 XGrabKey(display, key, mod|capsmod, 140 XGrabKey(display, key, mod|capsmod,
141 root, True, 141 root, True,
142 GrabModeAsync, GrabModeSync); 142 GrabModeAsync, GrabModeAsync);
143 143
144 //capslock+numlock 144 //capslock+numlock
145 XGrabKey(display, key, mod|capsmod|nummod, 145 XGrabKey(display, key, mod|capsmod|nummod,
146 root, True, 146 root, True,
147 GrabModeAsync, GrabModeSync); 147 GrabModeAsync, GrabModeAsync);
148 148
149 //capslock+scrolllock 149 //capslock+scrolllock
150 XGrabKey(display, key, mod|capsmod|scrollmod, 150 XGrabKey(display, key, mod|capsmod|scrollmod,
151 root, True, 151 root, True,
152 GrabModeAsync, GrabModeSync); 152 GrabModeAsync, GrabModeAsync);
153 153
154 //capslock+numlock+scrolllock 154 //capslock+numlock+scrolllock
155 XGrabKey(display, key, mod|capsmod|scrollmod|nummod, 155 XGrabKey(display, key, mod|capsmod|scrollmod|nummod,
156 root, True, 156 root, True,
157 GrabModeAsync, GrabModeSync); 157 GrabModeAsync, GrabModeAsync);
158 158
159 //numlock+scrollLock 159 //numlock+scrollLock
160 XGrabKey(display, key, mod|nummod|scrollmod, 160 XGrabKey(display, key, mod|nummod|scrollmod,
161 root, True, 161 root, True,
162 GrabModeAsync, GrabModeSync); 162 GrabModeAsync, GrabModeAsync);
163 163
164 } 164 }
165 165
diff --git a/src/Keys.cc b/src/Keys.cc
index b1f6763..2404a0a 100644
--- a/src/Keys.cc
+++ b/src/Keys.cc
@@ -94,6 +94,7 @@ using std::endl;
94using std::string; 94using std::string;
95using std::vector; 95using std::vector;
96using std::ifstream; 96using std::ifstream;
97using std::pair;
97 98
98Keys::Keys(): 99Keys::Keys():
99 m_display(FbTk::App::instance()->display()) 100 m_display(FbTk::App::instance()->display())
@@ -109,14 +110,8 @@ Keys::~Keys() {
109 110
110/// Destroys the keytree 111/// Destroys the keytree
111void Keys::deleteTree() { 112void Keys::deleteTree() {
112 for (keyspace_t::iterator map_it = m_map.begin(); map_it != m_map.end(); ++map_it) { 113 for (keyspace_t::iterator map_it = m_map.begin(); map_it != m_map.end(); ++map_it)
113 keylist_t::iterator it = map_it->second->begin();
114 const keylist_t::iterator it_end = map_it->second->end();
115 for ( ; it != it_end; it++)
116 delete *it;
117 map_it->second->clear();
118 delete map_it->second; 114 delete map_it->second;
119 }
120 m_map.clear(); 115 m_map.clear();
121} 116}
122 117
@@ -129,13 +124,10 @@ bool Keys::load(const char *filename) {
129 if (!filename) 124 if (!filename)
130 return false; 125 return false;
131 126
132 //ungrab all keys
133 FbTk::KeyUtil::ungrabKeys();
134
135 //free memory of previous grabs 127 //free memory of previous grabs
136 deleteTree(); 128 deleteTree();
137 129
138 m_map["default:"] = new keylist_t; 130 m_map["default:"] = new t_key(0,0);
139 131
140 FbTk::App::instance()->sync(false); 132 FbTk::App::instance()->sync(false);
141 133
@@ -144,21 +136,25 @@ bool Keys::load(const char *filename) {
144 if (!infile) 136 if (!infile)
145 return false; // faild to open file 137 return false; // faild to open file
146 138
147 m_current_line = 0;//current line, so we can tell the user where the fault is 139 unsigned int current_line = 0;//so we can tell the user where the fault is
148 140
149 while (!infile.eof()) { 141 while (!infile.eof()) {
150 string linebuffer; 142 string linebuffer;
151 143
152 getline(infile, linebuffer); 144 getline(infile, linebuffer);
153 145
154 m_current_line++; 146 current_line++;
155 147
156 addBinding(linebuffer); 148 if (!addBinding(linebuffer)) {
149 cerr<<_FB_CONSOLETEXT(Keys, InvalidKeyMod,
150 "Keys: Invalid key/modifier on line",
151 "A bad key/modifier string was found on line (number following)")<<" "<<
152 current_line<<"): "<<linebuffer<<endl;
153 }
157 } // end while eof 154 } // end while eof
158 155
159 m_current_line = 0;
160 m_filename = filename; 156 m_filename = filename;
161 m_keylist = m_map["default:"]; 157 keyMode("default");
162 return true; 158 return true;
163} 159}
164 160
@@ -189,16 +185,16 @@ bool Keys::addBinding(const string &linebuffer) {
189 return true; // still a valid line. 185 return true; // still a valid line.
190 186
191 unsigned int key = 0, mod = 0; 187 unsigned int key = 0, mod = 0;
192 t_key *current_key=0, *last_key=0;
193 size_t argc = 0; 188 size_t argc = 0;
194 string keyMode = "default:"; 189 t_key *current_key=m_map["default:"];
190 t_key *first_new_keylist = current_key, *first_new_key=0;
195 191
196 if (val[0][val[0].length()-1] == ':') { 192 if (val[0][val[0].length()-1] == ':') {
197 argc++; 193 argc++;
198 keyspace_t::iterator it = m_map.find(val[0]); 194 keyspace_t::iterator it = m_map.find(val[0]);
199 if (it == m_map.end()) 195 if (it == m_map.end())
200 m_map[val[0]] = new keylist_t; 196 m_map[val[0]] = new t_key(0,0);
201 keyMode = val[0]; 197 current_key = m_map[val[0]];
202 } 198 }
203 _FB_USES_NLS; 199 _FB_USES_NLS;
204 // for each argument 200 // for each argument
@@ -209,9 +205,7 @@ bool Keys::addBinding(const string &linebuffer) {
209 int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); 205 int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str());
210 if(tmpmod) 206 if(tmpmod)
211 mod |= tmpmod; //If it's a modifier 207 mod |= tmpmod; //If it's a modifier
212 else if (strcasecmp("NONE",val[argc].c_str()) == 0) 208 else if (strcasecmp("NONE",val[argc].c_str())) {
213 mod = 0;
214 else {
215 // keycode covers the following three two-byte cases: 209 // keycode covers the following three two-byte cases:
216 // 0x - hex 210 // 0x - hex
217 // +[1-9] - number between +1 and +9 211 // +[1-9] - number between +1 and +9
@@ -229,111 +223,86 @@ bool Keys::addBinding(const string &linebuffer) {
229 } else // convert from string symbol 223 } else // convert from string symbol
230 key = FbTk::KeyUtil::getKey(val[argc].c_str()); 224 key = FbTk::KeyUtil::getKey(val[argc].c_str());
231 225
232 if (key == 0) { 226 if (key == 0)
233 cerr<<_FB_CONSOLETEXT(Keys, InvalidKeyMod,
234 "Keys: Invalid key/modifier on line",
235 "A bad key/modifier string was found on line (number following)")<<" "<<
236 m_current_line<<"): "<<linebuffer<<endl;
237 return false; 227 return false;
238 } 228 if (!first_new_key) {
239 if (!current_key) { 229 first_new_keylist = current_key;
240 current_key = new t_key(key, mod); 230 current_key = current_key->find(key, mod);
241 last_key = current_key; 231 if (!current_key) {
232 first_new_key = new t_key(key, mod);
233 current_key = first_new_key;
234 } else if (*current_key->m_command) // already being used
235 return false;
242 } else { 236 } else {
243 t_key *temp_key = new t_key(key, mod); 237 t_key *temp_key = new t_key(key, mod);
244 last_key->keylist.push_back(temp_key); 238 current_key->keylist.push_back(temp_key);
245 last_key = temp_key; 239 current_key = temp_key;
246 } 240 }
241 mod = 0;
242 key = 0;
247 } 243 }
248 244
249 } else { // parse command line 245 } else { // parse command line
250 if (last_key == 0) { 246 if (!first_new_key)
251 cerr<<_FB_CONSOLETEXT(Keys, BadLine, "Keys: Error on line", "Error on line (number following)")<<": "<<m_current_line<<endl;
252 cerr<<"> "<<linebuffer<<endl;
253 return false; 247 return false;
254 }
255 bool ret_val = true;
256 const char *str =
257 FbTk::StringUtil::strcasestr(linebuffer.c_str(),
258 val[argc].c_str() + 1); // +1 to skip ':'
259 if (str == 0) {
260 cerr<<_FB_CONSOLETEXT(Keys, BadLine, "Keys: Error on line", "Error on line (number following)")<<": "<<m_current_line<<endl;
261 cerr<<"> "<<linebuffer<<endl;
262 ret_val = false;
263 } else {
264
265 last_key->m_command = CommandParser::instance().parseLine(str);
266
267 if (*last_key->m_command == 0) {
268 cerr<<_FB_CONSOLETEXT(Keys, BadLine, "Keys: Error on line", "Error on line (number following)")<<": "<<m_current_line<<endl;
269 cerr<<"> "<<linebuffer<<endl;
270 } else {
271 // need to change keymode here so it doesn't get changed by CommandParser
272 m_keylist = m_map[keyMode];
273 // Add the keychain to list
274 if (!mergeTree(current_key)) {
275 cerr<<_FB_CONSOLETEXT(Keys, BadMerge, "Keys: Failed to merge keytree!", "relatively technical error message. Key bindings are stored in a tree structure")<<endl;
276 ret_val = false;
277 }
278 }
279 }
280 delete current_key;
281 current_key = 0;
282 last_key = 0;
283 248
284 return ret_val; 249 const char *str = FbTk::StringUtil::strcasestr(linebuffer.c_str(),
250 val[argc].c_str() + 1); // +1 to skip ':'
251 if (str)
252 current_key->m_command = CommandParser::instance().parseLine(str);
285 253
254 if (!str || *current_key->m_command == 0 || mod) {
255 delete first_new_key;
256 return false;
257 }
258
259 // success
260 first_new_keylist->keylist.push_back(first_new_key);
261 return true;
286 } // end if 262 } // end if
287 } // end for 263 } // end for
288 264
289 return false; 265 return false;
290} 266}
291 267
292 268// return true if bound to a command, else false
293/**
294 @return the KeyAction of the XKeyEvent; return false if not bound
295*/
296bool Keys::doAction(XKeyEvent &ke) { 269bool Keys::doAction(XKeyEvent &ke) {
297 270
298 ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); 271 ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state);
299 272
300 static struct t_key* next_key = 0; 273 static t_key* next_key = m_keylist;
301 274 if (!next_key)
302 if (!next_key) { 275 next_key = m_keylist;
303 bool retval = false;
304 // need a local keylist, in case m_command->execute() changes it
305 keylist_t *keylist = m_keylist;
306 for (size_t i = 0; i < keylist->size(); i++) {
307 if (*(*keylist)[i] == ke) {
308 if ((*keylist)[i]->keylist.size()) {
309 next_key = (*keylist)[i];
310 return true; //still counts as being grabbed
311 }
312 if (*(*keylist)[i]->m_command != 0) {
313 (*keylist)[i]->m_command->execute();
314 retval = true;
315 }
316 }
317 }
318 return retval;
319 }
320 t_key *temp_key = next_key->find(ke); 276 t_key *temp_key = next_key->find(ke);
321 if (temp_key) { 277
322 if (temp_key->keylist.size()) { 278
323 next_key = temp_key; 279 // need to save this for emacs-style keybindings
324 return true; 280 static t_key *saved_keymode = 0;
325 } 281
326 next_key = 0; 282 if (temp_key && temp_key->keylist.size()) { // emacs-style
327 if (*temp_key->m_command == 0) 283 saved_keymode = m_keylist;
328 return false; 284 next_key = temp_key;
329 temp_key->m_command->execute(); 285 setKeyMode(next_key);
286 // grab "None Escape" to exit keychain in the middle
287 unsigned int esc = FbTk::KeyUtil::getKey("Escape");
288 FbTk::KeyUtil::grabKey(esc,0);
330 return true; 289 return true;
331 } 290 }
332 temp_key = next_key; 291 if (!temp_key || *temp_key->m_command == 0) {
333 next_key = 0; 292 next_key = 0;
334 if (*temp_key->m_command == 0) 293 if (saved_keymode) {
294 setKeyMode(saved_keymode);
295 saved_keymode = 0;
296 }
335 return false; 297 return false;
298 }
336 temp_key->m_command->execute(); 299 temp_key->m_command->execute();
300 if (saved_keymode) {
301 if (next_key == m_keylist) // don't reset keymode if command changed it
302 setKeyMode(saved_keymode);
303 saved_keymode = 0;
304 }
305 next_key = 0;
337 return true; 306 return true;
338} 307}
339 308
@@ -345,62 +314,21 @@ bool Keys::reconfigure(const char *filename) {
345 return load(filename); 314 return load(filename);
346} 315}
347 316
348/** 317void Keys::keyMode(string keyMode) {
349 Merges two chains and binds new keys
350 @return true on success else false.
351*/
352bool Keys::mergeTree(t_key *newtree, t_key *basetree) {
353 size_t baselist_i = 0;
354 if (basetree==0) {
355 for (; baselist_i<m_keylist->size(); baselist_i++) {
356 if ((*m_keylist)[baselist_i]->mod == newtree->mod &&
357 (*m_keylist)[baselist_i]->key == newtree->key) {
358 if (newtree->keylist.size() && *(*m_keylist)[baselist_i]->m_command == 0) {
359 //assumes the newtree only have one branch
360 return mergeTree(newtree->keylist[0], (*m_keylist)[baselist_i]);
361 } else
362 break;
363 }
364 }
365
366 if (baselist_i == m_keylist->size()) {
367 FbTk::KeyUtil::grabKey(newtree->key, newtree->mod);
368 m_keylist->push_back(new t_key(newtree));
369 if (newtree->keylist.size())
370 return mergeTree(newtree->keylist[0], m_keylist->back());
371 return true;
372 }
373
374 } else {
375 for (; baselist_i<basetree->keylist.size(); baselist_i++) {
376 if (basetree->keylist[baselist_i]->mod == newtree->mod &&
377 basetree->keylist[baselist_i]->key == newtree->key) {
378 if (newtree->keylist.size()) {
379 //assumes the newtree only have on branch
380 return mergeTree(newtree->keylist[0], basetree->keylist[baselist_i]);
381 } else
382 return false;
383 }
384 }
385 //if it wasn't in the list grab the key and add it to the list
386 if (baselist_i==basetree->keylist.size()) {
387 FbTk::KeyUtil::grabKey(newtree->key, newtree->mod);
388 basetree->keylist.push_back(new t_key(newtree));
389 if (newtree->keylist.size())
390 return mergeTree(newtree->keylist[0], basetree->keylist.back());
391 return true;
392 }
393 }
394
395 return false;
396}
397
398void Keys::keyMode(string keyMode = "default") {
399 keyspace_t::iterator it = m_map.find(keyMode + ":"); 318 keyspace_t::iterator it = m_map.find(keyMode + ":");
400 if (it == m_map.end()) 319 if (it == m_map.end())
401 m_keylist = m_map["default:"]; 320 setKeyMode(m_map["default:"]);
402 else 321 else
403 m_keylist = it->second; 322 setKeyMode(it->second);
323}
324
325void Keys::setKeyMode(t_key *keyMode) {
326 FbTk::KeyUtil::ungrabKeys();
327 keylist_t::iterator it = keyMode->keylist.begin();
328 keylist_t::iterator it_end = keyMode->keylist.end();
329 for (; it != it_end; ++it)
330 FbTk::KeyUtil::grabKey((*it)->key,(*it)->mod);
331 m_keylist = keyMode;
404} 332}
405 333
406Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) { 334Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) {
@@ -416,12 +344,7 @@ Keys::t_key::t_key(t_key *k) {
416} 344}
417 345
418Keys::t_key::~t_key() { 346Keys::t_key::~t_key() {
419 while (!keylist.empty()) { 347 for (keylist_t::iterator list_it = keylist.begin(); list_it != keylist.end(); ++list_it)
420 t_key *k = keylist.back(); 348 delete *list_it;
421 if (k != 0) { // make sure we don't have a bad key pointer 349 keylist.clear();
422 delete k;
423 keylist.pop_back();
424 }
425 }
426
427} 350}
diff --git a/src/Keys.hh b/src/Keys.hh
index 6b393e7..8e919b3 100644
--- a/src/Keys.hh
+++ b/src/Keys.hh
@@ -116,18 +116,13 @@ private:
116 keylist_t keylist; 116 keylist_t keylist;
117 }; 117 };
118 118
119 /** 119 void setKeyMode(t_key *keyMode);
120 merge two linked list
121 @return true on success, else false
122 */
123 bool mergeTree(t_key *newtree, t_key *basetree=0);
124 120
125 typedef std::map<std::string, keylist_t *> keyspace_t; 121 typedef std::map<std::string, t_key *> keyspace_t;
126 keylist_t *m_keylist; 122 t_key *m_keylist;
127 keyspace_t m_map; 123 keyspace_t m_map;
128 124
129 Display *m_display; ///< display connection 125 Display *m_display; ///< display connection
130 unsigned int m_current_line;
131}; 126};
132 127
133#endif // KEYS_HH 128#endif // KEYS_HH
diff --git a/src/fluxbox.cc b/src/fluxbox.cc
index 8a810a4..9fe22f7 100644
--- a/src/fluxbox.cc
+++ b/src/fluxbox.cc
@@ -1119,16 +1119,11 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) {
1119 case KeyPress: 1119 case KeyPress:
1120 // see if we need to keep watching for key releases 1120 // see if we need to keep watching for key releases
1121 m_watching_screen = 0; 1121 m_watching_screen = 0;
1122 if (m_key->doAction(ke)) { 1122 if (!m_key->doAction(ke)) // could still be cycling
1123 XAllowEvents(FbTk::App::instance()->display(), AsyncKeyboard, CurrentTime);
1124 // if we've done some action other than cycling focus
1125 if (old_watching_screen && m_watching_screen != old_watching_screen)
1126 old_watching_screen->notifyReleasedKeys(ke);
1127 } else {
1128 XAllowEvents(FbTk::App::instance()->display(), ReplayKeyboard, CurrentTime);
1129 // could still be cycling
1130 m_watching_screen = old_watching_screen; 1123 m_watching_screen = old_watching_screen;
1131 } 1124 else if (old_watching_screen &&
1125 m_watching_screen != old_watching_screen)
1126 old_watching_screen->notifyReleasedKeys(ke);
1132 break; 1127 break;
1133 case KeyRelease: { 1128 case KeyRelease: {
1134 // we ignore most key releases unless we need to use 1129 // we ignore most key releases unless we need to use