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