Replaced ToAscii by ToUnicode in the User driver interface.
[wine/wine64.git] / windows / x11drv / keyboard.c
blob5088a674452c4a5426ca9c6ddef84e2441c6f024
1 /*
2 * X11 keyboard driver
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1996 Albrecht Kleine
6 * Copyright 1997 David Faure
7 * Copyright 1998 Morten Welinder
8 * Copyright 1998 Ulrich Weigand
9 * Copyright 1999 Ove Kåven
12 #include "config.h"
14 #include <X11/Xatom.h>
15 #include <X11/keysym.h>
17 #include "ts_xlib.h"
18 #include "ts_xresource.h"
19 #include "ts_xutil.h"
21 #include <ctype.h>
22 #include <string.h>
24 #include "windef.h"
25 #include "wingdi.h"
26 #include "wine/winuser16.h"
27 #include "dinput.h"
28 #include "debugtools.h"
29 #include "user.h"
30 #include "keyboard.h"
31 #include "message.h"
32 #include "winnls.h"
33 #include "win.h"
34 #include "x11drv.h"
36 DEFAULT_DEBUG_CHANNEL(keyboard)
37 DECLARE_DEBUG_CHANNEL(key)
38 DECLARE_DEBUG_CHANNEL(dinput)
40 extern BYTE InputKeyStateTable[256];
42 extern LPBYTE pKeyStateTable;
44 int min_keycode, max_keycode, keysyms_per_keycode;
45 WORD keyc2vkey[256], keyc2scan[256];
47 static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
48 static int kcControl, kcAlt, kcShift, kcNumLock, kcCapsLock; /* keycodes */
50 static char KEYBOARD_MapDeadKeysym(KeySym keysym);
52 /* Keyboard translation tables */
53 #define MAIN_LEN 48
54 static const WORD main_key_scan_qwerty[MAIN_LEN] =
56 /* this is my (102-key) keyboard layout, sorry if it doesn't quite match yours */
57 0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
58 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
59 0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
60 0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
61 0x56 /* the 102nd key (actually to the right of l-shift) */
64 static const WORD main_key_vkey_qwerty[MAIN_LEN] =
66 /* NOTE: this layout must concur with the scan codes layout above */
67 VK_OEM_3,VK_1,VK_2,VK_3,VK_4,VK_5,VK_6,VK_7,VK_8,VK_9,VK_0,VK_OEM_MINUS,VK_OEM_PLUS,
68 VK_Q,VK_W,VK_E,VK_R,VK_T,VK_Y,VK_U,VK_I,VK_O,VK_P,VK_OEM_4,VK_OEM_6,
69 VK_A,VK_S,VK_D,VK_F,VK_G,VK_H,VK_J,VK_K,VK_L,VK_OEM_1,VK_OEM_7,VK_OEM_5,
70 VK_Z,VK_X,VK_C,VK_V,VK_B,VK_N,VK_M,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,
71 VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */
74 static const WORD main_key_vkey_azerty[MAIN_LEN] =
76 /* NOTE: this layout must concur with the scan codes layout above */
77 VK_OEM_7,VK_1,VK_2,VK_3,VK_4,VK_5,VK_6,VK_7,VK_8,VK_9,VK_0,VK_OEM_4,VK_OEM_PLUS,
78 VK_A,VK_Z,VK_E,VK_R,VK_T,VK_Y,VK_U,VK_I,VK_O,VK_P,VK_OEM_6,VK_OEM_1,
79 VK_Q,VK_S,VK_D,VK_F,VK_G,VK_H,VK_J,VK_K,VK_L,VK_M,VK_OEM_3,VK_OEM_5,
80 VK_W,VK_X,VK_C,VK_V,VK_B,VK_N,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_8,
81 VK_OEM_102 /* the 102nd key (actually to the right of l-shift) */
84 /* FIXME: add other layouts, such as DVORAK and German QWERTZ */
86 /*** DEFINE YOUR NEW LANGUAGE-SPECIFIC MAPPINGS BELOW, SEE EXISTING TABLES */
88 /* the VK mappings for the main keyboard will be auto-assigned as before,
89 so what we have here is just the character tables */
90 /* order: Normal, Shift, AltGr, Shift-AltGr */
91 /* We recommend you write just what is guaranteed to be correct (i.e. what's
92 written on the keycaps), not the bunch of special characters behind AltGr
93 and Shift-AltGr if it can vary among different X servers */
94 /* Remember that your 102nd key (to the right of l-shift) should be on a
95 separate line, see existing tables */
96 /* If Wine fails to match your new table, use -debugmsg +key to find out why */
97 /* Remember to also add your new table to the layout index table far below! */
99 /*** United States keyboard layout (mostly contributed by Uwe Bonnes) */
100 static const char main_key_US[MAIN_LEN][4] =
102 "`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
103 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","[{","]}",
104 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:","'\"","\\|",
105 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?"
108 /*** United States keyboard layout (phantom key version) */
109 /* (XFree86 reports the <> key even if it's not physically there) */
110 static const char main_key_US_phantom[MAIN_LEN][4] =
112 "`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
113 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","[{","]}",
114 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:","'\"","\\|",
115 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
116 "<>" /* the phantom key */
119 /*** British keyboard layout */
120 static const char main_key_UK[MAIN_LEN][4] =
122 "`","1!","2\"","3£","4$","5%","6^","7&","8*","9(","0)","-_","=+",
123 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","[{","]}",
124 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:","'@","#~",
125 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
126 "\\|"
129 /*** French keyboard layout (contributed by Eric Pouech) */
130 static const char main_key_FR[MAIN_LEN][4] =
132 "²","&1","é2~","\"3#","'4{","(5[","-6|","è7","_8\\","ç9^±","à0@",")°]","=+}",
133 "aA","zZ","eE","rR","tT","yY","uU","iI","oO","pP","^¨","$£¤",
134 "qQ","sSß","dD","fF","gG","hH","jJ","kK","lL","mM","ù%","*µ",
135 "wW","xX","cC","vV","bB","nN",",?",";.",":/","!§",
136 "<>"
139 /*** Icelandic keyboard layout (contributed by Ríkharður Egilsson) */
140 static const char main_key_IS[MAIN_LEN][4] =
142 "°","1!","2\"","3#","4$","5%","6&","7/{","8([","9)]","0=}","öÖ\\","-_",
143 "qQ@","wW","eE","rR","tT","yY","uU","iI","oO","pP","ðÐ","'?~",
144 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","æÆ","´^","+*`",
145 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","þÞ",
146 "<>|"
149 /*** German keyboard layout (contributed by Ulrich Weigand) */
150 static const char main_key_DE[MAIN_LEN][4] =
152 "^°","1!","2\"","3§","4$","5%","6&","7/{","8([","9)]","0=}","ß?\\","'",
153 "qQ","wW","eE","rR","tT","zZ","uU","iI","oO","pP","üÜ","+*~",
154 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","öÖ","äÄ","#´",
155 "yY","xX","cC","vV","bB","nN","mM",",;",".:","-_",
156 "<>"
159 /*** German keyboard layout without dead keys */
160 static const char main_key_DE_nodead[MAIN_LEN][4] =
162 "^°","1!","2\"","3§","4$","5%","6&","7/{","8([","9)]","0=}","ß?\\","´",
163 "qQ","wW","eE","rR","tT","zZ","uU","iI","oO","pP","üÜ","+*~",
164 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","öÖ","äÄ","#'",
165 "yY","xX","cC","vV","bB","nN","mM",",;",".:","-_",
166 "<>"
169 /*** Swiss German keyboard layout (contributed by Jonathan Naylor) */
170 static const char main_key_SG[MAIN_LEN][4] =
172 "§°","1+|","2\"@","3*#","4ç","5%","6&¬","7/¦","8(¢","9)","0=","'?´","^`~",
173 "qQ","wW","eE","rR","tT","zZ","uU","iI","oO","pP","üè[","¨!]",
174 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","öé","äà{","$£}",
175 "yY","xX","cC","vV","bB","nN","mM",",;",".:","-_",
176 "<>\\"
179 /*** Swiss French keyboard layout (contributed by Philippe Froidevaux) */
180 static const char main_key_SF[MAIN_LEN][4] =
182 "§°","1+|","2\"@","3*#","4ç","5%","6&¬","7/¦","8(¢","9)","0=","'?´","^`~",
183 "qQ","wW","eE","rR","tT","zZ","uU","iI","oO","pP","èü[","¨!]",
184 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","éö","àä{","$£}",
185 "yY","xX","cC","vV","bB","nN","mM",",;",".:","-_",
186 "<>\\"
189 /*** Norwegian keyboard layout (contributed by Ove Kåven) */
190 static const char main_key_NO[MAIN_LEN][4] =
192 "|§","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?","\\`´",
193 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~",
194 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","øØ","æÆ","'*",
195 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
196 "<>"
199 /*** Danish keyboard layout (contributed by Bertho Stultiens) */
200 static const char main_key_DA[MAIN_LEN][4] =
202 "½§","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?","´`|",
203 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~",
204 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","æÆ","øØ","'*",
205 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
206 "<>\\"
209 /*** Swedish keyboard layout (contributed by Peter Bortas) */
210 static const char main_key_SE[MAIN_LEN][4] =
212 "§½","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?\\","´`",
213 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~",
214 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","öÖ","äÄ","'*",
215 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
216 "<>|"
219 /*** Canadian French keyboard layout */
220 static const char main_key_CF[MAIN_LEN][4] =
222 "#|\\","1!±","2\"@","3/£","4$¢","5%¤","6?¬","7&¦","8*²","9(³","0)¼","-_½","=+¾",
223 "qQ","wW","eE","rR","tT","yY","uU","iI","oO§","pP¶","^^[","¸¨]",
224 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:~","``{","<>}",
225 "zZ","xX","cC","vV","bB","nN","mM",",'-",".","éÉ",
226 "«»°"
229 /*** Portuguese keyboard layout */
230 static const char main_key_PT[MAIN_LEN][4] =
232 "\\¦","1!","2\"@","3#£","4$§","5%","6&","7/{","8([","9)]","0=}","'?","«»",
233 "qQ", "wW","eE", "rR", "tT", "yY", "uU", "iI", "oO", "pP", "+*\\¨","\\'\\`",
234 "aA", "sS","dD", "fF", "gG", "hH", "jJ", "kK", "lL", "çÇ", "ºª", "\\~\\^",
235 "zZ", "xX","cC", "vV", "bB", "nN", "mM", ",;", ".:", "-_",
236 "<>"
239 /*** Italian keyboard layout */
240 static const char main_key_IT[MAIN_LEN][4] =
242 "\\|","1!¹","2\"²","3£³","4$¼","5%½","6&¾","7/{","8([","9)]","0=}","'?`","ì^~",
243 "qQ@","wW","eE","rR","tT","yY","uU","iI","oOø","pPþ","èé[","+*]",
244 "aA","sSß","dDð","fF","gG","hH","jJ","kK","lL","òç@","à°#","ù§",
245 "zZ","xX","cC","vV","bB","nN","mMµ",",;",".:·","-_",
246 "<>|"
249 /*** Finnish keyboard layout */
250 static const char main_key_FI[MAIN_LEN][4] =
252 "","1!","2\"@","3#","4$","5%","6&","7/{","8([","9)]","0=}","+?\\","\'`",
253 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","","\"^~",
254 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","","","'*",
255 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
256 "<>|"
259 /*** Russian keyboard layout (contributed by Pavel Roskin) */
260 static const char main_key_RU[MAIN_LEN][4] =
262 "`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
263 "qQÊê","wWÃã","eEÕõ","rRËë","tTÅå","yYÎî","uUÇç","iIÛû","oOÝý","pPÚú","[{Èè","]}ßÿ",
264 "aAÆæ","sSÙù","dD×÷","fFÁá","gGÐð","hHÒò","jJÏï","kKÌì","lLÄä",";:Öö","'\"Üü","\\|",
265 "zZÑñ","xXÞþ","cCÓó","vVÍí","bBÉé","nNÔô","mMØø",",<Ââ",".>Àà","/?"
268 /*** Russian keyboard layout KOI8-R */
269 static const char main_key_RU_koi8r[MAIN_LEN][4] =
271 "()","1!","2\"","3/","4$","5:","6,","7.","8;","9?","0%","-_","=+",
272 "Êê","Ãã","Õõ","Ëë","Åå","Îî","Çç","Ûû","Ýý","Úú","Èè","ßÿ",
273 "Ææ","Ùù","×÷","Áá","Ðð","Òò","Ïï","Ìì","Ää","Öö","Üü","\\|",
274 "Ññ","Þþ","Óó","Íí","Éé","Ôô","Øø","Ââ","Àà","/?",
275 "<>" /* the phantom key */
278 /*** Spanish keyboard layout (contributed by José Marcos López) */
279 static const char main_key_ES[MAIN_LEN][4] =
281 "ºª\\","1!|","2\"@","3·#","4$","5%","6&¬","7/","8(","9)","0=","'?","¡¿",
282 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","`^[","+*]",
283 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","ñÑ","'¨{","çÇ}",
284 "zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
285 "<>"
288 /*** Belgian keyboard layout ***/
289 static const char main_key_BE[MAIN_LEN][4] =
291 "","&1|","é2@","\"3#","'4","(5","§6^","è7","!8","ç9{","à0}",")°","-_",
292 "aA","zZ","eE¤","rR","tT","yY","uU","iI","oO","pP","^¨[","$*]",
293 "qQ","sSß","dD","fF","gG","hH","jJ","kK","lL","mM","ù%´","µ£`",
294 "wW","xX","cC","vV","bB","nN",",?",";.",":/","=+~",
295 "<>\\"
298 /*** Hungarian keyboard layout (contributed by Zoltán Kovács) */
299 static const char main_key_HU[MAIN_LEN][4] =
301 "0§","1'~","2\"·","3+^","4!¢","5%°","6/²","7=`","8(ÿ","9)´","öÖ½","üܨ","óÓ¸",
302 "qQ\\","wW|","eE","rR","tT","zZ","uU","iIÍ","oOø","pP","õÕ÷","úÚ×",
303 "aA","sSð","dDÐ","fF[","gG]","hH","jJí","kK³","lL£","éÉ$","áÁß","ûÛ¤",
304 "yY>","xX#","cC&","vV@","bB{","nN}","mM",",?;",".:·","-_*",
305 "íÍ<"
308 /*** Polish (programmer's) keyboard layout ***/
309 static const char main_key_PL[MAIN_LEN][4] =
311 "`~","1!","2@","3#","4$","5%","6^","7&§","8*","9(","0)","-_","=+",
312 "qQ","wW","eEêÊ","rR","tT","yY","uU","iI","oOóÓ","pP","[{","]}",
313 "aA±¡","sS¶¦","dD","fF","gG","hH","jJ","kK","lL³£",";:","'\"","\\|",
314 "zZ¿¯","xX¼¬","cCæÆ","vV","bB","nNñÑ","mM",",<",".>","/?",
315 "<>|"
318 /*** Croatian keyboard layout specific for me <jelly@srk.fer.hr> ***/
319 static const char main_key_HR_jelly[MAIN_LEN][4] =
321 "`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
322 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","[{¹©","]}ðÐ",
323 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";:èÈ","'\"æÆ","\\|¾®",
324 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
325 "<>|"
328 /*** Croatian keyboard layout ***/
329 static const char main_key_HR[MAIN_LEN][4] =
331 "¸¨","1!","2\"·","3#^","4$¢","5%°","6&²","7/`","8(ÿ","9)´","0=½","'?¨","+*¸",
332 "qQ\\","wW|","eE","rR","tT","zZ","uU","iI","oO","pP","¹©÷","ðÐ×",
333 "aA","sS","dD","fF[","gG]","hH","jJ","kK³","lL£","èÈ","æÆß","¾®¤",
334 "yY","xX","cC","vV@","bB{","nN}","mM§",",;",".:","-_/",
335 "<>"
338 /*** Japanese 106 keyboard layout ***/
339 static const char main_key_JA_jp106[MAIN_LEN][4] =
341 "1!","2\"","3#","4$","5%","6&","7'","8(","9)","0~","-=","^~","\\|",
342 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","@`","[{",
343 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";+",":*","]}",
344 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
345 "\\_",
348 /*** Japanese pc98x1 keyboard layout ***/
349 static const char main_key_JA_pc98x1[MAIN_LEN][4] =
351 "1!","2\"","3#","4$","5%","6&","7'","8(","9)","0","-=","^`","\\|",
352 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","@~","[{",
353 "aA","sS","dD","fF","gG","hH","jJ","kK","lL",";+",":*","]}",
354 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
355 "\\_",
358 /*** Brazilian ABNT-2 keyboard layout (contributed by Raul Gomes Fernandes) */
359 static const char main_key_PT_br[MAIN_LEN][4] =
361 "'\"","1!","2@","3#","4$","5%","6\"","7&","8*","9(","0)","-_","=+",
362 "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","'`","[{",
363 "aA","sS","dD","fF","gG","hH","jJ","kK","lL","çÇ","~^","]}",
364 "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?"
368 /*** Layout table. Add your keyboard mappings to this list */
369 static const struct {
370 const char *comment;
371 const UINT layout_cp; /* Code page for this layout */
372 const char (*key)[MAIN_LEN][4];
373 const WORD (*scan)[MAIN_LEN]; /* scan codes mapping */
374 const WORD (*vkey)[MAIN_LEN]; /* virtual key codes mapping */
375 } main_key_tab[]={
376 {"United States keyboard layout", 28591, &main_key_US, &main_key_scan_qwerty, &main_key_vkey_qwerty},
377 {"United States keyboard layout (phantom key version)", 28591, &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
378 {"British keyboard layout", 28591, &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
379 {"German keyboard layout", 28591, &main_key_DE, &main_key_scan_qwerty, &main_key_vkey_qwerty},
380 {"German keyboard layout without dead keys", 28591, &main_key_DE_nodead, &main_key_scan_qwerty, &main_key_vkey_qwerty},
381 {"Swiss German keyboard layout", 28591, &main_key_SG, &main_key_scan_qwerty, &main_key_vkey_qwerty},
382 {"Swedish keyboard layout", 28591, &main_key_SE, &main_key_scan_qwerty, &main_key_vkey_qwerty},
383 {"Norwegian keyboard layout", 28591, &main_key_NO, &main_key_scan_qwerty, &main_key_vkey_qwerty},
384 {"Danish keyboard layout", 28591, &main_key_DA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
385 {"French keyboard layout", 28591, &main_key_FR, &main_key_scan_qwerty, &main_key_vkey_azerty},
386 {"Canadian French keyboard layout", 28591, &main_key_CF, &main_key_scan_qwerty, &main_key_vkey_qwerty},
387 {"Belgian keyboard layout", 28591, &main_key_BE, &main_key_scan_qwerty, &main_key_vkey_azerty},
388 {"Swiss French keyboard layout", 28591, &main_key_SF, &main_key_scan_qwerty, &main_key_vkey_qwerty},
389 {"Portuguese keyboard layout", 28591, &main_key_PT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
390 {"Brazilian ABNT-2 keyboard layout", 28591, &main_key_PT_br, &main_key_scan_qwerty, &main_key_vkey_qwerty},
391 {"Finnish keyboard layout", 28591, &main_key_FI, &main_key_scan_qwerty, &main_key_vkey_qwerty},
392 {"Russian keyboard layout", 20866, &main_key_RU, &main_key_scan_qwerty, &main_key_vkey_qwerty},
393 {"Russian keyboard layout KOI8-R", 20866, &main_key_RU_koi8r, &main_key_scan_qwerty, &main_key_vkey_qwerty},
394 {"Spanish keyboard layout", 28591, &main_key_ES, &main_key_scan_qwerty, &main_key_vkey_qwerty},
395 {"Italian keyboard layout", 28591, &main_key_IT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
396 {"Icelandic keyboard layout", 28591, &main_key_IS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
397 {"Hungarian keyboard layout", 28592, &main_key_HU, &main_key_scan_qwerty, &main_key_vkey_qwerty},
398 {"Polish (programmer's) keyboard layout", 28592, &main_key_PL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
399 {"Croatian keyboard layout", 28592, &main_key_HR, &main_key_scan_qwerty, &main_key_vkey_qwerty},
400 {"Croatian keyboard layout (specific)", 28592, &main_key_HR_jelly, &main_key_scan_qwerty, &main_key_vkey_qwerty},
401 {"Japanese 106 keyboard layout", 932, &main_key_JA_jp106, &main_key_scan_qwerty, &main_key_vkey_qwerty},
402 {"Japanese pc98x1 keyboard layout", 932, &main_key_JA_pc98x1, &main_key_scan_qwerty, &main_key_vkey_qwerty},
404 {NULL, 0, NULL, NULL, NULL} /* sentinel */
406 static unsigned kbd_layout=0; /* index into above table of layouts */
408 /* maybe more of these scancodes should be extended? */
409 /* extended must be set for ALT_R, CTRL_R,
410 INS, DEL, HOME, END, PAGE_UP, PAGE_DOWN, ARROW keys,
411 keypad / and keypad ENTER (SDK 3.1 Vol.3 p 138) */
412 /* FIXME should we set extended bit for NumLock ? My
413 * Windows does ... DF */
414 /* Yes, to distinguish based on scan codes, also
415 for PrtScn key ... GA */
417 static const WORD special_key_vkey[] =
419 VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, /* FF08 */
420 0, 0, 0, VK_PAUSE, VK_SCROLL, 0, 0, 0, /* FF10 */
421 0, 0, 0, VK_ESCAPE /* FF18 */
423 static const WORD special_key_scan[] =
425 0x0E, 0x0F, 0, /*?*/ 0, 0, 0x1C, 0, 0, /* FF08 */
426 0, 0, 0, 0x45, 0x46, 0 , 0, 0, /* FF10 */
427 0, 0, 0, 0x01 /* FF18 */
430 static const WORD cursor_key_vkey[] =
432 VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_PRIOR,
433 VK_NEXT, VK_END /* FF50 */
435 static const WORD cursor_key_scan[] =
437 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F /* FF50 */
440 static const WORD misc_key_vkey[] =
442 VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, 0, /* FF60 */
443 VK_CANCEL, VK_HELP, VK_CANCEL, VK_CANCEL /* FF68 */
445 static const WORD misc_key_scan[] =
447 /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0, 0, 0, 0, /* FF60 */
448 /*?*/ 0, /*?*/ 0, 0x38, 0x146 /* FF68 */
451 static const WORD keypad_key_vkey[] =
453 0, VK_NUMLOCK, /* FF7E */
454 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */
455 0, 0, 0, 0, 0, VK_RETURN, 0, 0, /* FF88 */
456 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, /* FF90 */
457 VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, 0,
458 VK_INSERT, VK_DELETE, /* FF98 */
459 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */
460 0, 0, VK_MULTIPLY, VK_ADD, VK_SEPARATOR, VK_SUBTRACT,
461 VK_DECIMAL, VK_DIVIDE, /* FFA8 */
462 VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4,
463 VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* FFB0 */
464 VK_NUMPAD8, VK_NUMPAD9 /* FFB8 */
466 static const WORD keypad_key_scan[] =
468 0x138, 0x145, /* FF7E */
469 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */
470 0, 0, 0, 0, 0, 0x11C, 0, 0, /* FF88 */
471 0, 0, 0, 0, 0, 0x47, 0x4B, 0x48, /* FF90 */
472 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */
473 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */
474 0, 0, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135, /* FFA8 */
475 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */
476 0x48, 0x49 /* FFB8 */
479 static const WORD function_key_vkey[] =
481 VK_F1, VK_F2, /* FFBE */
482 VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, /* FFC0 */
483 VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16 /* FFC8 */
485 static const WORD function_key_scan[] =
487 0x3B, 0x3C, /* FFBE */
488 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */
489 0x57, 0x58, 0, 0, 0, 0 /* FFC8 */
492 static const WORD modifier_key_vkey[] =
494 VK_SHIFT, VK_SHIFT, VK_CONTROL, VK_CONTROL, VK_CAPITAL, 0, /* FFE1 */
495 VK_MENU, VK_MENU, VK_MENU, VK_MENU /* FFE7 */
497 static const WORD modifier_key_scan[] =
499 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0, /* FFE1 */
500 0x38, 0x138, 0x38, 0x138 /* FFE7 */
503 /* Returns the Windows virtual key code associated with the X event <e> */
504 static WORD EVENT_event_to_vkey( XKeyEvent *e)
506 KeySym keysym;
508 TSXLookupString(e, NULL, 0, &keysym, NULL);
510 if ((keysym >= 0xFFAE) && (keysym <= 0xFFB9) && (keysym != 0xFFAF)
511 && (e->state & NumLockMask))
512 /* Only the Keypad keys 0-9 and . send different keysyms
513 * depending on the NumLock state */
514 return keypad_key_vkey[(keysym & 0xFF) - 0x7E];
516 return keyc2vkey[e->keycode];
519 static BOOL NumState=FALSE, CapsState=FALSE, AltGrState=FALSE;
521 /**********************************************************************
522 * KEYBOARD_GenerateMsg
524 * Generate Down+Up messages when NumLock or CapsLock is pressed.
526 * Convention : called with vkey only VK_NUMLOCK or VK_CAPITAL
529 static void KEYBOARD_GenerateMsg( WORD vkey, WORD scan, int Evtype, INT event_x, INT event_y,
530 DWORD event_time )
532 BOOL * State = (vkey==VK_NUMLOCK? &NumState : &CapsState);
533 DWORD up, down;
535 if (*State) {
536 /* The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
537 don't treat it. It's from the same key press. Then the state goes to ON.
538 And from there, a 'release' event will switch off the toggle key. */
539 *State=FALSE;
540 TRACE("INTERM : don\'t treat release of toggle key. InputKeyStateTable[%#x] = %#x\n",vkey,pKeyStateTable[vkey]);
541 } else
543 down = (vkey==VK_NUMLOCK ? KEYEVENTF_EXTENDEDKEY : 0);
544 up = (vkey==VK_NUMLOCK ? KEYEVENTF_EXTENDEDKEY : 0) | KEYEVENTF_KEYUP;
545 if ( pKeyStateTable[vkey] & 0x1 ) /* it was ON */
547 if (Evtype!=KeyPress)
549 TRACE("ON + KeyRelease => generating DOWN and UP messages.\n");
550 KEYBOARD_SendEvent( vkey, scan, down,
551 event_x, event_y, event_time );
552 KEYBOARD_SendEvent( vkey, scan, up,
553 event_x, event_y, event_time );
554 *State=FALSE;
555 pKeyStateTable[vkey] &= ~0x01; /* Toggle state to off. */
558 else /* it was OFF */
559 if (Evtype==KeyPress)
561 TRACE("OFF + Keypress => generating DOWN and UP messages.\n");
562 KEYBOARD_SendEvent( vkey, scan, down,
563 event_x, event_y, event_time );
564 KEYBOARD_SendEvent( vkey, scan, up,
565 event_x, event_y, event_time );
566 *State=TRUE; /* Goes to intermediary state before going to ON */
567 pKeyStateTable[vkey] |= 0x01; /* Toggle state to on. */
572 /***********************************************************************
573 * KEYBOARD_UpdateOneState
575 * Updates internal state for <vkey>, depending on key <state> under X
578 static void KEYBOARD_UpdateOneState ( int vkey, int state )
580 /* Do something if internal table state != X state for keycode */
581 if (((pKeyStateTable[vkey] & 0x80)!=0) != state)
583 TRACE("Adjusting state for vkey %#.2x. State before %#.2x \n",
584 vkey, pKeyStateTable[vkey]);
586 /* Fake key being pressed inside wine */
587 KEYBOARD_SendEvent( vkey, 0, state? 0 : KEYEVENTF_KEYUP,
588 0, 0, GetTickCount() );
590 TRACE("State after %#.2x \n",pKeyStateTable[vkey]);
594 /***********************************************************************
595 * X11DRV_KEYBOARD_UpdateState
597 * Update modifiers state (Ctrl, Alt, Shift)
598 * when window is activated (called by EVENT_FocusIn in event.c)
600 * This handles the case where one uses Ctrl+... Alt+... or Shift+.. to switch
601 * from wine to another application and back.
602 * Toggle keys are handled in HandleEvent. (because XQueryKeymap says nothing
603 * about them)
605 void X11DRV_KEYBOARD_UpdateState ( void )
607 /* extract a bit from the char[32] bit suite */
608 #define KeyState(keycode) ((keys_return[keycode/8] & (1<<(keycode%8)))!=0)
610 char keys_return[32];
612 TRACE("called\n");
613 if (!TSXQueryKeymap(display, keys_return)) {
614 ERR("Error getting keymap !");
615 return;
618 /* Adjust the ALT and CONTROL state if any has been changed outside wine */
619 KEYBOARD_UpdateOneState(VK_MENU, KeyState(kcAlt));
620 KEYBOARD_UpdateOneState(VK_CONTROL, KeyState(kcControl));
621 KEYBOARD_UpdateOneState(VK_SHIFT, KeyState(kcShift));
622 #undef KeyState
625 /***********************************************************************
626 * X11DRV_KEYBOARD_HandleEvent
628 * Handle a X key event
630 void X11DRV_KEYBOARD_HandleEvent( WND *pWnd, XKeyEvent *event )
632 char Str[24];
633 KeySym keysym;
634 WORD vkey = 0, bScan;
635 DWORD dwFlags;
636 static BOOL force_extended = FALSE; /* hack for AltGr translation */
637 int ascii_chars;
639 INT event_x = (pWnd? pWnd->rectWindow.left : 0) + event->x;
640 INT event_y = (pWnd? pWnd->rectWindow.top : 0) + event->y;
641 DWORD event_time = event->time - X11DRV_server_startticks;
643 /* this allows support for dead keys */
644 if ((event->keycode >> 8) == 0x10)
645 event->keycode=(event->keycode & 0xff);
647 ascii_chars = TSXLookupString(event, Str, sizeof(Str), &keysym, NULL);
649 TRACE_(key)("state = %X\n", event->state);
651 /* If XKB extensions is used, the state mask for AltGr will used the group
652 index instead of the modifier mask. The group index is set in bits
653 13-14 of the state field in the XKeyEvent structure. So if AltGr is
654 pressed, look if the group index is diferent than 0. From XKB
655 extension documentation, the group index should for AltGr should
656 be 2 (event->state = 0x2000). It's probably better to not assume a
657 predefined group index and find it dynamically
659 Ref: X Keyboard Extension: Library specification (section 14.1.1 and 17.1.1) */
660 if ( AltGrState && (event->state & 0x6000) )
661 AltGrMask = event->state & 0x6000;
663 if (keysym == XK_Mode_switch)
665 TRACE_(key)("Alt Gr key event received\n");
666 event->keycode = kcControl; /* Simulate Control */
667 X11DRV_KEYBOARD_HandleEvent( pWnd, event );
669 event->keycode = kcAlt; /* Simulate Alt */
670 force_extended = TRUE;
671 X11DRV_KEYBOARD_HandleEvent( pWnd, event );
672 force_extended = FALSE;
674 /* Here we save the pressed/released state of the AltGr key, to be able to
675 identify the group index associated with AltGr on the next key pressed *
676 see comment above. */
677 AltGrState = (event->type == KeyPress) ? TRUE : FALSE;
679 return;
682 Str[ascii_chars] = '\0';
683 if (TRACE_ON(key)){
684 char *ksname;
686 ksname = TSXKeysymToString(keysym);
687 if (!ksname)
688 ksname = "No Name";
689 TRACE_(key)("%s : keysym=%lX (%s), ascii chars=%u / %X / '%s'\n",
690 (event->type == KeyPress) ? "KeyPress" : "KeyRelease",
691 keysym, ksname, ascii_chars, Str[0] & 0xff, Str);
694 vkey = EVENT_event_to_vkey(event);
695 if (force_extended) vkey |= 0x100;
697 TRACE_(key)("keycode 0x%x converted to vkey 0x%x\n",
698 event->keycode, vkey);
700 if (vkey)
702 switch (vkey & 0xff)
704 case VK_NUMLOCK:
705 KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, event->type, event_x, event_y,
706 event_time );
707 break;
708 case VK_CAPITAL:
709 TRACE("Caps Lock event. (type %d). State before : %#.2x\n",event->type,pKeyStateTable[vkey]);
710 KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, event->type, event_x, event_y,
711 event_time );
712 TRACE("State after : %#.2x\n",pKeyStateTable[vkey]);
713 break;
714 default:
715 /* Adjust the NUMLOCK state if it has been changed outside wine */
716 if (!(pKeyStateTable[VK_NUMLOCK] & 0x01) != !(event->state & NumLockMask))
718 TRACE("Adjusting NumLock state. \n");
719 KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, KeyPress, event_x, event_y,
720 event_time );
721 KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, KeyRelease, event_x, event_y,
722 event_time );
724 /* Adjust the CAPSLOCK state if it has been changed outside wine */
725 if (!(pKeyStateTable[VK_CAPITAL] & 0x01) != !(event->state & LockMask))
727 TRACE("Adjusting Caps Lock state.\n");
728 KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, KeyPress, event_x, event_y,
729 event_time );
730 KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, KeyRelease, event_x, event_y,
731 event_time );
733 /* Not Num nor Caps : end of intermediary states for both. */
734 NumState = FALSE;
735 CapsState = FALSE;
737 bScan = keyc2scan[event->keycode] & 0xFF;
738 TRACE_(key)("bScan = 0x%02x.\n", bScan);
740 dwFlags = 0;
741 if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP;
742 if ( vkey & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY;
743 if ( force_extended ) dwFlags |= KEYEVENTF_WINE_FORCEEXTENDED;
745 KEYBOARD_SendEvent( vkey & 0xff, bScan, dwFlags,
746 event_x, event_y, event_time );
751 /**********************************************************************
752 * X11DRV_KEYBOARD_DetectLayout
754 * Called from X11DRV_InitKeyboard
755 * This routine walks through the defined keyboard layouts and selects
756 * whichever matches most closely.
758 static void
759 X11DRV_KEYBOARD_DetectLayout (void)
761 unsigned current, match, mismatch, seq;
762 int score, keyc, i, key, pkey, ok, syms;
763 KeySym keysym;
764 const char (*lkey)[MAIN_LEN][4];
765 unsigned max_seq = 0;
766 int max_score = 0, ismatch = 0;
767 char ckey[4] =
768 {0, 0, 0, 0};
770 syms = keysyms_per_keycode;
771 if (syms > 4) {
772 WARN("%d keysyms per keycode not supported, set to 4", syms);
773 syms = 4;
775 for (current = 0; main_key_tab[current].comment; current++) {
776 TRACE("Attempting to match against \"%s\"\n", main_key_tab[current].comment);
777 match = 0;
778 mismatch = 0;
779 score = 0;
780 seq = 0;
781 lkey = main_key_tab[current].key;
782 pkey = -1;
783 for (keyc = min_keycode; keyc <= max_keycode; keyc++) {
784 /* get data for keycode from X server */
785 for (i = 0; i < syms; i++) {
786 keysym = TSXKeycodeToKeysym (display, keyc, i);
787 /* Allow both one-byte and two-byte national keysyms */
788 if ((keysym < 0x800) && (keysym != ' '))
789 ckey[i] = keysym & 0xFF;
790 else {
791 ckey[i] = KEYBOARD_MapDeadKeysym(keysym);
794 if (ckey[0]) {
795 /* search for a match in layout table */
796 /* right now, we just find an absolute match for defined positions */
797 /* (undefined positions are ignored, so if it's defined as "3#" in */
798 /* the table, it's okay that the X server has "3#£", for example) */
799 /* however, the score will be higher for longer matches */
800 for (key = 0; key < MAIN_LEN; key++) {
801 for (ok = 0, i = 0; (ok >= 0) && (i < syms); i++) {
802 if ((*lkey)[key][i] && ((*lkey)[key][i] == ckey[i]))
803 ok++;
804 if ((*lkey)[key][i] && ((*lkey)[key][i] != ckey[i]))
805 ok = -1;
807 if (ok > 0) {
808 score += ok;
809 break;
812 /* count the matches and mismatches */
813 if (ok > 0) {
814 match++;
815 /* and how much the keycode order matches */
816 if (key > pkey) seq++;
817 pkey = key;
818 } else {
819 TRACE_(key)("mismatch for keycode %d, character %c\n", keyc,
820 ckey[0]);
821 mismatch++;
822 score -= syms;
826 TRACE("matches=%d, mismatches=%d, score=%d\n",
827 match, mismatch, score);
828 if ((score > max_score) ||
829 ((score == max_score) && (seq > max_seq))) {
830 /* best match so far */
831 kbd_layout = current;
832 max_score = score;
833 max_seq = seq;
834 ismatch = !mismatch;
837 /* we're done, report results if necessary */
838 if (!ismatch) {
839 FIXME(
840 "Your keyboard layout was not found!\n"
841 "Instead of using closest match (%s) for scancode mapping.\n"
842 "Please define your layout in windows/x11drv/keyboard.c and submit them\n"
843 "to us for inclusion into future Wine releases.\n"
844 "See documentation/keyboard for more information.\n",
845 main_key_tab[kbd_layout].comment);
848 TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment);
851 /**********************************************************************
852 * X11DRV_InitKeyboard
854 void X11DRV_InitKeyboard(void)
856 KeySym *ksp;
857 XModifierKeymap *mmp;
858 KeySym keysym;
859 KeyCode *kcp;
860 XKeyEvent e2;
861 WORD scan, vkey, OEMvkey;
862 int keyc, i, keyn, syms;
863 char ckey[4]={0,0,0,0};
864 const char (*lkey)[MAIN_LEN][4];
866 TSXDisplayKeycodes(display, &min_keycode, &max_keycode);
867 ksp = TSXGetKeyboardMapping(display, min_keycode,
868 max_keycode + 1 - min_keycode, &keysyms_per_keycode);
869 /* We are only interested in keysyms_per_keycode.
870 There is no need to hold a local copy of the keysyms table */
871 TSXFree(ksp);
872 mmp = TSXGetModifierMapping(display);
873 kcp = mmp->modifiermap;
874 for (i = 0; i < 8; i += 1) /* There are 8 modifier keys */
876 int j;
878 for (j = 0; j < mmp->max_keypermod; j += 1, kcp += 1)
879 if (*kcp)
881 int k;
883 for (k = 0; k < keysyms_per_keycode; k += 1)
884 if (TSXKeycodeToKeysym(display, *kcp, k) == XK_Mode_switch)
886 AltGrMask = 1 << i;
887 TRACE_(key)("AltGrMask is %x\n", AltGrMask);
889 else if (TSXKeycodeToKeysym(display, *kcp, k) == XK_Num_Lock)
891 NumLockMask = 1 << i;
892 TRACE_(key)("NumLockMask is %x\n", NumLockMask);
896 TSXFreeModifiermap(mmp);
898 /* Detect the keyboard layout */
899 X11DRV_KEYBOARD_DetectLayout();
900 lkey = main_key_tab[kbd_layout].key;
901 syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode;
903 /* Now build two conversion arrays :
904 * keycode -> vkey + scancode + extended
905 * vkey + extended -> keycode */
907 e2.display = display;
908 e2.state = 0;
910 OEMvkey = VK_OEM_7; /* next is available. */
911 for (keyc = min_keycode; keyc <= max_keycode; keyc++)
913 e2.keycode = (KeyCode)keyc;
914 TSXLookupString(&e2, NULL, 0, &keysym, NULL);
915 vkey = 0; scan = 0;
916 if (keysym) /* otherwise, keycode not used */
918 if ((keysym >> 8) == 0xFF) /* non-character key */
920 int key = keysym & 0xff;
922 if (key >= 0x08 && key <= 0x1B) { /* special key */
923 vkey = special_key_vkey[key - 0x08];
924 scan = special_key_scan[key - 0x08];
925 } else if (key >= 0x50 && key <= 0x57) { /* cursor key */
926 vkey = cursor_key_vkey[key - 0x50];
927 scan = cursor_key_scan[key - 0x50];
928 } else if (key >= 0x60 && key <= 0x6B) { /* miscellaneous key */
929 vkey = misc_key_vkey[key - 0x60];
930 scan = misc_key_scan[key - 0x60];
931 } else if (key >= 0x7E && key <= 0xB9) { /* keypad key */
932 vkey = keypad_key_vkey[key - 0x7E];
933 scan = keypad_key_scan[key - 0x7E];
934 } else if (key >= 0xBE && key <= 0xCD) { /* function key */
935 vkey = function_key_vkey[key - 0xBE] | 0x100; /* set extended bit */
936 scan = function_key_scan[key - 0xBE];
937 } else if (key >= 0xE1 && key <= 0xEA) { /* modifier key */
938 vkey = modifier_key_vkey[key - 0xE1];
939 scan = modifier_key_scan[key - 0xE1];
940 } else if (key == 0xFF) { /* DEL key */
941 vkey = VK_DELETE;
942 scan = 0x153;
944 /* set extended bit when necessary */
945 if (scan & 0x100) vkey |= 0x100;
946 } else if (keysym == 0x20) { /* Spacebar */
947 vkey = VK_SPACE;
948 scan = 0x39;
949 } else {
950 /* we seem to need to search the layout-dependent scancodes */
951 int maxlen=0,maxval=-1,ok;
952 for (i=0; i<syms; i++) {
953 keysym = TSXKeycodeToKeysym(display, keyc, i);
954 if ((keysym<0x800) && (keysym!=' ')) {
955 ckey[i] = keysym & 0xFF;
956 } else {
957 ckey[i] = KEYBOARD_MapDeadKeysym(keysym);
960 /* find key with longest match streak */
961 for (keyn=0; keyn<MAIN_LEN; keyn++) {
962 for (ok=(*lkey)[keyn][i=0]; ok&&(i<4); i++)
963 if ((*lkey)[keyn][i] && (*lkey)[keyn][i]!=ckey[i]) ok=0;
964 if (ok||(i>maxlen)) {
965 maxlen=i; maxval=keyn;
967 if (ok) break;
969 if (maxval>=0) {
970 /* got it */
971 const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan;
972 const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey;
973 scan = (*lscan)[maxval];
974 vkey = (*lvkey)[maxval];
978 /* find a suitable layout-dependent VK code */
979 /* (most Winelib apps ought to be able to work without layout tables!) */
980 for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++)
982 keysym = TSXLookupKeysym(&e2, i);
983 if ((keysym >= VK_0 && keysym <= VK_9)
984 || (keysym >= VK_A && keysym <= VK_Z)) {
985 vkey = keysym;
989 for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++)
991 keysym = TSXLookupKeysym(&e2, i);
992 switch (keysym)
994 case ';': vkey = VK_OEM_1; break;
995 case '/': vkey = VK_OEM_2; break;
996 case '`': vkey = VK_OEM_3; break;
997 case '[': vkey = VK_OEM_4; break;
998 case '\\': vkey = VK_OEM_5; break;
999 case ']': vkey = VK_OEM_6; break;
1000 case '\'': vkey = VK_OEM_7; break;
1001 case ',': vkey = VK_OEM_COMMA; break;
1002 case '.': vkey = VK_OEM_PERIOD; break;
1003 case '-': vkey = VK_OEM_MINUS; break;
1004 case '+': vkey = VK_OEM_PLUS; break;
1008 if (!vkey)
1010 /* Others keys: let's assign OEM virtual key codes in the allowed range,
1011 * that is ([0xba,0xc0], [0xdb,0xe4], 0xe6 (given up) et [0xe9,0xf5]) */
1012 switch (++OEMvkey)
1014 case 0xc1 : OEMvkey=0xdb; break;
1015 case 0xe5 : OEMvkey=0xe9; break;
1016 case 0xf6 : OEMvkey=0xf5; WARN("No more OEM vkey available!\n");
1019 vkey = OEMvkey;
1021 if (TRACE_ON(keyboard))
1023 TRACE("OEM specific virtual key %X assigned to keycode %X:\n",
1024 OEMvkey, e2.keycode);
1025 TRACE("(");
1026 for (i = 0; i < keysyms_per_keycode; i += 1)
1028 char *ksname;
1030 keysym = TSXLookupKeysym(&e2, i);
1031 ksname = TSXKeysymToString(keysym);
1032 if (!ksname)
1033 ksname = "NoSymbol";
1034 DPRINTF( "%lX (%s) ", keysym, ksname);
1036 DPRINTF(")\n");
1040 keyc2vkey[e2.keycode] = vkey;
1041 keyc2scan[e2.keycode] = scan;
1042 } /* for */
1044 /* If some keys still lack scancodes, assign some arbitrary ones to them now */
1045 for (scan = 0x60, keyc = min_keycode; keyc <= max_keycode; keyc++)
1046 if (keyc2vkey[keyc]&&!keyc2scan[keyc]) {
1047 char *ksname;
1048 keysym = TSXKeycodeToKeysym(display, keyc, 0);
1049 ksname = TSXKeysymToString(keysym);
1050 if (!ksname) ksname = "NoSymbol";
1052 /* should make sure the scancode is unassigned here, but >=0x60 currently always is */
1054 TRACE_(key)("assigning scancode %02x to unidentified keycode %02x (%s)\n",scan,keyc,ksname);
1055 keyc2scan[keyc]=scan++;
1058 /* Now store one keycode for each modifier. Used to simulate keypresses. */
1059 kcControl = TSXKeysymToKeycode(display, XK_Control_L);
1060 kcAlt = TSXKeysymToKeycode(display, XK_Alt_L);
1061 if (!kcAlt) kcAlt = TSXKeysymToKeycode(display, XK_Meta_L);
1062 kcShift = TSXKeysymToKeycode(display, XK_Shift_L);
1063 kcNumLock = TSXKeysymToKeycode(display, XK_Num_Lock);
1064 kcCapsLock = TSXKeysymToKeycode(display, XK_Caps_Lock);
1067 /***********************************************************************
1068 * X11DRV_VkKeyScan
1070 WORD X11DRV_VkKeyScan(CHAR cChar)
1072 KeyCode keycode;
1073 KeySym keysym;
1074 int i,index;
1075 int highbyte=0;
1077 /* char->keysym (same for ANSI chars) */
1078 keysym=(unsigned char) cChar;/* (!) cChar is signed */
1079 if (keysym<=27) keysym+=0xFF00;/*special chars : return, backspace...*/
1081 keycode = TSXKeysymToKeycode(display, keysym); /* keysym -> keycode */
1082 if (!keycode)
1083 { /* It didn't work ... let's try with deadchar code. */
1084 keycode = TSXKeysymToKeycode(display, keysym | 0xFE00);
1087 TRACE("VkKeyScan '%c'(%#lx, %lu): got keycode %#.2x\n",
1088 cChar,keysym,keysym,keycode);
1090 if (keycode)
1092 for (index=-1, i=0; (i<8) && (index<0); i++) /* find shift state */
1093 if (TSXKeycodeToKeysym(display,keycode,i)==keysym) index=i;
1094 switch (index) {
1095 case -1 :
1096 WARN("Keysym %lx not found while parsing the keycode table\n",keysym); break;
1097 case 0 : break;
1098 case 1 : highbyte = 0x0100; break;
1099 case 2 : highbyte = 0x0600; break;
1100 case 3 : highbyte = 0x0700; break;
1101 default : ERR("index %d found by XKeycodeToKeysym. please report! \n",index);
1104 index : 0 adds 0x0000
1105 index : 1 adds 0x0100 (shift)
1106 index : ? adds 0x0200 (ctrl)
1107 index : 2 adds 0x0600 (ctrl+alt)
1108 index : 3 adds 0x0700 (ctrl+alt+shift)
1111 TRACE(" ... returning %#.2x\n", keyc2vkey[keycode]+highbyte);
1112 return keyc2vkey[keycode]+highbyte; /* keycode -> (keyc2vkey) vkey */
1115 /***********************************************************************
1116 * X11DRV_MapVirtualKey
1118 UINT16 X11DRV_MapVirtualKey(UINT16 wCode, UINT16 wMapType)
1120 #define returnMVK(value) { TRACE("returning 0x%x.\n",value); return value; }
1122 TRACE("MapVirtualKey wCode=0x%x wMapType=%d ... \n", wCode,wMapType);
1123 switch(wMapType) {
1124 case 0: { /* vkey-code to scan-code */
1125 /* let's do vkey -> keycode -> scan */
1126 int keyc;
1127 for (keyc=min_keycode; keyc<=max_keycode; keyc++)
1128 if ((keyc2vkey[keyc] & 0xFF) == wCode)
1129 returnMVK (keyc2scan[keyc] & 0xFF);
1130 TRACE("returning no scan-code.\n");
1131 return 0; }
1133 case 1: { /* scan-code to vkey-code */
1134 /* let's do scan -> keycode -> vkey */
1135 int keyc;
1136 for (keyc=min_keycode; keyc<=max_keycode; keyc++)
1137 if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
1138 returnMVK (keyc2vkey[keyc] & 0xFF);
1139 TRACE("returning no vkey-code.\n");
1140 return 0; }
1142 case 2: { /* vkey-code to unshifted ANSI code */
1143 /* (was FIXME) : what does unshifted mean ? 'a' or 'A' ? */
1144 /* My Windows returns 'A'. */
1145 /* let's do vkey -> keycode -> (XLookupString) ansi char */
1146 XKeyEvent e;
1147 KeySym keysym;
1148 int keyc;
1149 char s[2];
1150 e.display = display;
1151 e.state = 0; /* unshifted */
1153 e.keycode = 0;
1154 /* We exit on the first keycode found, to speed up the thing. */
1155 for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
1156 { /* Find a keycode that could have generated this virtual key */
1157 if ((keyc2vkey[keyc] & 0xFF) == wCode)
1158 { /* We filter the extended bit, we don't know it */
1159 e.keycode = keyc; /* Store it temporarily */
1160 if ((EVENT_event_to_vkey(&e) & 0xFF) != wCode) {
1161 e.keycode = 0; /* Wrong one (ex: because of the NumLock
1162 state), so set it to 0, we'll find another one */
1167 if ((wCode>=VK_NUMPAD0) && (wCode<=VK_NUMPAD9))
1168 e.keycode = TSXKeysymToKeycode(e.display, wCode-VK_NUMPAD0+XK_KP_0);
1170 if (wCode==VK_DECIMAL)
1171 e.keycode = TSXKeysymToKeycode(e.display, XK_KP_Decimal);
1173 if (!e.keycode)
1175 WARN("Unknown virtual key %X !!! \n", wCode);
1176 return 0; /* whatever */
1178 TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
1180 if (TSXLookupString(&e, s, 2, &keysym, NULL))
1181 returnMVK (*s);
1183 TRACE("returning no ANSI.\n");
1184 return 0;
1187 case 3: /* **NT only** scan-code to vkey-code but distinguish between */
1188 /* left and right */
1189 FIXME(" stub for NT\n");
1190 return 0;
1192 default: /* reserved */
1193 WARN("Unknown wMapType %d !\n", wMapType);
1194 return 0;
1196 return 0;
1199 /***********************************************************************
1200 * X11DRV_GetKeyNameText
1202 INT16 X11DRV_GetKeyNameText(LONG lParam, LPSTR lpBuffer, INT16 nSize)
1204 int vkey, ansi, scanCode;
1205 KeyCode keyc;
1206 KeySym keys;
1207 char *name;
1209 scanCode = lParam >> 16;
1210 scanCode &= 0x1ff; /* keep "extended-key" flag with code */
1212 /* FIXME: should use MVK type 3 (NT version that distinguishes right and left */
1213 vkey = X11DRV_MapVirtualKey(scanCode, 1);
1215 /* handle "don't care" bit (0x02000000) */
1216 if (!(lParam & 0x02000000)) {
1217 switch (vkey) {
1218 case VK_LSHIFT:
1219 case VK_RSHIFT:
1220 vkey = VK_SHIFT;
1221 break;
1222 case VK_LCONTROL:
1223 case VK_RCONTROL:
1224 vkey = VK_CONTROL;
1225 break;
1226 case VK_LMENU:
1227 case VK_RMENU:
1228 vkey = VK_MENU;
1229 break;
1230 default:
1231 break;
1235 ansi = X11DRV_MapVirtualKey(vkey, 2);
1236 TRACE("scan 0x%04x, vkey 0x%04x, ANSI 0x%04x\n", scanCode, vkey, ansi);
1238 /* first get the name of the "regular" keys which is the Upper case
1239 value of the keycap imprint. */
1240 if ( ((ansi >= 0x21) && (ansi <= 0x7e)) &&
1241 (scanCode != 0x137) && /* PrtScn */
1242 (scanCode != 0x135) && /* numpad / */
1243 (scanCode != 0x37 ) && /* numpad * */
1244 (scanCode != 0x4a ) && /* numpad - */
1245 (scanCode != 0x4e ) ) /* numpad + */
1247 if ((nSize >= 2) && lpBuffer)
1249 *lpBuffer = toupper((char)ansi);
1250 *(lpBuffer+1) = 0;
1251 return 1;
1253 else
1254 return 0;
1257 /* FIXME: horrible hack to fix function keys. Windows reports scancode
1258 without "extended-key" flag. However Wine generates scancode
1259 *with* "extended-key" flag. Seems to occur *only* for the
1260 function keys. Soooo.. We will leave the table alone and
1261 fudge the lookup here till the other part is found and fixed!!! */
1263 if ( ((scanCode >= 0x13b) && (scanCode <= 0x144)) ||
1264 (scanCode == 0x157) || (scanCode == 0x158))
1265 scanCode &= 0xff; /* remove "extended-key" flag for Fx keys */
1267 /* let's do scancode -> keycode -> keysym -> String */
1269 for (keyc=min_keycode; keyc<=max_keycode; keyc++)
1270 if ((keyc2scan[keyc]) == scanCode)
1271 break;
1272 if (keyc <= max_keycode)
1274 keys = TSXKeycodeToKeysym(display, keyc, 0);
1275 name = TSXKeysymToString(keys);
1276 TRACE("found scan=%04x keyc=%04x keysym=%04x string=%s\n",
1277 scanCode, keyc, (int)keys, name);
1278 if (lpBuffer && nSize && name)
1280 lstrcpynA(lpBuffer, name, nSize);
1281 return 1;
1285 /* Finally issue FIXME for unknown keys */
1287 FIXME("(%08lx,%p,%d): unsupported key, vkey=%04x, ansi=%04x\n",lParam,lpBuffer,nSize,vkey,ansi);
1288 if (lpBuffer && nSize)
1289 *lpBuffer = 0;
1290 return 0;
1293 /***********************************************************************
1294 * X11DRV_KEYBOARD_MapDeadKeysym
1296 static char KEYBOARD_MapDeadKeysym(KeySym keysym)
1298 switch (keysym)
1300 /* symbolic ASCII is the same as defined in rfc1345 */
1301 #ifdef XK_dead_tilde
1302 case XK_dead_tilde :
1303 #endif
1304 case 0x1000FE7E : /* Xfree's XK_Dtilde */
1305 return '~'; /* '? */
1306 #ifdef XK_dead_acute
1307 case XK_dead_acute :
1308 #endif
1309 case 0x1000FE27 : /* Xfree's XK_Dacute_accent */
1310 return 0xb4; /* '' */
1311 #ifdef XK_dead_circumflex
1312 case XK_dead_circumflex:
1313 #endif
1314 case 0x1000FE5E : /* Xfree's XK_Dcircumflex_accent */
1315 return '^'; /* '> */
1316 #ifdef XK_dead_grave
1317 case XK_dead_grave :
1318 #endif
1319 case 0x1000FE60 : /* Xfree's XK_Dgrave_accent */
1320 return '`'; /* '! */
1321 #ifdef XK_dead_diaeresis
1322 case XK_dead_diaeresis :
1323 #endif
1324 case 0x1000FE22 : /* Xfree's XK_Ddiaeresis */
1325 return 0xa8; /* ': */
1326 #ifdef XK_dead_cedilla
1327 case XK_dead_cedilla :
1328 return 0xb8; /* ', */
1329 #endif
1330 #ifdef XK_dead_macron
1331 case XK_dead_macron :
1332 return '-'; /* 'm isn't defined on iso-8859-x */
1333 #endif
1334 #ifdef XK_dead_breve
1335 case XK_dead_breve :
1336 return 0xa2; /* '( */
1337 #endif
1338 #ifdef XK_dead_abovedot
1339 case XK_dead_abovedot :
1340 return 0xff; /* '. */
1341 #endif
1342 #ifdef XK_dead_abovering
1343 case XK_dead_abovering :
1344 return '0'; /* '0 isn't defined on iso-8859-x */
1345 #endif
1346 #ifdef XK_dead_doubleacute
1347 case XK_dead_doubleacute :
1348 return 0xbd; /* '" */
1349 #endif
1350 #ifdef XK_dead_caron
1351 case XK_dead_caron :
1352 return 0xb7; /* '< */
1353 #endif
1354 #ifdef XK_dead_ogonek
1355 case XK_dead_ogonek :
1356 return 0xb2; /* '; */
1357 #endif
1358 /* FIXME: I don't know this three.
1359 case XK_dead_iota :
1360 return 'i';
1361 case XK_dead_voiced_sound :
1362 return 'v';
1363 case XK_dead_semivoiced_sound :
1364 return 's';
1367 TRACE("no character for dead keysym 0x%08lx\n",keysym);
1368 return 0;
1371 /***********************************************************************
1372 * X11DRV_ToUnicode
1374 * The ToUnicode function translates the specified virtual-key code and keyboard
1375 * state to the corresponding Windows character or characters.
1377 * If the specified key is a dead key, the return value is negative. Otherwise,
1378 * it is one of the following values:
1379 * Value Meaning
1380 * 0 The specified virtual key has no translation for the current state of the keyboard.
1381 * 1 One Windows character was copied to the buffer.
1382 * 2 Two characters were copied to the buffer. This usually happens when a
1383 * dead-key character (accent or diacritic) stored in the keyboard layout cannot
1384 * be composed with the specified virtual key to form a single character.
1386 * FIXME : should do the above (return 2 for non matching deadchar+char combinations)
1389 INT X11DRV_ToUnicode(UINT virtKey, UINT scanCode, LPBYTE lpKeyState,
1390 LPWSTR bufW, int bufW_size, UINT flags)
1392 XKeyEvent e;
1393 KeySym keysym;
1394 INT ret;
1395 int keyc;
1396 BYTE lpChar[2];
1398 if (scanCode==0) {
1399 /* This happens when doing Alt+letter : a fake 'down arrow' key press
1400 event is generated by windows. Just ignore it. */
1401 TRACE("scanCode=0, doing nothing\n");
1402 return 0;
1404 if (scanCode & 0x8000)
1406 TRACE("Key UP, doing nothing\n" );
1407 return 0;
1409 e.display = display;
1410 e.keycode = 0;
1411 e.state = 0;
1412 if (lpKeyState[VK_SHIFT] & 0x80)
1413 e.state |= ShiftMask;
1414 if (lpKeyState[VK_CAPITAL] & 0x01)
1415 e.state |= LockMask;
1416 if (lpKeyState[VK_CONTROL] & 0x80)
1418 if (lpKeyState[VK_MENU] & 0x80)
1419 e.state |= AltGrMask;
1420 else
1421 e.state |= ControlMask;
1423 if (lpKeyState[VK_NUMLOCK] & 0x01)
1424 e.state |= NumLockMask;
1425 TRACE_(key)("(%04X, %04X) : faked state = %X\n",
1426 virtKey, scanCode, e.state);
1427 /* We exit on the first keycode found, to speed up the thing. */
1428 for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
1429 { /* Find a keycode that could have generated this virtual key */
1430 if ((keyc2vkey[keyc] & 0xFF) == virtKey)
1431 { /* We filter the extended bit, we don't know it */
1432 e.keycode = keyc; /* Store it temporarily */
1433 if ((EVENT_event_to_vkey(&e) & 0xFF) != virtKey) {
1434 e.keycode = 0; /* Wrong one (ex: because of the NumLock
1435 state), so set it to 0, we'll find another one */
1440 if ((virtKey>=VK_NUMPAD0) && (virtKey<=VK_NUMPAD9))
1441 e.keycode = TSXKeysymToKeycode(e.display, virtKey-VK_NUMPAD0+XK_KP_0);
1443 if (virtKey==VK_DECIMAL)
1444 e.keycode = TSXKeysymToKeycode(e.display, XK_KP_Decimal);
1446 if (!e.keycode)
1448 WARN("Unknown virtual key %X !!! \n",virtKey);
1449 return virtKey; /* whatever */
1451 else TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
1453 ret = TSXLookupString(&e, (LPVOID)lpChar, 2, &keysym, NULL);
1454 if (ret == 0)
1456 BYTE dead_char;
1458 dead_char = KEYBOARD_MapDeadKeysym(keysym);
1459 if (dead_char)
1461 MultiByteToWideChar(main_key_tab[kbd_layout].layout_cp, 0, &dead_char, 1, bufW, bufW_size);
1462 ret = -1;
1464 else
1466 char *ksname;
1468 ksname = TSXKeysymToString(keysym);
1469 if (!ksname)
1470 ksname = "No Name";
1471 if ((keysym >> 8) != 0xff)
1473 ERR("Please report: no char for keysym %04lX (%s) :\n",
1474 keysym, ksname);
1475 ERR("(virtKey=%X,scanCode=%X,keycode=%X,state=%X)\n",
1476 virtKey, scanCode, e.keycode, e.state);
1480 else { /* ret != 0 */
1481 /* We have a special case to handle : Shift + arrow, shift + home, ...
1482 X returns a char for it, but Windows doesn't. Let's eat it. */
1483 if (!(lpKeyState[VK_NUMLOCK] & 0x01) /* NumLock is off */
1484 && (lpKeyState[VK_SHIFT] & 0x80) /* Shift is pressed */
1485 && (keysym>=XK_KP_0) && (keysym<=XK_KP_9))
1487 *(char*)lpChar = 0;
1488 ret = 0;
1491 /* We have another special case for delete key (XK_Delete) on an
1492 extended keyboard. X returns a char for it, but Windows doesn't */
1493 if (keysym == XK_Delete)
1495 *(char*)lpChar = 0;
1496 ret = 0;
1499 /* perform translation to unicode */
1500 if(ret)
1502 TRACE_(key)("Translating char 0x%02x from code page %d to unicode\n",
1503 *(BYTE *)lpChar, main_key_tab[kbd_layout].layout_cp);
1504 ret = MultiByteToWideChar(main_key_tab[kbd_layout].layout_cp, 0, (LPCSTR)lpChar, ret, bufW, bufW_size);
1508 TRACE_(key)("ToUnicode about to return %d with char %x %s\n",
1509 ret, bufW ? bufW[1] : 0, bufW ? "" : "(no buffer)");
1510 return ret;
1513 /***********************************************************************
1514 * X11DRV_GetBeepActive
1516 BOOL X11DRV_GetBeepActive(void)
1518 XKeyboardState keyboard_state;
1520 TSXGetKeyboardControl(display, &keyboard_state);
1522 return keyboard_state.bell_percent != 0;
1525 /***********************************************************************
1526 * X11DRV_SetBeepActive
1528 void X11DRV_SetBeepActive(BOOL bActivate)
1530 XKeyboardControl keyboard_value;
1532 if(bActivate)
1533 keyboard_value.bell_percent = -1;
1534 else
1535 keyboard_value.bell_percent = 0;
1537 TSXChangeKeyboardControl(display, KBBellPercent, &keyboard_value);
1540 /***********************************************************************
1541 * X11DRV_Beep
1543 void X11DRV_Beep(void)
1545 TSXBell(display, 0);
1548 /***********************************************************************
1549 * X11DRV_GetDIState
1551 BOOL X11DRV_GetDIState(DWORD len, LPVOID ptr)
1553 if (len==256) {
1554 int keyc,vkey;
1556 memset(ptr,0,256);
1557 for (keyc=min_keycode;keyc<max_keycode;keyc++)
1559 /* X keycode to virtual key */
1560 vkey = keyc2vkey[keyc] & 0xFF;
1561 /* The windows scancode is keyc-min_keycode */
1562 if (InputKeyStateTable[vkey]&0x80) {
1563 ((LPBYTE)ptr)[keyc-min_keycode]=0x80;
1564 ((LPBYTE)ptr)[(keyc-min_keycode)|0x80]=0x80;
1567 return TRUE;
1569 WARN("whoops, got len %ld?\n", len);
1570 return TRUE;
1573 /***********************************************************************
1574 * X11DRV_GetDIData
1576 BOOL X11DRV_GetDIData(
1577 BYTE *keystate,
1578 DWORD dodsize, LPDIDEVICEOBJECTDATA dod,
1579 LPDWORD entries, DWORD flags)
1581 int keyc,n,vkey,xentries;
1583 /* FIXME !!! */
1585 if (entries)
1586 xentries = *entries;
1587 else
1588 xentries = 1;
1590 n = 0;
1592 for (keyc=min_keycode;(keyc<max_keycode) && (n<*entries);keyc++)
1594 /* X keycode to virtual key */
1595 vkey = keyc2vkey[keyc] & 0xFF;
1596 if (keystate[vkey] == (InputKeyStateTable[vkey]&0x80))
1597 continue;
1598 if (dod) {
1599 /* add an entry */
1600 dod[n].dwOfs = keyc-min_keycode; /* scancode */
1601 dod[n].dwData = InputKeyStateTable[vkey]&0x80;
1602 dod[n].dwTimeStamp = 0; /* umm */
1603 dod[n].dwSequence = 0; /* umm */
1604 n++;
1606 if (!(flags & DIGDD_PEEK))
1607 keystate[vkey] = InputKeyStateTable[vkey]&0x80;
1611 if (n) TRACE_(dinput)("%d entries\n",n);
1612 *entries = n;
1614 return TRUE;
1617 /***********************************************************************
1618 * X11DRV_GetKeyboardConfig
1620 void X11DRV_GetKeyboardConfig(KEYBOARD_CONFIG *cfg) {
1621 XKeyboardState xks;
1623 /* For the moment, only get the auto-repeat mode */
1624 TSXGetKeyboardControl(display, &xks);
1625 cfg->auto_repeat = xks.global_auto_repeat;
1628 /***********************************************************************
1629 * X11DRV_SetKeyboardConfig
1631 extern void X11DRV_SetKeyboardConfig(KEYBOARD_CONFIG *cfg, DWORD mask) {
1632 XKeyboardControl xkc;
1633 unsigned long X_mask = 0;
1635 if (mask & WINE_KEYBOARD_CONFIG_AUTO_REPEAT) {
1636 X_mask |= KBAutoRepeatMode;
1637 xkc.auto_repeat_mode = cfg->auto_repeat;
1639 if (X_mask)
1640 TSXChangeKeyboardControl(display, X_mask, &xkc);