Initial revision
[bbkeys.git] / src / KeyClient.cpp
blob22bcaa6f132fb23842cd2c1c513d3110d2c62b0f
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // -- KeyClient.cpp --
3 // Copyright (c) 2001 - 2003 Jason 'vanRijn' Kasper <vR at movingparts dot net>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
23 // E_O_H_VR
25 #include "../config.h"
27 extern "C" {
29 #ifdef HAVE_SIGNAL_H
30 # include <signal.h>
31 #endif // HAVE_SIGNAL_H
33 #ifdef HAVE_SYS_SIGNAL_H
34 # include <sys/signal.h>
35 #endif // HAVE_SYS_SIGNAL_H
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 # include <sys/types.h>
40 #endif // HAVE_UNISTD_H
42 #ifdef HAVE_SYS_STAT_H
43 # include <sys/types.h>
44 # include <sys/stat.h>
45 #endif // HAVE_SYS_STAT_H
51 #include "version.h"
53 #include "KeyClient.h"
54 #include "LocalUtil.h"
55 #include "actions.hh"
57 #include <iostream>
58 #include <algorithm>
59 #include <vector>
61 //--------------------------------------------------------
62 // Constructor/Destructor
63 //--------------------------------------------------------
64 KeyClient::KeyClient (int argc, char **argv,
65 Config & config, std::string display):
66 bt::Application(BBTOOL, display.c_str()), _config(config)
69 // initialize our keyword map for the file tokenizer
70 initKeywords(_keywordMap);
72 // now connect to the X server
73 _display = XDisplay();
74 if (! _display ) {
75 cerr << "ERROR: Can't connect to X Server. Bummer! Exiting\n";
76 exit(2);
79 // check to see if we've been handed another config-file to use
80 _configFileName = bt::expandTilde(_config.getStringValue("config",
81 "~/.bbkeysrc") );
83 struct stat buf;
84 if (0 != stat(_configFileName.c_str(), &buf) ||!S_ISREG(buf.st_mode)) {
85 cerr << "ERROR: Couldn't load rc-file: [" << _configFileName
86 << "], falling back to default: [" << DEFAULTRC << "]\n";
87 _configFileName = DEFAULTRC;
91 _debug = _config.getBoolValue("debug", false);
93 // now, read in our configuration file and set both program settings
94 // and keybindings we're asked to handle
95 handleConfigFile();
97 // parse command options again to override what we read in from config file
98 parseOptions( argc, argv, _config );
100 _netclient = new Netclient(_display);
101 _active = _clients.end();
103 // here's our friendly little general-purpose keygrabber
104 _keyGrabber = new KeyGrabber(_display, numLockMask(), scrollLockMask() );
106 // now create a screen handler for each screen that exists
107 for (unsigned int i = 0; i < bt::Application::display().screenCount(); i++) {
108 ScreenHandler *screen = new ScreenHandler(this, i);
109 if (! screen->isManaged()) {
110 delete screen;
111 continue;
114 screen->initialize();
116 // add this screen to our collection
117 screenList.push_back(screen);
120 if (screenList.empty()) {
121 cerr << "KeyClient::KeyClient: no compatible window managers found, aborting.\n";
122 ::exit(3);
127 KeyClient::~KeyClient ()
130 // delete all screens
131 for_each(screenList.begin(), screenList.end(), bt::PointerAssassin());
133 if (_keybindings) delete _keybindings;
134 if (_netclient) delete _netclient;
135 if (_keyGrabber) delete _keyGrabber;
138 //--------------------------------------------------------
139 // reconfigure
140 //--------------------------------------------------------
141 void KeyClient::reconfigure ()
143 cout << "hey, goodie! I got a reconfigure request!!\n";
147 void KeyClient::handleConfigFile() {
149 FileTokenizer tokenizer(_keywordMap, _configFileName.c_str());
151 // clear off any of our keybindings we have and get them ready to go
152 if (_keybindings) {
153 _keybindings->unloadBindings();
154 } else {
155 _keybindings = new keytree(_display);
158 _keybindings->reset();
160 bool _doingConfig = false, _doingKeybindings = false;
162 TokenBlock *block = 0;
163 while ((block = tokenizer.next())) {
164 switch (block->tag) {
165 case ConfigOpts::begin: // um, we ignore these. =:)
166 break;
167 case ConfigOpts::end:
168 if (_doingConfig) {
169 _doingConfig = false;
171 break;
172 case ConfigOpts::config:
173 _doingConfig = true;
174 break;
175 case ConfigOpts::option:
176 if (_debug)
177 cout << "got a config option!, setting key: [" << block->name
178 << "] to value: [" << block->data << "]\n";
180 _config.setOption(block->name, block->data);
181 break;
182 case ConfigOpts::keybindings:
183 _doingKeybindings = true;
184 setKeybindings(tokenizer);
186 if (_debug)
187 _keybindings->showTree();
189 break;
190 default:
191 cerr << "unknown tag found in ConfigOpts block: ["
192 << block->tag << "], name: [" << block->name
193 << "], data: [" << block->data << "]\n";
194 break;
196 delete block;
199 if (_debug) {
200 cout << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n";
201 _config.showOptions();
206 void KeyClient::setKeybindings(FileTokenizer & tokenizer) {
208 // our modifier masks
209 struct {
210 const char *str;
211 unsigned int mask;
213 modifiers[] = {
214 { "mod1", Mod1Mask },
215 { "mod2", Mod2Mask },
216 { "mod3", Mod3Mask },
217 { "mod4", Mod4Mask },
218 { "mod5", Mod5Mask },
219 { "control", ControlMask },
220 { "shift", ShiftMask },
221 { "", 0 }
224 // this tells us how many levels deep we're nested. this will tell us when to return
225 int _iLevels =0;
227 TokenBlock *block = 0;
228 while ((block = tokenizer.next())) {
230 // if we hit an end, return
231 if (block->tag == ConfigOpts::end) {
232 --_iLevels;
233 _keybindings->retract();
234 // 0 is our root level, so if we're below that, we need to bail out
235 if (_iLevels <0) {
236 if (block) delete block;
237 return;
239 } else {
241 string fullKey = block->name;
243 if (fullKey.size() <=0) {
244 cerr << "ERROR: No key or modifier given. Ignoring this one, Jimmy.\n";
245 if (block) delete block;
246 continue;
248 // first, split our string containing our keys/modifiers and separate
249 // the keys from the modifiers
250 vector<string> results;
251 int matches = LocalUtil::splitString(fullKey, "-", results);
253 // here's our keyname. make sure it's a valid key
254 string _key = results[results.size() -1];
256 KeySym sym = XStringToKeysym(_key.c_str());
257 if (sym == 0) {
258 cerr << "ERROR: Invalid key (" << _key << ")! This may cause odd behavior.\n";
261 // now iterate through our modifiers and try to match the given string
262 // to a modifier mask. if we find it, xor it together with what we already have
263 unsigned int _mask=0;
265 for (int j=0; j < (matches -1); j++) {
267 bool found=false;
268 string mod = results[j];
270 for (int i = 0; modifiers[i].str != ""; ++i) {
271 if ( strcasecmp(modifiers[i].str, mod.c_str()) == 0 ) {
272 _mask |= modifiers[i].mask;
273 found = true;
274 break;
277 if (!found) cerr << "ERROR: Couldn't find modifier for mod: [" << mod << "]\n";
280 // now, if we have a chain, nest down a level and add the keybinding
281 if (block->tag == Action::chain) {
282 _iLevels++;
283 _keybindings->advanceOnNewNode();
284 _keybindings->setCurrentNodeProps(static_cast<Action::ActionType>(block->tag),
285 _mask, _key, block->data);
286 } else {
287 _keybindings->addAction(static_cast<Action::ActionType>(block->tag),
288 _mask, _key, block->data);
291 if (block) delete block;
295 void KeyClient::initKeywords(KeywordMap& keywords) {
297 // load our map with our keybinding labels
298 keywords.insert(KeywordMap::value_type("execute", Action::execute));
299 keywords.insert(KeywordMap::value_type("iconify", Action::iconify));
300 keywords.insert(KeywordMap::value_type("raise", Action::raise));
301 keywords.insert(KeywordMap::value_type("lower", Action::lower));
302 keywords.insert(KeywordMap::value_type("close", Action::close));
303 keywords.insert(KeywordMap::value_type("toggleshade", Action::toggleShade));
304 keywords.insert(KeywordMap::value_type("toggleomnipresent", Action::toggleOmnipresent));
305 keywords.insert(KeywordMap::value_type("movewindowup", Action::moveWindowUp));
306 keywords.insert(KeywordMap::value_type("movewindowdown", Action::moveWindowDown));
307 keywords.insert(KeywordMap::value_type("movewindowleft", Action::moveWindowLeft));
308 keywords.insert(KeywordMap::value_type("movewindowright", Action::moveWindowRight));
309 keywords.insert(KeywordMap::value_type("resizewindowwidth", Action::resizeWindowWidth));
310 keywords.insert(KeywordMap::value_type("resizewindowheight", Action::resizeWindowHeight));
312 keywords.insert(KeywordMap::value_type("togglemaximizefull", Action::toggleMaximizeFull));
313 keywords.insert(KeywordMap::value_type("togglemaximizevertical", Action::toggleMaximizeVertical));
314 keywords.insert(KeywordMap::value_type("togglemaximizehorizontal", Action::toggleMaximizeHorizontal));
316 keywords.insert(KeywordMap::value_type("sendtoworkspace", Action::sendToWorkspace));
318 keywords.insert(KeywordMap::value_type("nextwindow", Action::nextWindow));
319 keywords.insert(KeywordMap::value_type("prevwindow", Action::prevWindow));
320 keywords.insert(KeywordMap::value_type("nextwindowonallworkspaces", Action::nextWindowOnAllWorkspaces));
321 keywords.insert(KeywordMap::value_type("prevwindowonallworkspaces", Action::prevWindowOnAllWorkspaces));
323 keywords.insert(KeywordMap::value_type("changeworkspace", Action::changeWorkspace));
324 keywords.insert(KeywordMap::value_type("nextworkspace", Action::nextWorkspace));
325 keywords.insert(KeywordMap::value_type("prevworkspace", Action::prevWorkspace));
327 keywords.insert(KeywordMap::value_type("upworkspace", Action::upWorkspace));
328 keywords.insert(KeywordMap::value_type("downworkspace", Action::downWorkspace));
329 keywords.insert(KeywordMap::value_type("leftworkspace", Action::leftWorkspace));
330 keywords.insert(KeywordMap::value_type("rightworkspace", Action::rightWorkspace));
332 keywords.insert(KeywordMap::value_type("nextscreen", Action::nextScreen));
333 keywords.insert(KeywordMap::value_type("prevscreen", Action::prevScreen));
335 keywords.insert(KeywordMap::value_type("showrootmenu", Action::showRootMenu));
336 keywords.insert(KeywordMap::value_type("showworkspacemenu", Action::showWorkspaceMenu));
337 keywords.insert(KeywordMap::value_type("toggledecorations", Action::toggleDecorations));
339 keywords.insert(KeywordMap::value_type("togglegrabs", Action::toggleGrabs));
340 keywords.insert(KeywordMap::value_type("chain", Action::chain));
342 // the words associated with our high-level file labels
343 keywords.insert(KeywordMap::value_type("begin", ConfigOpts::begin));
344 keywords.insert(KeywordMap::value_type("end", ConfigOpts::end));
345 keywords.insert(KeywordMap::value_type("config", ConfigOpts::config));
346 keywords.insert(KeywordMap::value_type("keybindings", ConfigOpts::keybindings));
347 keywords.insert(KeywordMap::value_type("option", ConfigOpts::option));
351 bool KeyClient::handleSignal(int sig) {
352 switch (sig) {
353 case SIGHUP:
354 reconfigure();
355 break;
357 case SIGSEGV:
358 case SIGFPE:
359 case SIGINT:
360 case SIGTERM:
361 shutdown();
363 default:
364 return False;
367 return True;
370 void KeyClient::process_event(XEvent *e) {
371 // Send the event through the default EventHandlers.
372 bt::Application::process_event(e);
375 void KeyClient::cycleScreen(int current, bool forward) const {
376 unsigned int i;
377 for (i = 0; i < screenList.size(); ++i)
378 if (screenList[i]->screenNumber() == current) {
379 current = i;
380 break;
382 assert(i < screenList.size()); // current is for an unmanaged screen
384 int dest = current + (forward ? 1 : -1);
386 if (dest < 0) dest = (signed)screenList.size() - 1;
387 else if (dest >= (signed)screenList.size()) dest = 0;
389 const XWindow *target = screenList[dest]->lastActiveWindow();
390 if (target) target->focus();