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

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

Rewrote keyboard hook handler to post messages and swallow the one it gets from PM

File size: 10.7 KB
Line 
1/* $Id: kbdhook.cpp,v 1.2 2004-02-10 15:36:04 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 WinPostMsg(pqmsg->hwnd, WM_CHAR_SPECIAL, pqmsg->mp1, pqmsg->mp2);
214 return TRUE;
215
216 //
217 // AltGr needs special handling
218 //
219 // AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYDOWN (VK_MENU)
220 // WM_SYSKEYUP (VK_CONTROL)
221 // WM_KEYUP (VK_MENU)
222 //
223 // Ctrl+AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYUP (VK_CONTROL)
224 // WM_KEYDOWN (VK_CONTROL)
225 // WM_KEYDOWN (VK_MENU)
226 // WM_KEYUP (VK_MENU)
227 // WM_KEYUP (VK_CONTROL)
228 //
229 // AltGr+Ctrl -> WM_KEYDOWN (VK_CONTROL), WM_KEYDOWN (VK_MENU)
230 // WM_KEYDOWN (VK_CONTROL)
231 // WM_SYSKEYUP (VK_CONTROL)
232 // WM_SYSKEYUP (VK_CONTROL)
233 // WM_KEYUP (VK_MENU)
234 //
235 // AltGr down -> if Ctrl down, send WM_KEYUP (VK_CONTROL)
236 // endif
237 // Send WM_KEYDOWN (VK_CONTROL)
238 // Send WM_KEYDOWN (VK_MENU)
239 // AltGr up -> if !(Ctrl down before AltGr was pressed || Ctrl up)
240 // Send WM_SYSKEYUP (VK_CONTROL)
241 // endif
242 // Send WM_KEYDOWN (VK_MENU)
243 //
244 // NOTE: Ctrl = Ctrl-Left; AltGr doesn't care about the right Ctrl key
245 //
246 case PMSCAN_ALTRIGHT:
247 {
248 QMSG msg = *pqmsg;
249 ULONG ctrlstate;
250 ULONG flags;
251 ULONG mp1, mp2;
252
253 flags = SHORT1FROMMP(pqmsg->mp1);
254
255 if (flags & KC_KEYUP)
256 {
257 //AltGr up
258 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
259 if (!(ctrlstate & 0x8000))
260 {
261 //ctrl is up, translate this message to Ctrl key up
262 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
263 mp1 |= (1 << 16); //repeat count
264 mp1 |= (KC_ALT | KC_KEYUP | KC_VIRTUALKEY | KC_SCANCODE);
265 mp2 = (VK_CTRL << 16); //virtual keycode
266 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
267
268 //and finally, post the AltGr WM_CHAR message
269 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
270 }
271 else {
272 WinPostMsg(pqmsg->hwnd, WM_CHAR_SPECIAL, pqmsg->mp1, pqmsg->mp2);
273 }
274 }
275 else
276 {
277 //AltGr down
278 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
279 if (ctrlstate & 0x8000)
280 {
281 //ctrl is down, translate this message to Ctrl key up
282 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
283 mp1 |= (1 << 16); //repeat count
284 mp1 |= (KC_KEYUP | KC_VIRTUALKEY | KC_SCANCODE);
285 mp2 = (VK_CTRL << 16); //virtual keycode
286
287 WinPostMsg(pqmsg->hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
288 }
289 //send left control key down message
290 mp1 = (PMSCAN_CTRLLEFT << 24); //scancode
291 mp1 |= (1 << 16); //repeat count
292 mp1 |= (KC_CTRL | KC_VIRTUALKEY | KC_SCANCODE);
293 mp2 = (VK_CTRL << 16); //virtual keycode
294
295 if (ctrlstate & 0x8000)
296 {
297 //ctrl is down, must post this message
298 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
299 }
300 else
301 {
302 //translate this message into control key down
303 WinPostMsg(pqmsg->hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
304 }
305 //and finally, post the AltGr WM_CHAR message
306 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
307 }
308 return TRUE;
309 }
310 }
311 break; // WM_CHAR
312 }
313 } // end switch(msg)
314 return FALSE;
315}
Note: See TracBrowser for help on using the repository browser.