source: trunk/src/kernel32/kbdhook.cpp@ 10382

Last change on this file since 10382 was 10382, checked in by sandervl, 22 years ago

updates

File size: 10.7 KB
Line 
1/* $Id: kbdhook.cpp,v 1.1 2004-01-12 09:55:25 sandervl Exp $ */
2/*
3 * OS/2 native Presentation Manager hooks
4 *
5 *
6 * Large Portions (C) Ulrich M”ller, XWorkplace
7 * Copyright 2001 Patrick Haller (patrick.haller@innotek.de)
8 * Copyright 2002-2003 Innotek Systemberatung GmbH
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 */
13
14#define INCL_WIN
15#define INCL_WININPUT
16#define INCL_WINMESSAGEMGR
17#define INCL_WINHOOKS
18#define INCL_PM
19#define INCL_BASE
20#define INCL_ERRORS
21#include <os2wrap.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <signal.h>
26#include <pmscan.h>
27
28#include <odin.h>
29#include <pmkbdhk.h>
30#include <kbdhook.h>
31#include <custombuild.h>
32
33/*
34
35 To improve the ODIN keyboard management, it is required to intercept
36 any possible key from the Presentation Manager input queue.
37
38 Sketch:
39
40 - intercept keyboard messages before accelerators are translated
41 (HK_PREACCEL)
42 - determine if the message is targetted for an ODIN window
43 - if no ODIN window, pass thru
44 - if ODIN window
45 - check content of message.
46 - if sensible key event, rewrite message so PM won't handle it
47 itself (F1, F10, PrtScr, Ctrl, Alt, AltGr)
48 Ensure, GetKeyState() / GetAsyncKeyState() will still work properly!
49 The so rewritten message is handled specially inside the ODIN
50 message procedure. There, additionally required messages such as
51 WM_MENU if ALT is pressed need to be generated.
52 - if no sensible key, pass thru
53
54 Installation:
55 - automatically install hook on loading of the DLL
56 - automatically uninstall the hook on DLL termination
57 - DLL is GLOBAL INITTERM, used shared mem only!
58 - Whenever process terminates OS/2 frees all hooks process initiated,
59 no matter whether DLL is still in memory or not. So in sequence:
60 Load 1 VIO app, load 2 VIO app, close 1st VIO app - KBD hook stops working because
61 it was released by OS/2 on 1 process. Creating hooks for each VIO app solves
62 this problem. Hook processes needed messages and removes them from other
63 hooks if needed. On termination of process 1 we still have hook of process 2.
64*/
65
66
67static char szWindowClass[256] = "";
68
69BOOL EXPENTRY hookPreAccelHook(HAB hab, PQMSG pqmsg, ULONG option);
70
71/*
72 *@@ hookInit:
73 * registers (sets) all the hooks and initializes data.
74 * we do call this for every process.
75 */
76
77BOOL WIN32API hookInit(ULONG hab)
78{
79 if(szWindowClass[0] == 0)
80 {//query odin client window class name
81 strcpy(szWindowClass, QueryCustomStdClassName());
82 }
83 return WinSetHook(hab, HMQ_CURRENT, // queue hook
84 HK_PREACCEL, // pre-accelerator table hook (undocumented)
85 (PFN)hookPreAccelHook,
86 NULL);
87}
88
89/*
90 *@@ hookKill:
91 * deregisters the hook function and frees allocated
92 * resources.
93 */
94
95BOOL WIN32API hookKill(ULONG hab)
96{
97 WinReleaseHook(hab, HMQ_CURRENT,
98 HK_PREACCEL, // pre-accelerator table hook (undocumented)
99 (PFN)hookPreAccelHook,
100 NULL);
101 return TRUE;
102}
103
104//
105// This function detects if the current window is
106// a win32 window
107//
108
109static HWND hwndLastWin32Window = 0;
110
111BOOL i_isWin32Window(HWND hwnd)
112{
113 // Note: this hook is called on the stack of the current process
114 // so we rather keep the buffer small ...
115 CHAR szClass[80];
116
117 if (hwndLastWin32Window == hwnd)
118 return TRUE;
119
120 if (WinQueryClassName(hwnd, sizeof(szClass), szClass))
121 {
122 if (strcmp(szClass, szWindowClass) == 0)
123 {
124 hwndLastWin32Window = hwnd;
125 return TRUE;
126 }
127 }
128 return FALSE;
129}
130
131/*
132 *@@ hookPreAccelHook:
133 * this is the pre-accelerator-table hook. Like
134 * hookInputHook, this gets called when messages
135 * are coming in from the system input queue, but
136 * as opposed to a "regular" input hook, this hook
137 * gets called even before translation from accelerator
138 * tables occurs.
139 *
140 * Pre-accel hooks are not documented in the PM Reference.
141 * I have found this idea (and part of the implementation)
142 * in the source code of ProgramCommander/2, so thanks
143 * go out (once again) to Roman Stangl.
144 *
145 * In this hook, we check for the global object hotkeys
146 * so that we can use certain key combinations regardless
147 * of whether they might be currently used in application
148 * accelerator tables. This is especially useful for
149 * "Alt" key combinations, which are usually intercepted
150 * when menus have corresponding shortcuts.
151 *
152 * ODIN: Keep in mind that effort must be taken to make processing as
153 * optimal as possible as this is install by each and every Odin
154 * process and they will process and reject the exact same messages.
155 * Only the first called hook will actually do something useful.
156 */
157
158BOOL EXPENTRY hookPreAccelHook(HAB hab, PQMSG pqmsg, ULONG option)
159{
160 if (pqmsg == NULL)
161 return (FALSE);
162
163 switch(pqmsg->msg)
164 {
165
166 case WM_CHAR:
167 {
168 // is this an WIN32 window?
169 if (!i_isWin32Window(pqmsg->hwnd))
170 {
171 // no, so pass thru
172 return FALSE;
173 }
174
175 // check if we've encountered a so called critical key
176 // (this is the scan code which is supposed to be valid
177 // always for the hooks!)
178 switch ( CHAR4FROMMP(pqmsg->mp1) )
179 {
180 default:
181 return FALSE;
182
183 // Intercept PM Window Hotkeys such as
184 // Alt-F7 do enable window moving by keyboard.
185 case PMSCAN_F1:
186 case PMSCAN_F2:
187 case PMSCAN_F3:
188 case PMSCAN_F4:
189 case PMSCAN_F5:
190 case PMSCAN_F6:
191 case PMSCAN_F7:
192 case PMSCAN_F8:
193 case PMSCAN_F9:
194 case PMSCAN_F10:
195 case PMSCAN_F11:
196 case PMSCAN_F12:
197
198 // Try to prevent Ctrl-Esc, etc. from being intercepted by PM
199 case PMSCAN_ESC:
200 case PMSCAN_CTRLLEFT:
201 case PMSCAN_CTRLRIGHT:
202
203 case PMSCAN_PRINT:
204 case PMSCAN_ALTLEFT:
205 case PMSCAN_SCROLLLOCK:
206 case PMSCAN_ENTER:
207 case PMSCAN_PADENTER:
208 case PMSCAN_CAPSLOCK:
209 // OK, as we've got a special key here, we've got
210 // to rewrite the message so PM will ignore the key
211 // and won't translate the message to anything else.
212
213 pqmsg->msg = WM_CHAR_SPECIAL;
214
215 break;
216
217 //
218 // AltGr needs special handling
219 //
220 // AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYDOWN (VK_MENU)
221 // WM_SYSKEYUP (VK_CONTROL)
222 // WM_KEYUP (VK_MENU)
223 //
224 // Ctrl+AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYUP (VK_CONTROL)
225 // WM_KEYDOWN (VK_CONTROL)
226 // WM_KEYDOWN (VK_MENU)
227 // WM_KEYUP (VK_MENU)
228 // WM_KEYUP (VK_CONTROL)
229 //
230 // AltGr+Ctrl -> WM_KEYDOWN (VK_CONTROL), WM_KEYDOWN (VK_MENU)
231 // WM_KEYDOWN (VK_CONTROL)
232 // WM_SYSKEYUP (VK_CONTROL)
233 // WM_SYSKEYUP (VK_CONTROL)
234 // WM_KEYUP (VK_MENU)
235 //
236 // AltGr down -> if Ctrl down, send WM_KEYUP (VK_CONTROL)
237 // endif
238 // Send WM_KEYDOWN (VK_CONTROL)
239 // Send WM_KEYDOWN (VK_MENU)
240 // AltGr up -> if !(Ctrl down before AltGr was pressed || Ctrl up)
241 // Send WM_SYSKEYUP (VK_CONTROL)
242 // endif
243 // Send WM_KEYDOWN (VK_MENU)
244 //
245 // NOTE: Ctrl = Ctrl-Left; AltGr doesn't care about the right Ctrl key
246 //
247 case PMSCAN_ALTRIGHT:
248 {
249 QMSG msg = *pqmsg;
250 ULONG ctrlstate;
251 ULONG flags;
252 ULONG mp1, mp2;
253
254 flags = SHORT1FROMMP(pqmsg->mp1);
255
256 pqmsg->msg = WM_CHAR_SPECIAL;
257
258 if (flags & KC_KEYUP)
259 {
260 //AltGr up
261 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
262 if (!(ctrlstate & 0x8000))
263 {
264 //ctrl is up, translate this message to Ctrl key up
265 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
266 mp1 |= (1 << 16); //repeat count
267 mp1 |= (KC_ALT | KC_KEYUP | KC_VIRTUALKEY | KC_SCANCODE);
268 mp2 = (VK_CTRL << 16); //virtual keycode
269 pqmsg->msg = WM_CHAR_SPECIAL_ALTGRCONTROL;
270 pqmsg->mp1 = (MPARAM)mp1;
271 pqmsg->mp2 = (MPARAM)mp2;
272
273 //and finally, post the AltGr WM_CHAR message
274 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
275 }
276 //else do nothing
277 }
278 else
279 {
280 //AltGr down
281 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
282 if (ctrlstate & 0x8000)
283 {
284 //ctrl is down, translate this message to Ctrl key up
285 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
286 mp1 |= (1 << 16); //repeat count
287 mp1 |= (KC_KEYUP | KC_VIRTUALKEY | KC_SCANCODE);
288 mp2 = (VK_CTRL << 16); //virtual keycode
289 pqmsg->msg = WM_CHAR_SPECIAL_ALTGRCONTROL;
290 pqmsg->mp1 = (MPARAM)mp1;
291 pqmsg->mp2 = (MPARAM)mp2;
292 }
293 //send left control key down message
294 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
295 mp1 |= (1 << 16); //repeat count
296 mp1 |= (KC_CTRL | KC_VIRTUALKEY | KC_SCANCODE);
297 mp2 = (VK_CTRL << 16); //virtual keycode
298
299 if (ctrlstate & 0x8000)
300 {
301 //ctrl is down, must post this message
302 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
303 }
304 else
305 {
306 //translate this message into control key down
307 pqmsg->msg = WM_CHAR_SPECIAL_ALTGRCONTROL;
308 pqmsg->mp1 = (MPARAM)mp1;
309 pqmsg->mp2 = (MPARAM)mp2;
310 }
311 //and finally, post the AltGr WM_CHAR message
312 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
313 }
314 break;
315 }
316 }
317 break; // WM_CHAR
318 }
319 } // end switch(msg)
320 return FALSE;
321}
Note: See TracBrowser for help on using the repository browser.