source: trunk/src/pmkbdhk/pmkbdhk.cpp@ 10366

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

update

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