Fix for #1160244, #1099704, #1094107: if the xkb-extension is enabled and the user switches between his/her keyboardlayouts fluxbox's keybhandling doesn't work well anymore because xkeyevent.state contains also xkb-related flags and thus we have to handle that with caution. KeyUtils now contain 'isolateModifierMask()' to really work only on the modifiers. why not as part of cleanMods() ? because the XLookupString return false results, eg TextBox's would only print chars from the first keyboardlayout.
@@ -1,5 +1,14 @@
(Format: Year/Month/Day) Changes for 0.9.13 +*05/05/05: + * Fix #1160244, #1099704, #1094107 Mutiple keyboard layout (Mathias + thanx + to Vadim) + Switching to a secondary/third keyboard layout via the xkb-extensions + leads to broken NextWindow/PrevWindow and broken fbrun/CommandDialog: + - be aware of what xkb is doing to the event.state + - minor cleaning + CommandDialog.cc WorkspaceCmd.cc Keys.cc FbTk/KeyUtil.cc/hh + FbTk/TextBox.cc fluxbox.cc FbRun.cc *05/05/03: * Fix drawing of no-title menus, plus updating of int menu items (Simon) IntResMenuItem.hh/cc MenuItem.hh Menu.cc Screen.cc ToggleMenu.hh
@@ -33,6 +33,7 @@
#include "FbTk/ImageControl.hh" #include "FbTk/EventManager.hh" #include "FbTk/StringUtil.hh" +#include "FbTk/KeyUtil.hh" #include "FbTk/App.hh" #include <X11/keysym.h>@@ -43,11 +44,11 @@ #include <memory>
#include <stdexcept> using namespace std; -CommandDialog::CommandDialog(BScreen &screen, +CommandDialog::CommandDialog(BScreen &screen, const std::string &title, const std::string precommand) : FbTk::FbWindow(screen.rootWindow().screenNumber(), 0, 0, 200, 1, ExposureMask), m_textbox(*this, screen.winFrameTheme().font(), ""), - m_label(*this, screen.winFrameTheme().font(), title), + m_label(*this, screen.winFrameTheme().font(), title), m_gc(m_textbox), m_screen(screen), m_move_x(0),@@ -121,12 +122,13 @@ move(new_x, new_y);
} void CommandDialog::keyPressEvent(XKeyEvent &event) { - if (event.state) + unsigned int state = FbTk::KeyUtil::instance().isolateModifierMask(event.state); + if (state) return; KeySym ks; - char keychar[1]; - XLookupString(&event, keychar, 1, &ks, 0); + char keychar; + XLookupString(&event, &keychar, 1, &ks, 0); if (ks == XK_Return) { hide(); // hide and return focus to a FluxboxWindow@@ -135,7 +137,7 @@ std::auto_ptr<FbTk::Command> cmd(CommandParser::instance().
parseLine(m_precommand + m_textbox.text())); if (cmd.get()) cmd->execute(); - // post execute + // post execute if (*m_postcommand != 0) m_postcommand->execute();@@ -152,7 +154,7 @@ void CommandDialog::tabComplete() {
try { string::size_type first = m_textbox.text().find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" - "0123456789", + "0123456789", m_textbox.cursorPosition()); if (first == string::npos) first = 0;@@ -173,7 +175,7 @@ }
if (!matches.empty()) { // sort and apply larges match - std::sort(matches.begin(), matches.end(), less<string>()); + std::sort(matches.begin(), matches.end(), less<string>()); m_textbox.setText(m_textbox.text() + matches[0].substr(prefix.size())); } else XBell(FbTk::App::instance()->display(), 0);@@ -204,7 +206,7 @@
// setup label // we listen to motion notify too - m_label.setEventMask(m_label.eventMask() | ButtonPressMask | ButtonMotionMask); + m_label.setEventMask(m_label.eventMask() | ButtonPressMask | ButtonMotionMask); m_label.setGC(m_screen.winFrameTheme().labelTextFocusGC()); m_label.show();@@ -234,7 +236,7 @@
void CommandDialog::updateSizes() { m_label.moveResize(0, 0, width(), m_textbox.font().height() + 2); - + m_textbox.moveResize(2, m_label.height(), - width() - 4, m_textbox.font().height() + 2); + width() - 4, m_textbox.font().height() + 2); }
@@ -26,6 +26,30 @@ #include "App.hh"
#include <string> +namespace { + +struct t_modlist{ + char *str; + unsigned int mask; + bool operator == (const char *modstr) const { + return (strcasecmp(str, modstr) == 0 && mask !=0); + } +}; + +const struct t_modlist modlist[] = { + {"SHIFT", ShiftMask}, + {"LOCK", LockMask}, + {"CONTROL", ControlMask}, + {"MOD1", Mod1Mask}, + {"MOD2", Mod2Mask}, + {"MOD3", Mod3Mask}, + {"MOD4", Mod4Mask}, + {"MOD5", Mod5Mask}, + {0, 0} +}; + +}; + namespace FbTk { std::auto_ptr<KeyUtil> KeyUtil::s_keyutil;@@ -57,19 +81,7 @@ if (m_modmap)
XFreeModifiermap(m_modmap); m_modmap = XGetModifierMapping(App::instance()->display()); - // mask to use for modifier - static const int mods[] = { - ShiftMask, - LockMask, - ControlMask, - Mod1Mask, - Mod2Mask, - Mod3Mask, - Mod4Mask, - Mod5Mask, - 0 - }; - + // find modifiers and set them for (int i=0, realkey=0; i<8; ++i) { for (int key=0; key<m_modmap->max_keypermod; ++key, ++realkey) {@@ -77,17 +89,18 @@
if (m_modmap->modifiermap[realkey] == 0) continue; - KeySym ks = XKeycodeToKeysym(App::instance()->display(), m_modmap->modifiermap[realkey], 0); + KeySym ks = XKeycodeToKeysym(App::instance()->display(), + m_modmap->modifiermap[realkey], 0); switch (ks) { case XK_Caps_Lock: - m_capslock = mods[i]; + m_capslock = modlist[i].mask; break; case XK_Scroll_Lock: - m_scrolllock = mods[i]; + m_scrolllock = modlist[i].mask; break; case XK_Num_Lock: - m_numlock = mods[i]; + m_numlock = modlist[i].mask; break; } }@@ -164,31 +177,13 @@ XStringToKeysym(keystr));
} -struct t_modlist{ - char *str; - unsigned int mask; - bool operator == (const char *modstr) const { - return (strcasecmp(str, modstr) == 0 && mask !=0); - } -}; - /** @return the modifier for the modstr else zero on failure. */ unsigned int KeyUtil::getModifier(const char *modstr) { if (!modstr) return 0; - const static struct t_modlist modlist[] = { - {"SHIFT", ShiftMask}, - {"CONTROL", ControlMask}, - {"MOD1", Mod1Mask}, - {"MOD2", Mod2Mask}, - {"MOD3", Mod3Mask}, - {"MOD4", Mod4Mask}, - {"MOD5", Mod5Mask}, - {0, 0} - }; - + // find mod mask string for (unsigned int i=0; modlist[i].str !=0; i++) { if (modlist[i] == modstr)@@ -210,7 +205,8 @@
unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { XModifierKeymap *modmap = instance().m_modmap; - if (!modmap) return 0; + if (!modmap) + return 0; // search through modmap for this keycode for (int mod=0; mod < 8; mod++) {@@ -218,7 +214,7 @@ for (int key=0; key < modmap->max_keypermod; ++key) {
// modifiermap is an array with 8 sets of keycodes // each max_keypermod long, but in a linear array. if (modmap->modifiermap[modmap->max_keypermod*mod + key] == keycode) { - return (1<<mod); + return modlist[mod].mask; } } }
@@ -70,6 +70,14 @@ //remove numlock, capslock and scrolllock
return mods & ~(capslock() | numlock() ); } + /** + strip away everything which is actually not a modifier + eg, xkb-keyboardgroups are encoded as bit 13 and 14 + */ + unsigned int isolateModifierMask(unsigned int mods) { + return mods & (ShiftMask|LockMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask); + } + /** Convert the specified key into appropriate modifier mask @return corresponding modifier mask@@ -78,6 +86,7 @@ static unsigned int keycodeToModmask(unsigned int keycode);
int numlock() const { return Mod2Mask; } //m_numlock; } int capslock() const { return LockMask; } //m_capslock; } int scrolllock() const { return Mod5Mask; } //m_scrolllock; } + private: void loadModmap();
@@ -209,7 +209,7 @@ }
} void TextBox::keyPressEvent(XKeyEvent &event) { - // strip numlock and scrolllock mask + event.state = KeyUtil::instance().cleanMods(event.state); KeySym ks;@@ -218,8 +218,8 @@ XLookupString(&event, keychar, 1, &ks, 0);
// a modifier key by itself doesn't do anything if (IsModifierKey(ks)) return; - if (event.state) { // handle keybindings with state - if (event.state == ControlMask) { + if (FbTk::KeyUtil::instance().isolateModifierMask(event.state)) { // handle keybindings with state + if ((event.state & ControlMask) == ControlMask) { switch (ks) { case XK_b:@@ -248,7 +248,8 @@ m_cursor_pos = 0;
m_end_pos = 0; break; } - } else if (event.state == ShiftMask || event.state == 0x80) { // shif and altgr + } else if ((event.state & ShiftMask)== ShiftMask || + (event.state & 0x80) == 0x80) { // shif and altgr if (isprint(keychar[0])) { std::string val; val += keychar[0];
@@ -27,7 +27,6 @@
#include "FbTk/StringUtil.hh" #include "FbTk/App.hh" #include "FbTk/Command.hh" -#include "FbTk/KeyUtil.hh" #include "CommandParser.hh" #include "FbTk/I18n.hh"@@ -81,6 +80,8 @@
#include <X11/Xlib.h> #include <X11/Xproto.h> #include <X11/keysym.h> +#include <X11/Xutil.h> +#include <X11/XKBlib.h> #include <iostream> #include <fstream>@@ -102,7 +103,7 @@ if (filename != 0)
load(filename); } -Keys::~Keys() { +Keys::~Keys() { FbTk::KeyUtil::ungrabKeys(); deleteTree();@@ -112,12 +113,12 @@ /// Destroys the keytree
void Keys::deleteTree() { while (!m_keylist.empty()) { if (m_keylist.back()) - delete m_keylist.back(); + delete m_keylist.back(); m_keylist.pop_back(); } } -/** +/** Load and grab keys TODO: error checking @return true on success else false@@ -125,7 +126,7 @@ */
bool Keys::load(const char *filename) { if (!filename) return false; - + //ungrab all keys FbTk::KeyUtil::ungrabKeys();@@ -133,7 +134,7 @@ //free memory of previous grabs
deleteTree(); FbTk::App::instance()->sync(false); - + //open the file ifstream infile(filename); if (!infile)@@ -158,8 +159,8 @@ }
bool Keys::save(const char *filename) const { //!! - //!! TODO: fix keybinding saving - //!! (we probably need to save key actions + //!! TODO: fix keybinding saving + //!! (we probably need to save key actions //!! as strings instead of creating new Commands) // open file for writing@@ -178,16 +179,16 @@
// must have at least 1 argument if (val.size() <= 0) return true; // empty lines are valid. - + if (val[0][0] == '#' || val[0][0] == '!' ) //the line is commented return true; // still a valid line. - + unsigned int key = 0, mod = 0; char keyarg = 0; t_key *current_key=0, *last_key=0; _FB_USES_NLS; - // for each argument + // for each argument for (unsigned int argc=0; argc<val.size(); argc++) { if (val[argc][0] != ':') { // parse key(s)@@ -199,7 +200,7 @@
int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); if(tmpmod) mod |= tmpmod; //If it's a modifier - else { + else { key = FbTk::KeyUtil::getKey(val[argc].c_str()); // else get the key if (key == 0) { cerr<<_FBTEXT(Keys, InvalidKeyMod, "Keys: Invalid key/modifier on line", "A bad key/modifier string was found on line (number following)")<<" "<<@@ -209,13 +210,13 @@ }
if (!current_key) { current_key = new t_key(key, mod); last_key = current_key; - } else { + } else { t_key *temp_key = new t_key(key, mod); last_key->keylist.push_back(temp_key); last_key = temp_key; } } - } + } } else { // parse command line if (last_key == 0) {@@ -224,7 +225,7 @@ cerr<<"> "<<linebuffer<<endl;
return false; } bool ret_val = true; - const char *str = + const char *str = FbTk::StringUtil::strcasestr(linebuffer.c_str(), val[argc].c_str() + 1); // +1 to skip ':' if (str == 0) {@@ -263,41 +264,42 @@ /**
@return the KeyAction of the XKeyEvent */ void Keys::doAction(XKeyEvent &ke) { - static t_key *next_key = 0; - // Remove numlock, capslock and scrolllock + ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); - + + static struct t_key* next_key = 0; + if (!next_key) { - + for (unsigned int i=0; i<m_keylist.size(); i++) { if (*m_keylist[i] == ke) { if (m_keylist[i]->keylist.size()) { next_key = m_keylist[i]; - break; //end for-loop + break; //end for-loop } else { if (*m_keylist[i]->m_command != 0) m_keylist[i]->m_command->execute(); } } } - + } else { //check the nextkey - t_key *temp_key = next_key->find(ke); + t_key *temp_key = next_key->find(ke); if (temp_key) { if (temp_key->keylist.size()) { - next_key = temp_key; + next_key = temp_key; } else { next_key = 0; if (*temp_key->m_command != 0) temp_key->m_command->execute(); } } else { - temp_key = next_key; + temp_key = next_key; next_key = 0; if (*temp_key->m_command != 0) temp_key->m_command->execute(); - - } + + } } }@@ -318,7 +320,7 @@ bool Keys::mergeTree(t_key *newtree, t_key *basetree) {
if (basetree==0) { unsigned int baselist_i=0; for (; baselist_i<m_keylist.size(); baselist_i++) { - if (m_keylist[baselist_i]->mod == newtree->mod && + if (m_keylist[baselist_i]->mod == newtree->mod && m_keylist[baselist_i]->key == newtree->key) { if (newtree->keylist.size() && *m_keylist[baselist_i]->m_command == 0) { //assumes the newtree only have one branch@@ -330,12 +332,12 @@ }
if (baselist_i == m_keylist.size()) { FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); - m_keylist.push_back(new t_key(newtree)); + m_keylist.push_back(new t_key(newtree)); if (newtree->keylist.size()) return mergeTree(newtree->keylist[0], m_keylist.back()); return true; } - + } else { unsigned int baselist_i = 0; for (; baselist_i<basetree->keylist.size(); baselist_i++) {@@ -346,24 +348,24 @@ //assumes the newtree only have on branch
return mergeTree(newtree->keylist[0], basetree->keylist[baselist_i]); } else return false; - } + } } //if it wasn't in the list grab the key and add it to the list - if (baselist_i==basetree->keylist.size()) { + if (baselist_i==basetree->keylist.size()) { FbTk::KeyUtil::grabKey(newtree->key, newtree->mod); basetree->keylist.push_back(new t_key(newtree)); if (newtree->keylist.size()) return mergeTree(newtree->keylist[0], basetree->keylist.back()); - return true; - } + return true; + } } - + return false; } Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) { key = key_; - mod = mod_; + mod = mod_; m_command = command; }@@ -373,8 +375,8 @@ mod = k->mod;
m_command = k->m_command; } -Keys::t_key::~t_key() { - while (!keylist.empty()) { +Keys::t_key::~t_key() { + while (!keylist.empty()) { t_key *k = keylist.back(); if (k != 0) { // make sure we don't have a bad key pointer delete k;
@@ -31,6 +31,7 @@
#include "FbTk/NotCopyable.hh" #include "FbTk/RefCount.hh" #include "FbTk/Command.hh" +#include "FbTk/KeyUtil.hh" class Keys:private FbTk::NotCopyable { public:@@ -86,21 +87,22 @@ ~t_key();
inline t_key *find(unsigned int key_, unsigned int mod_) { for (unsigned int i=0; i<keylist.size(); i++) { - if (keylist[i]->key == key_ && keylist[i]->mod == mod_) + if (keylist[i]->key == key_ && keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(mod_)) return keylist[i]; } return 0; } inline t_key *find(XKeyEvent &ke) { for (unsigned int i=0; i<keylist.size(); i++) { - if (keylist[i]->key == ke.keycode && keylist[i]->mod == ke.state) + if (keylist[i]->key == ke.keycode && + keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) return keylist[i]; } return 0; } inline bool operator == (XKeyEvent &ke) const { - return (mod == ke.state && key == ke.keycode); + return (mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state) && key == ke.keycode); } FbTk::RefCount<FbTk::Command> m_command;
@@ -42,13 +42,13 @@ #include <functional>
#include <iostream> void NextWindowCmd::execute() { - BScreen *screen = Fluxbox::instance()->keyScreen(); if (screen != 0) { Fluxbox *fb = Fluxbox::instance(); // special case for commands from key events if (fb->lastEvent().type == KeyPress) { unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); + mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); if (mods == 0) // can't stacked cycle unless there is a mod to grab screen->nextFocus(m_option | BScreen::CYCLELINEAR); else {@@ -71,6 +71,7 @@ Fluxbox *fb = Fluxbox::instance();
// special case for commands from key events if (fb->lastEvent().type == KeyPress) { unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); + mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); if (mods == 0) // can't stacked cycle unless there is a mod to grab screen->prevFocus(m_option | BScreen::CYCLELINEAR); else {
@@ -777,11 +777,13 @@ // Update stored modifier mapping
#ifdef DEBUG cerr<<__FILE__<<"("<<__FUNCTION__<<"): MappingNotify"<<endl; #endif // DEBUG - - FbTk::KeyUtil::instance().init(); // reinitialise the key utils - // reconfigure keys (if the mapping changes, they don't otherwise update - m_key->reconfigure(StringUtil::expandFilename(*m_rc_keyfile).c_str()); - + if (e->xmapping.request == MappingKeyboard + || e->xmapping.request == MappingModifier) { + XRefreshKeyboardMapping(&e->xmapping); + FbTk::KeyUtil::instance().init(); // reinitialise the key utils + // reconfigure keys (if the mapping changes, they don't otherwise update + m_key->reconfigure(StringUtil::expandFilename(*m_rc_keyfile).c_str()); + } break; case CreateNotify: break;@@ -1076,7 +1078,6 @@
if (keyScreen() == 0 || mouseScreen() == 0) return; - switch (ke.type) { case KeyPress: m_key->doAction(ke);@@ -1089,9 +1090,10 @@ // we notify if _all_ of the watched modifiers are released
if (m_watching_screen && m_watch_keyrelease) { // mask the mod of the released key out // won't mask anything if it isn't a mod - ke.state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode); - - if ((m_watch_keyrelease & ke.state) == 0) { + unsigned int state = FbTk::KeyUtil::instance().isolateModifierMask(ke.state); + state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode); + + if ((m_watch_keyrelease & state) == 0) { m_watching_screen->notifyReleasedKeys(ke); XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime);@@ -1107,8 +1109,6 @@ }
default: break; } - - } /// handle system signals@@ -1951,12 +1951,15 @@ }
void Fluxbox::watchKeyRelease(BScreen &screen, unsigned int mods) { + if (mods == 0) { cerr<<"WARNING: attempt to grab without modifiers!"<<endl; return; } m_watching_screen = &screen; - m_watch_keyrelease = mods; + + // just make sure we are saving the mods with any other flags (xkb) + m_watch_keyrelease = FbTk::KeyUtil::instance().isolateModifierMask(mods); XGrabKeyboard(FbTk::App::instance()->display(), screen.rootWindow().window(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
@@ -223,7 +223,7 @@ clear();
} void FbRun::keyPressEvent(XKeyEvent &ke) { - // strip numlock, capslock and scrolllock mask + ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); int cp= cursorPosition();@@ -232,10 +232,11 @@ KeySym ks;
char keychar[1]; XLookupString(&ke, keychar, 1, &ks, 0); // a modifier key by itself doesn't do anything - if (IsModifierKey(ks)) return; + if (IsModifierKey(ks)) + return; - if (ke.state) { // a modifier key is down - if (ke.state == ControlMask) { + if (FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) { // a modifier key is down + if ((ke.state & ControlMask) == ControlMask) { switch (ks) { case XK_p: prevHistoryItem();@@ -248,7 +249,7 @@ tabCompleteHistory();
setCursorPosition(cp); break; } - } else if (ke.state == (Mod1Mask | ShiftMask)) { + } else if ((ke.state & (Mod1Mask|ShiftMask)) == (Mod1Mask | ShiftMask)) { switch (ks) { case XK_less: firstHistoryItem();