ede/pekwm/KeyGrabber.cc

391 lines
11 KiB
C++

//
// KeyGrabber.cc for pekwm
// Copyright © 2003-2009 Claes Nästén <me@pekdon.net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include "KeyGrabber.hh"
#include "Config.hh"
#include "PScreen.hh"
#include "CfgParser.hh"
#include "Util.hh"
#include <iostream>
extern "C" {
#include <X11/Xutil.h>
#include <X11/keysym.h>
}
using std::cerr;
using std::endl;
using std::string;
using std::list;
using std::vector;
using std::find;
KeyGrabber* KeyGrabber::_instance = 0;
//! @brief Constructor for Chain class
KeyGrabber::Chain::Chain(uint mod, uint key) :
_mod(mod), _key(key)
{
}
//! @brief Destructor for Chain class
KeyGrabber::Chain::~Chain(void)
{
unload();
}
//! @brief Unloads all chains and keys
void
KeyGrabber::Chain::unload(void)
{
list<KeyGrabber::Chain*>::iterator it(_chains.begin());
for (; it != _chains.end(); ++it) {
delete *it;
}
_chains.clear();
_keys.clear();
}
//! @brief Searches the _chains list for an action
KeyGrabber::Chain*
KeyGrabber::Chain::findChain(XKeyEvent *ev)
{
list<KeyGrabber::Chain*>::iterator it(_chains.begin());
for (; it != _chains.end(); ++it) {
if ((((*it)->getMod() == MOD_ANY) || ((*it)->getMod() == ev->state)) &&
(((*it)->getKey() == 0) || ((*it)->getKey() == ev->keycode))) {
return *it;
}
}
return 0;
}
//! @brief Searches the _keys list for an action
ActionEvent*
KeyGrabber::Chain::findAction(XKeyEvent *ev)
{
list<ActionEvent>::iterator it(_keys.begin());
for (; it != _keys.end(); ++it) {
if (((it->mod == MOD_ANY) || (it->mod == ev->state)) &&
((it->sym == 0) || (it->sym == ev->keycode))) {
return &*it;
}
}
return 0;
}
//! @brief KeyGrabber constructor
KeyGrabber::KeyGrabber(PScreen *scr)
: _scr(scr), _menu_chain(0, 0),
_global_chain(0, 0), _moveresize_chain(0, 0),
_input_dialog_chain(0, 0)
{
#ifdef DEBUG
if (_instance) {
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "KeyGrabber(" << this << ")::KeyGrabber(" << scr << ")" << endl
<< " *** _instance already set: " << _instance << endl;
}
#endif // DEBUG
_instance = this;
_num_lock = _scr->getNumLock();
_scroll_lock = _scr->getScrollLock();
}
//! @brief Destructor for KeyGrabber class
KeyGrabber::~KeyGrabber(void)
{
_instance = 0;
}
//! @brief Parses the "KeyFile" and inserts into _global_keys.
//! If _global_keys holds any keygrabs they will be flushed before
//! reloading the new keybindings.
bool
KeyGrabber::load(const std::string &file, bool force)
{
if (! force && ! Util::requireReload(_cfg_state, file)) {
return false;
}
CfgParser key_cfg;
if (! key_cfg.parse(file, CfgParserSource::SOURCE_FILE)) {
_cfg_state.clear();
if (! key_cfg.parse(SYSCONFDIR "/keys", CfgParserSource::SOURCE_FILE, true)) {
cerr << __FILE__ << "@" << __LINE__ << "Error: no keyfile at " << file
<< " or " << SYSCONFDIR "/keys" << endl;
return false;
}
}
if (key_cfg.is_dynamic_content()) {
_cfg_state.clear();
} else {
_cfg_state = key_cfg.get_file_list();
}
CfgParser::Entry *section;
section = key_cfg.get_entry_root()->find_section("GLOBAL");
if (section) {
_global_chain.unload();
parseGlobalChain(section, &_global_chain);
}
section = key_cfg.get_entry_root ()->find_section("MOVERESIZE");
if (section) {
_moveresize_chain.unload ();
parseMoveResizeChain (section, &_moveresize_chain);
}
// Previously there was only a CmdDialog section, however the text
// handling parts have been moved into InputDialog but to keep
// compatibility this check exists.
section = key_cfg.get_entry_root()->find_section("INPUTDIALOG");
if (! section) {
section = key_cfg.get_entry_root()->find_section("CMDDIALOG");
}
if (section) {
_input_dialog_chain.unload ();
parseInputDialogChain (section, &_input_dialog_chain);
}
section = key_cfg.get_entry_root ()->find_section("MENU");
if (section) {
_menu_chain.unload();
parseMenuChain(section, &_menu_chain);
}
return true;
}
//! @brief Parses chain, getting actions as plain ActionEvents
void
KeyGrabber::parseGlobalChain(CfgParser::Entry *section, KeyGrabber::Chain *chain)
{
ActionEvent ae;
uint key, mod;
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if ((*it)->get_section() && *(*it) == "CHAIN") {
// Figure out mod and key, create a new chain.
if (Config::instance()->parseKey((*it)->get_value(), mod, key)) {
KeyGrabber::Chain *sub_chain = new KeyGrabber::Chain(mod, key);
parseGlobalChain((*it)->get_section(), sub_chain);
chain->addChain(sub_chain);
}
} else if (Config::instance()->parseActionEvent((*it), ae, KEYGRABBER_OK, false)) {
chain->addAction(ae);
}
}
}
//! @brief Parses chain, getting actions as MoveResizeEvents
void
KeyGrabber::parseMoveResizeChain(CfgParser::Entry *section, KeyGrabber::Chain *chain)
{
ActionEvent ae;
uint key, mod;
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if ((*it)->get_section() && *(*it) == "CHAIN") {
// Figure out mod and key, create a new chain.
if (Config::instance()->parseKey((*it)->get_value(), mod, key)) {
KeyGrabber::Chain *sub_chain = new KeyGrabber::Chain(mod, key);
parseMoveResizeChain((*it)->get_section(), sub_chain);
chain->addChain(sub_chain);
}
} else if (Config::instance()->parseMoveResizeEvent((*it), ae)) {
chain->addAction(ae);
}
}
}
//! @brief Parses chain, getting actions as InputDialog Events
void
KeyGrabber::parseInputDialogChain(CfgParser::Entry *section, KeyGrabber::Chain *chain)
{
ActionEvent ae;
uint key, mod;
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if ((*it)->get_section() && *(*it) == "CHAIN") {
// Figure out mod and key, create a new chain.
if (Config::instance()->parseKey((*it)->get_value(), mod, key)) {
KeyGrabber::Chain *sub_chain = new KeyGrabber::Chain(mod, key);
parseInputDialogChain((*it)->get_section(), sub_chain);
chain->addChain(sub_chain);
}
} else if (Config::instance()->parseInputDialogEvent((*it), ae)) {
chain->addAction(ae);
}
}
}
//! @brief Parses chain, getting actions as MenuEvents
void
KeyGrabber::parseMenuChain(CfgParser::Entry *section, KeyGrabber::Chain *chain)
{
ActionEvent ae;
uint key, mod;
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if ((*it)->get_section() && *(*it) == "CHAIN") {
// Figure out mod and key, create a new chain.
if (Config::instance()->parseKey((*it)->get_value(), mod, key)) {
KeyGrabber::Chain *sub_chain = new KeyGrabber::Chain (mod, key);
parseGlobalChain((*it)->get_section(), sub_chain);
chain->addChain(sub_chain);
}
} else if (Config::instance()->parseMenuEvent((*it), ae)) {
chain->addAction(ae);
}
}
}
//! @brief Grabs all the keybindings in _keybindings on the Window win.
//! @param win Window to grab the keys on.
void
KeyGrabber::grabKeys(Window win)
{
const list<KeyGrabber::Chain*> chains = _global_chain.getChainList();
list<KeyGrabber::Chain*>::const_iterator c_it = chains.begin();
for (; c_it != chains.end(); ++c_it)
grabKey(win, (*c_it)->getMod(), (*c_it)->getKey());
const list<ActionEvent> keys = _global_chain.getKeyList();
list<ActionEvent>::const_iterator k_it = keys.begin();
for (; k_it != keys.end(); ++k_it)
grabKey(win, k_it->mod, k_it->sym);
}
//! @brief Grabs a key with state on Window win with "all possible" modifiers.
//! @param win Window to grab the key on.
//! @param mod Modifier state to grab.
//! @param key Key state to grab.
void
KeyGrabber::grabKey(Window win, uint mod, uint key)
{
Display *dpy = _scr->getDpy(); // convenience
XGrabKey(dpy, key, mod, win, true, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, key, mod|LockMask, win, true, GrabModeAsync, GrabModeAsync);
if (_num_lock) {
XGrabKey(dpy, key, mod|_num_lock,
win, true, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, key, mod|_num_lock|LockMask,
win, true, GrabModeAsync, GrabModeAsync);
}
if (_scroll_lock) {
XGrabKey(dpy, key, mod|_scroll_lock,
win, true, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, key, mod|_scroll_lock|LockMask,
win, true, GrabModeAsync, GrabModeAsync);
}
if (_num_lock && _scroll_lock) {
XGrabKey(dpy, key, mod|_num_lock|_scroll_lock,
win, true, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, key, mod|_num_lock|_scroll_lock|LockMask,
win, true, GrabModeAsync, GrabModeAsync);
}
}
//! @brief Ungrabs all the keybindings on the Window win.
//! @param win Window to ungrab keys on.
void
KeyGrabber::ungrabKeys(Window win)
{
XUngrabKey(_scr->getDpy(), AnyKey, AnyModifier, win);
}
//! @brief Tries to match the XKeyEvent to an usefull action and return it
//! @param ev XKeyEvent to match.
ActionEvent*
KeyGrabber::findAction(XKeyEvent *ev, KeyGrabber::Chain *chain)
{
if (! ev) {
return 0;
}
PScreen::stripStateModifiers(&ev->state);
ActionEvent *action = 0;
KeyGrabber::Chain *sub_chain = _global_chain.findChain(ev);
if (sub_chain && _scr->grabKeyboard(_scr->getRoot())) {
XEvent c_ev;
KeyGrabber::Chain *last_chain;
bool exit = false;
while (! exit) {
XMaskEvent(_scr->getDpy(), KeyPressMask, &c_ev);
PScreen::stripStateModifiers(&c_ev.xkey.state);
if (IsModifierKey(XKeycodeToKeysym(_scr->getDpy(),
c_ev.xkey.keycode, 0))) {
// do nothing
} else if ((last_chain = sub_chain->findChain(&c_ev.xkey))) {
sub_chain = last_chain;
} else {
action = sub_chain->findAction(&c_ev.xkey);
exit = true;
}
}
_scr->ungrabKeyboard();
} else {
action = chain->findAction(ev);
}
return action;
}
//! @brief Finds action matching ev, continues chain if needed
ActionEvent*
KeyGrabber::findAction(XKeyEvent *ev, PWinObj::Type type)
{
ActionEvent *ae = 0;
if (type == PWinObj::WO_MENU) {
ae = findAction(ev, &_menu_chain);
}
if (type == PWinObj::WO_CMD_DIALOG || type == PWinObj::WO_SEARCH_DIALOG) {
ae = findAction(ev, &_input_dialog_chain);
}
// no action the menu list, try the global list
if (! ae) {
ae = findAction(ev, &_global_chain);
}
return ae;
}
//! @brief Searches the _moveresize_chain for actions.
ActionEvent*
KeyGrabber::findMoveResizeAction(XKeyEvent *ev)
{
return findAction(ev, &_moveresize_chain);
}