1 /*************************************************************************
2 * based on xkeyboard.h by Leonid Zeitlin (lz@europe.com) *
3 * heavily modified by Ketmar // Vampire Avalon *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 *************************************************************************/
11 #include "xkeyboard.h"
13 //XkbNumKbdGroups -- maximum number of groups (currently 4)
16 XKeyboard::XKeyboard () {
17 Display
*dpy
= QX11Info::display();
18 int opcode
, errorBase
, major
= XkbMajorVersion
, minor
= XkbMinorVersion
;
20 // check the library version
21 if (!XkbLibraryVersion(&major
, &minor
)) {
22 qWarning() << QString(
23 "This program was built against XKB extension library\n"
24 "version %1.%2, but is run with the library version %3.%4.\n"
25 "This may cause various problems and even result in a complete\n"
26 "failure to function\n").arg(XkbMajorVersion
).arg(XkbMinorVersion
).arg(major
).arg(minor
);
29 // initialize the extension
30 mXKbAvailable
= XkbQueryExtension(dpy
, &opcode
, &mEventCode
, &errorBase
, &major
, &minor
);
33 "The X Server does not support a compatible XKB extension.\n"
34 "Either the server is not XKB-capable or the extension was disabled.\n"
35 "This program would not work with this server, so it will exit now\n";
37 // register for XKB events
38 // group state change, i.e. the current group changed:
39 XkbSelectEventDetails(dpy
, XkbUseCoreKbd
, XkbStateNotify
, XkbAllStateComponentsMask
, XkbGroupStateMask
);
40 // keyboard mapping change:
41 XkbSelectEventDetails(dpy
, XkbUseCoreKbd
, XkbMapNotify
, XkbAllMapComponentsMask
, XkbKeySymsMask
);
42 // group names change:
43 XkbSelectEventDetails(dpy
, XkbUseCoreKbd
, XkbNamesNotify
, XkbAllNamesMask
, XkbGroupNamesMask
);
45 XkbSelectEventDetails(dpy
, XkbUseCoreKbd
, XkbNewKeyboardNotify
, XkbAllNewKeyboardEventsMask
, XkbAllNewKeyboardEventsMask
);
50 XKeyboard::~XKeyboard () {
54 void XKeyboard::setActiveGroup (int groupno
) {
57 XkbLockGroup(QX11Info::display(), XkbUseCoreKbd
, groupno
);
58 // the following call is necessary
59 memset(&state
, 0, sizeof(state
));
60 XkbGetState(QX11Info::display(), XkbUseCoreKbd
, &state
);
64 int XKeyboard::activeGroup () {
67 memset(&state
, 0, sizeof(state
));
68 XkbGetState(QX11Info::display(), XkbUseCoreKbd
, &state
);
69 return (int)state
.group
;
73 int XKeyboard::groupCount () {
77 memset(&kbd
, 0, sizeof(kbd
));
78 kbd
.device_spec
= XkbUseCoreKbd
;
79 XkbGetControls(QX11Info::display(), XkbGroupsWrapMask
, &kbd
);
80 res
= (int)kbd
.ctrls
->num_groups
;
81 XkbFreeControls(&kbd
, XkbGroupsWrapMask
, 1);
87 static int IgnoreXError(Display
*, XErrorEvent
*) { return 0; }
91 void XKeyboard::groupNames (QStringList
&list
) {
96 Display
*dpy
= QX11Info::display();
98 memset(&kbd
, 0, sizeof(kbd
));
99 kbd
.device_spec
= XkbUseCoreKbd
;
101 XkbGetControls(dpy
, XkbGroupsWrapMask
, &kbd
);
102 count
= (int)kbd
.ctrls
->num_groups
;
103 XkbFreeControls(&kbd
, XkbGroupsWrapMask
, True
);
105 names
= (char **)calloc(count
+1, sizeof(char *));
106 XkbGetNames(dpy
, XkbGroupNamesMask
, &kbd
);
107 // XGetAtomNames below may generate BadAtom error, which is not a problem.
108 // (it may happen if the name for a group was not defined)
109 // Thus we temporarily ignore X errors
110 oh
= XSetErrorHandler(IgnoreXError
);
112 XGetAtomNames(dpy
, kbd
.names
->groups
, count
, names
);
113 // resume normal X error processing
114 XSetErrorHandler(oh
);
115 for (int f
= 0; f
< count
; ++f
) {
117 list
.append(names
[f
]);
120 list
.append(QString::null
);
124 XkbFreeNames(&kbd
, XkbGroupNamesMask
, 1);
128 // pc+us+ru(winkey):2+inet(evdev)+group(menu_toggle)
129 void XKeyboard::symbolNames (QStringList
&list
) {
133 Display
*dpy
= QX11Info::display();
135 memset(&kbd
, 0, sizeof(kbd
));
136 kbd
.device_spec
= XkbUseCoreKbd
;
138 XkbGetNames(dpy
, XkbSymbolsNameMask
, &kbd
);
140 symNameA
= kbd
.names
->symbols
;
141 if (symNameA
!= None
) {
144 oh
= XSetErrorHandler(IgnoreXError
);
145 symName
= XGetAtomName(dpy
, symNameA
);
146 // resume normal X error processing
147 XSetErrorHandler(oh
);
152 //qDebug() << "symName:" << symName;
159 qDebug("symName: [%s]", s
);
161 char *e
= strchr(s
, '+'), ch
, *t
;
163 if (e
== NULL
) e
= s
+strlen(s
);
165 if ((t
= strchr(s
, '(')) != NULL
) *t
= '\0';
166 if ((t
= strchr(s
, ':')) != NULL
) *t
= '\0';
167 if (strcasecmp(s
, "pc") != 0 && strlen(s
) == 2) {
168 qDebug(" found: [%s]", s
);
180 void XKeyboard::groupInfo (QXkbLayoutList
&list
) {
188 if (sn
.size() < 1) sn
<< "us";
190 for (int f
= 0; f
< gn
.size(); ++f
) {
194 if (f
< sn
.size()) i
.sym
= sn
[f
]; else i
.sym
= "us";
200 void XKeyboard::processEvent (XEvent
*ev
) {
202 if (ev
->type
== mEventCode
) {
203 XkbEvent
*xkbEv
= (XkbEvent
*)ev
;
205 if (xkbEv
->any
.xkb_type
== XkbStateNotify
) {
206 // state notify event, the current group has changed
207 emit
groupChanged(xkbEv
->state
.group
);
208 } else if ((xkbEv
->any
.xkb_type
== XkbMapNotify
&& (xkbEv
->map
.changed
& XkbKeySymsMask
)) ||
209 (xkbEv
->any
.xkb_type
== XkbNamesNotify
&& (xkbEv
->names
.changed
& XkbGroupNamesMask
)) ||
210 (xkbEv
->any
.xkb_type
== XkbNewKeyboardNotify
)) {
211 // keyboard layout has changed
212 emit
layoutChanged();