Created
May 17, 2019 18:01
-
-
Save szaszm/05e6a08e6b0e56149338e6172253c569 to your computer and use it in GitHub Desktop.
Thin wrapper around X API calls to query active keyboard layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// compile with `g++ getlayout.cpp -lX11 -lxkbfile` | |
// original source: https://gist.github.com/fikovnik/ef428e82a26774280c4fdf8f96ce8eeb | |
#include <cstdlib> | |
#include <cstring> | |
#include <stdexcept> | |
#include <memory> | |
#include <X11/XKBlib.h> | |
#include <X11/extensions/XKBrules.h> | |
namespace detail { | |
template<typename T> | |
struct XDeleter { | |
void operator()(T* ptr) const noexcept { XFree(ptr); } | |
}; | |
} | |
template<typename AtomT> | |
using XPtr = std::unique_ptr<AtomT, detail::XDeleter<AtomT>>; | |
class XDisplay { | |
private: | |
Display* const dpy; | |
public: | |
XDisplay() | |
:dpy{ XOpenDisplay(NULL) } | |
{ | |
if(!dpy) throw std::runtime_error{ "Cannot open display" }; | |
} | |
Display* get() const noexcept { return dpy; } | |
XkbStateRec xkbGetState(unsigned int device_spec = XkbUseCoreKbd) { | |
XkbStateRec state; | |
XkbGetState(dpy, device_spec, &state); | |
return state; | |
} | |
XkbDescPtr xkbGetKeyboard(unsigned int which = XkbAllComponentsMask, unsigned int device_spec = XkbUseCoreKbd) { | |
return XkbGetKeyboard(dpy, which, device_spec); | |
} | |
XPtr<char> getAtomName(Atom atom) { | |
return XPtr<char>{ XGetAtomName(dpy, atom) }; | |
} | |
std::string getKeyboardLayout() { | |
XkbStateRec state = this->xkbGetState(); | |
XkbRF_VarDefsRec vd; | |
std::memset(&vd, 0, sizeof(vd)); | |
XkbRF_GetNamesProp(this->get(), NULL, &vd); | |
char *tok = strtok(vd.layout, ","); | |
for (int i = 0; i < state.group; i++) { | |
tok = strtok(NULL, ","); | |
if (tok == NULL) { | |
throw std::runtime_error{ "Keyboard layout lookup error" }; | |
} | |
} | |
std::string result{ tok }; | |
// no better way, source: https://github.com/khurshid-alam/Unity-Settings-Daemon/commit/7135a08b39e17401338232ee3f0309582dd31202 | |
free(vd.model); | |
free(vd.layout); | |
free(vd.variant); | |
free(vd.options); | |
return result; | |
} | |
XDisplay(const XDisplay&) = delete; | |
XDisplay& operator=(const XDisplay&) = delete; | |
~XDisplay() noexcept { XCloseDisplay(dpy); } | |
}; | |
int main() { | |
XDisplay dpy; | |
auto kbd = dpy.xkbGetKeyboard(); | |
printf("Layout name: %s\n", dpy.getAtomName(kbd->names->groups[dpy.xkbGetState().group]).get()); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note: main() is leaking memory, and I don't know how to fix it.
XDisplay::getKeyboardLayout() doesn't leak, but its resource cleanup is ugly and not exception-safe.
related: sergei-mironov/xkb-switch#30