You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kkbswitch/kkbswitch/xkeyboard.cpp

249 lines
8.1 KiB

/***************************************************************************
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 <ntqwindowdefs.h>
#include <ntqstringlist.h>
#include <kdebug.h>
#include <tdelocale.h>
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"