--- /dev/null
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_IN.c - Input Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+//\r
+// This module handles dealing with the various input devices\r
+//\r
+// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+// User Mgr (for command line parms)\r
+//\r
+// Globals:\r
+// LastScan - The keyboard scan code of the last key pressed\r
+// LastASCII - The ASCII value of the last key pressed\r
+// DEBUG - there are more globals\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#define KeyInt 9 // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define JoyScaleMax 32768\r
+#define JoyScaleShift 8\r
+#define MaxJoyValue 5000\r
+\r
+// Global variables\r
+ boolean Keyboard[NumCodes],\r
+ JoysPresent[MaxJoys],\r
+ MousePresent;\r
+ boolean Paused;\r
+ char LastASCII;\r
+ ScanCode LastScan;\r
+ KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+ JoystickDef JoyDefs[MaxJoys];\r
+ ControlType Controls[MaxPlayers];\r
+ boolean GravisGamepad;\r
+ word GravisAction[4];\r
+ word GravisMap[4];\r
+\r
+ boolean Latch;\r
+ long MouseDownCount;\r
+ boolean LatchedButton0[MaxPlayers];\r
+ boolean LatchedButton1[MaxPlayers];\r
+\r
+ Demo DemoMode = demo_Off;\r
+ byte _seg *DemoBuffer;\r
+ word DemoOffset,DemoSize;\r
+\r
+// Internal variables\r
+static boolean IN_Started;\r
+static boolean CapsLock;\r
+static ScanCode CurCode,LastCode;\r
+static byte far ASCIINames[] = // Unshifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0\r
+ 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1\r
+ 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2\r
+ 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far ShiftNames[] = // Shifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0\r
+ 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1\r
+ 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2\r
+ 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far SpecialNames[] = // ASCII for 0xe0 prefixed codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2\r
+ 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+\r
+ *ScanNames[] = // Scan code names with single chars\r
+ {\r
+ "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+ "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+ "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+ "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+ "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+ }, // DEBUG - consolidate these\r
+ far ExtScanCodes[] = // Scan codes with >1 char names\r
+ {\r
+ 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+ 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+ 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+ 0x50,0x4b,0x4d,0x00\r
+ },\r
+ *ExtScanNames[] = // Names corresponding to ExtScanCodes\r
+ {\r
+ "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+ "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+ "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+ "Down","Left","Right",""\r
+ };\r
+static Direction DirTable[] = // Quick lookup for total direction\r
+ {\r
+ dir_NorthWest, dir_North, dir_NorthEast,\r
+ dir_West, dir_None, dir_East,\r
+ dir_SouthWest, dir_South, dir_SouthEast\r
+ };\r
+\r
+static void (*INL_KeyHook)(void);\r
+static void interrupt (*OldKeyVect)(void);\r
+\r
+static char *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+// Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+ byte k,c,\r
+ temp;\r
+ int player;\r
+\r
+ k = inportb(0x60); // Get the scan code\r
+\r
+ // Tell the XT keyboard controller to clear the key\r
+ outportb(0x61,(temp = inportb(0x61)) | 0x80);\r
+ outportb(0x61,temp);\r
+\r
+ if (k == 0xe0) // Special key prefix\r
+ special = true;\r
+ else if (k == 0xe1) // Handle Pause key\r
+ Paused = true;\r
+ else\r
+ {\r
+ if (k & 0x80) // Break code\r
+ {\r
+ k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+ Keyboard[k] = false;\r
+ }\r
+ else // Make code\r
+ {\r
+ LastCode = CurCode;\r
+ CurCode = LastScan = k;\r
+ Keyboard[k] = true;\r
+\r
+ if (Latch)\r
+ {\r
+ for (player = 0; player < MaxPlayers; player++)\r
+ {\r
+ if (Controls[player] == ctrl_Keyboard1)\r
+ {\r
+ if (CurCode == KbdDefs[0].button0)\r
+ LatchedButton0[player] = true;\r
+ else if (CurCode == KbdDefs[0].button1)\r
+ LatchedButton1[player] = true;\r
+ }\r
+ else if (Controls[player] == ctrl_Keyboard1) // BUG? should probably check for ctrl_Keyboard2 here...\r
+ {\r
+ if (CurCode == KbdDefs[1].button0)\r
+ LatchedButton0[player] = true;\r
+ else if (CurCode == KbdDefs[1].button1)\r
+ LatchedButton1[player] = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (special)\r
+ c = SpecialNames[k];\r
+ else\r
+ {\r
+ if (k == sc_CapsLock)\r
+ {\r
+ CapsLock ^= true;\r
+ // DEBUG - make caps lock light work\r
+ }\r
+\r
+ if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+ {\r
+ c = ShiftNames[k];\r
+ if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+ c += 'a' - 'A';\r
+ }\r
+ else\r
+ {\r
+ c = ASCIINames[k];\r
+ if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+ c -= 'a' - 'A';\r
+ }\r
+ }\r
+ if (c)\r
+ LastASCII = c;\r
+ }\r
+\r
+ special = false;\r
+ }\r
+\r
+ if (INL_KeyHook && !special)\r
+ INL_KeyHook();\r
+ outportb(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+ Mouse(MDelta);\r
+ *x = _CX;\r
+ *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+ word buttons;\r
+\r
+ Mouse(MButtons);\r
+ buttons = _BX;\r
+ return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+ byte xb,yb,\r
+ xs,ys;\r
+ word x,y;\r
+\r
+ x = y = 0;\r
+ xs = joy? 2 : 0; // Find shift value for x axis\r
+ xb = 1 << xs; // Use shift value to get x bit mask\r
+ ys = joy? 3 : 1; // Do the same for y axis\r
+ yb = 1 << ys;\r
+\r
+// Read the absolute joystick values\r
+asm pushf // Save some registers\r
+asm push si\r
+asm push di\r
+asm cli // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+asm mov dx,0x201\r
+asm in al,dx\r
+asm out dx,al // Clear the resistors\r
+\r
+asm mov ah,[xb] // Get masks into registers\r
+asm mov ch,[yb]\r
+\r
+asm xor si,si // Clear count registers\r
+asm xor di,di\r
+asm xor bh,bh // Clear high byte of bx for later\r
+\r
+asm push bp // Don't mess up stack frame\r
+asm mov bp,MaxJoyValue\r
+\r
+loop:\r
+asm in al,dx // Get bits indicating whether all are finished\r
+\r
+asm dec bp // Check bounding register\r
+asm jz done // We have a silly value - abort\r
+\r
+asm mov bl,al // Duplicate the bits\r
+asm and bl,ah // Mask off useless bits (in [xb])\r
+asm add si,bx // Possibly increment count register\r
+asm mov cl,bl // Save for testing later\r
+\r
+asm mov bl,al\r
+asm and bl,ch // [yb]\r
+asm add di,bx\r
+\r
+asm add cl,bl\r
+asm jnz loop // If both bits were 0, drop out\r
+\r
+done:\r
+asm pop bp\r
+\r
+asm mov cl,[xs] // Get the number of bits to shift\r
+asm shr si,cl // and shift the count that many times\r
+\r
+asm mov cl,[ys]\r
+asm shr di,cl\r
+\r
+asm mov [x],si // Store the values into the variables\r
+asm mov [y],di\r
+\r
+asm pop di\r
+asm pop si\r
+asm popf // Restore the registers\r
+\r
+ *xp = x;\r
+ *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyDelta() - Returns the relative movement of the specified\r
+// joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+ word x,y;\r
+ longword time;\r
+ JoystickDef *def;\r
+static longword lasttime;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+ def = JoyDefs + joy;\r
+\r
+ if (x < def->threshMinX)\r
+ {\r
+ if (x < def->joyMinX)\r
+ x = def->joyMinX;\r
+\r
+ x = -(x - def->threshMinX);\r
+ x *= def->joyMultXL;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? -127 : -x;\r
+ }\r
+ else if (x > def->threshMaxX)\r
+ {\r
+ if (x > def->joyMaxX)\r
+ x = def->joyMaxX;\r
+\r
+ x = x - def->threshMaxX;\r
+ x *= def->joyMultXH;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? 127 : x;\r
+ }\r
+ else\r
+ *dx = 0;\r
+\r
+ if (y < def->threshMinY)\r
+ {\r
+ if (y < def->joyMinY)\r
+ y = def->joyMinY;\r
+\r
+ y = -(y - def->threshMinY);\r
+ y *= def->joyMultYL;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? -127 : -y;\r
+ }\r
+ else if (y > def->threshMaxY)\r
+ {\r
+ if (y > def->joyMaxY)\r
+ y = def->joyMaxY;\r
+\r
+ y = y - def->threshMaxY;\r
+ y *= def->joyMultYH;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? 127 : y;\r
+ }\r
+ else\r
+ *dy = 0;\r
+\r
+ if (adaptive)\r
+ {\r
+ time = (TimeCount - lasttime) / 2;\r
+ if (time)\r
+ {\r
+ if (time > 8)\r
+ time = 8;\r
+ *dx *= time;\r
+ *dy *= time;\r
+ }\r
+ }\r
+ lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyButtons() - Returns the button status of the specified\r
+// joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register word result;\r
+\r
+ result = inportb(0x201); // Get all the joystick buttons\r
+ if (joy == 2)\r
+ {\r
+ // all 4 buttons (for Gravis Gamepad option)\r
+ result >>= 4;\r
+ result &= 15;\r
+ result ^= 15;\r
+ }\r
+ else\r
+ {\r
+ result >>= joy? 6 : 4; // Shift into bits 0-1\r
+ result &= 3; // Mask off the useless bits\r
+ result ^= 3;\r
+ }\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+// specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+ longword endtime;\r
+ word result1,result2;\r
+\r
+ do\r
+ {\r
+ result1 = INL_GetJoyButtons(joy);\r
+ endtime = TimeCount + 2;\r
+ while (TimeCount <= endtime)\r
+ ;\r
+ result2 = INL_GetJoyButtons(joy);\r
+ } while (result1 != result2);\r
+ return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+ INL_KeyHook = 0; // Clear key hook\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ OldKeyVect = getvect(KeyInt);\r
+ setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+ poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags\r
+\r
+ setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+ if (getvect(MouseInt))\r
+ {\r
+ Mouse(MReset);\r
+ if (_AX == 0xffff)\r
+ return(true);\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+// INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+ def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+ def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+ def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+ def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+// to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+ word d,r;\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+\r
+ def->joyMinX = minx;\r
+ def->joyMaxX = maxx;\r
+ r = maxx - minx;\r
+ d = r / 5;\r
+ def->threshMinX = ((r / 2) - d) + minx;\r
+ def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+ def->joyMinY = miny;\r
+ def->joyMaxY = maxy;\r
+ r = maxy - miny;\r
+ d = r / 5;\r
+ def->threshMinY = ((r / 2) - d) + miny;\r
+ def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+ INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartJoy() - Detects & auto-configures the specified joystick\r
+// The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+ word x,y;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+\r
+ if\r
+ (\r
+ ((x == 0) || (x > MaxJoyValue - 10))\r
+ || ((y == 0) || (y > MaxJoyValue - 10))\r
+ )\r
+ return(false);\r
+ else\r
+ {\r
+ IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+ return(true);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+ JoysPresent[joy] = false;\r
+}\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ClearButtonLatch() - Clears the button latch stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearButtonLatch(void)\r
+{\r
+ int player;\r
+\r
+asm pushf\r
+asm cli\r
+\r
+ MouseDownCount = 0;\r
+\r
+ for (player = 0; player < MaxPlayers; player++)\r
+ {\r
+ LatchedButton0[player] = LatchedButton1[player] = 0;\r
+ }\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// LatchSndHook() - Hook routine for joystick button latch\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+LatchSndHook(void)\r
+{\r
+ int player;\r
+ ControlType ctrl;\r
+ word buttons;\r
+\r
+ for (player = 0; player < MaxPlayers; player++)\r
+ {\r
+ ctrl = Controls[player];\r
+\r
+ if (ctrl == ctrl_Joystick1 || ctrl == ctrl_Joystick2)\r
+ {\r
+ buttons = INL_GetJoyButtons(ctrl - ctrl_Joystick1);\r
+\r
+ if (buttons & 1)\r
+ LatchedButton0[player] = true;\r
+ if (buttons & 2)\r
+ LatchedButton1[player] = true;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_LatchButtons() - Enables or disables button latch\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void IN_LatchButtons(boolean enabled)\r
+{\r
+ if (enabled)\r
+ {\r
+ Latch = false;\r
+ IN_ClearButtonLatch();\r
+ }\r
+\r
+ Latch = enabled;\r
+ SD_SetUserHook(Latch ? LatchSndHook : NULL);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+ boolean checkjoys,checkmouse;\r
+ word i;\r
+\r
+ if (IN_Started)\r
+ return;\r
+\r
+ checkjoys = true;\r
+ checkmouse = true;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],ParmStrings))\r
+ {\r
+ case 0:\r
+ checkjoys = false;\r
+ break;\r
+ case 1:\r
+ checkmouse = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ INL_StartKbd();\r
+ MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+ IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+ if\r
+ (\r
+ (!gotit)\r
+ || ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+ || ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+ || ((in == ctrl_Mouse) && !MousePresent)\r
+ )\r
+ in = ctrl_Keyboard1;\r
+ IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+ word i;\r
+\r
+ if (!IN_Started)\r
+ return;\r
+\r
+ INL_ShutMouse();\r
+ for (i = 0;i < MaxJoys;i++)\r
+ INL_ShutJoy(i);\r
+ INL_ShutKbd();\r
+\r
+ IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+// everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+ // BUG: interrupts should be disabled while setting INL_KeyHook!\r
+ INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+ int i;\r
+\r
+ LastScan = sc_None;\r
+ LastASCII = key_None;\r
+ for (i = 0;i < NumCodes;i++)\r
+ Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+ if (buttons & (1 << 0))\r
+ info->button0 = true;\r
+ if (buttons & (1 << 1))\r
+ info->button1 = true;\r
+\r
+ info->x += dx;\r
+ info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+// struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+ word i,\r
+ player,\r
+ buttons;\r
+ int dx,dy;\r
+\r
+ info->x = info->y = 0;\r
+ info->button0 = info->button1 = false;\r
+\r
+ if (MousePresent)\r
+ {\r
+ buttons = INL_GetMouseButtons();\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (!JoysPresent[i])\r
+ continue;\r
+\r
+ for (player = 0;player < MaxPlayers; player++)\r
+ {\r
+ if (Controls[player] == ctrl_Joystick1+i)\r
+ goto joyok;\r
+ }\r
+ continue;\r
+\r
+joyok:\r
+ buttons = INL_GetJoyButtons(i);\r
+ INL_GetJoyDelta(i,&dx,&dy,true);\r
+ dx /= 64;\r
+ dy /= 64;\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadControl() - Reads the device associated with the specified\r
+// player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+ boolean realdelta;\r
+ byte dbyte;\r
+ word buttons;\r
+ int i;\r
+ int dx,dy;\r
+ Motion mx,my;\r
+ ControlType type;\r
+register KeyboardDef *def;\r
+\r
+ dx = dy = 0;\r
+ mx = my = motion_None;\r
+ buttons = 0;\r
+\r
+ if (DemoMode == demo_Playback)\r
+ {\r
+ dbyte = DemoBuffer[DemoOffset + 1];\r
+ my = (dbyte & 3) - 1;\r
+ mx = ((dbyte >> 2) & 3) - 1;\r
+ buttons = (dbyte >> 4) & 3;\r
+\r
+ if (!(--DemoBuffer[DemoOffset]))\r
+ {\r
+ DemoOffset += 2;\r
+ if (DemoOffset >= DemoSize)\r
+ DemoMode = demo_PlayDone;\r
+ }\r
+\r
+ realdelta = false;\r
+ }\r
+ else if (DemoMode == demo_PlayDone)\r
+ Quit("Demo playback exceeded");\r
+ else\r
+ {\r
+ switch (type = Controls[player])\r
+ {\r
+ case ctrl_Keyboard1:\r
+ case ctrl_Keyboard2:\r
+ def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+ if (Keyboard[def->upleft])\r
+ mx = motion_Left,my = motion_Up;\r
+ else if (Keyboard[def->upright])\r
+ mx = motion_Right,my = motion_Up;\r
+ else if (Keyboard[def->downleft])\r
+ mx = motion_Left,my = motion_Down;\r
+ else if (Keyboard[def->downright])\r
+ mx = motion_Right,my = motion_Down;\r
+\r
+ if (Keyboard[def->up])\r
+ my = motion_Up;\r
+ else if (Keyboard[def->down])\r
+ my = motion_Down;\r
+\r
+ if (Keyboard[def->left])\r
+ mx = motion_Left;\r
+ else if (Keyboard[def->right])\r
+ mx = motion_Right;\r
+\r
+ if (Keyboard[def->button0])\r
+ buttons += 1 << 0;\r
+ if (Keyboard[def->button1])\r
+ buttons += 1 << 1;\r
+ realdelta = false;\r
+ break;\r
+ case ctrl_Joystick1:\r
+ case ctrl_Joystick2:\r
+ INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+ if (GravisGamepad)\r
+ {\r
+ buttons = INL_GetJoyButtons(2);\r
+ for (i=0; i<4; i++)\r
+ {\r
+ GravisAction[i] = buttons & (1 << GravisMap[i]);\r
+ }\r
+ }\r
+ buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+ realdelta = true;\r
+ break;\r
+ case ctrl_Mouse:\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ buttons = INL_GetMouseButtons();\r
+ realdelta = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (realdelta)\r
+ {\r
+ mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+ my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+ }\r
+ else\r
+ {\r
+ dx = mx * 127;\r
+ dy = my * 127;\r
+ }\r
+\r
+ info->x = dx;\r
+ info->xaxis = mx;\r
+ info->y = dy;\r
+ info->yaxis = my;\r
+ info->button0 = buttons & (1 << 0);\r
+ info->button1 = buttons & (1 << 1);\r
+ info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+ if (DemoMode == demo_Record)\r
+ {\r
+ // Pack the control info into a byte\r
+ dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+ if\r
+ (\r
+ (DemoBuffer[DemoOffset + 1] == dbyte)\r
+ && (DemoBuffer[DemoOffset] < 255)\r
+ )\r
+ (DemoBuffer[DemoOffset])++;\r
+ else\r
+ {\r
+ if (DemoOffset || DemoBuffer[DemoOffset])\r
+ DemoOffset += 2;\r
+\r
+ if (DemoOffset >= DemoSize)\r
+ Quit("Demo buffer overflow");\r
+\r
+ DemoBuffer[DemoOffset] = 1;\r
+ DemoBuffer[DemoOffset + 1] = dbyte;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetControlType() - Sets the control type to be used by the specified\r
+// player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+ // DEBUG - check that requested type is present?\r
+ Controls[player] = type;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+// size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+ if (!bufsize)\r
+ return(false);\r
+\r
+ MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+ DemoMode = demo_Record;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+ DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+ return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte _seg *buffer,word bufsize)\r
+{\r
+ DemoBuffer = buffer;\r
+ DemoMode = demo_Playback;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+ if ((DemoMode == demo_Record) && DemoOffset)\r
+ DemoOffset += 2;\r
+\r
+ DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+ if (DemoBuffer)\r
+ MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetScanName() - Returns a string containing the name of the\r
+// specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+ byte **p;\r
+ ScanCode far *s;\r
+\r
+ for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+ if (*s == scan)\r
+ return(*p);\r
+\r
+ return(ScanNames[scan]);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+// returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+ ScanCode result;\r
+\r
+ while (!(result = LastScan))\r
+ ;\r
+ LastScan = 0;\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+// returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+ char result;\r
+\r
+ while (!(result = LastASCII))\r
+ ;\r
+ LastASCII = '\0';\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+ word i;\r
+\r
+ while (!LastScan)\r
+ {\r
+ if (MousePresent)\r
+ {\r
+ if (INL_GetMouseButtons())\r
+ {\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (JoysPresent[i] || GravisGamepad)\r
+ {\r
+ if (IN_GetJoyButtonsDB(i))\r
+ {\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+ word i;\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+\r
+ if (MousePresent)\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i] || GravisGamepad)\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+\r
+ IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+// is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+ boolean result;\r
+ word i;\r
+\r
+ result = LastScan;\r
+\r
+ if (MousePresent)\r
+ if (INL_GetMouseButtons())\r
+ result = true;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i] || GravisGamepad)\r
+ if (INL_GetJoyButtons(i))\r
+ result = true;\r
+\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+// user pressing a key or a mouse button. If the clear flag is set, it\r
+// then either clears the key or waits for the user to let the mouse\r
+// button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(longword delay,boolean clear)\r
+{\r
+ longword lasttime;\r
+\r
+ lasttime = TimeCount;\r
+ do\r
+ {\r
+ if (IN_IsUserInput())\r
+ {\r
+ if (clear)\r
+ IN_AckBack();\r
+ return(true);\r
+ }\r
+ } while (TimeCount - lasttime < delay);\r
+ return(false);\r
+}\r