source: trunk/src/pmkbdhk/pmkbdhk.cpp

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

update

File size: 12.2 KB
RevLine 
[10289]1/* $Id: pmkbdhk.cpp,v 1.10 2003-10-22 16:10:59 sandervl Exp $ */
[7155]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)
[10286]8 * Copyright 2002-2003 Innotek Systemberatung GmbH
[7155]9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 */
[10286]13
[7155]14#define INCL_WIN
15#define INCL_WININPUT
16#define INCL_WINMESSAGEMGR
17#define INCL_WINHOOKS
18#define INCL_PM
[10286]19#define INCL_BASE
20#define INCL_ERRORS
[7155]21#include <os2.h>
22#include <stdlib.h>
[10286]23#include <stdio.h>
[7155]24#include <string.h>
[10286]25#include <signal.h>
[7155]26#include <pmscan.h>
27
28#include <pmkbdhk.h>
29#include "pmkbdhkp.h"
30
[10286]31
[7155]32/*
[10286]33
[7155]34 To improve the ODIN keyboard management, it is required to intercept
35 any possible key from the Presentation Manager input queue.
[10286]36
[7155]37 Sketch:
[10286]38
[7155]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
[10286]52
[7155]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!
[10286]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.
[7155]63*/
64
65
66BOOL EXPENTRY hookPreAccelHook(HAB hab, PQMSG pqmsg, ULONG option);
67
[10286]68HOOKDATA G_HookData = {0};
[7155]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;
[10286]79 break;
80 }
[7155]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:
[10286]94 * registers (sets) all the hooks and initializes data.
95 * we do call this for every process.
[7155]96 */
97
[10286]98BOOL WIN32API hookInit(HAB hab, PSZ pszWindowClassName)
[7155]99{
[10286]100 if (G_HookData.hmodDLL) // initialized by _DLL_InitTerm
101 {
102 BOOL fSuccess;
[7155]103
[10286]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.
[7155]109
[10286]110 G_HookData.habDaemonObject = hab;
111 G_HookData.fPreAccelHooked = WinSetHook(hab, NULLHANDLE, // system hook
112 HK_PREACCEL, // pre-accelerator table hook (undocumented)
[10287]113 (PFN)hookPreAccelHook,
[10286]114 G_HookData.hmodDLL);
[7155]115
[10286]116 strncpy(G_HookData.szWindowClass, pszWindowClassName, sizeof(G_HookData.szWindowClass));
117 return TRUE;
[7155]118 }
[10286]119 return FALSE;
[7155]120}
121
122/*
123 *@@ hookKill:
124 * deregisters the hook function and frees allocated
125 * resources.
126 */
127
[10286]128BOOL WIN32API hookKill()
[7155]129{
[10286]130 // PM will cleanup the hook when the the creators message queue is destroyed
131 // or process terminates.
[7155]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);
[10286]140
[7155]141 G_HookData.fPreAccelHooked = FALSE;
142 }
143
[10286]144 return TRUE;
[7155]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 ...
[10286]159 CHAR szClass[80];
160
[7155]161 if (hwndLastWin32Window == hwnd)
162 return TRUE;
[10286]163
164 if (WinQueryClassName(hwnd, sizeof(szClass), szClass))
[7155]165 {
[10286]166 if (strcmp(szClass, G_HookData.szWindowClass) == 0)
[7155]167 {
[10286]168 hwndLastWin32Window = hwnd;
169 return TRUE;
[7155]170 }
171 }
[10286]172 return FALSE;
[7155]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 *
[10286]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.
[7155]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 // ...
[10286]218
[7155]219 // is this an WIN32 window?
220 if (!i_isWin32Window(pqmsg->hwnd))
221 {
222 // no, so pass thru
223 return FALSE;
224 }
[10286]225
[7155]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 {
[10286]231 default:
232 return FALSE;
[10280]233
[10286]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:
[10280]248
[10286]249 // Try to prevent Ctrl-Esc, etc. from being intercepted by PM
250 case PMSCAN_ESC:
251 case PMSCAN_CTRLLEFT:
252 case PMSCAN_CTRLRIGHT:
[9813]253
[10286]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
[9813]268 //
269 // AltGr needs special handling
270 //
[10286]271 // AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYDOWN (VK_MENU)
[9813]272 // WM_SYSKEYUP (VK_CONTROL)
273 // WM_KEYUP (VK_MENU)
274 //
[10286]275 // Ctrl+AltGr -> WM_KEYDOWN (VK_CONTROL), WM_KEYUP (VK_CONTROL)
[9813]276 // WM_KEYDOWN (VK_CONTROL)
[10286]277 // WM_KEYDOWN (VK_MENU)
[9813]278 // WM_KEYUP (VK_MENU)
[10286]279 // WM_KEYUP (VK_CONTROL)
[9813]280 //
[10286]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)
[9813]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
[10286]297 //
298 case PMSCAN_ALTRIGHT:
299 {
300 QMSG msg = *pqmsg;
301 ULONG ctrlstate;
302 ULONG flags;
303 ULONG mp1, mp2;
[9813]304
[10286]305 flags = SHORT1FROMMP(pqmsg->mp1);
[9813]306
[10286]307 pqmsg->msg = WM_CHAR_SPECIAL;
[9813]308
[10286]309 if (flags & KC_KEYUP)
310 {
311 //AltGr up
[9813]312 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
[10286]313 if (!(ctrlstate & 0x8000))
314 {
315 //ctrl is up, translate this message to Ctrl key up
[9813]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
[10286]324 //and finally, post the AltGr WM_CHAR message
[9813]325 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
326 }
327 //else do nothing
[10286]328 }
329 else
330 {
331 //AltGr down
[9813]332 ctrlstate = WinGetPhysKeyState(HWND_DESKTOP, PMSCAN_CTRLLEFT);
[10286]333 if (ctrlstate & 0x8000)
334 {
335 //ctrl is down, translate this message to Ctrl key up
[9813]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
[10286]350 if (ctrlstate & 0x8000)
351 {
352 //ctrl is down, must post this message
[9813]353 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL_ALTGRCONTROL, (MPARAM)mp1, (MPARAM)mp2);
354 }
[10286]355 else
356 {
357 //translate this message into control key down
[9813]358 pqmsg->msg = WM_CHAR_SPECIAL_ALTGRCONTROL;
359 pqmsg->mp1 = (MPARAM)mp1;
360 pqmsg->mp2 = (MPARAM)mp2;
361 }
[10286]362 //and finally, post the AltGr WM_CHAR message
[9813]363 WinPostMsg(msg.hwnd, WM_CHAR_SPECIAL, msg.mp1, msg.mp2);
364 }
365 break;
[10286]366 }
[7155]367 }
368 break; // WM_CHAR
[10286]369 }
[7155]370 } // end switch(msg)
[10286]371 return FALSE;
[7155]372}
Note: See TracBrowser for help on using the repository browser.