1 /****************************************************************************
3 * SciTech OS Portability Manager Library
5 * ========================================================================
7 * The contents of this file are subject to the SciTech MGL Public
8 * License Version 1.0 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.scitechsoft.com/mgl-license.txt
12 * Software distributed under the License is distributed on an
13 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
17 * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 * The Initial Developer of the Original Code is SciTech Software, Inc.
20 * All Rights Reserved.
22 * ========================================================================
25 * Environment: IBM PC (OS/2)
27 * Description: OS/2 implementation for the SciTech cross platform
30 ****************************************************************************/
32 /*---------------------------- Global Variables ---------------------------*/
34 /* Define generous keyboard monitor circular buffer size to minimize
35 * the danger of losing keystrokes
37 #define KEYBUFSIZE (EVENTQSIZE + 10)
39 static int oldMouseState; /* Old mouse state */
40 static ulong oldKeyMessage; /* Old keyboard state */
41 static ushort keyUpMsg[256] = {0}; /* Table of key up messages */
42 static int rangeX,rangeY; /* Range of mouse coordinates */
43 HMOU _EVT_hMouse; /* Handle to the mouse driver */
44 HMONITOR _EVT_hKbdMon; /* Handle to the keyboard driver */
45 TID kbdMonTID = 0; /* Keyboard monitor thread ID */
46 HEV hevStart; /* Start event semaphore handle */
47 BOOL bMonRunning; /* Flag set if monitor thread OK */
48 HMTX hmtxKeyBuf; /* Mutex protecting key buffer */
49 KEYPACKET keyMonPkts[KEYBUFSIZE]; /* Array of monitor key packets */
50 int kpHead = 0; /* Key packet buffer head */
51 int kpTail = 0; /* Key packet buffer tail */
53 /*---------------------------- Implementation -----------------------------*/
55 /* These are not used under OS/2 */
56 #define _EVT_disableInt() 1
57 #define _EVT_restoreInt(flags)
59 /****************************************************************************
61 scanCode - Scan code to test
64 This macro determines if a specified key is currently down at the
65 time that the call is made.
66 ****************************************************************************/
67 #define _EVT_isKeyDown(scanCode) (keyUpMsg[scanCode] != 0)
69 /****************************************************************************
71 This function is used to return the number of ticks since system
72 startup in milliseconds. This should be the same value that is placed into
73 the time stamp fields of events, and is used to implement auto mouse down
75 ****************************************************************************/
76 ulong _EVT_getTicks(void)
79 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &count, sizeof(ULONG) );
83 /****************************************************************************
85 Converts a mickey movement value to a pixel adjustment value.
86 ****************************************************************************/
87 static int MickeyToPixel(
90 // TODO: We can add some code in here to handle 'acceleration' for
91 // the mouse cursor. For now just use the mickeys.
95 /* Some useful defines any typedefs used in the keyboard handling */
96 #define KEY_RELEASE 0x40
98 /****************************************************************************
100 Pumps all messages in the message queue from OS/2 into our event queue.
101 ****************************************************************************/
102 static void _EVT_pumpMessages(void)
104 KBDINFO keyInfo; /* Must not cross a 64K boundary */
105 KBDKEYINFO key; /* Must not cross a 64K boundary */
106 MOUQUEINFO mqueue; /* Must not cross a 64K boundary */
107 MOUEVENTINFO mouse; /* Must not cross a 64K boundary */
108 ushort mWait; /* Must not cross a 64K boundary */
109 KEYPACKET kp; /* Must not cross a 64K boundary */
112 ibool noInput = TRUE; /* Flag to determine if any input was available */
114 /* First of all, check if we should do any session switch work */
115 __PM_checkConsoleSwitch();
117 /* Pump all keyboard messages from our circular buffer */
119 /* Check that the monitor thread is still running */
121 PM_fatalError("Keyboard monitor thread died!");
123 /* Protect keypacket buffer with mutex */
124 DosRequestMutexSem(hmtxKeyBuf, SEM_INDEFINITE_WAIT);
125 if (kpHead == kpTail) {
126 DosReleaseMutexSem(hmtxKeyBuf);
132 /* Read packet from circular buffer and remove it */
133 memcpy(&kp, &keyMonPkts[kpTail], sizeof(KEYPACKET));
134 if (++kpTail == KEYBUFSIZE)
136 DosReleaseMutexSem(hmtxKeyBuf);
138 /* Compensate for the 0xE0 character */
139 if (kp.XlatedScan && kp.XlatedChar == 0xE0)
142 /* Determine type of keyboard event */
143 memset(&evt,0,sizeof(evt));
144 if (kp.KbdDDFlagWord & KEY_RELEASE)
145 evt.what = EVT_KEYUP;
147 evt.what = EVT_KEYDOWN;
149 /* Convert keyboard codes */
150 scan = kp.MonFlagWord >> 8;
151 if (evt.what == EVT_KEYUP) {
152 /* Get message for keyup code from table of cached down values */
153 evt.message = keyUpMsg[scan];
158 evt.message = ((ulong)scan << 8) | kp.XlatedChar;
159 if (evt.message == keyUpMsg[scan]) {
160 evt.what = EVT_KEYREPEAT;
161 evt.message |= 0x10000;
163 oldKeyMessage = evt.message & 0x0FFFF;
164 keyUpMsg[scan] = (ushort)evt.message;
167 /* Convert shift state modifiers */
168 if (kp.u.ShiftState & 0x0001)
169 evt.modifiers |= EVT_RIGHTSHIFT;
170 if (kp.u.ShiftState & 0x0002)
171 evt.modifiers |= EVT_LEFTSHIFT;
172 if (kp.u.ShiftState & 0x0100)
173 evt.modifiers |= EVT_LEFTCTRL;
174 if (kp.u.ShiftState & 0x0200)
175 evt.modifiers |= EVT_LEFTALT;
176 if (kp.u.ShiftState & 0x0400)
177 evt.modifiers |= EVT_RIGHTCTRL;
178 if (kp.u.ShiftState & 0x0800)
179 evt.modifiers |= EVT_RIGHTALT;
182 /* Add time stamp and add the event to the queue */
184 if (EVT.count < EVENTQSIZE)
188 /* Don't just flush because that terminally confuses the monitor */
190 KbdCharIn(&key, IO_NOWAIT, 0);
191 } while (key.fbStatus & KBDTRF_FINAL_CHAR_IN);
193 /* Pump all mouse messages */
194 KbdGetStatus(&keyInfo,0);
195 /* Check return code - mouse may not be operational!! */
196 if (MouGetNumQueEl(&mqueue,_EVT_hMouse) == NO_ERROR) {
197 while (mqueue.cEvents) {
198 while (mqueue.cEvents--) {
199 memset(&evt,0,sizeof(evt));
201 MouReadEventQue(&mouse,&mWait,_EVT_hMouse);
203 /* Update the mouse position. We get the mouse coordinates
204 * in mickeys so we have to translate these into pixels and
205 * move our mouse position. If we don't do this, OS/2 gives
206 * us the coordinates in character positions since it still
207 * thinks we are in text mode!
209 EVT.mx += MickeyToPixel(mouse.col);
210 EVT.my += MickeyToPixel(mouse.row);
211 if (EVT.mx < 0) EVT.mx = 0;
212 if (EVT.my < 0) EVT.my = 0;
213 if (EVT.mx > rangeX) EVT.mx = rangeX;
214 if (EVT.my > rangeY) EVT.my = rangeY;
215 evt.where_x = EVT.mx;
216 evt.where_y = EVT.my;
217 evt.relative_x = mouse.col;
218 evt.relative_y = mouse.row;
220 if (mouse.fs & (MOUSE_BN1_DOWN | MOUSE_MOTION_WITH_BN1_DOWN))
221 evt.modifiers |= EVT_LEFTBUT;
222 if (mouse.fs & (MOUSE_BN2_DOWN | MOUSE_MOTION_WITH_BN2_DOWN))
223 evt.modifiers |= EVT_RIGHTBUT;
224 if (mouse.fs & (MOUSE_BN3_DOWN | MOUSE_MOTION_WITH_BN3_DOWN))
225 evt.modifiers |= EVT_MIDDLEBUT;
226 if (keyInfo.fsState & 0x0001)
227 evt.modifiers |= EVT_RIGHTSHIFT;
228 if (keyInfo.fsState & 0x0002)
229 evt.modifiers |= EVT_LEFTSHIFT;
230 if (keyInfo.fsState & 0x0100)
231 evt.modifiers |= EVT_LEFTCTRL;
232 if (keyInfo.fsState & 0x0200)
233 evt.modifiers |= EVT_LEFTALT;
234 if (keyInfo.fsState & 0x0400)
235 evt.modifiers |= EVT_RIGHTCTRL;
236 if (keyInfo.fsState & 0x0800)
237 evt.modifiers |= EVT_RIGHTALT;
239 /* Check for left mouse click events */
240 /* 0x06 == (MOUSE_BN1_DOWN | MOUSE_MOTION_WITH_BN1_DOWN) */
241 if (((mouse.fs & 0x0006) && !(oldMouseState & 0x0006))
242 || (!(mouse.fs & 0x0006) && (oldMouseState & 0x0006))) {
243 if (mouse.fs & 0x0006)
244 evt.what = EVT_MOUSEDOWN;
246 evt.what = EVT_MOUSEUP;
247 evt.message = EVT_LEFTBMASK;
249 if (EVT.count < EVENTQSIZE)
253 /* Check for right mouse click events */
254 /* 0x0018 == (MOUSE_BN2_DOWN | MOUSE_MOTION_WITH_BN2_DOWN) */
255 if (((mouse.fs & 0x0018) && !(oldMouseState & 0x0018))
256 || (!(mouse.fs & 0x0018) && (oldMouseState & 0x0018))) {
257 if (mouse.fs & 0x0018)
258 evt.what = EVT_MOUSEDOWN;
260 evt.what = EVT_MOUSEUP;
261 evt.message = EVT_RIGHTBMASK;
263 if (EVT.count < EVENTQSIZE)
267 /* Check for middle mouse click events */
268 /* 0x0060 == (MOUSE_BN3_DOWN | MOUSE_MOTION_WITH_BN3_DOWN) */
269 if (((mouse.fs & 0x0060) && !(oldMouseState & 0x0060))
270 || (!(mouse.fs & 0x0060) && (oldMouseState & 0x0060))) {
271 if (mouse.fs & 0x0060)
272 evt.what = EVT_MOUSEDOWN;
274 evt.what = EVT_MOUSEUP;
275 evt.message = EVT_MIDDLEBMASK;
277 if (EVT.count < EVENTQSIZE)
281 /* Check for mouse movement event */
282 if (mouse.fs & 0x002B) {
283 evt.what = EVT_MOUSEMOVE;
284 if (EVT.oldMove != -1) {
285 EVT.evtq[EVT.oldMove].where_x = evt.where_x;/* Modify existing one */
286 EVT.evtq[EVT.oldMove].where_y = evt.where_y;
289 EVT.oldMove = EVT.freeHead; /* Save id of this move event */
290 if (EVT.count < EVENTQSIZE)
295 /* Save current mouse state */
296 oldMouseState = mouse.fs;
298 MouGetNumQueEl(&mqueue,_EVT_hMouse);
303 /* If there was no input available, give up the current timeslice
304 * Note: DosSleep(0) will effectively do nothing if no other thread is ready. Hence
305 * DosSleep(0) will still use 100% CPU _but_ should not interfere with other programs.
311 /****************************************************************************
313 This macro/function is used to converts the scan codes reported by the
314 keyboard to our event libraries normalised format. We only have one scan
315 code for the 'A' key, and use shift modifiers to determine if it is a
316 Ctrl-F1, Alt-F1 etc. The raw scan codes from the keyboard work this way,
317 but the OS gives us 'cooked' scan codes, we have to translate them back
319 ****************************************************************************/
320 #define _EVT_maskKeyCode(evt)
322 /****************************************************************************
324 Keyboard monitor thread. Needed to catch both keyup and keydown events.
325 ****************************************************************************/
326 static void _kbdMonThread(
331 USHORT count = sizeof(KEYPACKET);
336 /* Raise thread priority for higher responsiveness */
337 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
338 monInbuf.cb = sizeof(monInbuf) - sizeof(monInbuf.cb);
339 monOutbuf.cb = sizeof(monOutbuf) - sizeof(monOutbuf.cb);
342 /* Register the buffers to be used for monitoring for current session */
343 if (DosMonReg(_EVT_hKbdMon, &monInbuf, (ULONG*)&monOutbuf,MONITOR_END, -1)) {
344 DosPostEventSem(hevStart); /* unblock the main thread */
348 /* Unblock the main thread and tell it we're OK*/
350 DosPostEventSem(hevStart);
351 while (bMonRunning) { /* Start an endless loop */
352 /* Read data from keyboard driver */
353 rc = DosMonRead((PBYTE)&monInbuf, IO_WAIT, (PBYTE)&kp, (PUSHORT)&count);
357 printf("Error in DosMonRead, rc = %ld\n", rc);
363 /* Pass FLUSH packets immediately */
364 if (kp.MonFlagWord & 4) {
366 printf("Flush packet!\n");
368 DosMonWrite((PBYTE)&monOutbuf, (PBYTE)&kp, count);
372 //TODO: to be removed
373 /* Skip extended scancodes & some others */
374 if (((kp.MonFlagWord >> 8) == 0xE0) || ((kp.KbdDDFlagWord & 0x0F) == 0x0F)) {
375 DosMonWrite((PBYTE)&monOutbuf, (PBYTE)&kp, count);
379 // printf("RawScan = %X, XlatedScan = %X, fbStatus = %X, KbdDDFlags = %X\n",
380 // kp.MonFlagWord >> 8, kp.XlatedScan, kp.u.ShiftState, kp.KbdDDFlagWord);
382 /* Protect access to buffer with mutex semaphore */
383 rc = DosRequestMutexSem(hmtxKeyBuf, 1000);
386 printf("Can't get access to mutex, rc = %ld\n", rc);
392 /* Store packet in circular buffer, drop it if it's full */
394 if (kpNew == KEYBUFSIZE)
396 if (kpNew != kpTail) {
397 memcpy(&keyMonPkts[kpHead], &kp, sizeof(KEYPACKET));
399 /* Convert break to make code */
400 keyMonPkts[kpHead].MonFlagWord &= 0x7FFF;
403 DosReleaseMutexSem(hmtxKeyBuf);
405 /* Finally write the packet */
406 rc = DosMonWrite((PBYTE)&monOutbuf, (PBYTE)&kp, count);
410 printf("Error in DosMonWrite, rc = %ld\n", rc);
419 /****************************************************************************
421 Safely abort the event module upon catching a fatal error.
422 ****************************************************************************/
427 PM_fatalError("Unhandled exception!");
430 /****************************************************************************
432 mouseMove - Callback function to call wheneve the mouse needs to be moved
435 Initiliase the event handling module. Here we install our mouse handling ISR
436 to be called whenever any button's are pressed or released. We also build
437 the free list of events in the event queue.
439 We use handler number 2 of the mouse libraries interrupt handlers for our
440 event handling routines.
441 ****************************************************************************/
442 void EVTAPI EVT_init(
443 _EVT_mouseMoveHandler mouseMove)
447 /* Initialise the event queue */
449 EVT.mouseMove = mouseMove;
453 memset(keyUpMsg,0,sizeof(keyUpMsg));
455 /* Open the mouse driver, and set it up to report events in mickeys */
456 MouOpen(NULL,&_EVT_hMouse);
458 MouSetEventMask(&stat,_EVT_hMouse);
459 stat = (MOU_NODRAW | MOU_MICKEYS) << 8;
460 MouSetDevStatus(&stat,_EVT_hMouse);
462 /* Open the keyboard monitor */
463 if (DosMonOpen((PSZ)"KBD$", &_EVT_hKbdMon))
464 PM_fatalError("Unable to open keyboard monitor!");
466 /* Create event semaphore, the monitor will post it when it's initalized */
467 if (DosCreateEventSem(NULL, &hevStart, 0, FALSE))
468 PM_fatalError("Unable to create event semaphore!");
470 /* Create mutex semaphore protecting the keypacket buffer */
471 if (DosCreateMutexSem(NULL, &hmtxKeyBuf, 0, FALSE))
472 PM_fatalError("Unable to create mutex semaphore!");
474 /* Start keyboard monitor thread, use 32K stack */
475 kbdMonTID = _beginthread(_kbdMonThread, NULL, 0x8000, NULL);
477 /* Now block until the monitor thread is up and running */
478 /* Give the thread one second */
479 DosWaitEventSem(hevStart, 1000);
480 if (!bMonRunning) { /* Check the thread is OK */
481 DosMonClose(_EVT_hKbdMon);
482 PM_fatalError("Keyboard monitor thread didn't initialize!");
485 /* Catch program termination signals so we can clean up properly */
486 signal(SIGABRT, _EVT_abort);
487 signal(SIGFPE, _EVT_abort);
488 signal(SIGINT, _EVT_abort);
491 /****************************************************************************
493 Changes the range of coordinates returned by the mouse functions to the
494 specified range of values. This is used when changing between graphics
495 modes set the range of mouse coordinates for the new display mode.
496 ****************************************************************************/
497 void EVTAPI EVT_setMouseRange(
505 /****************************************************************************
507 Modifes the mouse coordinates as necessary if scaling to OS coordinates,
508 and sets the OS mouse cursor position.
509 ****************************************************************************/
510 #define _EVT_setMousePos(x,y)
512 /****************************************************************************
514 Initiailises the internal event handling modules. The EVT_suspend function
515 can be called to suspend event handling (such as when shelling out to DOS),
516 and this function can be used to resume it again later.
517 ****************************************************************************/
518 void EVT_resume(void)
520 // Do nothing for OS/2
523 /****************************************************************************
525 Suspends all of our event handling operations. This is also used to
526 de-install the event handling code.
527 ****************************************************************************/
528 void EVT_suspend(void)
530 // Do nothing for OS/2
533 /****************************************************************************
535 Exits the event module for program terminatation.
536 ****************************************************************************/
541 /* Restore signal handlers */
542 signal(SIGABRT, SIG_DFL);
543 signal(SIGFPE, SIG_DFL);
544 signal(SIGINT, SIG_DFL);
546 /* Close the mouse driver */
547 MouClose(_EVT_hMouse);
549 /* Stop the keyboard monitor thread and close the monitor */
551 rc = DosKillThread(kbdMonTID);
554 printf("DosKillThread failed, rc = %ld\n", rc);
556 rc = DosMonClose(_EVT_hKbdMon);
559 printf("DosMonClose failed, rc = %ld\n", rc);
562 DosCloseEventSem(hevStart);
563 DosCloseMutexSem(hmtxKeyBuf);