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

Last change on this file since 8889 was 7864, checked in by sandervl, 24 years ago

PF: fixes for return & enter key handling

File size: 14.5 KB
Line 
1/* $Id: pmkbdhk.cpp,v 1.3 2002-02-11 13:45:33 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 *
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 */
13#define INCL_WIN
14#define INCL_WININPUT
15#define INCL_WINMESSAGEMGR
16#define INCL_WINHOOKS
17#define INCL_PM
18#define INCL_DOSERRORS
19#define INCL_DOSSEMAPHORES
20#include <os2.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <pmscan.h>
25
26#include <pmkbdhk.h>
27#include "pmkbdhkp.h"
28
29/*
30
31 To improve the ODIN keyboard management, it is required to intercept
32 any possible key from the Presentation Manager input queue.
33
34 Sketch:
35
36 - intercept keyboard messages before accelerators are translated
37 (HK_PREACCEL)
38 - determine if the message is targetted for an ODIN window
39 - if no ODIN window, pass thru
40 - if ODIN window
41 - check content of message.
42 - if sensible key event, rewrite message so PM won't handle it
43 itself (F1, F10, PrtScr, Ctrl, Alt, AltGr)
44 Ensure, GetKeyState() / GetAsyncKeyState() will still work properly!
45 The so rewritten message is handled specially inside the ODIN
46 message procedure. There, additionally required messages such as
47 WM_MENU if ALT is pressed need to be generated.
48 - if no sensible key, pass thru
49
50 Installation:
51 - automatically install hook on loading of the DLL
52 - automatically uninstall the hook on DLL termination
53 - DLL is GLOBAL INITTERM, used shared mem only!
54
55*/
56
57
58/**********
59 * Prototypes
60 **********/
61
62BOOL EXPENTRY hookPreAccelHook(HAB hab, PQMSG pqmsg, ULONG option);
63
64// _CRT_init is the C run-time environment initialization function.
65// It will return 0 to indicate success and -1 to indicate failure.
66int _Optlink _CRT_init(void);
67
68// _CRT_term is the C run-time environment termination function.
69// It only needs to be called when the C run-time functions are statically
70// linked, as is the case with XWorkplace.
71void _Optlink _CRT_term(void);
72
73
74
75/******************************************************************
76 *
77 * Module Global Variables
78 *
79 ******************************************************************/
80
81HOOKDATA G_HookData;
82
83
84/******************************************************************
85 *
86 * Helper functions
87 *
88 ******************************************************************/
89
90/*
91 *@@ InitializeGlobalsForHooks:
92 * this gets called from hookInit to initialize
93 * the global variables. We query the PM desktop,
94 * the window list, and other stuff we need all
95 * the time.
96 */
97
98VOID InitializeGlobalsForHooks(VOID)
99{
100 HENUM henum;
101 HWND hwndThis;
102 BOOL fFound;
103
104 // PM desktop (the WPS desktop is handled by the daemon)
105 G_HookData.hwndPMDesktop = WinQueryDesktopWindow(G_HookData.habDaemonObject,
106 NULLHANDLE);
107
108 WinQueryWindowProcess(HWND_DESKTOP, &G_HookData.pidPM, NULL);
109 // V0.9.7 (2001-01-21) [umoeller]
110
111 // enumerate desktop window to find the window list:
112 // according to PMTREE, the window list has the following
113 // window hierarchy:
114 // WC_FRAME
115 // +--- WC_TITLEBAR
116 // +--- Menu
117 // +--- WindowList
118 // +---
119 fFound = FALSE;
120 henum = WinBeginEnumWindows(HWND_DESKTOP);
121 while ( (!fFound)
122 && (hwndThis = WinGetNextWindow(henum))
123 )
124 {
125 CHAR szClass[200];
126 if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
127 {
128 if (!strcmp(szClass, "#1"))
129 {
130 // frame window: check the children
131 HENUM henumFrame;
132 HWND hwndChild;
133 henumFrame = WinBeginEnumWindows(hwndThis);
134 while ( (!fFound)
135 && (hwndChild = WinGetNextWindow(henumFrame))
136 )
137 {
138 CHAR szChildClass[200];
139 if (WinQueryClassName(hwndChild, sizeof(szChildClass), szChildClass))
140 {
141 if (!strcmp(szChildClass, "WindowList"))
142 {
143 // yup, found:
144 G_HookData.hwndWindowList = hwndThis;
145 fFound = TRUE;
146 }
147 }
148 }
149 WinEndEnumWindows(henumFrame);
150 }
151 }
152 }
153 WinEndEnumWindows(henum);
154
155}
156
157
158/******************************************************************
159 *
160 * Hook interface
161 *
162 ******************************************************************/
163
164/*
165 *@@ _DLL_InitTerm:
166 * this function gets called automatically by the OS/2 DLL
167 * during DosLoadModule processing, on the thread which
168 * invoked DosLoadModule.
169 *
170 * We override this function (which is normally provided by
171 * the runtime library) to intercept this DLL's module handle.
172 *
173 * Since OS/2 calls this function directly, it must have
174 * _System linkage.
175 *
176 * Note: You must then link using the /NOE option, because
177 * the VAC++ runtimes also contain a _DLL_Initterm, and the
178 * linker gets in trouble otherwise.
179 * The XWorkplace makefile takes care of this.
180 *
181 * This function must return 0 upon errors or 1 otherwise.
182 *
183 *@@changed V0.9.0 [umoeller]: reworked locale initialization
184 */
185
186unsigned long _System _DLL_InitTerm(unsigned long hModule,
187 unsigned long ulFlag)
188{
189 switch (ulFlag)
190 {
191 case 0:
192 {
193 // store the DLL handle in the global variable
194 G_HookData.hmodDLL = hModule;
195
196 // now initialize the C run-time environment before we
197 // call any runtime functions
198 if (_CRT_init() == -1)
199 return (0); // error
200
201 break; }
202
203 case 1:
204 // DLL being freed: cleanup runtime
205 _CRT_term();
206 break;
207
208 default:
209 // other code: beep for error
210 DosBeep(100, 100);
211 return (0); // error
212 }
213
214 // a non-zero value must be returned to indicate success
215 return (1);
216}
217
218/*
219 *@@ hookInit:
220 * registers (sets) all the hooks and initializes data.
221 *
222 * In any case, a pointer to the DLL's static HOOKDATA
223 * structure is returned. In this struct, the caller
224 * can examine the two flags for whether the hooks
225 * were successfully installed.
226 *
227 * Note: All the exported hook* interface functions must
228 * only be called by the same process, which is the
229 * XWorkplace daemon (XWPDAEMN.EXE).
230 *
231 * This gets called by XWPDAEMN.EXE when
232 *
233 *@@changed V0.9.1 (2000-02-01) [umoeller]: fixed missing global updates
234 *@@changed V0.9.2 (2000-02-21) [umoeller]: added new system hooks
235 */
236
237PHOOKDATA EXPENTRY hookInit(HAB hab)
238{
239 APIRET arc = NO_ERROR;
240
241 if (arc == NO_ERROR)
242 {
243 G_HookData.habDaemonObject = hab;
244
245 // G_HookData.hmtxPageMage = hmtxPageMage;
246
247 if (G_HookData.hmodDLL) // initialized by _DLL_InitTerm
248 {
249 // BOOL fSuccess = FALSE;
250
251 // initialize globals needed by the hook
252 InitializeGlobalsForHooks();
253
254 // install hooks, but only once...
255#if 0
256 if (!G_HookData.fLockupHooked)
257 G_HookData.fLockupHooked = WinSetHook(G_HookData.habDaemonObject,
258 NULLHANDLE, // system hook
259 HK_LOCKUP, // lockup hook
260 (PFN)hookLockupHook,
261 G_HookData.hmodDLL);
262#endif
263 if (!G_HookData.fPreAccelHooked)
264 G_HookData.fPreAccelHooked = WinSetHook(G_HookData.habDaemonObject,
265 NULLHANDLE, // system hook
266 HK_PREACCEL, // pre-accelerator table hook (undocumented)
267 (PFN)hookPreAccelHook,
268 G_HookData.hmodDLL);
269 }
270
271 }
272
273 return (&G_HookData);
274}
275
276/*
277 *@@ hookKill:
278 * deregisters the hook function and frees allocated
279 * resources.
280 *
281 * Note: This function must only be called by the same
282 * process which called hookInit (that is, the daemon),
283 * or resources cannot be properly freed.
284 *
285 *@@changed V0.9.1 (2000-02-01) [umoeller]: fixed missing global updates
286 *@@changed V0.9.2 (2000-02-21) [umoeller]: added new system hooks
287 *@@changed V0.9.3 (2000-04-20) [umoeller]: added function keys support
288 */
289
290BOOL EXPENTRY hookKill(void)
291{
292 BOOL brc = FALSE;
293
294 if (G_HookData.fPreAccelHooked)
295 {
296 WinReleaseHook(G_HookData.habDaemonObject,
297 NULLHANDLE,
298 HK_PREACCEL, // pre-accelerator table hook (undocumented)
299 (PFN)hookPreAccelHook,
300 G_HookData.hmodDLL);
301 brc = TRUE;
302 G_HookData.fPreAccelHooked = FALSE;
303 }
304
305#if 0
306 if (G_HookData.fLockupHooked)
307 {
308 WinReleaseHook(G_HookData.habDaemonObject,
309 NULLHANDLE,
310 HK_LOCKUP, // lockup hook
311 (PFN)hookLockupHook,
312 G_HookData.hmodDLL);
313 brc = TRUE;
314 G_HookData.fLockupHooked = FALSE;
315 }
316#endif
317
318 return (brc);
319}
320
321
322//
323// This function detects if the current window is
324// a win32 window
325//
326
327static HWND hwndLastWin32Window = 0;
328
329BOOL i_isWin32Window(HWND hwnd)
330{
331 // Note: this hook is called on the stack of the current process
332 // so we rather keep the buffer small ...
333 CHAR szClass[80];
334
335 if (hwndLastWin32Window == hwnd)
336 return TRUE;
337
338 for(;;)
339 {
340 if (WinQueryClassName(hwnd, sizeof(szClass), szClass))
341 {
342 if (strcmp(szClass, WIN32_STDFRAMECLASS) == 0)
343 {
344 hwndLastWin32Window = hwnd;
345 return TRUE;
346 }
347 else
348 {
349 // walk up the tree
350 hwnd = WinQueryWindow(hwnd,
351 QW_PARENT);
352
353 // no parent window found?
354 if (NULLHANDLE == hwnd)
355 {
356 hwndLastWin32Window = 0;
357 return FALSE;
358 }
359 }
360 }
361 else
362 {
363 hwndLastWin32Window = 0;
364 return FALSE;
365 }
366 }
367}
368
369
370/******************************************************************
371 *
372 * Pre-accelerator hook
373 *
374 ******************************************************************/
375
376/*
377 *@@ hookPreAccelHook:
378 * this is the pre-accelerator-table hook. Like
379 * hookInputHook, this gets called when messages
380 * are coming in from the system input queue, but
381 * as opposed to a "regular" input hook, this hook
382 * gets called even before translation from accelerator
383 * tables occurs.
384 *
385 * Pre-accel hooks are not documented in the PM Reference.
386 * I have found this idea (and part of the implementation)
387 * in the source code of ProgramCommander/2, so thanks
388 * go out (once again) to Roman Stangl.
389 *
390 * In this hook, we check for the global object hotkeys
391 * so that we can use certain key combinations regardless
392 * of whether they might be currently used in application
393 * accelerator tables. This is especially useful for
394 * "Alt" key combinations, which are usually intercepted
395 * when menus have corresponding shortcuts.
396 *
397 * As a result, as opposed to other hotkey software you
398 * might know, XWorkplace does properly react to "Ctrl+Alt"
399 * keyboard combinations, even if a menu would get called
400 * with the "Alt" key. ;-)
401 *
402 * As with hookInputHook, we return TRUE if the message is
403 * to be swallowed, or FALSE if the current application (or
404 * the next hook in the hook chain) should still receive the
405 * message.
406 *
407 *@@changed V0.9.3 (2000-04-09) [umoeller]: added check for system lockup
408 *@@changed V0.9.3 (2000-04-09) [umoeller]: added PageMage hotkeys
409 *@@changed V0.9.3 (2000-04-09) [umoeller]: added KC_SCANCODE check
410 *@@changed V0.9.3 (2000-04-10) [umoeller]: moved debug code to hook
411 */
412
413BOOL EXPENTRY hookPreAccelHook(HAB hab, PQMSG pqmsg, ULONG option)
414{
415 // set return value:
416 // per default, pass message on to next hook or application
417 BOOL brc = FALSE;
418
419 if (pqmsg == NULL)
420 return (FALSE);
421
422 switch(pqmsg->msg)
423 {
424 /*
425 * WM_CHAR:
426 * keyboard activity.
427 */
428
429 case WM_CHAR:
430 {
431 // Note:
432 // what happens it the system is locked up?
433 // do we have to detect the screensaver to kick in?
434 // if ( (!G_HookData.hwndLockupFrame) // system not locked up
435 // ...
436
437
438 // is this an WIN32 window?
439 if (!i_isWin32Window(pqmsg->hwnd))
440 {
441 // no, so pass thru
442 return FALSE;
443 }
444
445 // check if we've encountered a so called critical key
446 // (this is the scan code which is supposed to be valid
447 // always for the hooks! */
448 switch ( CHAR4FROMMP(pqmsg->mp1) )
449 {
450 default:
451 return FALSE;
452
453 // Intercept PM Window Hotkeys such as
454 // Alt-F7 do enable window moving by keyboard.
455 case PMSCAN_F1:
456 case PMSCAN_F2:
457 case PMSCAN_F3:
458 case PMSCAN_F4:
459 case PMSCAN_F5:
460 case PMSCAN_F6:
461 case PMSCAN_F7:
462 case PMSCAN_F8:
463 case PMSCAN_F9:
464 case PMSCAN_F10:
465 case PMSCAN_F11:
466 case PMSCAN_F12:
467
468 // Try to prevent Ctrl-Esc, etc. from being intercepted by PM
469 case PMSCAN_ESC:
470 case PMSCAN_CTRLLEFT:
471 case PMSCAN_CTRLRIGHT:
472
473 case PMSCAN_PRINT:
474 case PMSCAN_ALTLEFT:
475 case PMSCAN_ALTRIGHT:
476 case PMSCAN_SCROLLLOCK:
477 case PMSCAN_ENTER:
478 case PMSCAN_PADENTER:
479 case PMSCAN_CAPSLOCK:
480 case PMSCAN_SHIFTLEFT:
481 case PMSCAN_SHIFTRIGHT:
482 // OK, as we've got a special key here, we've got
483 // to rewrite the message so PM will ignore the key
484 // and won't translate the message to anything else.
485
486 pqmsg->msg = WM_CHAR_SPECIAL;
487
488 break;
489 }
490 }
491
492 break; // WM_CHAR
493 } // end switch(msg)
494
495 return (brc);
496}
497
Note: See TracBrowser for help on using the repository browser.