/*************************************************************************** xkeyboard.cpp - description ------------------- begin : Sun Jul 8 2001 copyright : (C) 2001 by Leonid Zeitlin email : lz@europe.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "xkeyboard.h" #include #include #include #include XKeyboard *XKeyboard::m_self = 0; XKeyboard::XKeyboard() { Display *display = tqt_xdisplay(); #ifdef HAVE_LIBXKLAVIER // XklSetDebugLevel(0); XklSetLogAppender(XklLogAppender); XklInit(display); XklRegisterStateCallback(XklStateCallback, this); XklRegisterConfigCallback(XklConfigCallback, this); #else int opcode, errorBase, major = XkbMajorVersion, minor = XkbMinorVersion; // check the library version if (!XkbLibraryVersion(&major, &minor)) { kdWarning() << i18n("This program was built against XKB extension library\n" "version %1.%2, but is run with the library version %3.%4.\n" "This may cause various problems and even result in a complete\n" "failure to function\n").arg(XkbMajorVersion).arg(XkbMinorVersion).arg(major).arg(minor); } // initialize the extension m_xkb_available = XkbQueryExtension(display, &opcode, &m_event_code, &errorBase, &major, &minor); if (!m_xkb_available) { kdError() << i18n("The X Server does not support a compatible XKB extension.\n" "Either the server is not XKB-capable or the extension was disabled.\n" "This program would not work with this server, so it will exit now\n"); } else { // register for XKB events //// group state change, i.e. the current group changed: XkbSelectEventDetails(display, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); //// keyboard mapping change: XkbSelectEventDetails(display, XkbUseCoreKbd, XkbMapNotify, XkbAllMapComponentsMask, XkbKeySymsMask); //// group names change: XkbSelectEventDetails(display, XkbUseCoreKbd, XkbNamesNotify, XkbAllNamesMask, XkbGroupNamesMask); //// new keyboard: XkbSelectEventDetails(display, XkbUseCoreKbd, XkbNewKeyboardNotify, XkbAllNewKeyboardEventsMask, XkbAllNewKeyboardEventsMask); // retrieve the number of keyboard groups retrieveNumKbdGroups(); } #endif m_self = this; } XKeyboard::~XKeyboard(){ #ifdef HAVE_LIBXKLAVIER XklStopListen(); XklTerm(); #endif } /** Determine if the given XEvent e is an XKB event with type XkbStateNotify. * If so, newgroupno will return the new keyboard group #. * In other words, return value of true means that this XEvent tells us * that the user just switched the keyboard group to the new value * newgroupno */ /*bool XKeyboard::isXkbStateNotifyEvent(XEvent *e, int *newgroupno){ bool ret = false; if (e->type == m_event_code) { XkbEvent *kb_ev = (XkbEvent *) e; if (kb_ev->any.xkb_type == XkbStateNotify) { ret = true; *newgroupno = kb_ev->state.group; } } return ret; }*/ /** Set the current keyboard group to the given groupno */ void XKeyboard::setGroupNo(int groupno){ #ifdef HAVE_LIBXKLAVIER XklLockGroup(groupno); #else XkbLockGroup(tqt_xdisplay(), XkbUseCoreKbd, groupno); #endif } #ifndef HAVE_LIBXKLAVIER extern "C" { static int IgnoreXError(Display *, XErrorEvent *) { return 0; } } #endif /** Get the names of the currently configured keyboard groups */ void XKeyboard::getGroupNames(TQStringList &list){ #ifdef HAVE_LIBXKLAVIER const char** groupnames = XklGetGroupNames(); int numgroups = XklGetNumGroups(); for (int i = 0; i < numgroups; i++) list.append(groupnames[i]); #else XkbDescRec xkb; Display *display = tqt_xdisplay(); char *names[XkbNumKbdGroups]; memset(&xkb, 0, sizeof(xkb)); xkb.device_spec = XkbUseCoreKbd; XkbGetNames(display, XkbGroupNamesMask, &xkb); memset(names, 0, sizeof(char *) * XkbNumKbdGroups); // XGetAtomNames below may generate BadAtom error, which is not a problem. // (it may happen if the name for a group was not defined) // Thus we temporarily ignore X errors XErrorHandler old_handler = XSetErrorHandler(IgnoreXError); XGetAtomNames(display, xkb.names->groups, m_numgroups, names); // resume normal X error processing XSetErrorHandler(old_handler); for (int i = 0; i < m_numgroups; i++) { if (names[i]) { list.append(names[i]); XFree(names[i]); } else list.append(TQString::null); } XkbFreeNames(&xkb, XkbGroupNamesMask, 1); #endif } XKeyboard * XKeyboard::self() { return m_self; } /** return the current keyboard group index */ int XKeyboard::getGroupNo(){ #ifdef HAVE_LIBXKLAVIER return XklGetCurrentState()->group; #else XkbStateRec rec; XkbGetState(tqt_xdisplay(), XkbUseCoreKbd, &rec); return (int) rec.group; #endif } /** Returns if the given event notifies us of a keyboard layout change that requires a * reconfiguration * (e.g. new group added, group names changed, etc.) */ /*bool XKeyboard::isLayoutChangeEvent(XEvent *e){ if (e->type == m_event_code) { XkbEvent *xkb_ev = (XkbEvent *) e; if ((xkb_ev->any.xkb_type == XkbMapNotify) && (xkb_ev->map.changed & XkbKeySymsMask) || (xkb_ev->any.xkb_type == XkbNamesNotify) && (xkb_ev->names.changed & XkbGroupNamesMask) || (xkb_ev->any.xkb_type == XkbNewKeyboardNotify)) { retrieveNumKbdGroups(); return true; } } return false; }*/ #ifndef HAVE_LIBXKLAVIER /** No descriptions */ void XKeyboard::retrieveNumKbdGroups(){ XkbDescRec xkb; memset(&xkb, 0, sizeof(xkb)); /* Interestingly, in RedHat 6.0 (XFree86 3.3.3.1) the XkbGetControls call below works even if xkb.device_spec is not set. But in RedHat 7.1 (XFree86 4.0.3) it returns BadImplementation status code, and you have to specify xkb.device_spec = XkbUseCoreKbd. */ xkb.device_spec = XkbUseCoreKbd; XkbGetControls(tqt_xdisplay(), XkbGroupsWrapMask, &xkb); m_numgroups = xkb.ctrls->num_groups; XkbFreeControls(&xkb, XkbGroupsWrapMask, 1); } #endif /** Examines an X Event passed to it and takes actions if the event is of * interest to XKeyboard */ void XKeyboard::processEvent(XEvent *ev) { #ifdef HAVE_LIBXKLAVIER XklFilterEvents(ev); #else if (ev->type == m_event_code) { // This an XKB event XkbEvent *xkb_ev = (XkbEvent *) ev; if (xkb_ev->any.xkb_type == XkbStateNotify) { // state notify event, the current group has changed emit groupChanged(xkb_ev->state.group); } else if ((xkb_ev->any.xkb_type == XkbMapNotify) && (xkb_ev->map.changed & XkbKeySymsMask) || (xkb_ev->any.xkb_type == XkbNamesNotify) && (xkb_ev->names.changed & XkbGroupNamesMask) || (xkb_ev->any.xkb_type == XkbNewKeyboardNotify)) { // keyboard layout has changed retrieveNumKbdGroups(); emit layoutChanged(); } } #endif } #ifdef HAVE_LIBXKLAVIER void XKeyboard::XklStateCallback(XklStateChange changeType, int group, Bool /*restore*/, void */*userData*/) { if (changeType == GROUP_CHANGED) emit XKeyboard::self()->groupChanged(group); } void XKeyboard::XklConfigCallback(void */*userData*/) { emit XKeyboard::self()->layoutChanged(); } void XKeyboard::XklLogAppender(const char file[], const char function[], int level, const char format[], va_list args) { int size = vsnprintf(NULL, 0, format, args); char *str = new char[size + 1]; vsnprintf(str, size, format, args); kdDebug() << file << "/" << function << ": " << str << endl; delete[] str; } #endif #include "xkeyboard.moc"