| /**************************************************************************** |
| * |
| * SciTech OS Portability Manager Library |
| * |
| * ======================================================================== |
| * |
| * The contents of this file are subject to the SciTech MGL Public |
| * License Version 1.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.scitechsoft.com/mgl-license.txt |
| * |
| * Software distributed under the License is distributed on an |
| * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. |
| * |
| * The Initial Developer of the Original Code is SciTech Software, Inc. |
| * All Rights Reserved. |
| * |
| * ======================================================================== |
| * |
| * Language: ANSI C |
| * Environment: 32-bit DOS |
| * |
| * Description: 32-bit DOS implementation for the SciTech cross platform |
| * event library. |
| * |
| ****************************************************************************/ |
| |
| /*--------------------------- Global variables ----------------------------*/ |
| |
| ibool _VARAPI _EVT_useEvents = true; /* True to use event handling */ |
| ibool _VARAPI _EVT_installed = 0; /* Event handers installed? */ |
| uchar _VARAPI *_EVT_biosPtr = NULL; /* Pointer to the BIOS data area */ |
| static ibool haveMouse = false; /* True if we have a mouse */ |
| |
| /*---------------------------- Implementation -----------------------------*/ |
| |
| /* External assembler functions */ |
| |
| void EVTAPI _EVT_pollJoystick(void); |
| uint EVTAPI _EVT_disableInt(void); |
| uint EVTAPI _EVT_restoreInt(uint flags); |
| void EVTAPI _EVT_codeStart(void); |
| void EVTAPI _EVT_codeEnd(void); |
| void EVTAPI _EVT_cCodeStart(void); |
| void EVTAPI _EVT_cCodeEnd(void); |
| int EVTAPI _EVT_getKeyCode(void); |
| void EVTAPI _EVT_pumpMessages(void); |
| int EVTAPI EVT_rdinx(int port,int index); |
| void EVTAPI EVT_wrinx(int port,int index,int value); |
| |
| #ifdef NO_KEYBOARD_INTERRUPT |
| /**************************************************************************** |
| REMARKS: |
| This function is used to pump all keyboard messages from the BIOS keyboard |
| handler into our event queue. This can be used to avoid using the |
| installable keyboard handler if this is causing problems. |
| ****************************************************************************/ |
| static void EVTAPI _EVT_pumpMessages(void) |
| { |
| RMREGS regs; |
| uint key,ps; |
| |
| /* Since the keyboard ISR has not been installed if NO_IDE_BUG has |
| * been defined, we first check for any pending keyboard events |
| * here, and if there are some insert them into the event queue to |
| * be picked up later - what a kludge. |
| */ |
| while ((key = _EVT_getKeyCode()) != 0) { |
| ps = _EVT_disableInt(); |
| addKeyEvent(EVT_KEYDOWN, key); |
| _EVT_restoreInt(ps); |
| } |
| |
| regs.x.ax = 0x0B; /* Reset Move Mouse */ |
| PM_int86(0x33,®s,®s); |
| } |
| #endif |
| |
| /**************************************************************************** |
| REMARKS: |
| This function is used to return the number of ticks since system |
| startup in milliseconds. This should be the same value that is placed into |
| the time stamp fields of events, and is used to implement auto mouse down |
| events. |
| ****************************************************************************/ |
| ulong _EVT_getTicks(void) |
| { |
| return (ulong)PM_getLong(_EVT_biosPtr+0x6C) * 55UL; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Reboots the machine from DOS (warm boot) |
| ****************************************************************************/ |
| static void Reboot(void) |
| { |
| PMREGS regs; |
| PMSREGS sregs; |
| |
| ushort *rebootType = PM_mapRealPointer(0x40,0x72); |
| *rebootType = 0x1234; |
| PM_callRealMode(0xFFFF,0x0000,®s,&sregs); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Include generic raw scancode keyboard module. |
| ****************************************************************************/ |
| #define SUPPORT_CTRL_ALT_DEL |
| #include "common/keyboard.c" |
| |
| /**************************************************************************** |
| REMARKS: |
| This function fools the DOS mouse driver into thinking that it is running |
| in graphics mode, rather than text mode so we always get virtual coordinates |
| correctly rather than character coordinates. |
| ****************************************************************************/ |
| int _EVT_foolMouse(void) |
| { |
| int oldmode = PM_getByte(_EVT_biosPtr+0x49); |
| PM_setByte(_EVT_biosPtr+0x49,0x10); |
| oldmode |= (EVT_rdinx(0x3C4,0x2) << 8); |
| return oldmode; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| This function unfools the DOS mouse driver after we have finished calling it. |
| ****************************************************************************/ |
| void _EVT_unfoolMouse( |
| int oldmode) |
| { |
| PM_setByte(_EVT_biosPtr+0x49,oldmode); |
| |
| /* Some mouse drivers reset the plane mask register for VGA plane 4 |
| * modes, which screws up the display on some VGA compatible controllers |
| * in SuperVGA modes. We reset the value back again in here to solve |
| * the problem. |
| */ |
| EVT_wrinx(0x3C4,0x2,oldmode >> 8); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Determines if we have a mouse attached and functioning. |
| ****************************************************************************/ |
| static ibool detectMouse(void) |
| { |
| RMREGS regs; |
| RMSREGS sregs; |
| uchar *p; |
| ibool retval; |
| |
| regs.x.ax = 0x3533; /* Get interrupt vector 0x33 */ |
| PM_int86x(0x21,®s,®s,&sregs); |
| |
| /* Check that interrupt vector 0x33 is not a zero, and that the first |
| * instruction in the interrupt vector is not an IRET instruction |
| */ |
| p = PM_mapRealPointer(sregs.es, regs.x.bx); |
| retval = ((sregs.es != 0) || (regs.x.bx != 0)) && (PM_getByte(p) != 207); |
| return retval; |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| what - Event code |
| message - Event message |
| x,y - Mouse position at time of event |
| but_stat - Mouse button status at time of event |
| |
| REMARKS: |
| Adds a new mouse event to the event queue. This routine is called from within |
| the mouse interrupt subroutine, so it must be efficient. |
| |
| NOTE: Interrupts MUST be OFF while this routine is called to ensure we have |
| mutually exclusive access to our internal data structures for |
| interrupt driven systems (like under DOS). |
| ****************************************************************************/ |
| static void addMouseEvent( |
| uint what, |
| uint message, |
| int x, |
| int y, |
| int mickeyX, |
| int mickeyY, |
| uint but_stat) |
| { |
| event_t evt; |
| |
| if (EVT.count < EVENTQSIZE) { |
| /* Save information in event record. */ |
| evt.when = _EVT_getTicks(); |
| evt.what = what; |
| evt.message = message; |
| evt.modifiers = but_stat; |
| evt.where_x = x; /* Save mouse event position */ |
| evt.where_y = y; |
| evt.relative_x = mickeyX; |
| evt.relative_y = mickeyY; |
| evt.modifiers |= EVT.keyModifiers; |
| addEvent(&evt); /* Add to tail of event queue */ |
| } |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| mask - Event mask |
| butstate - Button state |
| x - Mouse x coordinate |
| y - Mouse y coordinate |
| |
| REMARKS: |
| Mouse event handling routine. This gets called when a mouse event occurs, |
| and we call the addMouseEvent() routine to add the appropriate mouse event |
| to the event queue. |
| |
| Note: Interrupts are ON when this routine is called by the mouse driver code. |
| ****************************************************************************/ |
| static void EVTAPI mouseISR( |
| uint mask, |
| uint butstate, |
| int x, |
| int y, |
| int mickeyX, |
| int mickeyY) |
| { |
| uint ps; |
| uint buttonMask; |
| |
| if (mask & 1) { |
| /* Save the current mouse coordinates */ |
| EVT.mx = x; EVT.my = y; |
| |
| /* If the last event was a movement event, then modify the last |
| * event rather than post a new one, so that the queue will not |
| * become saturated. Before we modify the data structures, we |
| * MUST ensure that interrupts are off. |
| */ |
| ps = _EVT_disableInt(); |
| if (EVT.oldMove != -1) { |
| EVT.evtq[EVT.oldMove].where_x = x; /* Modify existing one */ |
| EVT.evtq[EVT.oldMove].where_y = y; |
| EVT.evtq[EVT.oldMove].relative_x += mickeyX; |
| EVT.evtq[EVT.oldMove].relative_y += mickeyY; |
| } |
| else { |
| EVT.oldMove = EVT.freeHead; /* Save id of this move event */ |
| addMouseEvent(EVT_MOUSEMOVE,0,x,y,mickeyX,mickeyY,butstate); |
| } |
| _EVT_restoreInt(ps); |
| } |
| if (mask & 0x2A) { |
| ps = _EVT_disableInt(); |
| buttonMask = 0; |
| if (mask & 2) buttonMask |= EVT_LEFTBMASK; |
| if (mask & 8) buttonMask |= EVT_RIGHTBMASK; |
| if (mask & 32) buttonMask |= EVT_MIDDLEBMASK; |
| addMouseEvent(EVT_MOUSEDOWN,buttonMask,x,y,0,0,butstate); |
| EVT.oldMove = -1; |
| _EVT_restoreInt(ps); |
| } |
| if (mask & 0x54) { |
| ps = _EVT_disableInt(); |
| buttonMask = 0; |
| if (mask & 2) buttonMask |= EVT_LEFTBMASK; |
| if (mask & 8) buttonMask |= EVT_RIGHTBMASK; |
| if (mask & 32) buttonMask |= EVT_MIDDLEBMASK; |
| addMouseEvent(EVT_MOUSEUP,buttonMask,x,y,0,0,butstate); |
| EVT.oldMove = -1; |
| _EVT_restoreInt(ps); |
| } |
| EVT.oldKey = -1; |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Keyboard interrupt handler function. |
| |
| NOTE: Interrupts are OFF when this routine is called by the keyboard ISR, |
| and we leave them OFF the entire time. |
| ****************************************************************************/ |
| static void EVTAPI keyboardISR(void) |
| { |
| processRawScanCode(PM_inpb(0x60)); |
| PM_outpb(0x20,0x20); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Safely abort the event module upon catching a fatal error. |
| ****************************************************************************/ |
| void _EVT_abort() |
| { |
| EVT_exit(); |
| PM_fatalError("Unhandled exception!"); |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| mouseMove - Callback function to call wheneve the mouse needs to be moved |
| |
| REMARKS: |
| Initiliase the event handling module. Here we install our mouse handling ISR |
| to be called whenever any button's are pressed or released. We also build |
| the free list of events in the event queue. |
| |
| We use handler number 2 of the mouse libraries interrupt handlers for our |
| event handling routines. |
| ****************************************************************************/ |
| void EVTAPI EVT_init( |
| _EVT_mouseMoveHandler mouseMove) |
| { |
| int i; |
| |
| PM_init(); |
| EVT.mouseMove = mouseMove; |
| _EVT_biosPtr = PM_getBIOSPointer(); |
| EVT_resume(); |
| |
| /* Grab all characters pending in the keyboard buffer and stuff |
| * them into our event buffer. This allows us to pick up any keypresses |
| * while the program is initialising. |
| */ |
| while ((i = _EVT_getKeyCode()) != 0) |
| addKeyEvent(EVT_KEYDOWN,i); |
| } |
| |
| /**************************************************************************** |
| REMARKS: |
| Initiailises the internal event handling modules. The EVT_suspend function |
| can be called to suspend event handling (such as when shelling out to DOS), |
| and this function can be used to resume it again later. |
| ****************************************************************************/ |
| void EVTAPI EVT_resume(void) |
| { |
| static int locked = 0; |
| int stat; |
| uchar mods; |
| PM_lockHandle lh; /* Unused in DOS */ |
| |
| if (_EVT_useEvents) { |
| /* Initialise the event queue and enable our interrupt handlers */ |
| initEventQueue(); |
| #ifndef NO_KEYBOARD_INTERRUPT |
| PM_setKeyHandler(keyboardISR); |
| #endif |
| #ifndef NO_MOUSE_INTERRUPT |
| if ((haveMouse = detectMouse()) != 0) { |
| int oldmode = _EVT_foolMouse(); |
| PM_setMouseHandler(0xFFFF,mouseISR); |
| _EVT_unfoolMouse(oldmode); |
| } |
| #endif |
| |
| /* Read the keyboard modifier flags from the BIOS to get the |
| * correct initialisation state. The only state we care about is |
| * the correct toggle state flags such as SCROLLLOCK, NUMLOCK and |
| * CAPSLOCK. |
| */ |
| EVT.keyModifiers = 0; |
| mods = PM_getByte(_EVT_biosPtr+0x17); |
| if (mods & 0x10) |
| EVT.keyModifiers |= EVT_SCROLLLOCK; |
| if (mods & 0x20) |
| EVT.keyModifiers |= EVT_NUMLOCK; |
| if (mods & 0x40) |
| EVT.keyModifiers |= EVT_CAPSLOCK; |
| |
| /* Lock all of the code and data used by our protected mode interrupt |
| * handling routines, so that it will continue to work correctly |
| * under real mode. |
| */ |
| if (!locked) { |
| /* It is difficult to ensure that we lock our global data, so we |
| * do this by taking the address of a variable locking all data |
| * 2Kb on either side. This should properly cover the global data |
| * used by the module (the other alternative is to declare the |
| * variables in assembler, in which case we know it will be |
| * correct). |
| */ |
| stat = !PM_lockDataPages(&EVT,sizeof(EVT),&lh); |
| stat |= !PM_lockDataPages(&_EVT_biosPtr,sizeof(_EVT_biosPtr),&lh); |
| stat |= !PM_lockCodePages((__codePtr)_EVT_cCodeStart,(int)_EVT_cCodeEnd-(int)_EVT_cCodeStart,&lh); |
| stat |= !PM_lockCodePages((__codePtr)_EVT_codeStart,(int)_EVT_codeEnd-(int)_EVT_codeStart,&lh); |
| if (stat) { |
| PM_fatalError("Page locking services failed - interrupt handling not safe!"); |
| exit(1); |
| } |
| locked = 1; |
| } |
| |
| /* Catch program termination signals so we can clean up properly */ |
| signal(SIGABRT, _EVT_abort); |
| signal(SIGFPE, _EVT_abort); |
| signal(SIGINT, _EVT_abort); |
| _EVT_installed = true; |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS |
| Changes the range of coordinates returned by the mouse functions to the |
| specified range of values. This is used when changing between graphics |
| modes set the range of mouse coordinates for the new display mode. |
| ****************************************************************************/ |
| void EVTAPI EVT_setMouseRange( |
| int xRes, |
| int yRes) |
| { |
| RMREGS regs; |
| |
| if (haveMouse) { |
| int oldmode = _EVT_foolMouse(); |
| PM_resetMouseDriver(1); |
| regs.x.ax = 7; /* Mouse function 7 - Set horizontal min and max */ |
| regs.x.cx = 0; |
| regs.x.dx = xRes; |
| PM_int86(0x33,®s,®s); |
| regs.x.ax = 8; /* Mouse function 8 - Set vertical min and max */ |
| regs.x.cx = 0; |
| regs.x.dx = yRes; |
| PM_int86(0x33,®s,®s); |
| _EVT_unfoolMouse(oldmode); |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS |
| Modifes the mouse coordinates as necessary if scaling to OS coordinates, |
| and sets the OS mouse cursor position. |
| ****************************************************************************/ |
| void _EVT_setMousePos( |
| int *x, |
| int *y) |
| { |
| RMREGS regs; |
| |
| if (haveMouse) { |
| int oldmode = _EVT_foolMouse(); |
| regs.x.ax = 4; /* Mouse function 4 - Set mouse position */ |
| regs.x.cx = *x; /* New horizontal coordinate */ |
| regs.x.dx = *y; /* New vertical coordinate */ |
| PM_int86(0x33,®s,®s); |
| _EVT_unfoolMouse(oldmode); |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS |
| Suspends all of our event handling operations. This is also used to |
| de-install the event handling code. |
| ****************************************************************************/ |
| void EVTAPI EVT_suspend(void) |
| { |
| uchar mods; |
| |
| if (_EVT_installed) { |
| /* Restore the interrupt handlers */ |
| PM_restoreKeyHandler(); |
| if (haveMouse) |
| PM_restoreMouseHandler(); |
| signal(SIGABRT, SIG_DFL); |
| signal(SIGFPE, SIG_DFL); |
| signal(SIGINT, SIG_DFL); |
| |
| /* Set the keyboard modifier flags in the BIOS to our values */ |
| EVT_allowLEDS(true); |
| mods = PM_getByte(_EVT_biosPtr+0x17) & ~0x70; |
| if (EVT.keyModifiers & EVT_SCROLLLOCK) |
| mods |= 0x10; |
| if (EVT.keyModifiers & EVT_NUMLOCK) |
| mods |= 0x20; |
| if (EVT.keyModifiers & EVT_CAPSLOCK) |
| mods |= 0x40; |
| PM_setByte(_EVT_biosPtr+0x17,mods); |
| |
| /* Flag that we are no longer installed */ |
| _EVT_installed = false; |
| } |
| } |
| |
| /**************************************************************************** |
| REMARKS |
| Exits the event module for program terminatation. |
| ****************************************************************************/ |
| void EVTAPI EVT_exit(void) |
| { |
| EVT_suspend(); |
| } |