summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2010-03-10 10:13:33 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2010-03-15 15:02:58 (GMT)
commit761bccd408c72e1d1d10d6c69f9f1d01fff30a0c (patch)
treeb94e1d15e9e3b5637675dbd8b59bdd72af09c011
parentc63fe6fcfc83028419dd84d13d24e7cb1295ac7f (diff)
downloadQt-761bccd408c72e1d1d10d6c69f9f1d01fff30a0c.zip
Qt-761bccd408c72e1d1d10d6c69f9f1d01fff30a0c.tar.gz
Qt-761bccd408c72e1d1d10d6c69f9f1d01fff30a0c.tar.bz2
Improve keyboard layout detection on X11
When the keyboard configuration includes multiple layouts in separate groups, the _XKB_RULES_NAME property lists the layouts separated by a comma. Previously we did not handle this. We query the current keyboard group on startup and monitor XkbStateNotify events for group changes. The existing infrastructre from QKeyMapper deals with sending the KeyboardLayoutChange event to all toplevel windows already. This change includes a bit of refactoring to put the XKB detection and initialization in qapplication_x11.cpp and XKB opcode, eventbase, and errorbase into qt_x11_p.h (like with the other extensions). Task-number: QTBUG-3631 Reviewed-by: denis
-rw-r--r--src/gui/kernel/qapplication_x11.cpp63
-rw-r--r--src/gui/kernel/qkeymapper_p.h2
-rw-r--r--src/gui/kernel/qkeymapper_x11.cpp34
-rw-r--r--src/gui/kernel/qt_x11_p.h6
4 files changed, 78 insertions, 27 deletions
diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp
index 20a7ff2..985f825 100644
--- a/src/gui/kernel/qapplication_x11.cpp
+++ b/src/gui/kernel/qapplication_x11.cpp
@@ -709,6 +709,10 @@ static int qt_x_errhandler(Display *dpy, XErrorEvent *err)
extensionName = "XInputExtension";
else if (err->request_code == X11->mitshm_major)
extensionName = "MIT-SHM";
+#ifndef QT_NO_XKB
+ else if(err->request_code == X11->xkb_major)
+ extensionName = "XKEYBOARD";
+#endif
char minor_str[256];
if (extensionName) {
@@ -1635,6 +1639,11 @@ void qt_init(QApplicationPrivate *priv, int,
X11->xinput_eventbase = 0;
X11->xinput_errorbase = 0;
+ X11->use_xkb = false;
+ X11->xkb_major = 0;
+ X11->xkb_eventbase = 0;
+ X11->xkb_errorbase = 0;
+
// MIT-SHM
X11->use_mitshm = false;
X11->use_mitshm_pixmaps = false;
@@ -2108,6 +2117,33 @@ void qt_init(QApplicationPrivate *priv, int,
}
#endif // QT_NO_XINPUT
+#ifndef QT_NO_XKB
+ int xkblibMajor = XkbMajorVersion;
+ int xkblibMinor = XkbMinorVersion;
+ X11->use_xkb = XkbQueryExtension(X11->display,
+ &X11->xkb_major,
+ &X11->xkb_eventbase,
+ &X11->xkb_errorbase,
+ &xkblibMajor,
+ &xkblibMinor);
+ if (X11->use_xkb) {
+ // If XKB is detected, set the GrabsUseXKBState option so input method
+ // compositions continue to work (ie. deadkeys)
+ unsigned int state = XkbPCF_GrabsUseXKBStateMask;
+ (void) XkbSetPerClientControls(X11->display, state, &state);
+
+ // select for group change events
+ XkbSelectEventDetails(X11->display,
+ XkbUseCoreKbd,
+ XkbStateNotify,
+ XkbAllStateComponentsMask,
+ XkbGroupStateMask);
+
+ // current group state is queried when creating the keymapper, no need to do it here
+ }
+#endif
+
+
#if !defined(QT_NO_FONTCONFIG)
int dpi = 0;
getXDefault("Xft", FC_DPI, &dpi);
@@ -2186,15 +2222,6 @@ void qt_init(QApplicationPrivate *priv, int,
// initialize key mapper
QKeyMapper::changeKeyboard();
-#ifndef QT_NO_XKB
- if (qt_keymapper_private()->useXKB) {
- // If XKB is detected, set the GrabsUseXKBState option so input method
- // compositions continue to work (ie. deadkeys)
- unsigned int state = XkbPCF_GrabsUseXKBStateMask;
- (void) XkbSetPerClientControls(X11->display, state, &state);
- }
-#endif // QT_NO_XKB
-
// Misc. initialization
#if 0 //disabled for now..
QSegfaultHandler::initialize(priv->argv, priv->argc);
@@ -3229,6 +3256,24 @@ int QApplication::x11ProcessEvent(XEvent* event)
QKeyMapper::changeKeyboard();
return 0;
}
+#ifndef QT_NO_XKB
+ else if (X11->use_xkb && event->type == X11->xkb_eventbase) {
+ XkbAnyEvent *xkbevent = (XkbAnyEvent *) event;
+ switch (xkbevent->xkb_type) {
+ case XkbStateNotify:
+ {
+ XkbStateNotifyEvent *xkbstateevent = (XkbStateNotifyEvent *) xkbevent;
+ if ((xkbstateevent->changed & XkbGroupStateMask) != 0) {
+ qt_keymapper_private()->xkb_currentGroup = xkbstateevent->group;
+ QKeyMapper::changeKeyboard();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif
if (!widget) { // don't know this windows
QWidget* popup = QApplication::activePopupWidget();
diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h
index 3e42d6e..38f141e 100644
--- a/src/gui/kernel/qkeymapper_p.h
+++ b/src/gui/kernel/qkeymapper_p.h
@@ -183,7 +183,7 @@ public:
const XEvent *,
bool grab);
- bool useXKB;
+ int xkb_currentGroup;
QXCoreDesc coreDesc;
#elif defined(Q_WS_MAC)
diff --git a/src/gui/kernel/qkeymapper_x11.cpp b/src/gui/kernel/qkeymapper_x11.cpp
index d6d1042..428ac3e 100644
--- a/src/gui/kernel/qkeymapper_x11.cpp
+++ b/src/gui/kernel/qkeymapper_x11.cpp
@@ -248,22 +248,17 @@ qt_XTranslateKey(register QXCoreDesc *dpy,
QKeyMapperPrivate::QKeyMapperPrivate()
- : keyboardInputDirection(Qt::LeftToRight), useXKB(false)
+ : keyboardInputDirection(Qt::LeftToRight), xkb_currentGroup(0)
{
memset(&coreDesc, 0, sizeof(coreDesc));
#ifndef QT_NO_XKB
- int opcode = -1;
- int xkbEventBase = -1;
- int xkbErrorBase = -1;
- int xkblibMajor = XkbMajorVersion;
- int xkblibMinor = XkbMinorVersion;
- if (XkbQueryExtension(X11->display, &opcode, &xkbEventBase, &xkbErrorBase, &xkblibMajor, &xkblibMinor))
- useXKB = true;
-#endif
-
-#if 0
- qDebug() << "useXKB =" << useXKB;
+ if (X11->use_xkb) {
+ // get the current group
+ XkbStateRec xkbState;
+ if (XkbGetState(X11->display, XkbUseCoreKbd, &xkbState) == Success)
+ xkb_currentGroup = xkbState.group;
+ }
#endif
}
@@ -276,7 +271,7 @@ QKeyMapperPrivate::~QKeyMapperPrivate()
QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event)
{
#ifndef QT_NO_XKB
- if (useXKB)
+ if (X11->use_xkb)
return possibleKeysXKB(event);
#endif
return possibleKeysCore(event);
@@ -486,7 +481,7 @@ enum {
void QKeyMapperPrivate::clearMappings()
{
#ifndef QT_NO_XKB
- if (useXKB) {
+ if (X11->use_xkb) {
// try to determine the layout name and input direction by reading the _XKB_RULES_NAMES property off
// the root window
QByteArray layoutName;
@@ -515,8 +510,13 @@ void QKeyMapperPrivate::clearMappings()
p += qstrlen(p) + 1;
} while (p < end);
- layoutName = QByteArray::fromRawData(names[2], qstrlen(names[2]));
- variantName = QByteArray::fromRawData(names[3], qstrlen(names[3]));
+ // the layout names and variants are saved in the _XKB_RULES_NAMES property as a comma separated list
+ QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(',');
+ if (uint(xkb_currentGroup) < uint(layoutNames.count()))
+ layoutName = layoutNames.at(xkb_currentGroup);
+ QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(',');
+ if (uint(xkb_currentGroup) < uint(variantNames.count()))
+ variantName = variantNames.at(xkb_currentGroup);
}
// ### ???
@@ -574,7 +574,7 @@ void QKeyMapperPrivate::clearMappings()
// look at the modifier mapping, and get the correct masks for alt, meta, super, hyper, and mode_switch
#ifndef QT_NO_XKB
- if (useXKB) {
+ if (X11->use_xkb) {
XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd);
for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) {
const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0;
diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h
index 8af4df5..e1b2625 100644
--- a/src/gui/kernel/qt_x11_p.h
+++ b/src/gui/kernel/qt_x11_p.h
@@ -439,6 +439,12 @@ struct QX11Data
int xinput_eventbase;
int xinput_errorbase;
+ // for XKEYBOARD support
+ bool use_xkb;
+ int xkb_major;
+ int xkb_eventbase;
+ int xkb_errorbase;
+
QList<QWidget *> deferred_map;
struct ScrollInProgress {
long id;