Skip to content

Instantly share code, notes, and snippets.

@szaszm
Created May 17, 2019 18:01
Show Gist options
  • Save szaszm/05e6a08e6b0e56149338e6172253c569 to your computer and use it in GitHub Desktop.
Save szaszm/05e6a08e6b0e56149338e6172253c569 to your computer and use it in GitHub Desktop.
Thin wrapper around X API calls to query active keyboard layout
// 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;
}
@szaszm
Copy link
Author

szaszm commented May 17, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment