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
47 #include <sys/types.h>
54 #include "KeyClient.h"
55 #include "LocalUtil.h"
62 //--------------------------------------------------------
63 // Constructor/Destructor
64 //--------------------------------------------------------
65 KeyClient::KeyClient (int argc
, char **argv
,
66 Config
& config
, std::string display
):
67 bt::Application(BBTOOL
, display
.c_str(), true), _config(config
)
70 // save off what we're constructed with for reconfiguring later...
74 // initialize our keyword map for the file tokenizer
75 initKeywords(_keywordMap
);
77 // now connect to the X server
78 _display
= XDisplay();
80 cerr
<< BBTOOL
<< ": " << "KeyClient: ERROR: Can't connect to X Server. Bummer! Exiting\n";
84 // check to see if we've been handed another config-file to use
85 _configFileName
= bt::expandTilde(_config
.getStringValue("config",
89 if (0 != stat(_configFileName
.c_str(), &buf
) ||!S_ISREG(buf
.st_mode
)) {
90 cerr
<< BBTOOL
<< ": " << "KeyClient: ERROR: Couldn't load rc-file: [" << _configFileName
91 << "], falling back to default: [" << DEFAULTRC
<< "]\n";
92 _configFileName
= DEFAULTRC
;
94 _last_time_config_changed
= buf
.st_mtime
;
97 _debug
= _config
.getBoolValue("debug", false);
99 // here's our friendly little general-purpose keygrabber
100 _keyGrabber
= new KeyGrabber(_display
, numLockMask(), scrollLockMask() );
102 _netclient
= new Netclient(_display
);
103 _active
= _clients
.end();
108 KeyClient::~KeyClient ()
111 // delete all screens
112 for_each(screenList
.begin(), screenList
.end(), bt::PointerAssassin());
114 if (_keybindings
) delete _keybindings
;
115 if (_netclient
) delete _netclient
;
116 if (_keyGrabber
) delete _keyGrabber
;
119 void KeyClient::initialize() {
121 // now, read in our configuration file and set both program settings
122 // and keybindings we're asked to handle
125 // parse command options again to override what we read in from config file
126 parseOptions( _argc
, _argv
, _config
);
129 // now create a screen handler for each screen that exists
130 for (unsigned int i
= 0; i
< bt::Application::display().screenCount(); i
++) {
131 ScreenHandler
*screen
= new ScreenHandler(this, i
);
132 if (! screen
->isManaged()) {
137 screen
->initialize();
139 // add this screen to our collection
140 screenList
.push_back(screen
);
143 if (screenList
.empty()) {
144 cerr
<< BBTOOL
<< ": " << "KeyClient: initialize: no compatible window managers found, aborting.\n";
148 _autoConfigCheckTimeout
= (_config
.getNumberValue("autoConfigCheckTimeout", 10)) * 1000;
149 _autoConfig
= _config
.getBoolValue("autoConfig", true);
151 if (!config_check_timer
) {
152 config_check_timer
= new bt::Timer(this, this);
154 config_check_timer
->setTimeout(_autoConfigCheckTimeout
);
155 config_check_timer
->recurring(True
);
156 config_check_timer
->start();
161 //--------------------------------------------------------
163 //--------------------------------------------------------
164 void KeyClient::reconfigure ()
167 std::cout
<< BBTOOL
<< ": " << "KeyClient: reconfigure: hey, goodie! I got a reconfigure request!!\n";
170 // delete all screens
171 for_each(screenList
.begin(), screenList
.end(), bt::PointerAssassin());
174 // initialize and/or clear our config
178 if (config_check_timer
) {
179 config_check_timer
->halt();
187 void KeyClient::handleConfigFile() {
189 FileTokenizer
tokenizer(_keywordMap
, _configFileName
.c_str());
191 // clear off any of our keybindings we have and get them ready to go
193 _keybindings
->unloadBindings();
195 _keybindings
= new keytree(_display
);
198 _keybindings
->reset();
200 bool _doingConfig
= false, _doingKeybindings
= false;
202 TokenBlock
*block
= 0;
203 while ((block
= tokenizer
.next())) {
204 switch (block
->tag
) {
205 case ConfigOpts::begin
: // um, we ignore these. =:)
207 case ConfigOpts::end
:
209 _doingConfig
= false;
212 case ConfigOpts::config
:
215 case ConfigOpts::option
:
217 cout
<< BBTOOL
<< ": " << "got a config option!, setting key: [" << block
->name
218 << "] to value: [" << block
->data
<< "]\n";
220 _config
.setOption(block
->name
, block
->data
);
222 case ConfigOpts::keybindings
:
223 _doingKeybindings
= true;
224 setKeybindings(tokenizer
);
227 _keybindings
->showTree();
231 cerr
<< BBTOOL
<< ": " << "unknown tag found in ConfigOpts block: ["
232 << block
->tag
<< "], name: [" << block
->name
233 << "], data: [" << block
->data
<< "]\n";
240 cout
<< "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n";
241 _config
.showOptions();
246 void KeyClient::setKeybindings(FileTokenizer
& tokenizer
) {
248 // our modifier masks
254 { "mod1", Mod1Mask
},
255 { "mod2", Mod2Mask
},
256 { "mod3", Mod3Mask
},
257 { "mod4", Mod4Mask
},
258 { "mod5", Mod5Mask
},
259 { "control", ControlMask
},
260 { "shift", ShiftMask
},
264 // this tells us how many levels deep we're nested. this will tell us when to return
267 TokenBlock
*block
= 0;
268 while ((block
= tokenizer
.next())) {
270 // if we hit an end, return
271 if (block
->tag
== ConfigOpts::end
) {
273 _keybindings
->retract();
274 // 0 is our root level, so if we're below that, we need to bail out
276 if (block
) delete block
;
281 string fullKey
= block
->name
;
283 if (fullKey
.size() <=0) {
284 cerr
<< BBTOOL
<< ": " << "ERROR: No key or modifier given. Ignoring this one, Jimmy.\n";
285 if (block
) delete block
;
288 // first, split our string containing our keys/modifiers and separate
289 // the keys from the modifiers
290 vector
<string
> results
;
291 int matches
= LocalUtil::splitString(fullKey
, "-", results
);
293 // here's our keyname. make sure it's a valid key
294 string _key
= results
[results
.size() -1];
296 KeySym sym
= XStringToKeysym(_key
.c_str());
298 cerr
<< BBTOOL
<< ": " << "ERROR: Invalid key (" << _key
<< ")! This may cause odd behavior.\n";
301 // now iterate through our modifiers and try to match the given string
302 // to a modifier mask. if we find it, xor it together with what we already have
303 unsigned int _mask
=0;
305 for (int j
=0; j
< (matches
-1); j
++) {
308 string mod
= results
[j
];
310 for (int i
= 0; modifiers
[i
].str
!= ""; ++i
) {
311 if ( strcasecmp(modifiers
[i
].str
, mod
.c_str()) == 0 ) {
312 _mask
|= modifiers
[i
].mask
;
317 if (!found
) cerr
<< BBTOOL
<< ": " << "ERROR: Couldn't find modifier for mod: [" << mod
<< "]\n";
320 // now, if we have a chain, nest down a level and add the keybinding
321 if (block
->tag
== Action::chain
) {
323 _keybindings
->advanceOnNewNode();
324 _keybindings
->setCurrentNodeProps(static_cast<Action::ActionType
>(block
->tag
),
325 _mask
, _key
, block
->data
);
327 _keybindings
->addAction(static_cast<Action::ActionType
>(block
->tag
),
328 _mask
, _key
, block
->data
);
331 if (block
) delete block
;
335 void KeyClient::initKeywords(KeywordMap
& keywords
) {
337 // load our map with our keybinding labels
338 keywords
.insert(KeywordMap::value_type("execute", Action::execute
));
339 keywords
.insert(KeywordMap::value_type("iconify", Action::iconify
));
340 keywords
.insert(KeywordMap::value_type("raise", Action::raise
));
341 keywords
.insert(KeywordMap::value_type("lower", Action::lower
));
342 keywords
.insert(KeywordMap::value_type("close", Action::close
));
343 keywords
.insert(KeywordMap::value_type("toggleshade", Action::toggleShade
));
344 keywords
.insert(KeywordMap::value_type("toggleomnipresent", Action::toggleOmnipresent
));
345 keywords
.insert(KeywordMap::value_type("movewindowup", Action::moveWindowUp
));
346 keywords
.insert(KeywordMap::value_type("movewindowdown", Action::moveWindowDown
));
347 keywords
.insert(KeywordMap::value_type("movewindowleft", Action::moveWindowLeft
));
348 keywords
.insert(KeywordMap::value_type("movewindowright", Action::moveWindowRight
));
349 keywords
.insert(KeywordMap::value_type("resizewindowwidth", Action::resizeWindowWidth
));
350 keywords
.insert(KeywordMap::value_type("resizewindowheight", Action::resizeWindowHeight
));
352 keywords
.insert(KeywordMap::value_type("togglemaximizefull", Action::toggleMaximizeFull
));
353 keywords
.insert(KeywordMap::value_type("togglemaximizevertical", Action::toggleMaximizeVertical
));
354 keywords
.insert(KeywordMap::value_type("togglemaximizehorizontal", Action::toggleMaximizeHorizontal
));
356 keywords
.insert(KeywordMap::value_type("sendtoworkspace", Action::sendToWorkspace
));
358 keywords
.insert(KeywordMap::value_type("nextwindow", Action::nextWindow
));
359 keywords
.insert(KeywordMap::value_type("prevwindow", Action::prevWindow
));
360 keywords
.insert(KeywordMap::value_type("nextwindowonallworkspaces", Action::nextWindowOnAllWorkspaces
));
361 keywords
.insert(KeywordMap::value_type("prevwindowonallworkspaces", Action::prevWindowOnAllWorkspaces
));
363 keywords
.insert(KeywordMap::value_type("changeworkspace", Action::changeWorkspace
));
364 keywords
.insert(KeywordMap::value_type("nextworkspace", Action::nextWorkspace
));
365 keywords
.insert(KeywordMap::value_type("prevworkspace", Action::prevWorkspace
));
367 keywords
.insert(KeywordMap::value_type("upworkspace", Action::upWorkspace
));
368 keywords
.insert(KeywordMap::value_type("downworkspace", Action::downWorkspace
));
369 keywords
.insert(KeywordMap::value_type("leftworkspace", Action::leftWorkspace
));
370 keywords
.insert(KeywordMap::value_type("rightworkspace", Action::rightWorkspace
));
372 keywords
.insert(KeywordMap::value_type("nextscreen", Action::nextScreen
));
373 keywords
.insert(KeywordMap::value_type("prevscreen", Action::prevScreen
));
375 keywords
.insert(KeywordMap::value_type("showrootmenu", Action::showRootMenu
));
376 keywords
.insert(KeywordMap::value_type("showworkspacemenu", Action::showWorkspaceMenu
));
377 keywords
.insert(KeywordMap::value_type("toggledecorations", Action::toggleDecorations
));
379 keywords
.insert(KeywordMap::value_type("togglegrabs", Action::toggleGrabs
));
380 keywords
.insert(KeywordMap::value_type("chain", Action::chain
));
382 // the words associated with our high-level file labels
383 keywords
.insert(KeywordMap::value_type("begin", ConfigOpts::begin
));
384 keywords
.insert(KeywordMap::value_type("end", ConfigOpts::end
));
385 keywords
.insert(KeywordMap::value_type("config", ConfigOpts::config
));
386 keywords
.insert(KeywordMap::value_type("keybindings", ConfigOpts::keybindings
));
387 keywords
.insert(KeywordMap::value_type("option", ConfigOpts::option
));
391 bool KeyClient::process_signal(int sig
) {
399 return (bt::Application::process_signal(sig
) );
404 void KeyClient::process_event(XEvent
*e
) {
405 // Send the event through the default EventHandlers.
406 bt::Application::process_event(e
);
409 void KeyClient::cycleScreen(int current
, bool forward
) const {
411 for (i
= 0; i
< screenList
.size(); ++i
)
412 if (screenList
[i
]->getScreenNumber() == current
) {
416 assert(i
< screenList
.size()); // current is for an unmanaged screen
418 int dest
= current
+ (forward
? 1 : -1);
420 if (dest
< 0) dest
= (signed)screenList
.size() - 1;
421 else if (dest
>= (signed)screenList
.size()) dest
= 0;
423 const XWindow
*target
= screenList
[dest
]->lastActiveWindow();
424 if (target
) target
->focus();
427 void KeyClient::timeout(bt::Timer
*timer
) {
429 std::cout
<< BBTOOL
<< ": " << "KeyClient: timeout: got timeout from timer...." << std::endl
;
430 if (timer
== config_check_timer
) {
435 void KeyClient::checkConfigFile() {
437 struct stat file_status
;
439 if (stat(_configFileName
.c_str(), &file_status
) != 0) {
441 std::cerr
<< BBTOOL
<< ": " << "Could not open config file: [" << _configFileName
<< "]";
442 } else if (file_status
.st_mtime
!= _last_time_config_changed
) {
444 std::cout
<< BBTOOL
<< ": " << "KeyClient: checkConfigFile: config file time changed..." << std::endl
;
445 _last_time_config_changed
= file_status
.st_mtime
;