1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // keytree.cc for Epistrophy - a key handler for NETWM/EWMH window managers.
3 // Copyright (c) 2002 - 2002 Ben Jansens <ben at orodu.net>
4 //
5 // Modified for use and inclusion in bbkeys by Jason 'vanRijn' Kasper
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
24
25 #include "keytree.hh"
26
27 #include <string>
28 #include <sstream>
29 #include <iostream>
30
keytree(Display * display)31 keytree::keytree(Display *display): _display(display)
32 {
33 _head = new keynode;
34 _head->parent = NULL;
35 _head->action = NULL; // head's action is always NULL
36 _current = _head;
37 // for complete initialization, initialize() has to be called as well. We
38 // call initialize() when we are certain that the config object (which the
39 // timer uses) has been fully initialized. (see parser::parse())
40 }
41
~keytree()42 keytree::~keytree()
43 {
44 clearTree(_head);
45 }
46
unloadBindings()47 void keytree::unloadBindings()
48 {
49 ChildList::iterator it, end = _head->children.end();
50 for (it = _head->children.begin(); it != end; ++it)
51 clearTree(*it);
52
53 _head->children.clear();
54 reset();
55 }
56
clearTree(keynode * node)57 void keytree::clearTree(keynode *node)
58 {
59 if (!node)
60 return;
61
62 ChildList::iterator it, end = node->children.end();
63 for (it = node->children.begin(); it != end; ++it)
64 clearTree(*it);
65
66 node->children.clear();
67
68 if (node->action)
69 delete node->action;
70 delete node;
71 node = NULL;
72 }
73
grabDefaults(ScreenHandler * scr)74 void keytree::grabDefaults(ScreenHandler *scr)
75 {
76 grabChildren(_head, scr);
77 }
78
ungrabDefaults(ScreenHandler * scr)79 void keytree::ungrabDefaults(ScreenHandler *scr)
80 {
81 Action *act;
82
83 ChildList::const_iterator it, end = _head->children.end();
84 for (it = _head->children.begin(); it != end; ++it) {
85 act = (*it)->action;
86 if (act && act->type() != Action::toggleGrabs)
87 scr->ungrabKey(act->keycode(), act->modifierMask());
88 }
89 }
90
grabChildren(keynode * node,ScreenHandler * scr)91 void keytree::grabChildren(keynode *node, ScreenHandler *scr)
92 {
93 Action *act;
94
95 ChildList::const_iterator it, end = node->children.end();
96 for (it = node->children.begin(); it != end; ++it) {
97 act = (*it)->action;
98 if (act) {
99 bool ret = scr->grabKey(act->keycode(), act->modifierMask());
100 if (!ret) {
101 string key;
102 KeySym _sym = XKeycodeToKeysym(_display, act->keycode(), 0);
103
104 if (_sym == NoSymbol) key="key not found";
105 else key = XKeysymToString(_sym);
106
107 cerr << BBTOOL << ": keytree : grabChildren : "
108 << "could not activate keybinding for "
109 << "key: [" << key
110 << "], mask: [" << act->modifierMask()
111 << "], action: [" << act->getActionName() << "]\n";
112 }
113 }
114 }
115 }
116
ungrabChildren(keynode * node,ScreenHandler * scr)117 void keytree::ungrabChildren(keynode *node, ScreenHandler *scr)
118 {
119 ChildList::const_iterator head_it, head_end = _head->children.end();
120 ChildList::const_iterator it, end = node->children.end();
121 bool ungrab = true;
122
123 // when ungrabbing children, make sure that we don't ungrab any topmost keys
124 // (children of the head node) This would render those topmost keys useless.
125 // Topmost keys are _never_ ungrabbed, since they are only grabbed at startup
126
127 for (it = node->children.begin(); it != end; ++it) {
128 if ( (*it)->action ) {
129 for (head_it = _head->children.begin(); head_it != head_end; ++head_it) {
130 if ( (*it)->action->modifierMask() == (*head_it)->action->modifierMask() &&
131 (*it)->action->keycode() == (*head_it)->action->keycode())
132 {
133 ungrab = false;
134 break;
135 }
136 }
137
138 if (ungrab)
139 scr->ungrabKey( (*it)->action->keycode(), (*it)->action->modifierMask());
140
141 ungrab = true;
142 }
143 }
144 }
145
getAction(const XKeyEvent * const e,unsigned int & state,ScreenHandler * scr)146 Action * keytree::getAction(const XKeyEvent * const e, unsigned int & state,
147 ScreenHandler *scr)
148 {
149 Action *act;
150
151 // we're done with the children. ungrab them
152 if (_current != _head)
153 ungrabChildren(_current, scr);
154
155 // With XKB e->xkey.state can hold the group index in high bits, in
156 // addition to the standard modifier bits. This does not happen on the
157 // first grabbed event, but happens when doing stacked cycling (when
158 // XGrabKeyboard is active). In order to recognize subsequent keypresses,
159 // we must clear all unneeded bits in the state field.
160
161 state &= (ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
162
163 ChildList::const_iterator it, end = _current->children.end();
164 for (it = _current->children.begin(); it != end; ++it) {
165 act = (*it)->action;
166
167 if (e->keycode == act->keycode() && state == act->modifierMask()) {
168
169 if (act->type() == Action::cancelChain ) {
170 // user is cancelling the chain explicitly
171 _current = _head;
172 return act;
173 }
174 else if ( isLeaf(*it) ) {
175 // node is a leaf, so an action will be executed
176 _current = _head;
177 return act;
178 }
179 else {
180 // node is not a leaf, so we advance down the tree, and grab the
181 // children of the new current node. no action is executed
182 _current = *it;
183 grabChildren(_current, scr);
184 return act;
185 }
186 }
187 }
188
189 // action not found. back to the head
190 _current = _head;
191 return (Action *)NULL;
192 }
193
addAction(Action::ActionType action,unsigned int mask,string key,string arg)194 void keytree::addAction(Action::ActionType action, unsigned int mask,
195 string key, string arg)
196 {
197 if (action == Action::toggleGrabs && _current != _head) {
198 // the toggleGrabs key can only be set up as a root key, since if
199 // it was a chain key, we'd have to not ungrab the whole chain up
200 // to that key. which kinda defeats the purpose of this function.
201 return;
202 }
203
204 KeySym sym = XStringToKeysym(key.c_str());
205 KeyCode keyCode = XKeysymToKeycode(_display, sym);
206
207 if (keyCode == 0) {
208 cerr << BBTOOL << ": " << "keytree::addAction: keyCode for key: [" << key
209 << "] not found. can't add it. skipping.\n";
210 return;
211 }
212
213 keynode *tmp = new keynode;
214
215 tmp->action = new Action(action, _display, keyCode, mask, arg);
216 tmp->parent = _current;
217 _current->children.push_back(tmp);
218 }
219
advanceOnNewNode()220 void keytree::advanceOnNewNode()
221 {
222 keynode *tmp = new keynode;
223 tmp->action = NULL;
224 tmp->parent = _current;
225 _current->children.push_back(tmp);
226 _current = tmp;
227 }
228
retract()229 void keytree::retract()
230 {
231 if (_current != _head)
232 _current = _current->parent;
233 }
234
setCurrentNodeProps(Action::ActionType action,unsigned int mask,string key,string arg)235 void keytree::setCurrentNodeProps(Action::ActionType action, unsigned int mask,
236 string key, string arg)
237 {
238 if (_current->action)
239 delete _current->action;
240
241 KeySym sym = XStringToKeysym(key.c_str());
242 _current->action = new Action(action, _display,
243 XKeysymToKeycode(_display, sym),
244 mask, arg);
245 }
246
showTree(keynode * node)247 void keytree::showTree(keynode *node) {
248 if (!node)
249 return;
250
251 ChildList::iterator it, end = node->children.end();
252 for (it = node->children.begin(); it != end; ++it)
253 showTree(*it);
254
255 if (node->action) {
256 cout << BBTOOL << ": " << "showTree: [" << node->action->toString() << "]" << endl;
257 }
258
259 }
260
261