include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / win32u / input.c
blob794dde58359270c8193b77b5254b256926a5afe9
1 /*
2 * USER Input processing
4 * Copyright 1993 Bob Amstadt
5 * Copyright 1993 David Metcalfe
6 * Copyright 1996 Albrecht Kleine
7 * Copyright 1996 Frans van Dorsselaer
8 * Copyright 1997 David Faure
9 * Copyright 1998 Morten Welinder
10 * Copyright 1998 Ulrich Weigand
11 * Copyright 2001 Eric Pouech
12 * Copyright 2002 Alexandre Julliard
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #if 0
30 #pragma makedep unix
31 #endif
33 #include "ntstatus.h"
34 #define WIN32_NO_STATUS
35 #include "win32u_private.h"
36 #include "ntuser_private.h"
37 #include "wine/server.h"
38 #include "wine/debug.h"
39 #include "kbd.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(win);
42 WINE_DECLARE_DEBUG_CHANNEL(keyboard);
44 static const WCHAR keyboard_layouts_keyW[] =
46 '\\','R','e','g','i','s','t','r','y',
47 '\\','M','a','c','h','i','n','e',
48 '\\','S','y','s','t','e','m',
49 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
50 '\\','C','o','n','t','r','o','l',
51 '\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s'
53 static const WCHAR escW[] = {'E','s','c',0};
54 static const WCHAR backspaceW[] = {'B','a','c','k','s','p','a','c','e',0};
55 static const WCHAR tabW[] = {'T','a','b',0};
56 static const WCHAR enterW[] = {'E','n','t','e','r',0};
57 static const WCHAR ctrlW[] = {'C','t','r','l',0};
58 static const WCHAR shiftW[] = {'S','h','i','f','t',0};
59 static const WCHAR right_shiftW[] = {'R','i','g','h','t',' ','S','h','i','f','t',0};
60 static const WCHAR num_mulW[] = {'N','u','m',' ','*',0};
61 static const WCHAR altW[] = {'A','l','t',0};
62 static const WCHAR spaceW[] = {'S','p','a','c','e',0};
63 static const WCHAR caps_lockW[] = {'C','a','p','s',' ','L','o','c','k',0};
64 static const WCHAR f1W[] = {'F','1',0};
65 static const WCHAR f2W[] = {'F','2',0};
66 static const WCHAR f3W[] = {'F','3',0};
67 static const WCHAR f4W[] = {'F','4',0};
68 static const WCHAR f5W[] = {'F','5',0};
69 static const WCHAR f6W[] = {'F','6',0};
70 static const WCHAR f7W[] = {'F','7',0};
71 static const WCHAR f8W[] = {'F','8',0};
72 static const WCHAR f9W[] = {'F','9',0};
73 static const WCHAR f10W[] = {'F','1','0',0};
74 static const WCHAR pauseW[] = {'P','a','u','s','e',0};
75 static const WCHAR scroll_lockW[] = {'S','c','r','o','l','l',' ','L','o','c','k',0};
76 static const WCHAR num_7W[] = {'N','u','m',' ','7',0};
77 static const WCHAR num_8W[] = {'N','u','m',' ','8',0};
78 static const WCHAR num_9W[] = {'N','u','m',' ','9',0};
79 static const WCHAR num_minusW[] = {'N','u','m',' ','-',0};
80 static const WCHAR num_4W[] = {'N','u','m',' ','4',0};
81 static const WCHAR num_5W[] = {'N','u','m',' ','5',0};
82 static const WCHAR num_6W[] = {'N','u','m',' ','6',0};
83 static const WCHAR num_plusW[] = {'N','u','m',' ','+',0};
84 static const WCHAR num_1W[] = {'N','u','m',' ','1',0};
85 static const WCHAR num_2W[] = {'N','u','m',' ','2',0};
86 static const WCHAR num_3W[] = {'N','u','m',' ','3',0};
87 static const WCHAR num_0W[] = {'N','u','m',' ','0',0};
88 static const WCHAR num_delW[] = {'N','u','m',' ','D','e','l',0};
89 static const WCHAR sys_reqW[] = {'S','y','s',' ','R','e','q',0};
90 static const WCHAR f11W[] = {'F','1','1',0};
91 static const WCHAR f12W[] = {'F','1','2',0};
92 static const WCHAR f13W[] = {'F','1','3',0};
93 static const WCHAR f14W[] = {'F','1','4',0};
94 static const WCHAR f15W[] = {'F','1','5',0};
95 static const WCHAR f16W[] = {'F','1','6',0};
96 static const WCHAR f17W[] = {'F','1','7',0};
97 static const WCHAR f18W[] = {'F','1','8',0};
98 static const WCHAR f19W[] = {'F','1','9',0};
99 static const WCHAR f20W[] = {'F','2','0',0};
100 static const WCHAR f21W[] = {'F','2','1',0};
101 static const WCHAR f22W[] = {'F','2','2',0};
102 static const WCHAR f23W[] = {'F','2','3',0};
103 static const WCHAR f24W[] = {'F','2','4',0};
104 static const WCHAR num_enterW[] = {'N','u','m',' ','E','n','t','e','r',0};
105 static const WCHAR right_ctrlW[] = {'R','i','g','h','t',' ','C','t','r','l',0};
106 static const WCHAR num_divW[] = {'N','u','m',' ','/',0};
107 static const WCHAR prnt_scrnW[] = {'P','r','n','t',' ','S','c','r','n',0};
108 static const WCHAR right_altW[] = {'R','i','g','h','t',' ','A','l','t',0};
109 static const WCHAR num_lockW[] = {'N','u','m',' ','L','o','c','k',0};
110 static const WCHAR breakW[] = {'B','r','e','a','k',0};
111 static const WCHAR homeW[] = {'H','o','m','e',0};
112 static const WCHAR upW[] = {'U','p',0};
113 static const WCHAR page_upW[] = {'P','a','g','e',' ','U','p',0};
114 static const WCHAR leftW[] = {'L','e','f','t',0};
115 static const WCHAR rightW[] = {'R','i','g','h','t',0};
116 static const WCHAR endW[] = {'E','n','d',0};
117 static const WCHAR downW[] = {'D','o','w','n',0};
118 static const WCHAR page_downW[] = {'P','a','g','e',' ','D','o','w','n',0};
119 static const WCHAR insertW[] = {'I','n','s','e','r','t',0};
120 static const WCHAR deleteW[] = {'D','e','l','e','t','e',0};
121 static const WCHAR zerozeroW[] = {'<','0','0','>',0};
122 static const WCHAR helpW[] = {'H','e','l','p',0};
123 static const WCHAR left_windowsW[] = {'L','e','f','t',' ','W','i','n','d','o','w','s',0};
124 static const WCHAR right_windowsW[] = {'R','i','g','h','t',' ','W','i','n','d','o','w','s',0};
125 static const WCHAR applicationW[] = {'A','p','p','l','i','c','a','t','i','o','n',0};
127 static const VK_TO_BIT vk_to_bit[] =
129 {.Vk = VK_SHIFT, .ModBits = KBDSHIFT},
130 {.Vk = VK_CONTROL, .ModBits = KBDCTRL},
131 {.Vk = VK_MENU, .ModBits = KBDALT},
132 {0},
135 static const MODIFIERS modifiers =
137 .pVkToBit = (VK_TO_BIT *)vk_to_bit,
138 .wMaxModBits = 7,
139 .ModNumber = {0, 1, 2, 3, 0, 1, 0, 0},
142 static const VK_TO_WCHARS2 vk_to_wchars2[] =
144 {.VirtualKey = VK_OEM_3, .wch = {'`', '~'}},
145 {.VirtualKey = '1', .wch = {'1', '!'}},
146 {.VirtualKey = '3', .wch = {'3', '#'}},
147 {.VirtualKey = '4', .wch = {'4', '$'}},
148 {.VirtualKey = '5', .wch = {'5', '%'}},
149 {.VirtualKey = '7', .wch = {'7', '&'}},
150 {.VirtualKey = '8', .wch = {'8', '*'}},
151 {.VirtualKey = '9', .wch = {'9', '('}},
152 {.VirtualKey = '0', .wch = {'0', ')'}},
153 {.VirtualKey = VK_OEM_PLUS, .wch = {'=', '+'}},
154 {.VirtualKey = 'Q', .wch = {'q', 'Q'}, .Attributes = CAPLOK},
155 {.VirtualKey = 'W', .wch = {'w', 'W'}, .Attributes = CAPLOK},
156 {.VirtualKey = 'E', .wch = {'e', 'E'}, .Attributes = CAPLOK},
157 {.VirtualKey = 'R', .wch = {'r', 'R'}, .Attributes = CAPLOK},
158 {.VirtualKey = 'T', .wch = {'t', 'T'}, .Attributes = CAPLOK},
159 {.VirtualKey = 'Y', .wch = {'y', 'Y'}, .Attributes = CAPLOK},
160 {.VirtualKey = 'U', .wch = {'u', 'U'}, .Attributes = CAPLOK},
161 {.VirtualKey = 'I', .wch = {'i', 'I'}, .Attributes = CAPLOK},
162 {.VirtualKey = 'O', .wch = {'o', 'O'}, .Attributes = CAPLOK},
163 {.VirtualKey = 'P', .wch = {'p', 'P'}, .Attributes = CAPLOK},
164 {.VirtualKey = 'A', .wch = {'a', 'A'}, .Attributes = CAPLOK},
165 {.VirtualKey = 'S', .wch = {'s', 'S'}, .Attributes = CAPLOK},
166 {.VirtualKey = 'D', .wch = {'d', 'D'}, .Attributes = CAPLOK},
167 {.VirtualKey = 'F', .wch = {'f', 'F'}, .Attributes = CAPLOK},
168 {.VirtualKey = 'G', .wch = {'g', 'G'}, .Attributes = CAPLOK},
169 {.VirtualKey = 'H', .wch = {'h', 'H'}, .Attributes = CAPLOK},
170 {.VirtualKey = 'J', .wch = {'j', 'J'}, .Attributes = CAPLOK},
171 {.VirtualKey = 'K', .wch = {'k', 'K'}, .Attributes = CAPLOK},
172 {.VirtualKey = 'L', .wch = {'l', 'L'}, .Attributes = CAPLOK},
173 {.VirtualKey = VK_OEM_1, .wch = {';', ':'}},
174 {.VirtualKey = VK_OEM_7, .wch = {'\'', '\"'}},
175 {.VirtualKey = 'Z', .wch = {'z', 'Z'}, .Attributes = CAPLOK},
176 {.VirtualKey = 'X', .wch = {'x', 'X'}, .Attributes = CAPLOK},
177 {.VirtualKey = 'C', .wch = {'c', 'C'}, .Attributes = CAPLOK},
178 {.VirtualKey = 'V', .wch = {'v', 'V'}, .Attributes = CAPLOK},
179 {.VirtualKey = 'B', .wch = {'b', 'B'}, .Attributes = CAPLOK},
180 {.VirtualKey = 'N', .wch = {'n', 'N'}, .Attributes = CAPLOK},
181 {.VirtualKey = 'M', .wch = {'m', 'M'}, .Attributes = CAPLOK},
182 {.VirtualKey = VK_OEM_COMMA, .wch = {',', '<'}},
183 {.VirtualKey = VK_OEM_PERIOD, .wch = {'.', '>'}},
184 {.VirtualKey = VK_OEM_2, .wch = {'/', '?'}},
185 {.VirtualKey = VK_DECIMAL, .wch = {'.', '.'}},
186 {.VirtualKey = VK_TAB, .wch = {'\t', '\t'}},
187 {.VirtualKey = VK_ADD, .wch = {'+', '+'}},
188 {.VirtualKey = VK_DIVIDE, .wch = {'/', '/'}},
189 {.VirtualKey = VK_MULTIPLY, .wch = {'*', '*'}},
190 {.VirtualKey = VK_SUBTRACT, .wch = {'-', '-'}},
191 {0},
194 static const VK_TO_WCHARS3 vk_to_wchars3[] =
196 {.VirtualKey = VK_OEM_4, .wch = {'[', '{', '\x001b'}},
197 {.VirtualKey = VK_OEM_6, .wch = {']', '}', '\x001d'}},
198 {.VirtualKey = VK_OEM_5, .wch = {'\\', '|', '\x001c'}},
199 {.VirtualKey = VK_OEM_102, .wch = {'\\', '|', '\x001c'}},
200 {.VirtualKey = VK_BACK, .wch = {'\b', '\b', '\x007f'}},
201 {.VirtualKey = VK_ESCAPE, .wch = {'\x001b', '\x001b', '\x001b'}},
202 {.VirtualKey = VK_RETURN, .wch = {'\r', '\r', '\n'}},
203 {.VirtualKey = VK_SPACE, .wch = {' ', ' ', ' '}},
204 {.VirtualKey = VK_CANCEL, .wch = {'\x0003', '\x0003', '\x0003'}},
205 {0},
208 static const VK_TO_WCHARS4 vk_to_wchars4[] =
210 {.VirtualKey = '2', .wch = {'2', '@', WCH_NONE, '\x0000'}},
211 {.VirtualKey = '6', .wch = {'6', '^', WCH_NONE, '\x001e'}},
212 {.VirtualKey = VK_OEM_MINUS, .wch = {'-', '_', WCH_NONE, '\x001f'}},
213 {0},
216 static const VK_TO_WCHARS1 vk_to_wchars1[] =
218 {.VirtualKey = VK_NUMPAD0, .wch = {'0'}},
219 {.VirtualKey = VK_NUMPAD1, .wch = {'1'}},
220 {.VirtualKey = VK_NUMPAD2, .wch = {'2'}},
221 {.VirtualKey = VK_NUMPAD3, .wch = {'3'}},
222 {.VirtualKey = VK_NUMPAD4, .wch = {'4'}},
223 {.VirtualKey = VK_NUMPAD5, .wch = {'5'}},
224 {.VirtualKey = VK_NUMPAD6, .wch = {'6'}},
225 {.VirtualKey = VK_NUMPAD7, .wch = {'7'}},
226 {.VirtualKey = VK_NUMPAD8, .wch = {'8'}},
227 {.VirtualKey = VK_NUMPAD9, .wch = {'9'}},
228 {0},
231 static const VK_TO_WCHAR_TABLE vk_to_wchar_table[] =
233 {.pVkToWchars = (VK_TO_WCHARS1 *)vk_to_wchars3, .nModifications = 3, .cbSize = sizeof(vk_to_wchars3[0])},
234 {.pVkToWchars = (VK_TO_WCHARS1 *)vk_to_wchars4, .nModifications = 4, .cbSize = sizeof(vk_to_wchars4[0])},
235 {.pVkToWchars = (VK_TO_WCHARS1 *)vk_to_wchars2, .nModifications = 2, .cbSize = sizeof(vk_to_wchars2[0])},
236 {.pVkToWchars = (VK_TO_WCHARS1 *)vk_to_wchars1, .nModifications = 1, .cbSize = sizeof(vk_to_wchars1[0])},
237 {0},
240 static const VSC_LPWSTR key_names[] =
242 {.vsc = 0x01, .pwsz = (WCHAR *)escW},
243 {.vsc = 0x0e, .pwsz = (WCHAR *)backspaceW},
244 {.vsc = 0x0f, .pwsz = (WCHAR *)tabW},
245 {.vsc = 0x1c, .pwsz = (WCHAR *)enterW},
246 {.vsc = 0x1d, .pwsz = (WCHAR *)ctrlW},
247 {.vsc = 0x2a, .pwsz = (WCHAR *)shiftW},
248 {.vsc = 0x36, .pwsz = (WCHAR *)right_shiftW},
249 {.vsc = 0x37, .pwsz = (WCHAR *)num_mulW},
250 {.vsc = 0x38, .pwsz = (WCHAR *)altW},
251 {.vsc = 0x39, .pwsz = (WCHAR *)spaceW},
252 {.vsc = 0x3a, .pwsz = (WCHAR *)caps_lockW},
253 {.vsc = 0x3b, .pwsz = (WCHAR *)f1W},
254 {.vsc = 0x3c, .pwsz = (WCHAR *)f2W},
255 {.vsc = 0x3d, .pwsz = (WCHAR *)f3W},
256 {.vsc = 0x3e, .pwsz = (WCHAR *)f4W},
257 {.vsc = 0x3f, .pwsz = (WCHAR *)f5W},
258 {.vsc = 0x40, .pwsz = (WCHAR *)f6W},
259 {.vsc = 0x41, .pwsz = (WCHAR *)f7W},
260 {.vsc = 0x42, .pwsz = (WCHAR *)f8W},
261 {.vsc = 0x43, .pwsz = (WCHAR *)f9W},
262 {.vsc = 0x44, .pwsz = (WCHAR *)f10W},
263 {.vsc = 0x45, .pwsz = (WCHAR *)pauseW},
264 {.vsc = 0x46, .pwsz = (WCHAR *)scroll_lockW},
265 {.vsc = 0x47, .pwsz = (WCHAR *)num_7W},
266 {.vsc = 0x48, .pwsz = (WCHAR *)num_8W},
267 {.vsc = 0x49, .pwsz = (WCHAR *)num_9W},
268 {.vsc = 0x4a, .pwsz = (WCHAR *)num_minusW},
269 {.vsc = 0x4b, .pwsz = (WCHAR *)num_4W},
270 {.vsc = 0x4c, .pwsz = (WCHAR *)num_5W},
271 {.vsc = 0x4d, .pwsz = (WCHAR *)num_6W},
272 {.vsc = 0x4e, .pwsz = (WCHAR *)num_plusW},
273 {.vsc = 0x4f, .pwsz = (WCHAR *)num_1W},
274 {.vsc = 0x50, .pwsz = (WCHAR *)num_2W},
275 {.vsc = 0x51, .pwsz = (WCHAR *)num_3W},
276 {.vsc = 0x52, .pwsz = (WCHAR *)num_0W},
277 {.vsc = 0x53, .pwsz = (WCHAR *)num_delW},
278 {.vsc = 0x54, .pwsz = (WCHAR *)sys_reqW},
279 {.vsc = 0x57, .pwsz = (WCHAR *)f11W},
280 {.vsc = 0x58, .pwsz = (WCHAR *)f12W},
281 {.vsc = 0x7c, .pwsz = (WCHAR *)f13W},
282 {.vsc = 0x7d, .pwsz = (WCHAR *)f14W},
283 {.vsc = 0x7e, .pwsz = (WCHAR *)f15W},
284 {.vsc = 0x7f, .pwsz = (WCHAR *)f16W},
285 {.vsc = 0x80, .pwsz = (WCHAR *)f17W},
286 {.vsc = 0x81, .pwsz = (WCHAR *)f18W},
287 {.vsc = 0x82, .pwsz = (WCHAR *)f19W},
288 {.vsc = 0x83, .pwsz = (WCHAR *)f20W},
289 {.vsc = 0x84, .pwsz = (WCHAR *)f21W},
290 {.vsc = 0x85, .pwsz = (WCHAR *)f22W},
291 {.vsc = 0x86, .pwsz = (WCHAR *)f23W},
292 {.vsc = 0x87, .pwsz = (WCHAR *)f24W},
293 {0},
296 static const VSC_LPWSTR key_names_ext[] =
298 {.vsc = 0x1c, .pwsz = (WCHAR *)num_enterW},
299 {.vsc = 0x1d, .pwsz = (WCHAR *)right_ctrlW},
300 {.vsc = 0x35, .pwsz = (WCHAR *)num_divW},
301 {.vsc = 0x37, .pwsz = (WCHAR *)prnt_scrnW},
302 {.vsc = 0x38, .pwsz = (WCHAR *)right_altW},
303 {.vsc = 0x45, .pwsz = (WCHAR *)num_lockW},
304 {.vsc = 0x46, .pwsz = (WCHAR *)breakW},
305 {.vsc = 0x47, .pwsz = (WCHAR *)homeW},
306 {.vsc = 0x48, .pwsz = (WCHAR *)upW},
307 {.vsc = 0x49, .pwsz = (WCHAR *)page_upW},
308 {.vsc = 0x4b, .pwsz = (WCHAR *)leftW},
309 {.vsc = 0x4d, .pwsz = (WCHAR *)rightW},
310 {.vsc = 0x4f, .pwsz = (WCHAR *)endW},
311 {.vsc = 0x50, .pwsz = (WCHAR *)downW},
312 {.vsc = 0x51, .pwsz = (WCHAR *)page_downW},
313 {.vsc = 0x52, .pwsz = (WCHAR *)insertW},
314 {.vsc = 0x53, .pwsz = (WCHAR *)deleteW},
315 {.vsc = 0x54, .pwsz = (WCHAR *)zerozeroW},
316 {.vsc = 0x56, .pwsz = (WCHAR *)helpW},
317 {.vsc = 0x5b, .pwsz = (WCHAR *)left_windowsW},
318 {.vsc = 0x5c, .pwsz = (WCHAR *)right_windowsW},
319 {.vsc = 0x5d, .pwsz = (WCHAR *)applicationW},
320 {0},
323 static const USHORT vsc_to_vk[] =
325 T00, T01, T02, T03, T04, T05, T06, T07,
326 T08, T09, T0A, T0B, T0C, T0D, T0E, T0F,
327 T10, T11, T12, T13, T14, T15, T16, T17,
328 T18, T19, T1A, T1B, T1C, T1D, T1E, T1F,
329 T20, T21, T22, T23, T24, T25, T26, T27,
330 T28, T29, T2A, T2B, T2C, T2D, T2E, T2F,
331 T30, T31, T32, T33, T34, T35, T36 | KBDEXT, T37 | KBDMULTIVK,
332 T38, T39, T3A, T3B, T3C, T3D, T3E, T3F,
333 T40, T41, T42, T43, T44, T45 | KBDEXT | KBDMULTIVK, T46 | KBDMULTIVK, T47 | KBDNUMPAD | KBDSPECIAL,
334 T48 | KBDNUMPAD | KBDSPECIAL, T49 | KBDNUMPAD | KBDSPECIAL, T4A, T4B | KBDNUMPAD | KBDSPECIAL,
335 T4C | KBDNUMPAD | KBDSPECIAL, T4D | KBDNUMPAD | KBDSPECIAL, T4E, T4F | KBDNUMPAD | KBDSPECIAL,
336 T50 | KBDNUMPAD | KBDSPECIAL, T51 | KBDNUMPAD | KBDSPECIAL, T52 | KBDNUMPAD | KBDSPECIAL,
337 T53 | KBDNUMPAD | KBDSPECIAL, T54, T55, T56, T57,
338 T58, T59, T5A, T5B, T5C, T5D, T5E, T5F,
339 T60, T61, T62, T63, T64, T65, T66, T67,
340 T68, T69, T6A, T6B, T6C, T6D, T6E, T6F,
341 T70, T71, T72, T73, T74, T75, T76, T77,
342 T78, T79, T7A, T7B, T7C, T7D, T7E
345 static const VSC_VK vsc_to_vk_e0[] =
347 {0x10, X10 | KBDEXT},
348 {0x19, X19 | KBDEXT},
349 {0x1d, X1D | KBDEXT},
350 {0x20, X20 | KBDEXT},
351 {0x21, X21 | KBDEXT},
352 {0x22, X22 | KBDEXT},
353 {0x24, X24 | KBDEXT},
354 {0x2e, X2E | KBDEXT},
355 {0x30, X30 | KBDEXT},
356 {0x32, X32 | KBDEXT},
357 {0x35, X35 | KBDEXT},
358 {0x37, X37 | KBDEXT},
359 {0x38, X38 | KBDEXT},
360 {0x47, X47 | KBDEXT},
361 {0x48, X48 | KBDEXT},
362 {0x49, X49 | KBDEXT},
363 {0x4b, X4B | KBDEXT},
364 {0x4d, X4D | KBDEXT},
365 {0x4f, X4F | KBDEXT},
366 {0x50, X50 | KBDEXT},
367 {0x51, X51 | KBDEXT},
368 {0x52, X52 | KBDEXT},
369 {0x53, X53 | KBDEXT},
370 {0x5b, X5B | KBDEXT},
371 {0x5c, X5C | KBDEXT},
372 {0x5d, X5D | KBDEXT},
373 {0x5f, X5F | KBDEXT},
374 {0x65, X65 | KBDEXT},
375 {0x66, X66 | KBDEXT},
376 {0x67, X67 | KBDEXT},
377 {0x68, X68 | KBDEXT},
378 {0x69, X69 | KBDEXT},
379 {0x6a, X6A | KBDEXT},
380 {0x6b, X6B | KBDEXT},
381 {0x6c, X6C | KBDEXT},
382 {0x6d, X6D | KBDEXT},
383 {0x1c, X1C | KBDEXT},
384 {0x46, X46 | KBDEXT},
385 {0},
388 static const VSC_VK vsc_to_vk_e1[] =
390 {0x1d, Y1D},
391 {0},
394 static const KBDTABLES kbdus_tables =
396 .pCharModifiers = (MODIFIERS *)&modifiers,
397 .pVkToWcharTable = (VK_TO_WCHAR_TABLE *)vk_to_wchar_table,
398 .pKeyNames = (VSC_LPWSTR *)key_names,
399 .pKeyNamesExt = (VSC_LPWSTR *)key_names_ext,
400 .pusVSCtoVK = (USHORT *)vsc_to_vk,
401 .bMaxVSCtoVK = ARRAY_SIZE(vsc_to_vk),
402 .pVSCtoVK_E0 = (VSC_VK *)vsc_to_vk_e0,
403 .pVSCtoVK_E1 = (VSC_VK *)vsc_to_vk_e1,
404 .fLocaleFlags = MAKELONG(0, KBD_VERSION),
407 static LONG clipping_cursor; /* clipping thread counter */
409 BOOL grab_pointer = TRUE;
410 BOOL grab_fullscreen = FALSE;
412 static void kbd_tables_init_vsc2vk( const KBDTABLES *tables, USHORT vsc2vk[0x300] )
414 const VSC_VK *entry;
415 WORD vsc;
417 memset( vsc2vk, 0, 0x300 * sizeof(USHORT) );
419 for (vsc = 0; tables->pusVSCtoVK && vsc <= tables->bMaxVSCtoVK; ++vsc)
421 if (tables->pusVSCtoVK[vsc] == VK__none_) continue;
422 vsc2vk[vsc] = tables->pusVSCtoVK[vsc];
424 for (entry = tables->pVSCtoVK_E0; entry && entry->Vsc; entry++)
426 if (entry->Vk == VK__none_) continue;
427 vsc2vk[entry->Vsc + 0x100] = entry->Vk;
429 for (entry = tables->pVSCtoVK_E1; entry && entry->Vsc; entry++)
431 if (entry->Vk == VK__none_) continue;
432 vsc2vk[entry->Vsc + 0x200] = entry->Vk;
436 #define NEXT_ENTRY(t, e) ((void *)&(e)->wch[(t)->nModifications])
438 static void kbd_tables_init_vk2char( const KBDTABLES *tables, BYTE vk2char[0x100] )
440 const VK_TO_WCHAR_TABLE *table;
441 const VK_TO_WCHARS1 *entry;
443 memset( vk2char, 0, 0x100 );
445 for (table = tables->pVkToWcharTable; table->pVkToWchars; table++)
447 for (entry = table->pVkToWchars; entry->VirtualKey; entry = NEXT_ENTRY(table, entry))
449 if (entry->VirtualKey & ~0xff) continue;
450 vk2char[entry->VirtualKey] = entry->wch[0];
455 static UINT kbd_tables_get_mod_bits( const KBDTABLES *tables, UINT mod )
457 const MODIFIERS *mods = tables->pCharModifiers;
458 WORD bits;
460 for (bits = 0; bits <= mods->wMaxModBits; ++bits)
461 if (mods->ModNumber[bits] == mod) return bits;
463 return -1;
466 static UINT kbd_tables_get_mod_num( const KBDTABLES *tables, const BYTE *state, BOOL caps )
468 const MODIFIERS *mods = tables->pCharModifiers;
469 const VK_TO_BIT *entry;
470 WORD bits = 0;
472 for (entry = mods->pVkToBit; entry->Vk; ++entry)
473 if (state[entry->Vk] & 0x80) bits |= entry->ModBits;
474 if (caps) bits |= KBDSHIFT;
476 if (bits > mods->wMaxModBits) return -1;
477 return mods->ModNumber[bits];
480 static WORD kbd_tables_wchar_to_vkey( const KBDTABLES *tables, WCHAR wch )
482 const VK_TO_WCHAR_TABLE *table;
483 const VK_TO_WCHARS1 *entry;
484 WORD bits;
485 BYTE mod;
487 if (wch == '\x001b') return VK_ESCAPE;
489 for (table = tables->pVkToWcharTable; table->pVkToWchars; table++)
491 for (entry = table->pVkToWchars; entry->VirtualKey; entry = NEXT_ENTRY(table, entry))
493 for (mod = 0; mod < table->nModifications; ++mod)
495 if (entry->wch[mod] == WCH_NONE || entry->wch[mod] != wch) continue;
496 bits = kbd_tables_get_mod_bits( tables, mod );
497 return (bits << 8) | entry->VirtualKey;
502 if (wch >= 0x0001 && wch <= 0x001a) return (0x200) | ('A' + wch - 1); /* CTRL + A-Z */
503 return wch >= 0x0080 ? -1 : 0;
506 static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const BYTE *state )
508 UINT mod, caps_mod, alt, ctrl, caps;
509 const VK_TO_WCHAR_TABLE *table;
510 const VK_TO_WCHARS1 *entry;
512 alt = state[VK_MENU] & 0x80;
513 ctrl = state[VK_CONTROL] & 0x80;
514 caps = state[VK_CAPITAL] & 1;
516 if (ctrl && alt && !(tables->fLocaleFlags & KLLF_ALTGR)) return WCH_NONE;
517 if (!ctrl && vkey == VK_ESCAPE) return VK_ESCAPE;
518 if (ctrl && !alt)
520 if (vkey >= 'A' && vkey <= 'Z') return vkey - 'A' + 1;
521 tables = &kbdus_tables;
523 if (vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9) tables = &kbdus_tables;
525 mod = caps_mod = kbd_tables_get_mod_num( tables, state, FALSE );
526 if (caps) caps_mod = kbd_tables_get_mod_num( tables, state, TRUE );
528 for (table = tables->pVkToWcharTable; table->pVkToWchars; table++)
530 if (table->nModifications <= mod) continue;
531 for (entry = table->pVkToWchars; entry->VirtualKey; entry = NEXT_ENTRY(table, entry))
533 if (entry->VirtualKey != vkey) continue;
534 /* SGCAPS attribute may be set on entries where VK_CAPITAL and VK_SHIFT behave differently.
535 * The entry corresponds to the mapping when Caps Lock is on, and a second entry follows it
536 * with the mapping when Caps Lock is off.
538 if ((entry->Attributes & SGCAPS) && !caps) entry = NEXT_ENTRY(table, entry);
539 if ((entry->Attributes & CAPLOK) && table->nModifications > caps_mod) return entry->wch[caps_mod];
540 return entry->wch[mod];
544 return WCH_NONE;
547 #undef NEXT_ENTRY
549 /*******************************************************************
550 * NtUserGetForegroundWindow (win32u.@)
552 HWND WINAPI NtUserGetForegroundWindow(void)
554 struct object_lock lock = OBJECT_LOCK_INIT;
555 const input_shm_t *input_shm;
556 NTSTATUS status;
557 HWND hwnd = 0;
559 while ((status = get_shared_input( 0, &lock, &input_shm )) == STATUS_PENDING)
560 hwnd = wine_server_ptr_handle( input_shm->active );
561 if (status) hwnd = 0;
563 return hwnd;
566 /* see GetActiveWindow */
567 HWND get_active_window(void)
569 GUITHREADINFO info;
570 info.cbSize = sizeof(info);
571 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
574 /* see GetCapture */
575 HWND get_capture(void)
577 GUITHREADINFO info;
578 info.cbSize = sizeof(info);
579 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0;
582 /* see GetFocus */
583 HWND get_focus(void)
585 GUITHREADINFO info;
586 info.cbSize = sizeof(info);
587 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
590 /**********************************************************************
591 * NtUserAttachThreadInput (win32u.@)
593 BOOL WINAPI NtUserAttachThreadInput( DWORD from, DWORD to, BOOL attach )
595 BOOL ret;
597 SERVER_START_REQ( attach_thread_input )
599 req->tid_from = from;
600 req->tid_to = to;
601 req->attach = attach;
602 ret = !wine_server_call_err( req );
604 SERVER_END_REQ;
605 return ret;
608 /***********************************************************************
609 * update_mouse_coords
611 * Helper for NtUserSendInput.
613 static void update_mouse_coords( INPUT *input )
615 if (!(input->mi.dwFlags & MOUSEEVENTF_MOVE)) return;
617 if (input->mi.dwFlags & MOUSEEVENTF_ABSOLUTE)
619 RECT rc;
621 if (input->mi.dwFlags & MOUSEEVENTF_VIRTUALDESK)
622 rc = get_virtual_screen_rect( 0 );
623 else
624 rc = get_primary_monitor_rect( 0 );
626 input->mi.dx = rc.left + ((input->mi.dx * (rc.right - rc.left)) >> 16);
627 input->mi.dy = rc.top + ((input->mi.dy * (rc.bottom - rc.top)) >> 16);
629 else
631 int accel[3];
633 /* dx and dy can be negative numbers for relative movements */
634 NtUserSystemParametersInfo( SPI_GETMOUSE, 0, accel, 0 );
636 if (!accel[2]) return;
638 if (abs( input->mi.dx ) > accel[0])
640 input->mi.dx *= 2;
641 if (abs( input->mi.dx ) > accel[1] && accel[2] == 2) input->mi.dx *= 2;
643 if (abs(input->mi.dy) > accel[0])
645 input->mi.dy *= 2;
646 if (abs( input->mi.dy ) > accel[1] && accel[2] == 2) input->mi.dy *= 2;
651 /***********************************************************************
652 * NtUserSendInput (win32u.@)
654 UINT WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size )
656 UINT i;
657 NTSTATUS status = STATUS_SUCCESS;
659 if (size != sizeof(INPUT))
661 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
662 return 0;
665 if (!count)
667 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
668 return 0;
671 if (!inputs)
673 RtlSetLastWin32Error( ERROR_NOACCESS );
674 return 0;
677 for (i = 0; i < count; i++)
679 INPUT input = inputs[i];
680 switch (input.type)
682 case INPUT_MOUSE:
683 /* we need to update the coordinates to what the server expects */
684 update_mouse_coords( &input );
685 /* fallthrough */
686 case INPUT_KEYBOARD:
687 status = send_hardware_message( 0, SEND_HWMSG_INJECTED, &input, 0 );
688 break;
689 case INPUT_HARDWARE:
690 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
691 return 0;
694 if (status)
696 RtlSetLastWin32Error( RtlNtStatusToDosError(status) );
697 break;
701 return i;
704 /***********************************************************************
705 * NtUserSetCursorPos (win32u.@)
707 BOOL WINAPI NtUserSetCursorPos( INT x, INT y )
709 POINT pt = { x, y };
710 BOOL ret;
711 INT prev_x, prev_y, new_x, new_y;
712 UINT dpi;
714 if ((dpi = get_thread_dpi()))
716 HMONITOR monitor = monitor_from_point( pt, MONITOR_DEFAULTTOPRIMARY, get_thread_dpi() );
717 pt = map_dpi_point( pt, dpi, get_monitor_dpi( monitor ));
720 SERVER_START_REQ( set_cursor )
722 req->flags = SET_CURSOR_POS;
723 req->x = pt.x;
724 req->y = pt.y;
725 if ((ret = !wine_server_call( req )))
727 prev_x = reply->prev_x;
728 prev_y = reply->prev_y;
729 new_x = reply->new_x;
730 new_y = reply->new_y;
733 SERVER_END_REQ;
734 if (ret && (prev_x != new_x || prev_y != new_y)) user_driver->pSetCursorPos( new_x, new_y );
735 return ret;
738 /***********************************************************************
739 * get_cursor_pos
741 BOOL get_cursor_pos( POINT *pt )
743 struct object_lock lock = OBJECT_LOCK_INIT;
744 const desktop_shm_t *desktop_shm;
745 BOOL ret;
746 DWORD last_change = 0;
747 NTSTATUS status;
748 UINT dpi;
750 if (!pt) return FALSE;
752 while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING)
754 pt->x = desktop_shm->cursor.x;
755 pt->y = desktop_shm->cursor.y;
756 last_change = desktop_shm->cursor.last_change;
758 ret = !status;
760 /* query new position from graphics driver if we haven't updated recently */
761 if (ret && NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt );
762 if (ret && (dpi = get_thread_dpi()))
764 HMONITOR monitor = monitor_from_point( *pt, MONITOR_DEFAULTTOPRIMARY, 0 );
765 *pt = map_dpi_point( *pt, get_monitor_dpi( monitor ), dpi );
767 return ret;
770 /***********************************************************************
771 * NtUserGetCursorInfo (win32u.@)
773 BOOL WINAPI NtUserGetCursorInfo( CURSORINFO *info )
775 struct object_lock lock = OBJECT_LOCK_INIT;
776 const input_shm_t *input_shm;
777 NTSTATUS status;
779 if (!info) return FALSE;
781 while ((status = get_shared_input( 0, &lock, &input_shm )) == STATUS_PENDING)
783 info->hCursor = wine_server_ptr_handle( input_shm->cursor );
784 info->flags = (input_shm->cursor_count >= 0) ? CURSOR_SHOWING : 0;
786 if (status)
788 info->hCursor = 0;
789 info->flags = CURSOR_SHOWING;
792 get_cursor_pos( &info->ptScreenPos );
793 return TRUE;
796 static void check_for_events( UINT flags )
798 struct peek_message_filter filter =
800 .internal = TRUE,
801 .flags = PM_REMOVE,
803 MSG msg;
805 if (!user_driver->pProcessEvents( flags ))
806 flush_window_surfaces( TRUE );
808 peek_message( &msg, &filter );
811 /**********************************************************************
812 * GetAsyncKeyState (win32u.@)
814 SHORT WINAPI NtUserGetAsyncKeyState( INT key )
816 const desktop_shm_t *desktop_shm;
817 struct object_lock lock = OBJECT_LOCK_INIT;
818 NTSTATUS status;
819 BYTE state = 0;
820 SHORT ret = 0;
822 if (key < 0 || key >= 256) return 0;
824 check_for_events( QS_INPUT );
826 while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING)
827 state = desktop_shm->keystate[key];
829 if (status) return 0;
830 if (!(state & 0x40)) return (state & 0x80) << 8;
832 /* Need to make a server call to reset the last pressed bit */
833 SERVER_START_REQ( get_key_state )
835 req->async = 1;
836 req->key = key;
837 if (!wine_server_call( req ))
839 if (reply->state & 0x40) ret |= 0x0001;
840 if (reply->state & 0x80) ret |= 0x8000;
843 SERVER_END_REQ;
845 return ret;
848 /***********************************************************************
849 * get_shared_queue_bits
851 static BOOL get_shared_queue_bits( UINT *wake_bits, UINT *changed_bits )
853 struct object_lock lock = OBJECT_LOCK_INIT;
854 const queue_shm_t *queue_shm;
855 UINT status;
857 *wake_bits = *changed_bits = 0;
858 while ((status = get_shared_queue( &lock, &queue_shm )) == STATUS_PENDING)
860 *wake_bits = queue_shm->wake_bits;
861 *changed_bits = queue_shm->changed_bits;
864 if (status) return FALSE;
865 return TRUE;
868 /***********************************************************************
869 * NtUserGetQueueStatus (win32u.@)
871 DWORD WINAPI NtUserGetQueueStatus( UINT flags )
873 UINT ret, wake_bits, changed_bits;
875 if (flags & ~(QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_SMRESULT))
877 RtlSetLastWin32Error( ERROR_INVALID_FLAGS );
878 return 0;
881 check_for_events( flags );
883 if (get_shared_queue_bits( &wake_bits, &changed_bits ) && !(changed_bits & flags))
884 ret = MAKELONG( changed_bits & flags, wake_bits & flags );
885 else SERVER_START_REQ( get_queue_status )
887 req->clear_bits = flags;
888 wine_server_call( req );
889 ret = MAKELONG( reply->changed_bits & flags, reply->wake_bits & flags );
891 SERVER_END_REQ;
892 return ret;
895 /***********************************************************************
896 * get_input_state
898 DWORD get_input_state(void)
900 UINT wake_bits, changed_bits;
902 check_for_events( QS_INPUT );
904 if (!get_shared_queue_bits( &wake_bits, &changed_bits )) return 0;
905 return wake_bits & (QS_KEY | QS_MOUSEBUTTON);
908 /***********************************************************************
909 * get_locale_kbd_layout
911 static HKL get_locale_kbd_layout(void)
913 LCID layout;
915 /* FIXME:
917 * layout = main_key_tab[kbd_layout].lcid;
919 * Winword uses return value of GetKeyboardLayout as a codepage
920 * to translate ANSI keyboard messages to unicode. But we have
921 * a problem with it: for instance Polish keyboard layout is
922 * identical to the US one, and therefore instead of the Polish
923 * locale id we return the US one.
926 NtQueryDefaultLocale( TRUE, &layout );
927 layout = MAKELONG( layout, layout );
928 return ULongToHandle( layout );
931 /***********************************************************************
932 * NtUserGetKeyboardLayout (win32u.@)
934 * Device handle for keyboard layout defaulted to
935 * the language id. This is the way Windows default works.
937 HKL WINAPI NtUserGetKeyboardLayout( DWORD thread_id )
939 struct user_thread_info *thread = get_user_thread_info();
940 HKL layout = thread->kbd_layout;
942 if (thread_id && thread_id != GetCurrentThreadId())
943 FIXME( "couldn't return keyboard layout for thread %04x\n", (int)thread_id );
945 if (!layout) return get_locale_kbd_layout();
946 return layout;
949 /**********************************************************************
950 * NtUserGetKeyState (win32u.@)
952 * An application calls the GetKeyState function in response to a
953 * keyboard-input message. This function retrieves the state of the key
954 * at the time the input message was generated.
956 SHORT WINAPI NtUserGetKeyState( INT vkey )
958 SHORT retval = 0;
960 SERVER_START_REQ( get_key_state )
962 req->key = vkey;
963 if (!wine_server_call( req )) retval = (signed char)(reply->state & 0x81);
965 SERVER_END_REQ;
966 TRACE("key (0x%x) -> %x\n", vkey, retval);
967 return retval;
970 /**********************************************************************
971 * NtUserGetKeyboardState (win32u.@)
973 BOOL WINAPI NtUserGetKeyboardState( BYTE *state )
975 BOOL ret;
976 UINT i;
978 TRACE("(%p)\n", state);
980 memset( state, 0, 256 );
981 SERVER_START_REQ( get_key_state )
983 req->key = -1;
984 wine_server_set_reply( req, state, 256 );
985 ret = !wine_server_call_err( req );
986 for (i = 0; i < 256; i++) state[i] &= 0x81;
988 SERVER_END_REQ;
989 return ret;
992 /**********************************************************************
993 * NtUserSetKeyboardState (win32u.@)
995 BOOL WINAPI NtUserSetKeyboardState( BYTE *state )
997 BOOL ret;
999 SERVER_START_REQ( set_key_state )
1001 wine_server_add_data( req, state, 256 );
1002 ret = !wine_server_call_err( req );
1004 SERVER_END_REQ;
1005 return ret;
1008 /******************************************************************************
1009 * NtUserVkKeyScanEx (win32u.@)
1011 WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout )
1013 const KBDTABLES *kbd_tables;
1014 SHORT ret;
1016 TRACE_(keyboard)( "chr %s, layout %p\n", debugstr_wn(&chr, 1), layout );
1018 if ((ret = user_driver->pVkKeyScanEx( chr, layout )) != -256) return ret;
1020 if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
1021 ret = kbd_tables_wchar_to_vkey( kbd_tables, chr );
1022 if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
1024 TRACE_(keyboard)( "ret %04x\n", ret );
1025 return ret;
1029 /******************************************************************************
1030 * NtUserMapVirtualKeyEx (win32u.@)
1032 UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
1034 USHORT vsc2vk[0x300];
1035 BYTE vk2char[0x100];
1036 const KBDTABLES *kbd_tables;
1037 UINT ret = 0;
1039 TRACE_(keyboard)( "code %u, type %u, layout %p.\n", code, type, layout );
1041 if ((ret = user_driver->pMapVirtualKeyEx( code, type, layout )) != -1) return ret;
1043 if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
1045 switch (type)
1047 case MAPVK_VK_TO_VSC_EX:
1048 case MAPVK_VK_TO_VSC:
1049 switch (code)
1051 case VK_SHIFT: code = VK_LSHIFT; break;
1052 case VK_CONTROL: code = VK_LCONTROL; break;
1053 case VK_MENU: code = VK_LMENU; break;
1054 case VK_NUMPAD0: code = VK_INSERT; break;
1055 case VK_NUMPAD1: code = VK_END; break;
1056 case VK_NUMPAD2: code = VK_DOWN; break;
1057 case VK_NUMPAD3: code = VK_NEXT; break;
1058 case VK_NUMPAD4: code = VK_LEFT; break;
1059 case VK_NUMPAD5: code = VK_CLEAR; break;
1060 case VK_NUMPAD6: code = VK_RIGHT; break;
1061 case VK_NUMPAD7: code = VK_HOME; break;
1062 case VK_NUMPAD8: code = VK_UP; break;
1063 case VK_NUMPAD9: code = VK_PRIOR; break;
1064 case VK_DECIMAL: code = VK_DELETE; break;
1067 kbd_tables_init_vsc2vk( kbd_tables, vsc2vk );
1068 for (ret = 0; ret < ARRAY_SIZE(vsc2vk); ++ret) if ((vsc2vk[ret] & 0xff) == code) break;
1069 if (ret >= ARRAY_SIZE(vsc2vk)) ret = 0;
1071 if (type == MAPVK_VK_TO_VSC)
1073 if (ret >= 0x200) ret = 0;
1074 else ret &= 0xff;
1076 else if (ret >= 0x100) ret += 0xdf00;
1077 break;
1078 case MAPVK_VSC_TO_VK:
1079 case MAPVK_VSC_TO_VK_EX:
1080 kbd_tables_init_vsc2vk( kbd_tables, vsc2vk );
1082 if (code & 0xe000) code -= 0xdf00;
1083 if (code >= ARRAY_SIZE(vsc2vk)) ret = 0;
1084 else ret = vsc2vk[code] & 0xff;
1086 if (type == MAPVK_VSC_TO_VK)
1088 switch (ret)
1090 case VK_LSHIFT: case VK_RSHIFT: ret = VK_SHIFT; break;
1091 case VK_LCONTROL: case VK_RCONTROL: ret = VK_CONTROL; break;
1092 case VK_LMENU: case VK_RMENU: ret = VK_MENU; break;
1095 break;
1096 case MAPVK_VK_TO_CHAR:
1097 kbd_tables_init_vk2char( kbd_tables, vk2char );
1098 if (code >= ARRAY_SIZE(vk2char)) ret = 0;
1099 else if (code >= 'A' && code <= 'Z') ret = code;
1100 else ret = vk2char[code];
1101 break;
1102 default:
1103 FIXME_(keyboard)( "unknown type %d\n", type );
1104 break;
1107 if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
1109 TRACE_(keyboard)( "returning 0x%04x\n", ret );
1110 return ret;
1113 /***********************************************************************
1114 * map_scan_to_kbd_vkey
1116 * Map a scancode to a virtual key with KBD information.
1118 USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout )
1120 const KBDTABLES *kbd_tables;
1121 USHORT vsc2vk[0x300];
1122 UINT vkey;
1124 if ((vkey = user_driver->pMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout )) != -1) return vkey;
1126 if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
1128 kbd_tables_init_vsc2vk( kbd_tables, vsc2vk );
1129 if (scan & 0xe000) scan -= 0xdf00;
1130 if (scan >= ARRAY_SIZE(vsc2vk)) vkey = 0;
1131 else vkey = vsc2vk[scan];
1133 if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
1135 return vkey;
1138 /****************************************************************************
1139 * NtUserGetKeyNameText (win32u.@)
1141 INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
1143 INT code = ((lparam >> 16) & 0x1ff), vkey, len;
1144 HKL layout = NtUserGetKeyboardLayout( 0 );
1145 const KBDTABLES *kbd_tables;
1146 VSC_LPWSTR *key_name;
1148 TRACE_(keyboard)( "lparam %#x, buffer %p, size %d.\n", (int)lparam, buffer, size );
1150 if (!buffer || !size) return 0;
1151 if ((len = user_driver->pGetKeyNameText( lparam, buffer, size )) >= 0) return len;
1153 if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
1155 if (lparam & 0x2000000)
1157 USHORT vsc2vk[0x300];
1158 kbd_tables_init_vsc2vk( kbd_tables, vsc2vk );
1159 switch ((vkey = vsc2vk[code] & 0xff))
1161 case VK_RSHIFT:
1162 case VK_RCONTROL:
1163 case VK_RMENU:
1164 for (code = 0; code < ARRAY_SIZE(vsc2vk); ++code)
1165 if ((vsc2vk[code] & 0xff) == (vkey - 1)) break;
1166 break;
1170 if (code < 0x100) key_name = kbd_tables->pKeyNames;
1171 else key_name = kbd_tables->pKeyNamesExt;
1172 while (key_name->vsc && key_name->vsc != (BYTE)code) key_name++;
1174 if (key_name->vsc == (BYTE)code && key_name->pwsz)
1176 len = min( size - 1, wcslen( key_name->pwsz ) );
1177 memcpy( buffer, key_name->pwsz, len * sizeof(WCHAR) );
1179 else if (size > 1)
1181 HKL hkl = NtUserGetKeyboardLayout( 0 );
1182 vkey = NtUserMapVirtualKeyEx( code & 0xff, MAPVK_VSC_TO_VK, hkl );
1183 buffer[0] = NtUserMapVirtualKeyEx( vkey, MAPVK_VK_TO_CHAR, hkl );
1184 len = 1;
1186 buffer[len] = 0;
1188 if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
1190 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_w(buffer) );
1191 return len;
1194 /****************************************************************************
1195 * NtUserToUnicodeEx (win32u.@)
1197 INT WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
1198 WCHAR *str, int size, UINT flags, HKL layout )
1200 const KBDTABLES *kbd_tables;
1201 INT len;
1203 TRACE_(keyboard)( "virt %#x, scan %#x, state %p, str %p, size %d, flags %#x, layout %p.\n",
1204 virt, scan, state, str, size, flags, layout );
1206 if (!state || !size) return 0;
1207 if ((len = user_driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout )) >= -1) return len;
1209 if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
1210 if (scan & 0x8000) str[0] = 0; /* key up */
1211 else str[0] = kbd_tables_vkey_to_wchar( kbd_tables, virt, state );
1212 if (size > 1) str[1] = 0;
1214 if (str[0] != WCH_NONE) len = 1;
1215 else str[0] = len = 0;
1217 if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
1219 TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_wn(str, len) );
1220 return len;
1223 /**********************************************************************
1224 * NtUserActivateKeyboardLayout (win32u.@)
1226 HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags )
1228 struct user_thread_info *info = get_user_thread_info();
1229 HKL old_layout;
1230 LCID locale;
1231 HWND focus;
1233 TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags );
1235 if (flags) FIXME_(keyboard)( "flags %x not supported\n", flags );
1237 if (layout == (HKL)HKL_NEXT || layout == (HKL)HKL_PREV)
1239 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
1240 FIXME_(keyboard)( "HKL_NEXT and HKL_PREV not supported\n" );
1241 return 0;
1244 if (LOWORD(layout) != MAKELANGID(LANG_INVARIANT, SUBLANG_DEFAULT) &&
1245 (NtQueryDefaultLocale( TRUE, &locale ) || LOWORD(layout) != locale))
1247 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
1248 FIXME_(keyboard)( "Changing user locale is not supported\n" );
1249 return 0;
1252 if (!user_driver->pActivateKeyboardLayout( layout, flags ))
1253 return 0;
1255 old_layout = info->kbd_layout;
1256 if (old_layout != layout)
1258 HWND ime_hwnd = get_default_ime_window( 0 );
1259 const NLS_LOCALE_DATA *data;
1260 CHARSETINFO cs = {0};
1262 if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_HKL_DEACTIVATE, HandleToUlong(old_layout) );
1264 if (HIWORD(layout) & 0x8000)
1265 FIXME( "Aliased keyboard layout not yet implemented\n" );
1266 else if (!(data = get_locale_data( HIWORD(layout) )))
1267 WARN( "Failed to find locale data for %04x\n", HIWORD(layout) );
1268 else
1269 translate_charset_info( ULongToPtr(data->idefaultansicodepage), &cs, TCI_SRCCODEPAGE );
1271 info->kbd_layout = layout;
1272 info->kbd_layout_id = 0;
1274 if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_HKL_ACTIVATE, HandleToUlong(layout) );
1276 if ((focus = get_focus()) && get_window_thread( focus, NULL ) == GetCurrentThreadId())
1277 send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout );
1280 if (!old_layout) return get_locale_kbd_layout();
1281 return old_layout;
1286 /***********************************************************************
1287 * NtUserGetKeyboardLayoutList (win32u.@)
1289 * Return number of values available if either input parm is
1290 * 0, per MS documentation.
1292 UINT WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts )
1294 char buffer[4096];
1295 KEY_NODE_INFORMATION *key_info = (KEY_NODE_INFORMATION *)buffer;
1296 KEY_VALUE_PARTIAL_INFORMATION *value_info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1297 DWORD count, tmp, i = 0;
1298 HKEY hkey, subkey;
1299 HKL layout;
1301 TRACE_(keyboard)( "size %d, layouts %p.\n", size, layouts );
1303 if ((count = user_driver->pGetKeyboardLayoutList( size, layouts )) != ~0) return count;
1305 layout = get_locale_kbd_layout();
1306 count = 0;
1308 count++;
1309 if (size && layouts)
1311 layouts[count - 1] = layout;
1312 if (count == size) return count;
1315 if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
1317 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key_info,
1318 sizeof(buffer) - sizeof(WCHAR), &tmp ))
1320 if (!(subkey = reg_open_key( hkey, key_info->Name, key_info->NameLength ))) continue;
1321 key_info->Name[key_info->NameLength / sizeof(WCHAR)] = 0;
1322 tmp = wcstoul( key_info->Name, NULL, 16 );
1323 if (query_reg_ascii_value( subkey, "Layout Id", value_info, sizeof(buffer) ) &&
1324 value_info->Type == REG_SZ)
1325 tmp = 0xf000 | (wcstoul( (const WCHAR *)value_info->Data, NULL, 16 ) & 0xfff);
1326 NtClose( subkey );
1328 tmp = MAKELONG( LOWORD( layout ), LOWORD( tmp ) );
1329 if (layout == UlongToHandle( tmp )) continue;
1331 count++;
1332 if (size && layouts)
1334 layouts[count - 1] = UlongToHandle( tmp );
1335 if (count == size) break;
1338 NtClose( hkey );
1341 return count;
1344 /****************************************************************************
1345 * NtUserGetKeyboardLayoutName (win32u.@)
1347 BOOL WINAPI NtUserGetKeyboardLayoutName( WCHAR *name )
1349 struct user_thread_info *info = get_user_thread_info();
1350 char buffer[4096];
1351 KEY_NODE_INFORMATION *key = (KEY_NODE_INFORMATION *)buffer;
1352 KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1353 WCHAR klid[KL_NAMELENGTH];
1354 UINT id;
1355 ULONG len, i = 0;
1356 HKEY hkey, subkey;
1357 HKL layout;
1359 TRACE_(keyboard)( "name %p\n", name );
1361 if (!name)
1363 RtlSetLastWin32Error( ERROR_NOACCESS );
1364 return FALSE;
1367 if (info->kbd_layout_id)
1369 snprintf( buffer, sizeof(buffer), "%08X", info->kbd_layout_id );
1370 asciiz_to_unicode( name, buffer );
1371 return TRUE;
1374 layout = NtUserGetKeyboardLayout( 0 );
1375 id = HandleToUlong( layout );
1376 if (HIWORD( id ) == LOWORD( id )) id = LOWORD( id );
1377 snprintf( buffer, sizeof(buffer), "%08X", id );
1378 asciiz_to_unicode( name, buffer );
1380 if ((hkey = reg_open_key( NULL, keyboard_layouts_keyW, sizeof(keyboard_layouts_keyW) )))
1382 while (!NtEnumerateKey( hkey, i++, KeyNodeInformation, key,
1383 sizeof(buffer) - sizeof(WCHAR), &len ))
1385 if (!(subkey = reg_open_key( hkey, key->Name, key->NameLength ))) continue;
1386 memcpy( klid, key->Name, key->NameLength );
1387 klid[key->NameLength / sizeof(WCHAR)] = 0;
1388 if (query_reg_ascii_value( subkey, "Layout Id", value, sizeof(buffer) ) &&
1389 value->Type == REG_SZ)
1390 id = 0xf000 | (wcstoul( (const WCHAR *)value->Data, NULL, 16 ) & 0xfff);
1391 else
1392 id = wcstoul( klid, NULL, 16 );
1393 NtClose( subkey );
1395 if (HIWORD( layout ) == id)
1397 lstrcpynW( name, klid, KL_NAMELENGTH );
1398 break;
1401 NtClose( hkey );
1404 info->kbd_layout_id = wcstoul( name, NULL, 16 );
1406 TRACE_(keyboard)( "ret %s\n", debugstr_w( name ) );
1407 return TRUE;
1410 /***********************************************************************
1411 * NtUserRegisterHotKey (win32u.@)
1413 BOOL WINAPI NtUserRegisterHotKey( HWND hwnd, INT id, UINT modifiers, UINT vk )
1415 BOOL ret;
1416 int replaced = 0;
1418 TRACE_(keyboard)( "(%p,%d,0x%08x,%X)\n", hwnd, id, modifiers, vk );
1420 if ((!hwnd || is_current_thread_window( hwnd )) &&
1421 !user_driver->pRegisterHotKey( hwnd, modifiers, vk ))
1422 return FALSE;
1424 SERVER_START_REQ( register_hotkey )
1426 req->window = wine_server_user_handle( hwnd );
1427 req->id = id;
1428 req->flags = modifiers;
1429 req->vkey = vk;
1430 if ((ret = !wine_server_call_err( req )))
1432 replaced = reply->replaced;
1433 modifiers = reply->flags;
1434 vk = reply->vkey;
1437 SERVER_END_REQ;
1439 if (ret && replaced)
1440 user_driver->pUnregisterHotKey(hwnd, modifiers, vk);
1442 return ret;
1445 /***********************************************************************
1446 * NtUserUnregisterHotKey (win32u.@)
1448 BOOL WINAPI NtUserUnregisterHotKey( HWND hwnd, INT id )
1450 BOOL ret;
1451 UINT modifiers, vk;
1453 TRACE_(keyboard)("(%p,%d)\n",hwnd,id);
1455 SERVER_START_REQ( unregister_hotkey )
1457 req->window = wine_server_user_handle( hwnd );
1458 req->id = id;
1459 if ((ret = !wine_server_call_err( req )))
1461 modifiers = reply->flags;
1462 vk = reply->vkey;
1465 SERVER_END_REQ;
1467 if (ret)
1468 user_driver->pUnregisterHotKey(hwnd, modifiers, vk);
1470 return ret;
1473 /***********************************************************************
1474 * NtUserGetMouseMovePointsEx (win32u.@)
1476 int WINAPI NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout,
1477 int count, DWORD resolution )
1479 cursor_pos_t *pos, positions[64];
1480 int copied;
1481 unsigned int i;
1484 TRACE( "%d, %p, %p, %d, %d\n", size, ptin, ptout, count, (int)resolution );
1486 if ((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > ARRAY_SIZE( positions )))
1488 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1489 return -1;
1492 if (!ptin || (!ptout && count))
1494 RtlSetLastWin32Error( ERROR_NOACCESS );
1495 return -1;
1498 if (resolution != GMMP_USE_DISPLAY_POINTS)
1500 FIXME( "only GMMP_USE_DISPLAY_POINTS is supported for now\n" );
1501 RtlSetLastWin32Error( ERROR_POINT_NOT_FOUND );
1502 return -1;
1505 SERVER_START_REQ( get_cursor_history )
1507 wine_server_set_reply( req, &positions, sizeof(positions) );
1508 if (wine_server_call_err( req )) return -1;
1510 SERVER_END_REQ;
1512 for (i = 0; i < ARRAY_SIZE( positions ); i++)
1514 pos = &positions[i];
1515 if (ptin->x == pos->x && ptin->y == pos->y && (!ptin->time || ptin->time == pos->time))
1516 break;
1519 if (i == ARRAY_SIZE( positions ))
1521 RtlSetLastWin32Error( ERROR_POINT_NOT_FOUND );
1522 return -1;
1525 for (copied = 0; copied < count && i < ARRAY_SIZE( positions ); copied++, i++)
1527 pos = &positions[i];
1528 ptout[copied].x = pos->x;
1529 ptout[copied].y = pos->y;
1530 ptout[copied].time = pos->time;
1531 ptout[copied].dwExtraInfo = pos->info;
1534 return copied;
1537 static WORD get_key_state(void)
1539 WORD ret = 0;
1541 if (get_system_metrics( SM_SWAPBUTTON ))
1543 if (NtUserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_LBUTTON;
1544 if (NtUserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_RBUTTON;
1546 else
1548 if (NtUserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_LBUTTON;
1549 if (NtUserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_RBUTTON;
1551 if (NtUserGetAsyncKeyState(VK_MBUTTON) & 0x80) ret |= MK_MBUTTON;
1552 if (NtUserGetAsyncKeyState(VK_SHIFT) & 0x80) ret |= MK_SHIFT;
1553 if (NtUserGetAsyncKeyState(VK_CONTROL) & 0x80) ret |= MK_CONTROL;
1554 if (NtUserGetAsyncKeyState(VK_XBUTTON1) & 0x80) ret |= MK_XBUTTON1;
1555 if (NtUserGetAsyncKeyState(VK_XBUTTON2) & 0x80) ret |= MK_XBUTTON2;
1556 return ret;
1559 struct tracking_list
1561 TRACKMOUSEEVENT info;
1562 POINT pos; /* center of hover rectangle */
1565 /* FIXME: move tracking stuff into per-thread data */
1566 static struct tracking_list tracking_info;
1568 static void check_mouse_leave( HWND hwnd, int hittest )
1570 if (tracking_info.info.hwndTrack != hwnd)
1572 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1573 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSELEAVE, 0, 0 );
1574 else
1575 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSELEAVE, 0, 0 );
1577 tracking_info.info.dwFlags &= ~TME_LEAVE;
1579 else
1581 if (hittest == HTCLIENT)
1583 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1585 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSELEAVE, 0, 0 );
1586 tracking_info.info.dwFlags &= ~TME_LEAVE;
1589 else
1591 if (!(tracking_info.info.dwFlags & TME_NONCLIENT))
1593 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSELEAVE, 0, 0 );
1594 tracking_info.info.dwFlags &= ~TME_LEAVE;
1600 void update_mouse_tracking_info( HWND hwnd )
1602 int hover_width = 0, hover_height = 0, hittest;
1603 POINT pos;
1605 TRACE( "hwnd %p\n", hwnd );
1607 get_cursor_pos( &pos );
1608 hwnd = window_from_point( hwnd, pos, &hittest );
1610 TRACE( "point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest );
1612 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERWIDTH, 0, &hover_width, 0 );
1613 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERHEIGHT, 0, &hover_height, 0 );
1615 TRACE( "tracked pos %s, current pos %s, hover width %d, hover height %d\n",
1616 wine_dbgstr_point(&tracking_info.pos), wine_dbgstr_point(&pos),
1617 hover_width, hover_height );
1619 if (tracking_info.info.dwFlags & TME_LEAVE)
1620 check_mouse_leave( hwnd, hittest );
1622 if (tracking_info.info.hwndTrack != hwnd)
1623 tracking_info.info.dwFlags &= ~TME_HOVER;
1625 if (tracking_info.info.dwFlags & TME_HOVER)
1627 /* has the cursor moved outside the rectangle centered around pos? */
1628 if ((abs( pos.x - tracking_info.pos.x ) > (hover_width / 2)) ||
1629 (abs( pos.y - tracking_info.pos.y ) > (hover_height / 2)))
1631 tracking_info.pos = pos;
1633 else
1635 if (hittest == HTCLIENT)
1637 screen_to_client(hwnd, &pos);
1638 TRACE( "client cursor pos %s\n", wine_dbgstr_point(&pos) );
1640 NtUserPostMessage( tracking_info.info.hwndTrack, WM_MOUSEHOVER,
1641 get_key_state(), MAKELPARAM( pos.x, pos.y ) );
1643 else
1645 if (tracking_info.info.dwFlags & TME_NONCLIENT)
1646 NtUserPostMessage( tracking_info.info.hwndTrack, WM_NCMOUSEHOVER,
1647 hittest, MAKELPARAM( pos.x, pos.y ) );
1650 /* stop tracking mouse hover */
1651 tracking_info.info.dwFlags &= ~TME_HOVER;
1655 /* stop the timer if the tracking list is empty */
1656 if (!(tracking_info.info.dwFlags & (TME_HOVER | TME_LEAVE)))
1658 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1659 tracking_info.info.hwndTrack = 0;
1660 tracking_info.info.dwFlags = 0;
1661 tracking_info.info.dwHoverTime = 0;
1665 /***********************************************************************
1666 * NtUserTrackMouseEvent (win32u.@)
1668 BOOL WINAPI NtUserTrackMouseEvent( TRACKMOUSEEVENT *info )
1670 DWORD hover_time;
1671 int hittest;
1672 HWND hwnd;
1673 POINT pos;
1675 TRACE( "size %u, flags %#x, hwnd %p, time %u\n",
1676 (int)info->cbSize, (int)info->dwFlags, info->hwndTrack, (int)info->dwHoverTime );
1678 if (info->cbSize != sizeof(TRACKMOUSEEVENT))
1680 WARN( "wrong size %u\n", (int)info->cbSize );
1681 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
1682 return FALSE;
1685 if (info->dwFlags & TME_QUERY)
1687 *info = tracking_info.info;
1688 info->cbSize = sizeof(TRACKMOUSEEVENT);
1689 return TRUE;
1692 if (!is_window( info->hwndTrack ))
1694 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1695 return FALSE;
1698 hover_time = (info->dwFlags & TME_HOVER) ? info->dwHoverTime : HOVER_DEFAULT;
1700 if (hover_time == HOVER_DEFAULT || hover_time == 0)
1701 NtUserSystemParametersInfo( SPI_GETMOUSEHOVERTIME, 0, &hover_time, 0 );
1703 get_cursor_pos( &pos );
1704 hwnd = window_from_point( info->hwndTrack, pos, &hittest );
1705 TRACE( "point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest );
1707 if (info->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT))
1708 FIXME( "ignoring flags %#x\n", (int)info->dwFlags & ~(TME_CANCEL | TME_HOVER | TME_LEAVE | TME_NONCLIENT) );
1710 if (info->dwFlags & TME_CANCEL)
1712 if (tracking_info.info.hwndTrack == info->hwndTrack)
1714 tracking_info.info.dwFlags &= ~(info->dwFlags & ~TME_CANCEL);
1716 /* if we aren't tracking on hover or leave remove this entry */
1717 if (!(tracking_info.info.dwFlags & (TME_HOVER | TME_LEAVE)))
1719 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1720 tracking_info.info.hwndTrack = 0;
1721 tracking_info.info.dwFlags = 0;
1722 tracking_info.info.dwHoverTime = 0;
1726 else
1728 /* In our implementation, it's possible that another window will receive
1729 * WM_MOUSEMOVE and call TrackMouseEvent before TrackMouseEventProc is
1730 * called. In such a situation, post the WM_MOUSELEAVE now. */
1731 if ((tracking_info.info.dwFlags & TME_LEAVE) && tracking_info.info.hwndTrack != NULL)
1732 check_mouse_leave(hwnd, hittest);
1734 kill_system_timer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE );
1735 tracking_info.info.hwndTrack = 0;
1736 tracking_info.info.dwFlags = 0;
1737 tracking_info.info.dwHoverTime = 0;
1739 if (info->hwndTrack == hwnd)
1741 /* Adding new mouse event to the tracking list */
1742 tracking_info.info = *info;
1743 tracking_info.info.dwHoverTime = hover_time;
1745 /* Initialize HoverInfo variables even if not hover tracking */
1746 tracking_info.pos = pos;
1748 NtUserSetSystemTimer( tracking_info.info.hwndTrack, SYSTEM_TIMER_TRACK_MOUSE, hover_time );
1752 return TRUE;
1755 /**********************************************************************
1756 * set_capture_window
1758 BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
1760 HWND previous = 0;
1761 UINT flags = 0;
1762 BOOL ret;
1764 if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU;
1765 if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE;
1767 SERVER_START_REQ( set_capture_window )
1769 req->handle = wine_server_user_handle( hwnd );
1770 req->flags = flags;
1771 if ((ret = !wine_server_call_err( req )))
1773 previous = wine_server_ptr_handle( reply->previous );
1774 hwnd = wine_server_ptr_handle( reply->full_handle );
1777 SERVER_END_REQ;
1779 if (ret)
1781 user_driver->pSetCapture( hwnd, gui_flags );
1783 if (previous)
1784 send_message( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
1786 if (prev_ret) *prev_ret = previous;
1788 return ret;
1791 /**********************************************************************
1792 * NtUserSetCapture (win32u.@)
1794 HWND WINAPI NtUserSetCapture( HWND hwnd )
1796 HWND previous = 0;
1798 set_capture_window( hwnd, 0, &previous );
1799 return previous;
1802 /**********************************************************************
1803 * release_capture
1805 BOOL release_capture(void)
1807 HWND previous = NULL;
1808 BOOL ret;
1810 ret = set_capture_window( 0, 0, &previous );
1812 /* Somebody may have missed some mouse movements */
1813 if (ret && previous)
1815 INPUT input = { .type = INPUT_MOUSE };
1816 input.mi.dwFlags = MOUSEEVENTF_MOVE;
1817 NtUserSendInput( 1, &input, sizeof(input) );
1820 return ret;
1823 /*****************************************************************
1824 * set_focus_window
1826 * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages
1828 static HWND set_focus_window( HWND hwnd )
1830 HWND previous = 0, ime_hwnd;
1831 BOOL ret;
1833 SERVER_START_REQ( set_focus_window )
1835 req->handle = wine_server_user_handle( hwnd );
1836 if ((ret = !wine_server_call_err( req )))
1837 previous = wine_server_ptr_handle( reply->previous );
1839 SERVER_END_REQ;
1840 if (!ret) return 0;
1841 if (previous == hwnd) return previous;
1843 if (previous)
1845 send_message( previous, WM_KILLFOCUS, (WPARAM)hwnd, 0 );
1847 ime_hwnd = get_default_ime_window( previous );
1848 if (ime_hwnd)
1849 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_DEACTIVATE,
1850 HandleToUlong(previous) );
1852 if (hwnd != get_focus()) return previous; /* changed by the message */
1854 if (is_window(hwnd))
1856 user_driver->pSetFocus(hwnd);
1858 ime_hwnd = get_default_ime_window( hwnd );
1859 if (ime_hwnd)
1860 send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE,
1861 HandleToUlong(hwnd) );
1863 NtUserNotifyWinEvent( EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, 0 );
1865 send_message( hwnd, WM_SETFOCUS, (WPARAM)previous, 0 );
1867 return previous;
1870 /*******************************************************************
1871 * set_active_window
1873 static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus )
1875 HWND previous = get_active_window();
1876 BOOL ret;
1877 DWORD old_thread, new_thread;
1878 CBTACTIVATESTRUCT cbt;
1880 if (previous == hwnd)
1882 if (prev) *prev = hwnd;
1883 goto done;
1886 /* call CBT hook chain */
1887 cbt.fMouse = mouse;
1888 cbt.hWndActive = previous;
1889 if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, sizeof(cbt) )) return FALSE;
1891 if (is_window( previous ))
1893 send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd );
1894 send_message( previous, WM_ACTIVATE,
1895 MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd );
1898 SERVER_START_REQ( set_active_window )
1900 req->handle = wine_server_user_handle( hwnd );
1901 if ((ret = !wine_server_call_err( req )))
1902 previous = wine_server_ptr_handle( reply->previous );
1904 SERVER_END_REQ;
1905 if (!ret) return FALSE;
1906 if (prev) *prev = previous;
1907 if (previous == hwnd) goto done;
1909 if (hwnd)
1911 NtUserNotifyWinEvent( EVENT_SYSTEM_FOREGROUND, hwnd, 0, 0 );
1913 /* send palette messages */
1914 if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 ))
1915 send_message_timeout( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0,
1916 SMTO_ABORTIFHUNG, 2000, FALSE );
1917 if (!is_window(hwnd)) return FALSE;
1920 old_thread = previous ? get_window_thread( previous, NULL ) : 0;
1921 new_thread = hwnd ? get_window_thread( hwnd, NULL ) : 0;
1923 if (old_thread != new_thread)
1925 HWND *list, *phwnd;
1927 if ((list = list_window_children( NULL, get_desktop_window(), NULL, 0 )))
1929 if (old_thread)
1931 for (phwnd = list; *phwnd; phwnd++)
1933 if (get_window_thread( *phwnd, NULL ) == old_thread)
1934 send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread );
1937 if (new_thread)
1939 for (phwnd = list; *phwnd; phwnd++)
1941 if (get_window_thread( *phwnd, NULL ) == new_thread)
1942 send_message( *phwnd, WM_ACTIVATEAPP, 1, old_thread );
1945 free( list );
1949 if (is_window(hwnd))
1951 send_message( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous );
1952 send_message( hwnd, WM_ACTIVATE,
1953 MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, is_iconic(hwnd) ),
1954 (LPARAM)previous );
1955 if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
1956 NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
1959 /* now change focus if necessary */
1960 if (focus)
1962 GUITHREADINFO info;
1964 info.cbSize = sizeof(info);
1965 NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info );
1966 /* Do not change focus if the window is no more active */
1967 if (hwnd == info.hwndActive)
1969 if (!info.hwndFocus || !hwnd || NtUserGetAncestor( info.hwndFocus, GA_ROOT ) != hwnd)
1970 set_focus_window( hwnd );
1974 done:
1975 if (hwnd) clip_fullscreen_window( hwnd, FALSE );
1976 return TRUE;
1979 /**********************************************************************
1980 * NtUserSetActiveWindow (win32u.@)
1982 HWND WINAPI NtUserSetActiveWindow( HWND hwnd )
1984 HWND prev;
1986 TRACE( "%p\n", hwnd );
1988 if (hwnd)
1990 LONG style;
1992 hwnd = get_full_window_handle( hwnd );
1993 if (!is_window( hwnd ))
1995 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1996 return 0;
1999 style = get_window_long( hwnd, GWL_STYLE );
2000 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD)
2001 return get_active_window(); /* Windows doesn't seem to return an error here */
2004 if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0;
2005 return prev;
2008 /*****************************************************************
2009 * NtUserSetFocus (win32u.@)
2011 HWND WINAPI NtUserSetFocus( HWND hwnd )
2013 HWND hwndTop = hwnd;
2014 HWND previous = get_focus();
2016 TRACE( "%p prev %p\n", hwnd, previous );
2018 if (hwnd)
2020 /* Check if we can set the focus to this window */
2021 hwnd = get_full_window_handle( hwnd );
2022 if (!is_window( hwnd ))
2024 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
2025 return 0;
2027 if (hwnd == previous) return previous; /* nothing to do */
2028 for (;;)
2030 HWND parent;
2031 LONG style = get_window_long( hwndTop, GWL_STYLE );
2032 if (style & (WS_MINIMIZE | WS_DISABLED)) return 0;
2033 if (!(style & WS_CHILD)) break;
2034 parent = NtUserGetAncestor( hwndTop, GA_PARENT );
2035 if (!parent || parent == get_desktop_window())
2037 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
2038 break;
2040 if (parent == get_hwnd_message_parent()) return 0;
2041 hwndTop = parent;
2044 /* call hooks */
2045 if (call_hooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)hwnd, (LPARAM)previous, 0 )) return 0;
2047 /* activate hwndTop if needed. */
2048 if (hwndTop != get_active_window())
2050 if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0;
2051 if (!is_window( hwnd )) return 0; /* Abort if window destroyed */
2053 /* Do not change focus if the window is no longer active */
2054 if (hwndTop != get_active_window()) return 0;
2057 else /* NULL hwnd passed in */
2059 if (!previous) return 0; /* nothing to do */
2060 if (call_hooks( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)previous, 0 )) return 0;
2063 /* change focus and send messages */
2064 return set_focus_window( hwnd );
2067 /*******************************************************************
2068 * set_foreground_window
2070 BOOL set_foreground_window( HWND hwnd, BOOL mouse )
2072 BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE;
2073 HWND previous = 0;
2075 if (mouse) hwnd = get_full_window_handle( hwnd );
2077 SERVER_START_REQ( set_foreground_window )
2079 req->handle = wine_server_user_handle( hwnd );
2080 if ((ret = !wine_server_call_err( req )))
2082 previous = wine_server_ptr_handle( reply->previous );
2083 send_msg_old = reply->send_msg_old;
2084 send_msg_new = reply->send_msg_new;
2087 SERVER_END_REQ;
2089 if (ret && previous != hwnd)
2091 if (send_msg_old) /* old window belongs to other thread */
2092 NtUserMessageCall( previous, WM_WINE_SETACTIVEWINDOW, 0, 0,
2093 0, NtUserSendNotifyMessage, FALSE );
2094 else if (send_msg_new) /* old window belongs to us but new one to other thread */
2095 ret = set_active_window( 0, NULL, mouse, TRUE );
2097 if (send_msg_new) /* new window belongs to other thread */
2098 NtUserMessageCall( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0,
2099 0, NtUserSendNotifyMessage, FALSE );
2100 else /* new window belongs to us */
2101 ret = set_active_window( hwnd, NULL, mouse, TRUE );
2103 return ret;
2106 static struct
2108 HBITMAP bitmap;
2109 unsigned int timeout;
2110 } caret = {0, 500};
2112 static void display_caret( HWND hwnd, const RECT *r )
2114 HDC dc, mem_dc;
2116 /* do not use DCX_CACHE here, since coördinates are in logical units */
2117 if (!(dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE )))
2118 return;
2119 mem_dc = NtGdiCreateCompatibleDC(dc);
2120 if (mem_dc)
2122 HBITMAP prev_bitmap;
2124 prev_bitmap = NtGdiSelectBitmap( mem_dc, caret.bitmap );
2125 NtGdiBitBlt( dc, r->left, r->top, r->right-r->left, r->bottom-r->top, mem_dc, 0, 0, SRCINVERT, 0, 0 );
2126 NtGdiSelectBitmap( mem_dc, prev_bitmap );
2127 NtGdiDeleteObjectApp( mem_dc );
2129 NtUserReleaseDC( hwnd, dc );
2132 static unsigned int get_caret_registry_timeout(void)
2134 char value_buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[11 * sizeof(WCHAR)])];
2135 KEY_VALUE_PARTIAL_INFORMATION *value = (void *)value_buffer;
2136 unsigned int ret = 500;
2137 HKEY key;
2139 if (!(key = reg_open_hkcu_key( "Control Panel\\Desktop" )))
2140 return ret;
2142 if (query_reg_ascii_value( key, "CursorBlinkRate", value, sizeof(value_buffer) ))
2143 ret = wcstoul( (WCHAR *)value->Data, NULL, 10 );
2144 NtClose( key );
2145 return ret;
2148 /*****************************************************************
2149 * NtUserCreateCaret (win32u.@)
2151 BOOL WINAPI NtUserCreateCaret( HWND hwnd, HBITMAP bitmap, int width, int height )
2153 HBITMAP caret_bitmap = 0;
2154 int old_state = 0;
2155 int hidden = 0;
2156 HWND prev = 0;
2157 BOOL ret;
2158 RECT r;
2160 TRACE( "hwnd %p, bitmap %p, width %d, height %d\n", hwnd, bitmap, width, height );
2162 if (!hwnd) return FALSE;
2164 if (bitmap && bitmap != (HBITMAP)1)
2166 BITMAP bitmap_data;
2168 if (!NtGdiExtGetObjectW( bitmap, sizeof(bitmap_data), &bitmap_data )) return FALSE;
2169 width = bitmap_data.bmWidth;
2170 height = bitmap_data.bmHeight;
2171 caret_bitmap = NtGdiCreateBitmap( bitmap_data.bmWidth, bitmap_data.bmHeight,
2172 bitmap_data.bmPlanes, bitmap_data.bmBitsPixel, NULL );
2173 if (caret_bitmap)
2175 size_t size = bitmap_data.bmWidthBytes * bitmap_data.bmHeight;
2176 BYTE *bits = malloc( size );
2178 NtGdiGetBitmapBits( bitmap, size, bits );
2179 NtGdiSetBitmapBits( caret_bitmap, size, bits );
2180 free( bits );
2183 else
2185 HDC dc;
2187 if (!width) width = get_system_metrics( SM_CXBORDER );
2188 if (!height) height = get_system_metrics( SM_CYBORDER );
2190 /* create the uniform bitmap on the fly */
2191 dc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
2192 if (dc)
2194 HDC mem_dc = NtGdiCreateCompatibleDC( dc );
2195 if (mem_dc)
2197 if ((caret_bitmap = NtGdiCreateCompatibleBitmap( mem_dc, width, height )))
2199 HBITMAP prev_bitmap = NtGdiSelectBitmap( mem_dc, caret_bitmap );
2200 SetRect( &r, 0, 0, width, height );
2201 fill_rect( mem_dc, &r, GetStockObject( bitmap ? GRAY_BRUSH : WHITE_BRUSH ));
2202 NtGdiSelectBitmap( mem_dc, prev_bitmap );
2204 NtGdiDeleteObjectApp( mem_dc );
2206 NtUserReleaseDC( hwnd, dc );
2209 if (!caret_bitmap) return FALSE;
2211 SERVER_START_REQ( set_caret_window )
2213 req->handle = wine_server_user_handle( hwnd );
2214 req->width = width;
2215 req->height = height;
2216 if ((ret = !wine_server_call_err( req )))
2218 prev = wine_server_ptr_handle( reply->previous );
2219 r = wine_server_get_rect( reply->old_rect );
2220 old_state = reply->old_state;
2221 hidden = reply->old_hide;
2224 SERVER_END_REQ;
2225 if (!ret) return FALSE;
2227 if (prev && !hidden) /* hide the previous one */
2229 /* FIXME: won't work if prev belongs to a different process */
2230 kill_system_timer( prev, SYSTEM_TIMER_CARET );
2231 if (old_state) display_caret( prev, &r );
2234 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
2235 caret.bitmap = caret_bitmap;
2236 caret.timeout = get_caret_registry_timeout();
2237 return TRUE;
2240 /*******************************************************************
2241 * destroy_caret
2243 BOOL destroy_caret(void)
2245 int old_state = 0;
2246 int hidden = 0;
2247 HWND prev = 0;
2248 BOOL ret;
2249 RECT r;
2251 SERVER_START_REQ( set_caret_window )
2253 req->handle = 0;
2254 req->width = 0;
2255 req->height = 0;
2256 if ((ret = !wine_server_call_err( req )))
2258 prev = wine_server_ptr_handle( reply->previous );
2259 r = wine_server_get_rect( reply->old_rect );
2260 old_state = reply->old_state;
2261 hidden = reply->old_hide;
2264 SERVER_END_REQ;
2266 if (ret && prev && !hidden)
2268 /* FIXME: won't work if prev belongs to a different process */
2269 kill_system_timer( prev, SYSTEM_TIMER_CARET );
2270 if (old_state) display_caret( prev, &r );
2272 if (caret.bitmap) NtGdiDeleteObjectApp( caret.bitmap );
2273 caret.bitmap = 0;
2274 return ret;
2277 /*****************************************************************
2278 * NtUserGetCaretBlinkTime (win32u.@)
2280 UINT WINAPI NtUserGetCaretBlinkTime(void)
2282 return caret.timeout;
2285 /*******************************************************************
2286 * set_caret_blink_time
2288 BOOL set_caret_blink_time( unsigned int time )
2290 TRACE( "time %u\n", time );
2292 caret.timeout = time;
2293 /* FIXME: update the timer */
2294 return TRUE;
2297 /*****************************************************************
2298 * NtUserGetCaretPos (win32u.@)
2300 BOOL WINAPI NtUserGetCaretPos( POINT *pt )
2302 BOOL ret;
2304 SERVER_START_REQ( set_caret_info )
2306 req->flags = 0; /* don't set anything */
2307 req->handle = 0;
2308 req->x = 0;
2309 req->y = 0;
2310 req->hide = 0;
2311 req->state = 0;
2312 if ((ret = !wine_server_call_err( req )))
2314 pt->x = reply->old_rect.left;
2315 pt->y = reply->old_rect.top;
2318 SERVER_END_REQ;
2319 return ret;
2322 /*******************************************************************
2323 * set_caret_pos
2325 BOOL set_caret_pos( int x, int y )
2327 int old_state = 0;
2328 int hidden = 0;
2329 HWND hwnd = 0;
2330 BOOL ret;
2331 RECT r;
2333 TRACE( "(%d, %d)\n", x, y );
2335 SERVER_START_REQ( set_caret_info )
2337 req->flags = SET_CARET_POS|SET_CARET_STATE;
2338 req->handle = 0;
2339 req->x = x;
2340 req->y = y;
2341 req->hide = 0;
2342 req->state = CARET_STATE_ON_IF_MOVED;
2343 if ((ret = !wine_server_call_err( req )))
2345 hwnd = wine_server_ptr_handle( reply->full_handle );
2346 r = wine_server_get_rect( reply->old_rect );
2347 old_state = reply->old_state;
2348 hidden = reply->old_hide;
2351 SERVER_END_REQ;
2352 if (ret && !hidden && (x != r.left || y != r.top))
2354 if (old_state) display_caret( hwnd, &r );
2355 r.right += x - r.left;
2356 r.bottom += y - r.top;
2357 r.left = x;
2358 r.top = y;
2359 display_caret( hwnd, &r );
2360 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2362 return ret;
2365 /*****************************************************************
2366 * NtUserShowCaret (win32u.@)
2368 BOOL WINAPI NtUserShowCaret( HWND hwnd )
2370 int hidden = 0;
2371 BOOL ret;
2372 RECT r;
2374 SERVER_START_REQ( set_caret_info )
2376 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2377 req->handle = wine_server_user_handle( hwnd );
2378 req->x = 0;
2379 req->y = 0;
2380 req->hide = -1;
2381 req->state = CARET_STATE_ON;
2382 if ((ret = !wine_server_call_err( req )))
2384 hwnd = wine_server_ptr_handle( reply->full_handle );
2385 r = wine_server_get_rect( reply->old_rect );
2386 hidden = reply->old_hide;
2389 SERVER_END_REQ;
2391 if (ret && hidden == 1) /* hidden was 1 so it's now 0 */
2393 display_caret( hwnd, &r );
2394 NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout );
2396 return ret;
2399 /*****************************************************************
2400 * NtUserHideCaret (win32u.@)
2402 BOOL WINAPI NtUserHideCaret( HWND hwnd )
2404 int old_state = 0;
2405 int hidden = 0;
2406 BOOL ret;
2407 RECT r;
2409 SERVER_START_REQ( set_caret_info )
2411 req->flags = SET_CARET_HIDE | SET_CARET_STATE;
2412 req->handle = wine_server_user_handle( hwnd );
2413 req->x = 0;
2414 req->y = 0;
2415 req->hide = 1;
2416 req->state = CARET_STATE_OFF;
2417 if ((ret = !wine_server_call_err( req )))
2419 hwnd = wine_server_ptr_handle( reply->full_handle );
2420 r = wine_server_get_rect( reply->old_rect );
2421 old_state = reply->old_state;
2422 hidden = reply->old_hide;
2425 SERVER_END_REQ;
2427 if (ret && !hidden)
2429 if (old_state) display_caret( hwnd, &r );
2430 kill_system_timer( hwnd, SYSTEM_TIMER_CARET );
2432 return ret;
2435 void toggle_caret( HWND hwnd )
2437 BOOL ret;
2438 RECT r;
2439 int hidden = 0;
2441 SERVER_START_REQ( set_caret_info )
2443 req->flags = SET_CARET_STATE;
2444 req->handle = wine_server_user_handle( hwnd );
2445 req->x = 0;
2446 req->y = 0;
2447 req->hide = 0;
2448 req->state = CARET_STATE_TOGGLE;
2449 if ((ret = !wine_server_call( req )))
2451 hwnd = wine_server_ptr_handle( reply->full_handle );
2452 r = wine_server_get_rect( reply->old_rect );
2453 hidden = reply->old_hide;
2456 SERVER_END_REQ;
2458 if (ret && !hidden) display_caret( hwnd, &r );
2462 /**********************************************************************
2463 * NtUserEnableMouseInPointer (win32u.@)
2465 BOOL WINAPI NtUserEnableMouseInPointer( BOOL enable )
2467 FIXME( "enable %u stub!\n", enable );
2468 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
2469 return FALSE;
2472 /**********************************************************************
2473 * NtUserIsMouseInPointerEnabled (win32u.@)
2475 BOOL WINAPI NtUserIsMouseInPointerEnabled(void)
2477 FIXME( "stub!\n" );
2478 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
2479 return FALSE;
2482 static BOOL is_captured_by_system(void)
2484 GUITHREADINFO info;
2485 info.cbSize = sizeof(info);
2486 return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) && info.hwndCapture && (info.flags & (GUI_INMOVESIZE | GUI_INMENUMODE));
2489 /***********************************************************************
2490 * clip_fullscreen_window
2492 * Turn on clipping if the active window is fullscreen.
2494 BOOL clip_fullscreen_window( HWND hwnd, BOOL reset )
2496 struct user_thread_info *thread_info = get_user_thread_info();
2497 MONITORINFO monitor_info = {.cbSize = sizeof(MONITORINFO)};
2498 RECT rect;
2499 HMONITOR monitor;
2500 DWORD style;
2501 UINT dpi;
2502 BOOL ret;
2504 if (hwnd == NtUserGetDesktopWindow()) return FALSE;
2505 if (hwnd != NtUserGetForegroundWindow()) return FALSE;
2507 style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
2508 if (!(style & WS_VISIBLE)) return FALSE;
2509 if ((style & (WS_POPUP | WS_CHILD)) == WS_CHILD) return FALSE;
2510 /* maximized windows don't count as full screen */
2511 if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) return FALSE;
2513 dpi = get_dpi_for_window( hwnd );
2514 if (!NtUserGetWindowRect( hwnd, &rect, dpi )) return FALSE;
2515 if (!NtUserIsWindowRectFullScreen( &rect, dpi )) return FALSE;
2516 if (is_captured_by_system()) return FALSE;
2517 if (NtGetTickCount() - thread_info->clipping_reset < 1000) return FALSE;
2518 if (!reset && clipping_cursor && thread_info->clipping_cursor) return FALSE; /* already clipping */
2520 if (!(monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST ))) return FALSE;
2521 if (!get_monitor_info( monitor, &monitor_info, 0 )) return FALSE;
2522 if (!grab_fullscreen)
2524 RECT virtual_rect = NtUserGetVirtualScreenRect();
2525 if (!EqualRect( &monitor_info.rcMonitor, &virtual_rect )) return FALSE;
2526 if (is_virtual_desktop()) return FALSE;
2529 TRACE( "win %p clipping fullscreen\n", hwnd );
2531 SERVER_START_REQ( set_cursor )
2533 req->flags = SET_CURSOR_CLIP | SET_CURSOR_FSCLIP;
2534 req->clip = wine_server_rectangle( monitor_info.rcMonitor );
2535 ret = !wine_server_call( req );
2537 SERVER_END_REQ;
2539 return ret;
2542 /**********************************************************************
2543 * NtUserGetPointerInfoList (win32u.@)
2545 BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_PTR unk0, UINT_PTR unk1, SIZE_T size,
2546 UINT32 *entry_count, UINT32 *pointer_count, void *pointer_info )
2548 FIXME( "id %#x, type %#x, unk0 %#zx, unk1 %#zx, size %#zx, entry_count %p, pointer_count %p, pointer_info %p stub!\n",
2549 id, (int)type, (size_t)unk0, (size_t)unk1, (size_t)size, entry_count, pointer_count, pointer_info );
2550 RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
2551 return FALSE;
2554 BOOL get_clip_cursor( RECT *rect, UINT dpi )
2556 struct object_lock lock = OBJECT_LOCK_INIT;
2557 const desktop_shm_t *desktop_shm;
2558 NTSTATUS status;
2560 if (!rect) return FALSE;
2562 while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING)
2563 *rect = wine_server_get_rect( desktop_shm->cursor.clip );
2565 if (!status)
2567 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, 0 );
2568 *rect = map_dpi_rect( *rect, get_monitor_dpi( monitor ), dpi );
2570 return !status;
2573 BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset )
2575 struct user_thread_info *thread_info = get_user_thread_info();
2576 RECT rect, virtual_rect = NtUserGetVirtualScreenRect();
2577 BOOL was_clipping, empty = !!(flags & SET_CURSOR_NOCLIP);
2579 TRACE( "hwnd %p, flags %#x, reset %u\n", hwnd, flags, reset );
2581 if ((was_clipping = thread_info->clipping_cursor)) InterlockedDecrement( &clipping_cursor );
2582 thread_info->clipping_cursor = FALSE;
2584 if (reset)
2586 thread_info->clipping_reset = NtGetTickCount();
2587 return user_driver->pClipCursor( NULL, TRUE );
2590 if (!grab_pointer) return TRUE;
2592 /* we are clipping if the clip rectangle is smaller than the screen */
2593 get_clip_cursor( &rect, 0 );
2594 intersect_rect( &rect, &rect, &virtual_rect );
2595 if (EqualRect( &rect, &virtual_rect )) empty = TRUE;
2596 if (empty && !(flags & SET_CURSOR_FSCLIP))
2598 /* if currently clipping, check if we should switch to fullscreen clipping */
2599 if (was_clipping && clip_fullscreen_window( hwnd, TRUE )) return TRUE;
2600 return user_driver->pClipCursor( NULL, FALSE );
2603 if (!user_driver->pClipCursor( &rect, FALSE )) return FALSE;
2604 InterlockedIncrement( &clipping_cursor );
2605 thread_info->clipping_cursor = TRUE;
2606 return TRUE;
2609 /***********************************************************************
2610 * NtUserClipCursor (win32u.@)
2612 BOOL WINAPI NtUserClipCursor( const RECT *rect )
2614 UINT dpi;
2615 BOOL ret;
2616 RECT new_rect;
2618 TRACE( "Clipping to %s\n", wine_dbgstr_rect(rect) );
2620 if (rect)
2622 if (rect->left > rect->right || rect->top > rect->bottom) return FALSE;
2623 if ((dpi = get_thread_dpi()))
2625 HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, dpi );
2626 new_rect = map_dpi_rect( *rect, dpi, get_monitor_dpi( monitor ));
2627 rect = &new_rect;
2631 SERVER_START_REQ( set_cursor )
2633 if (rect)
2635 req->flags = SET_CURSOR_CLIP;
2636 req->clip = wine_server_rectangle( *rect );
2638 else req->flags = SET_CURSOR_NOCLIP;
2640 ret = !wine_server_call( req );
2642 SERVER_END_REQ;
2644 return ret;