diff --git a/X11Exception.h b/X11Exception.h new file mode 100644 index 0000000..c0468bf --- /dev/null +++ b/X11Exception.h @@ -0,0 +1,29 @@ +// X11Exception.h +// C++ exception that wraps X11 errors. +// Copyright (C) 2008 Jay Bromley +// +// $Id: X11Exception.h 26 2008-04-09 08:47:11Z jay $ + +#ifndef X11EXCEPTION_H_FE39A315_6827_447B_AE62_5FA2C3FD391F +#define X11EXCEPTION_H_FE39A315_6827_447B_AE62_5FA2C3FD391F + +#include + +class X11Exception : public std::exception +{ +public: + X11Exception() : _reason("unknown") {} + X11Exception(const std::string& what) : _reason(what) {} + virtual ~X11Exception() throw () {}; + virtual const char* what() const throw () { return _reason.c_str(); } + +private: + std::string _reason; +}; + +#endif // GAMEEXCEPTION_H_FE39A315_6827_447B_AE62_5FA2C3FD391F + +// Local Variables: +// mode: c++ +// End: + diff --git a/XKeyboard.cpp b/XKeyboard.cpp new file mode 100644 index 0000000..ea8f625 --- /dev/null +++ b/XKeyboard.cpp @@ -0,0 +1,404 @@ +// xkeyboard.cpp +// Implementation of a class to get keyboard layout information and change layouts +// Copyright (C) 2008 by Jay Bromley +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// $Id: XKeyboard.cpp 53 2008-07-18 08:38:47Z jay $ + +#include "XKeyboard.h" +#include "X11Exception.h" +#include +#include +#include +#include +#include + +// XKeyboard ----------------------------------------------------------- + +XKeyboard::XKeyboard() + : _display(0), _groupCount(0), _currentGroupNum(0), + _deviceId(XkbUseCoreKbd) +{ + + XkbIgnoreExtension(False); + + char* displayName = strdup(""); + int eventCode; + int errorReturn; + int major = XkbMajorVersion; + int minor = XkbMinorVersion;; + int reasonReturn; + _display = XkbOpenDisplay(displayName, &eventCode, &errorReturn, &major, + &minor, &reasonReturn); + switch (reasonReturn) { + case XkbOD_BadLibraryVersion: + throw X11Exception("Bad XKB library version."); + break; + case XkbOD_ConnectionRefused: + throw X11Exception("Connection to X server refused."); + break; + case XkbOD_BadServerVersion: + throw X11Exception("Bad X11 server version."); + break; + case XkbOD_NonXkbServer: + throw X11Exception("XKB not present."); + break; + case XkbOD_Success: + break; + } + + if (initializeXkb() != True) { + throw X11Exception("XKB not initialized."); + } + + XkbSelectEventDetails(_display, XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, XkbGroupStateMask); + + XkbStateRec xkbState; + XkbGetState(_display, _deviceId, &xkbState); + _currentGroupNum = (_currentGroupNum != xkbState.group) ? xkbState.group : _currentGroupNum; + accomodateGroupXkb(); +} + +Bool XKeyboard::initializeXkb() +{ + // Initialize the XKB extension. + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + int opCode; + Bool status = XkbQueryExtension(_display, &opCode, &_baseEventCode, &_baseErrorCode, &major, &minor); + + XkbDescRec* kbdDescPtr = XkbAllocKeyboard(); + if (kbdDescPtr == NULL) { + std::cerr << "Failed to get keyboard description." << std::endl; + return False; + } + + kbdDescPtr->dpy = _display; + if (_deviceId != XkbUseCoreKbd) { + kbdDescPtr->device_spec = _deviceId; + } + + XkbGetControls(_display, XkbAllControlsMask, kbdDescPtr); + XkbGetNames(_display, XkbSymbolsNameMask, kbdDescPtr); + XkbGetNames(_display, XkbGroupNamesMask, kbdDescPtr); + + if (kbdDescPtr->names == NULL) { + std::cerr << "Failed to get keyboard description." << std::endl; + return False; + } + + // Count the number of configured groups. + const Atom* groupSource = kbdDescPtr->names->groups; + if (kbdDescPtr->ctrls != NULL) { + _groupCount = kbdDescPtr->ctrls->num_groups; + } else { + _groupCount = 0; + while (_groupCount < XkbNumKbdGroups && + groupSource[_groupCount] != None) { + _groupCount++; + } + } + + // There is always at least one group. + if (_groupCount == 0) { + _groupCount = 1; + } + + // Get the group names. + const Atom* tmpGroupSource = kbdDescPtr->names->groups; + Atom curGroupAtom; + std::string groupName; + for (int i = 0; i < _groupCount; i++) { + if ((curGroupAtom = tmpGroupSource[i]) != None) { + char* groupNameC = XGetAtomName(_display, curGroupAtom); + if (groupNameC == NULL) { + _groupNames.push_back(""); + } else { + groupName = groupNameC; + std::string::size_type pos = groupName.find('(', 0); + if (pos != std::string::npos) { + groupName = groupName.substr(0, pos + 1); + } + _groupNames.push_back(groupName); + } + XFree(groupNameC); + } + } + + // Get the symbol name and parse it for layout symbols. + Atom symNameAtom = kbdDescPtr->names->symbols; + std::string symName; + if (symNameAtom != None) { + char* symNameC = XGetAtomName(_display, symNameAtom); + symName = symNameC; + XFree(symNameC); + if (symName.empty()) { + return False; + } + } else { + return False; + } + + XkbSymbolParser symParser; + symParser.parse(symName, _symbolNames); + int count = _symbolNames.size(); + if (count == 1 && _groupNames[0].empty() && _symbolNames[0] == "jp") { + _groupCount = 2; + _symbolNames[1] = _symbolNames[0]; + _symbolNames[0] = "us"; + _groupNames[0] = "US/ASCII"; + _groupNames[1] = "Japanese"; + } else { + if (count < _groupCount) { + int j = count; + int k = _groupCount; + while (--j >= 0) _symbolNames[--k] = _symbolNames[j]; + while (--k >= 0) _symbolNames[k] = "en_US"; + } + } + + count = _groupNames.size(); + for (int i = 0; i < count; i++) { + if (_groupNames[i].empty()) { + std::string name = getSymbolNameByResNum(i); + if (name.empty()) { + name = "U/A"; + } + std::cerr << "Group Name " << i + 1 << " is undefined, set to '" + << name << "'!\n"; + _groupNames[i] = name; + } + } + + XkbStateRec xkbState; + XkbGetState(_display, _deviceId, &xkbState); + _currentGroupNum = xkbState.group; + + return True; +} + +std::string XKeyboard::getSymbolNameByResNum(int groupResNum) +{ + return _symbolNames[groupNumResToXkb(groupResNum)]; +} + +std::string XKeyboard::getGroupNameByResNum(int groupResNum) +{ + return _groupNames[groupNumResToXkb(groupResNum)]; +} + +int XKeyboard::groupNumResToXkb(int groupResNum) +{ + return groupLookup(groupResNum, _groupNames, _symbolNames, _groupCount); +} + +int XKeyboard::groupLookup(int srcValue, StringVector fromText, StringVector toText, int count) +{ + const std::string srcText = fromText[srcValue]; + + if (!srcText.empty()) { + std::string targetText; + + for (int i = 0; i < count; i++) { + targetText = toText[i]; + if (compareNoCase(srcText, targetText) == 0) { + srcValue = i; + break; + } + } + } + + return srcValue; +} + +void XKeyboard::accomodateGroupXkb() +{ + XkbStateRec state; + XkbGetState(_display, _deviceId, &state); + _currentGroupNum = state.group; +} + + +XKeyboard::~XKeyboard() +{ + XCloseDisplay(_display); + _display = NULL; +} + +int XKeyboard::groupCount() const +{ + return _groupCount; +} + +StringVector XKeyboard::groupNames() const +{ + return _groupNames; +} + +StringVector XKeyboard::groupSymbols() const +{ + return _symbolNames; +} + +int XKeyboard::currentGroupNum() const +{ + XkbStateRec xkbState; + XkbGetState(_display, _deviceId, &xkbState); + return static_cast(xkbState.group); +} + +std::string XKeyboard::currentGroupName() const +{ + return _groupNames[currentGroupNum()]; +} + +std::string XKeyboard::currentGroupSymbol() const +{ + return _symbolNames[currentGroupNum()]; +} + +bool XKeyboard::setGroupByNum(int groupNum) +{ + if (_groupCount <= 1) { + return false; + } + + Bool result = XkbLockGroup(_display, _deviceId, groupNum); + if (result == False) { + return false; + } + accomodateGroupXkb(); + return true; +} + +bool XKeyboard::changeGroup(int increment) +{ + Bool result = XkbLockGroup(_display, _deviceId, + (_currentGroupNum + increment) % _groupCount); + if (result == False) { + return false; + } + accomodateGroupXkb(); + return true; +} + + +// XkbSymbolParser ----------------------------------------------------- + +XkbSymbolParser::XkbSymbolParser() +{ + _nonSymbols.push_back("group"); + _nonSymbols.push_back("inet"); + _nonSymbols.push_back("pc"); +} + +XkbSymbolParser::~XkbSymbolParser() +{ + _nonSymbols.clear(); +} + +void XkbSymbolParser::parse(const std::string& symbols, StringVector& symbolList) +{ + bool inSymbol = false; + std::string curSymbol; + + for (int i = 0; i < symbols.size(); i++) { + char ch = symbols[i]; + if (ch == '+') { + if (inSymbol) { + if (isXkbLayoutSymbol(curSymbol)) { + symbolList.push_back(curSymbol); + } + curSymbol.clear(); + } else { + inSymbol = true; + } + } else if (inSymbol && (isalpha(static_cast(ch)) || ch == '_')) { + curSymbol.append(1, ch); + } else { + if (inSymbol) { + if (isXkbLayoutSymbol(curSymbol)) { + symbolList.push_back(curSymbol); + } + curSymbol.clear(); + inSymbol = false; + } + } + } + + if (inSymbol && !curSymbol.empty() && isXkbLayoutSymbol(curSymbol)) { + symbolList.push_back(curSymbol); + } +} + +bool XkbSymbolParser::isXkbLayoutSymbol(const std::string& symbol) { + StringVectorIter result = find(_nonSymbols.begin(), _nonSymbols.end(), symbol); + return result == _nonSymbols.end(); +} + +// Helper functions ---------------------------------------------------- + +int compareNoCase(const std::string& s1, const std::string& s2) +{ + std::string::const_iterator it1 = s1.begin(); + std::string::const_iterator it2 = s2.begin(); + + //Has the end of at least one of the strings been reached? + while (it1 != s1.end() && it2 != s2.end()) { + // Do the letters differ? + if (::toupper(*it1) != ::toupper(*it2)) { + // return -1 to indicate 'smaller than', 1 otherwise + return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1; + } + + // Proceed to the next character in each string. + ++it1; + ++it2; + } + + size_t size1 = s1.size(); + size_t size2 = s2.size(); + + // Return -1, 0 or 1 according to strings' lengths. + if (size1 == size2) { + return 0; + } + return (size1 < size2) ? -1 : 1; +} + +// std::ostream& operator<<(std::ostream& os, const XKeyboard& xkb) +// { +// os << "xkb {\n\t" << xkb.groupCount() << " groups {" << xkb.groupNames() +// << "},\n\tsymbols {" << xkb.groupSymbols() << "}\n\tcurrent group: " +// << xkb.currentGroupSymbol() << " - " << xkb.currentGroupName() +// << " (" << xkb.currentGroupNum() << ")\n}"; +// return os; +// } + +// std::ostream& operator<<(std::ostream& os, const StringVector& sv) +// { +// for (int i = 0; i < sv.size(); i++) { +// os << (i == 0 ? "" : ", ") << sv[i]; +// } +// return os; +// } + +// Main entry point (test) --------------------------------------------- + +// int main(int argc, char** argv) +// { +// XKeyboard xkb; +// std::cout << xkb << std::endl; +// xkb.changeGroup(1); +// std::cout << xkb << std::endl; +// xkb.changeGroup(1); +// std::cout << xkb << std::endl; +// xkb.changeGroup(1); +// std::cout << xkb << std::endl; +// return EXIT_SUCCESS; +// } diff --git a/XKeyboard.h b/XKeyboard.h new file mode 100644 index 0000000..9509a37 --- /dev/null +++ b/XKeyboard.h @@ -0,0 +1,90 @@ +// xkeyboard.h +// Interface for a class to get keyboard layout information and change layouts +// Copyright (C) 2008 by Jay Bromley +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// $Id: XKeyboard.h 29 2008-04-09 21:37:44Z jay $ + +#ifndef XKEYBOARD_H_1C79861A_49B3_4A95_88D6_455C22FEB222 +#define XKEYBOARD_H_1C79861A_49B3_4A95_88D6_455C22FEB222 + +#include +#include +#include +#include + + +typedef std::vector StringVector; + + +// XKeyboard ----------------------------------------------------------- + +class XKeyboard +{ +public: + XKeyboard(); + ~XKeyboard(); + int groupCount() const; + StringVector groupNames() const; + StringVector groupSymbols() const; + int currentGroupNum() const; + std::string currentGroupName() const; + std::string currentGroupSymbol() const; + bool setGroupByNum(int groupNum); + bool changeGroup(int increment); + + //friend std::ostream& operator<<(std::ostream& os, const XKeyboard& xkb); + +private: + Bool initializeXkb(); + std::string getSymbolNameByResNum(int groupResNum); + int groupNumResToXkb(int groupNumRes); + std::string getGroupNameByResNum(int groupResNum); + int groupLookup(int srcValue, StringVector fromText, StringVector toText, int count); + void accomodateGroupXkb(); + + Display* _display; + int _groupCount; + StringVector _groupNames; + StringVector _symbolNames; + int _currentGroupNum; + + int _deviceId; + int _baseEventCode; + int _baseErrorCode; +}; + + +// XkbSymbolParser ----------------------------------------------------- + +class XkbSymbolParser +{ +public: + typedef std::vector::iterator StringVectorIter; + + XkbSymbolParser(); + ~XkbSymbolParser(); + void parse(const std::string& symbols, std::vector& symbolList); + +private: + bool isXkbLayoutSymbol(const std::string& symbol); + + StringVector _nonSymbols; +}; + + +// Helper functions ---------------------------------------------------- + +int compareNoCase(const std::string& s1, const std::string& s2); +// std::ostream& operator<<(std::ostream& os, const XKeyboard& xkb); +// std::ostream& operator<<(std::ostream& os, const StringVector& v); + +#endif // XKEYBOARD_H_1C79861A_49B3_4A95_88D6_455C22FEB222 + +// Local Variables: +// mode: c++ +// End: