1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
3 // Copyright (c) 2001 - 2003 Jason 'vanRijn' Kasper <vR at movingparts dot net>
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.
25 #include "../config.h"
31 #endif // HAVE_SIGNAL_H
33 #ifdef HAVE_SYS_SIGNAL_H
34 # include <sys/signal.h>
35 #endif // HAVE_SYS_SIGNAL_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
53 #include "KeyClient.h"
54 #include "LocalUtil.h"
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();
75 cerr
<< "ERROR: Can't connect to X Server. Bummer! Exiting\n";
79 // check to see if we've been handed another config-file to use
80 _configFileName
= bt::expandTilde(_config
.getStringValue("config",
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
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()) {
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";
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 //--------------------------------------------------------
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
153 _keybindings
->unloadBindings();
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. =:)
167 case ConfigOpts::end
:
169 _doingConfig
= false;
172 case ConfigOpts::config
:
175 case ConfigOpts::option
:
177 cout
<< "got a config option!, setting key: [" << block
->name
178 << "] to value: [" << block
->data
<< "]\n";
180 _config
.setOption(block
->name
, block
->data
);
182 case ConfigOpts::keybindings
:
183 _doingKeybindings
= true;
184 setKeybindings(tokenizer
);
187 _keybindings
->showTree();
191 cerr
<< "unknown tag found in ConfigOpts block: ["
192 << block
->tag
<< "], name: [" << block
->name
193 << "], data: [" << block
->data
<< "]\n";
200 cout
<< "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n";
201 _config
.showOptions();
206 void KeyClient::setKeybindings(FileTokenizer
& tokenizer
) {
208 // our modifier masks
214 { "mod1", Mod1Mask
},
215 { "mod2", Mod2Mask
},
216 { "mod3", Mod3Mask
},
217 { "mod4", Mod4Mask
},
218 { "mod5", Mod5Mask
},
219 { "control", ControlMask
},
220 { "shift", ShiftMask
},
224 // this tells us how many levels deep we're nested. this will tell us when to return
227 TokenBlock
*block
= 0;
228 while ((block
= tokenizer
.next())) {
230 // if we hit an end, return
231 if (block
->tag
== ConfigOpts::end
) {
233 _keybindings
->retract();
234 // 0 is our root level, so if we're below that, we need to bail out
236 if (block
) delete block
;
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
;
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());
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
++) {
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
;
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
) {
283 _keybindings
->advanceOnNewNode();
284 _keybindings
->setCurrentNodeProps(static_cast<Action::ActionType
>(block
->tag
),
285 _mask
, _key
, block
->data
);
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
) {
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 {
377 for (i
= 0; i
< screenList
.size(); ++i
)
378 if (screenList
[i
]->screenNumber() == current
) {
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();