]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/ID_IN.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / ID_IN.C
1 /* Reconstructed Commander Keen 4-6 Source Code\r
2  * Copyright (C) 2021 K1n9_Duk3\r
3  *\r
4  * This file is primarily based on:\r
5  * Catacomb 3-D Source Code\r
6  * Copyright (C) 1993-2014 Flat Rock Software\r
7  *\r
8  * This program is free software; you can redistribute it and/or modify\r
9  * it under the terms of the GNU General Public License as published by\r
10  * the Free Software Foundation; either version 2 of the License, or\r
11  * (at your option) any later version.\r
12  *\r
13  * This program is distributed in the hope that it will be useful,\r
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16  * GNU General Public License for more details.\r
17  *\r
18  * You should have received a copy of the GNU General Public License along\r
19  * with this program; if not, write to the Free Software Foundation, Inc.,\r
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
21  */\r
22 \r
23 //\r
24 //      ID Engine\r
25 //      ID_IN.c - Input Manager\r
26 //      v1.0d1\r
27 //      By Jason Blochowiak\r
28 //\r
29 \r
30 //\r
31 //      This module handles dealing with the various input devices\r
32 //\r
33 //      Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
34 //                              User Mgr (for command line parms)\r
35 //\r
36 //      Globals:\r
37 //              LastScan - The keyboard scan code of the last key pressed\r
38 //              LastASCII - The ASCII value of the last key pressed\r
39 //      DEBUG - there are more globals\r
40 //\r
41 \r
42 #include "ID_HEADS.H"\r
43 #pragma hdrstop\r
44 \r
45 #define KeyInt  9       // The keyboard ISR number\r
46 \r
47 // Stuff for the joystick\r
48 #define JoyScaleMax             32768\r
49 #define JoyScaleShift   8\r
50 #define MaxJoyValue             5000\r
51 \r
52 //      Global variables\r
53                 boolean         Keyboard[NumCodes],\r
54                                         JoysPresent[MaxJoys],\r
55                                         MousePresent;\r
56                 boolean         Paused;\r
57                 char            LastASCII;\r
58                 ScanCode        LastScan;\r
59                 KeyboardDef     KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
60                 JoystickDef     JoyDefs[MaxJoys];\r
61                 ControlType     Controls[MaxPlayers];\r
62                 boolean GravisGamepad;\r
63                 word            GravisAction[4];\r
64                 word            GravisMap[4];\r
65 \r
66                 boolean Latch;\r
67                 long    MouseDownCount;\r
68                 boolean LatchedButton0[MaxPlayers];\r
69                 boolean LatchedButton1[MaxPlayers];\r
70 \r
71                 Demo            DemoMode = demo_Off;\r
72                 byte _seg       *DemoBuffer;\r
73                 word            DemoOffset,DemoSize;\r
74 \r
75 //      Internal variables\r
76 static  boolean         IN_Started;\r
77 static  boolean         CapsLock;\r
78 static  ScanCode        CurCode,LastCode;\r
79 static  byte        far ASCIINames[] =          // Unshifted ASCII for scan codes\r
80                                         {\r
81 //       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
82         0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,        // 0\r
83         'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',        // 1\r
84         'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',        // 2\r
85         'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
86         0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
87         '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
88         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
89         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
90                                         },\r
91                                         far ShiftNames[] =              // Shifted ASCII for scan codes\r
92                                         {\r
93 //       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
94         0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,        // 0\r
95         'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',        // 1\r
96         'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',        // 2\r
97         'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
98         0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
99         '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
100         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
101         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
102                                         },\r
103                                         far SpecialNames[] =    // ASCII for 0xe0 prefixed codes\r
104                                         {\r
105 //       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
106         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 0\r
107         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,13 ,0  ,0  ,0  ,        // 1\r
108         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 2\r
109         0  ,0  ,0  ,0  ,0  ,'/',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
110         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 4\r
111         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
112         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
113         0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
114                                         },\r
115 \r
116                                         *ScanNames[] =          // Scan code names with single chars\r
117                                         {\r
118         "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
119         "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
120         "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
121         "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
122         "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
123         "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
124         "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
125         "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
126                                         },      // DEBUG - consolidate these\r
127                                         far ExtScanCodes[] =    // Scan codes with >1 char names\r
128                                         {\r
129         1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
130         0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
131         0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
132         0x50,0x4b,0x4d,0x00\r
133                                         },\r
134                                         *ExtScanNames[] =       // Names corresponding to ExtScanCodes\r
135                                         {\r
136         "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
137         "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
138         "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
139         "Down","Left","Right",""\r
140                                         };\r
141 static  Direction       DirTable[] =            // Quick lookup for total direction\r
142                                         {\r
143                                                 dir_NorthWest,  dir_North,      dir_NorthEast,\r
144                                                 dir_West,               dir_None,       dir_East,\r
145                                                 dir_SouthWest,  dir_South,      dir_SouthEast\r
146                                         };\r
147 \r
148 static  void                    (*INL_KeyHook)(void);\r
149 static  void interrupt  (*OldKeyVect)(void);\r
150 \r
151 static  char                    *ParmStrings[] = {"nojoys","nomouse",nil};\r
152 \r
153 //      Internal routines\r
154 \r
155 ///////////////////////////////////////////////////////////////////////////\r
156 //\r
157 //      INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
158 //\r
159 ///////////////////////////////////////////////////////////////////////////\r
160 static void interrupt\r
161 INL_KeyService(void)\r
162 {\r
163 static  boolean special;\r
164                 byte    k,c,\r
165                                 temp;\r
166                 int     player;\r
167 \r
168         k = inportb(0x60);      // Get the scan code\r
169 \r
170         // Tell the XT keyboard controller to clear the key\r
171         outportb(0x61,(temp = inportb(0x61)) | 0x80);\r
172         outportb(0x61,temp);\r
173 \r
174         if (k == 0xe0)          // Special key prefix\r
175                 special = true;\r
176         else if (k == 0xe1)     // Handle Pause key\r
177                 Paused = true;\r
178         else\r
179         {\r
180                 if (k & 0x80)   // Break code\r
181                 {\r
182                         k &= 0x7f;\r
183 \r
184 // DEBUG - handle special keys: ctl-alt-delete, print scrn\r
185 \r
186                         Keyboard[k] = false;\r
187                 }\r
188                 else                    // Make code\r
189                 {\r
190                         LastCode = CurCode;\r
191                         CurCode = LastScan = k;\r
192                         Keyboard[k] = true;\r
193 \r
194                         if (Latch)\r
195                         {\r
196                                 for (player = 0; player < MaxPlayers; player++)\r
197                                 {\r
198                                         if (Controls[player] == ctrl_Keyboard1)\r
199                                         {\r
200                                                 if (CurCode == KbdDefs[0].button0)\r
201                                                         LatchedButton0[player] = true;\r
202                                                 else if (CurCode == KbdDefs[0].button1)\r
203                                                         LatchedButton1[player] = true;\r
204                                         }\r
205                                         else if (Controls[player] == ctrl_Keyboard1)    // BUG? should probably check for ctrl_Keyboard2 here...\r
206                                         {\r
207                                                 if (CurCode == KbdDefs[1].button0)\r
208                                                         LatchedButton0[player] = true;\r
209                                                 else if (CurCode == KbdDefs[1].button1)\r
210                                                         LatchedButton1[player] = true;\r
211                                         }\r
212                                 }\r
213                         }\r
214 \r
215                         if (special)\r
216                                 c = SpecialNames[k];\r
217                         else\r
218                         {\r
219                                 if (k == sc_CapsLock)\r
220                                 {\r
221                                         CapsLock ^= true;\r
222                                         // DEBUG - make caps lock light work\r
223                                 }\r
224 \r
225                                 if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
226                                 {\r
227                                         c = ShiftNames[k];\r
228                                         if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
229                                                 c += 'a' - 'A';\r
230                                 }\r
231                                 else\r
232                                 {\r
233                                         c = ASCIINames[k];\r
234                                         if ((c >= 'a') && (c <= 'z') && CapsLock)\r
235                                                 c -= 'a' - 'A';\r
236                                 }\r
237                         }\r
238                         if (c)\r
239                                 LastASCII = c;\r
240                 }\r
241 \r
242                 special = false;\r
243         }\r
244 \r
245         if (INL_KeyHook && !special)\r
246                 INL_KeyHook();\r
247         outportb(0x20,0x20);\r
248 }\r
249 \r
250 ///////////////////////////////////////////////////////////////////////////\r
251 //\r
252 //      INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
253 //              mouse driver\r
254 //\r
255 ///////////////////////////////////////////////////////////////////////////\r
256 static void\r
257 INL_GetMouseDelta(int *x,int *y)\r
258 {\r
259         Mouse(MDelta);\r
260         *x = _CX;\r
261         *y = _DX;\r
262 }\r
263 \r
264 ///////////////////////////////////////////////////////////////////////////\r
265 //\r
266 //      INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
267 //              mouse driver\r
268 //\r
269 ///////////////////////////////////////////////////////////////////////////\r
270 static word\r
271 INL_GetMouseButtons(void)\r
272 {\r
273         word    buttons;\r
274 \r
275         Mouse(MButtons);\r
276         buttons = _BX;\r
277         return(buttons);\r
278 }\r
279 \r
280 ///////////////////////////////////////////////////////////////////////////\r
281 //\r
282 //      IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
283 //\r
284 ///////////////////////////////////////////////////////////////////////////\r
285 void\r
286 IN_GetJoyAbs(word joy,word *xp,word *yp)\r
287 {\r
288         byte    xb,yb,\r
289                         xs,ys;\r
290         word    x,y;\r
291 \r
292         x = y = 0;\r
293         xs = joy? 2 : 0;                // Find shift value for x axis\r
294         xb = 1 << xs;                   // Use shift value to get x bit mask\r
295         ys = joy? 3 : 1;                // Do the same for y axis\r
296         yb = 1 << ys;\r
297 \r
298 // Read the absolute joystick values\r
299 asm             pushf                           // Save some registers\r
300 asm             push    si\r
301 asm             push    di\r
302 asm             cli                                     // Make sure an interrupt doesn't screw the timings\r
303 \r
304 \r
305 asm             mov             dx,0x201\r
306 asm             in              al,dx\r
307 asm             out             dx,al           // Clear the resistors\r
308 \r
309 asm             mov             ah,[xb]         // Get masks into registers\r
310 asm             mov             ch,[yb]\r
311 \r
312 asm             xor             si,si           // Clear count registers\r
313 asm             xor             di,di\r
314 asm             xor             bh,bh           // Clear high byte of bx for later\r
315 \r
316 asm             push    bp                      // Don't mess up stack frame\r
317 asm             mov             bp,MaxJoyValue\r
318 \r
319 loop:\r
320 asm             in              al,dx           // Get bits indicating whether all are finished\r
321 \r
322 asm             dec             bp                      // Check bounding register\r
323 asm             jz              done            // We have a silly value - abort\r
324 \r
325 asm             mov             bl,al           // Duplicate the bits\r
326 asm             and             bl,ah           // Mask off useless bits (in [xb])\r
327 asm             add             si,bx           // Possibly increment count register\r
328 asm             mov             cl,bl           // Save for testing later\r
329 \r
330 asm             mov             bl,al\r
331 asm             and             bl,ch           // [yb]\r
332 asm             add             di,bx\r
333 \r
334 asm             add             cl,bl\r
335 asm             jnz             loop            // If both bits were 0, drop out\r
336 \r
337 done:\r
338 asm     pop             bp\r
339 \r
340 asm             mov             cl,[xs]         // Get the number of bits to shift\r
341 asm             shr             si,cl           //  and shift the count that many times\r
342 \r
343 asm             mov             cl,[ys]\r
344 asm             shr             di,cl\r
345 \r
346 asm             mov             [x],si          // Store the values into the variables\r
347 asm             mov             [y],di\r
348 \r
349 asm             pop             di\r
350 asm             pop             si\r
351 asm             popf                            // Restore the registers\r
352 \r
353         *xp = x;\r
354         *yp = y;\r
355 }\r
356 \r
357 ///////////////////////////////////////////////////////////////////////////\r
358 //\r
359 //      INL_GetJoyDelta() - Returns the relative movement of the specified\r
360 //              joystick (from +/-127, scaled adaptively)\r
361 //\r
362 ///////////////////////////////////////////////////////////////////////////\r
363 static void\r
364 INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
365 {\r
366         word            x,y;\r
367         longword        time;\r
368         JoystickDef     *def;\r
369 static  longword        lasttime;\r
370 \r
371         IN_GetJoyAbs(joy,&x,&y);\r
372         def = JoyDefs + joy;\r
373 \r
374         if (x < def->threshMinX)\r
375         {\r
376                 if (x < def->joyMinX)\r
377                         x = def->joyMinX;\r
378 \r
379                 x = -(x - def->threshMinX);\r
380                 x *= def->joyMultXL;\r
381                 x >>= JoyScaleShift;\r
382                 *dx = (x > 127)? -127 : -x;\r
383         }\r
384         else if (x > def->threshMaxX)\r
385         {\r
386                 if (x > def->joyMaxX)\r
387                         x = def->joyMaxX;\r
388 \r
389                 x = x - def->threshMaxX;\r
390                 x *= def->joyMultXH;\r
391                 x >>= JoyScaleShift;\r
392                 *dx = (x > 127)? 127 : x;\r
393         }\r
394         else\r
395                 *dx = 0;\r
396 \r
397         if (y < def->threshMinY)\r
398         {\r
399                 if (y < def->joyMinY)\r
400                         y = def->joyMinY;\r
401 \r
402                 y = -(y - def->threshMinY);\r
403                 y *= def->joyMultYL;\r
404                 y >>= JoyScaleShift;\r
405                 *dy = (y > 127)? -127 : -y;\r
406         }\r
407         else if (y > def->threshMaxY)\r
408         {\r
409                 if (y > def->joyMaxY)\r
410                         y = def->joyMaxY;\r
411 \r
412                 y = y - def->threshMaxY;\r
413                 y *= def->joyMultYH;\r
414                 y >>= JoyScaleShift;\r
415                 *dy = (y > 127)? 127 : y;\r
416         }\r
417         else\r
418                 *dy = 0;\r
419 \r
420         if (adaptive)\r
421         {\r
422                 time = (TimeCount - lasttime) / 2;\r
423                 if (time)\r
424                 {\r
425                         if (time > 8)\r
426                                 time = 8;\r
427                         *dx *= time;\r
428                         *dy *= time;\r
429                 }\r
430         }\r
431         lasttime = TimeCount;\r
432 }\r
433 \r
434 ///////////////////////////////////////////////////////////////////////////\r
435 //\r
436 //      INL_GetJoyButtons() - Returns the button status of the specified\r
437 //              joystick\r
438 //\r
439 ///////////////////////////////////////////////////////////////////////////\r
440 static word\r
441 INL_GetJoyButtons(word joy)\r
442 {\r
443 register        word    result;\r
444 \r
445         result = inportb(0x201);        // Get all the joystick buttons\r
446         if (joy == 2)\r
447         {\r
448                 // all 4 buttons (for Gravis Gamepad option)\r
449                 result >>= 4;\r
450                 result &= 15;\r
451                 result ^= 15;\r
452         }\r
453         else\r
454         {\r
455                 result >>= joy? 6 : 4;  // Shift into bits 0-1\r
456                 result &= 3;                            // Mask off the useless bits\r
457                 result ^= 3;\r
458         }\r
459         return(result);\r
460 }\r
461 \r
462 ///////////////////////////////////////////////////////////////////////////\r
463 //\r
464 //      IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
465 //              specified joystick\r
466 //\r
467 ///////////////////////////////////////////////////////////////////////////\r
468 word\r
469 IN_GetJoyButtonsDB(word joy)\r
470 {\r
471         longword        endtime;\r
472         word            result1,result2;\r
473 \r
474         do\r
475         {\r
476                 result1 = INL_GetJoyButtons(joy);\r
477                 endtime = TimeCount + 2;\r
478                 while (TimeCount <= endtime)\r
479                         ;\r
480                 result2 = INL_GetJoyButtons(joy);\r
481         } while (result1 != result2);\r
482         return(result1);\r
483 }\r
484 \r
485 ///////////////////////////////////////////////////////////////////////////\r
486 //\r
487 //      INL_StartKbd() - Sets up my keyboard stuff for use\r
488 //\r
489 ///////////////////////////////////////////////////////////////////////////\r
490 static void\r
491 INL_StartKbd(void)\r
492 {\r
493         INL_KeyHook = 0;        // Clear key hook\r
494 \r
495         IN_ClearKeysDown();\r
496 \r
497         OldKeyVect = getvect(KeyInt);\r
498         setvect(KeyInt,INL_KeyService);\r
499 }\r
500 \r
501 ///////////////////////////////////////////////////////////////////////////\r
502 //\r
503 //      INL_ShutKbd() - Restores keyboard control to the BIOS\r
504 //\r
505 ///////////////////////////////////////////////////////////////////////////\r
506 static void\r
507 INL_ShutKbd(void)\r
508 {\r
509         poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0);       // Clear ctrl/alt/shift flags\r
510 \r
511         setvect(KeyInt,OldKeyVect);\r
512 }\r
513 \r
514 ///////////////////////////////////////////////////////////////////////////\r
515 //\r
516 //      INL_StartMouse() - Detects and sets up the mouse\r
517 //\r
518 ///////////////////////////////////////////////////////////////////////////\r
519 static boolean\r
520 INL_StartMouse(void)\r
521 {\r
522         if (getvect(MouseInt))\r
523         {\r
524                 Mouse(MReset);\r
525                 if (_AX == 0xffff)\r
526                         return(true);\r
527         }\r
528         return(false);\r
529 }\r
530 \r
531 ///////////////////////////////////////////////////////////////////////////\r
532 //\r
533 //      INL_ShutMouse() - Cleans up after the mouse\r
534 //\r
535 ///////////////////////////////////////////////////////////////////////////\r
536 static void\r
537 INL_ShutMouse(void)\r
538 {\r
539 }\r
540 \r
541 //\r
542 //      INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
543 //\r
544 static void\r
545 INL_SetJoyScale(word joy)\r
546 {\r
547         JoystickDef     *def;\r
548 \r
549         def = &JoyDefs[joy];\r
550         def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
551         def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
552         def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
553         def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
554 }\r
555 \r
556 ///////////////////////////////////////////////////////////////////////////\r
557 //\r
558 //      IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
559 //              to set up scaling values\r
560 //\r
561 ///////////////////////////////////////////////////////////////////////////\r
562 void\r
563 IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
564 {\r
565         word            d,r;\r
566         JoystickDef     *def;\r
567 \r
568         def = &JoyDefs[joy];\r
569 \r
570         def->joyMinX = minx;\r
571         def->joyMaxX = maxx;\r
572         r = maxx - minx;\r
573         d = r / 5;\r
574         def->threshMinX = ((r / 2) - d) + minx;\r
575         def->threshMaxX = ((r / 2) + d) + minx;\r
576 \r
577         def->joyMinY = miny;\r
578         def->joyMaxY = maxy;\r
579         r = maxy - miny;\r
580         d = r / 5;\r
581         def->threshMinY = ((r / 2) - d) + miny;\r
582         def->threshMaxY = ((r / 2) + d) + miny;\r
583 \r
584         INL_SetJoyScale(joy);\r
585 }\r
586 \r
587 ///////////////////////////////////////////////////////////////////////////\r
588 //\r
589 //      INL_StartJoy() - Detects & auto-configures the specified joystick\r
590 //                                      The auto-config assumes the joystick is centered\r
591 //\r
592 ///////////////////////////////////////////////////////////////////////////\r
593 static boolean\r
594 INL_StartJoy(word joy)\r
595 {\r
596         word            x,y;\r
597 \r
598         IN_GetJoyAbs(joy,&x,&y);\r
599 \r
600         if\r
601         (\r
602                 ((x == 0) || (x > MaxJoyValue - 10))\r
603         ||      ((y == 0) || (y > MaxJoyValue - 10))\r
604         )\r
605                 return(false);\r
606         else\r
607         {\r
608                 IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
609                 return(true);\r
610         }\r
611 }\r
612 \r
613 ///////////////////////////////////////////////////////////////////////////\r
614 //\r
615 //      INL_ShutJoy() - Cleans up the joystick stuff\r
616 //\r
617 ///////////////////////////////////////////////////////////////////////////\r
618 static void\r
619 INL_ShutJoy(word joy)\r
620 {\r
621         JoysPresent[joy] = false;\r
622 }\r
623 \r
624 //      Public routines\r
625 \r
626 ///////////////////////////////////////////////////////////////////////////\r
627 //\r
628 //      IN_ClearButtonLatch() - Clears the button latch stuff\r
629 //\r
630 ///////////////////////////////////////////////////////////////////////////\r
631 void\r
632 IN_ClearButtonLatch(void)\r
633 {\r
634         int player;\r
635 \r
636 asm             pushf\r
637 asm             cli\r
638 \r
639         MouseDownCount = 0;\r
640 \r
641         for (player = 0; player < MaxPlayers; player++)\r
642         {\r
643                 LatchedButton0[player] = LatchedButton1[player] = 0;\r
644         }\r
645 \r
646 asm             popf\r
647 }\r
648 \r
649 ///////////////////////////////////////////////////////////////////////////\r
650 //\r
651 //      LatchSndHook() - Hook routine for joystick button latch\r
652 //\r
653 ///////////////////////////////////////////////////////////////////////////\r
654 void\r
655 LatchSndHook(void)\r
656 {\r
657         int player;\r
658         ControlType ctrl;\r
659         word buttons;\r
660 \r
661         for (player = 0; player < MaxPlayers; player++)\r
662         {\r
663                 ctrl = Controls[player];\r
664 \r
665                 if (ctrl == ctrl_Joystick1 || ctrl == ctrl_Joystick2)\r
666                 {\r
667                         buttons = INL_GetJoyButtons(ctrl - ctrl_Joystick1);\r
668 \r
669                         if (buttons & 1)\r
670                                 LatchedButton0[player] = true;\r
671                         if (buttons & 2)\r
672                                 LatchedButton1[player] = true;\r
673                 }\r
674         }\r
675 }\r
676 \r
677 ///////////////////////////////////////////////////////////////////////////\r
678 //\r
679 //      IN_LatchButtons() - Enables or disables button latch\r
680 //\r
681 ///////////////////////////////////////////////////////////////////////////\r
682 void IN_LatchButtons(boolean enabled)\r
683 {\r
684         if (enabled)\r
685         {\r
686                 Latch = false;\r
687                 IN_ClearButtonLatch();\r
688         }\r
689 \r
690         Latch = enabled;\r
691         SD_SetUserHook(Latch ? LatchSndHook : NULL);\r
692 }\r
693 \r
694 ///////////////////////////////////////////////////////////////////////////\r
695 //\r
696 //      IN_Startup() - Starts up the Input Mgr\r
697 //\r
698 ///////////////////////////////////////////////////////////////////////////\r
699 void\r
700 IN_Startup(void)\r
701 {\r
702         boolean checkjoys,checkmouse;\r
703         word    i;\r
704 \r
705         if (IN_Started)\r
706                 return;\r
707 \r
708         checkjoys = true;\r
709         checkmouse = true;\r
710         for (i = 1;i < _argc;i++)\r
711         {\r
712                 switch (US_CheckParm(_argv[i],ParmStrings))\r
713                 {\r
714                 case 0:\r
715                         checkjoys = false;\r
716                         break;\r
717                 case 1:\r
718                         checkmouse = false;\r
719                         break;\r
720                 }\r
721         }\r
722 \r
723         INL_StartKbd();\r
724         MousePresent = checkmouse? INL_StartMouse() : false;\r
725 \r
726         for (i = 0;i < MaxJoys;i++)\r
727                 JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
728 \r
729         IN_Started = true;\r
730 }\r
731 \r
732 ///////////////////////////////////////////////////////////////////////////\r
733 //\r
734 //      IN_Default() - Sets up default conditions for the Input Mgr\r
735 //\r
736 ///////////////////////////////////////////////////////////////////////////\r
737 void\r
738 IN_Default(boolean gotit,ControlType in)\r
739 {\r
740         if\r
741         (\r
742                 (!gotit)\r
743         ||      ((in == ctrl_Joystick1) && !JoysPresent[0])\r
744         ||      ((in == ctrl_Joystick2) && !JoysPresent[1])\r
745         ||      ((in == ctrl_Mouse) && !MousePresent)\r
746         )\r
747                 in = ctrl_Keyboard1;\r
748         IN_SetControlType(0,in);\r
749 }\r
750 \r
751 ///////////////////////////////////////////////////////////////////////////\r
752 //\r
753 //      IN_Shutdown() - Shuts down the Input Mgr\r
754 //\r
755 ///////////////////////////////////////////////////////////////////////////\r
756 void\r
757 IN_Shutdown(void)\r
758 {\r
759         word    i;\r
760 \r
761         if (!IN_Started)\r
762                 return;\r
763 \r
764         INL_ShutMouse();\r
765         for (i = 0;i < MaxJoys;i++)\r
766                 INL_ShutJoy(i);\r
767         INL_ShutKbd();\r
768 \r
769         IN_Started = false;\r
770 }\r
771 \r
772 ///////////////////////////////////////////////////////////////////////////\r
773 //\r
774 //      IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
775 //                      everytime a real make/break code gets hit\r
776 //\r
777 ///////////////////////////////////////////////////////////////////////////\r
778 void\r
779 IN_SetKeyHook(void (*hook)())\r
780 {\r
781         // BUG: interrupts should be disabled while setting INL_KeyHook!\r
782         INL_KeyHook = hook;\r
783 }\r
784 \r
785 ///////////////////////////////////////////////////////////////////////////\r
786 //\r
787 //      IN_ClearKeyDown() - Clears the keyboard array\r
788 //\r
789 ///////////////////////////////////////////////////////////////////////////\r
790 void\r
791 IN_ClearKeysDown(void)\r
792 {\r
793         int     i;\r
794 \r
795         LastScan = sc_None;\r
796         LastASCII = key_None;\r
797         for (i = 0;i < NumCodes;i++)\r
798                 Keyboard[i] = false;\r
799 }\r
800 \r
801 ///////////////////////////////////////////////////////////////////////////\r
802 //\r
803 //      INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
804 //\r
805 ///////////////////////////////////////////////////////////////////////////\r
806 static void\r
807 INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
808 {\r
809         if (buttons & (1 << 0))\r
810                 info->button0 = true;\r
811         if (buttons & (1 << 1))\r
812                 info->button1 = true;\r
813 \r
814         info->x += dx;\r
815         info->y += dy;\r
816 }\r
817 \r
818 ///////////////////////////////////////////////////////////////////////////\r
819 //\r
820 //      IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
821 //              struct\r
822 //\r
823 ///////////////////////////////////////////////////////////////////////////\r
824 void\r
825 IN_ReadCursor(CursorInfo *info)\r
826 {\r
827         word    i,\r
828                 player,\r
829                         buttons;\r
830         int             dx,dy;\r
831 \r
832         info->x = info->y = 0;\r
833         info->button0 = info->button1 = false;\r
834 \r
835         if (MousePresent)\r
836         {\r
837                 buttons = INL_GetMouseButtons();\r
838                 INL_GetMouseDelta(&dx,&dy);\r
839                 INL_AdjustCursor(info,buttons,dx,dy);\r
840         }\r
841 \r
842         for (i = 0;i < MaxJoys;i++)\r
843         {\r
844                 if (!JoysPresent[i])\r
845                         continue;\r
846 \r
847                 for (player = 0;player < MaxPlayers; player++)\r
848                 {\r
849                         if (Controls[player] == ctrl_Joystick1+i)\r
850                                 goto joyok;\r
851                 }\r
852                 continue;\r
853 \r
854 joyok:\r
855                 buttons = INL_GetJoyButtons(i);\r
856                 INL_GetJoyDelta(i,&dx,&dy,true);\r
857                 dx /= 64;\r
858                 dy /= 64;\r
859                 INL_AdjustCursor(info,buttons,dx,dy);\r
860         }\r
861 }\r
862 \r
863 ///////////////////////////////////////////////////////////////////////////\r
864 //\r
865 //      IN_ReadControl() - Reads the device associated with the specified\r
866 //              player and fills in the control info struct\r
867 //\r
868 ///////////////////////////////////////////////////////////////////////////\r
869 void\r
870 IN_ReadControl(int player,ControlInfo *info)\r
871 {\r
872                         boolean         realdelta;\r
873                         byte            dbyte;\r
874                         word            buttons;\r
875                         int                     i;\r
876                         int                     dx,dy;\r
877                         Motion          mx,my;\r
878                         ControlType     type;\r
879 register        KeyboardDef     *def;\r
880 \r
881         dx = dy = 0;\r
882         mx = my = motion_None;\r
883         buttons = 0;\r
884 \r
885         if (DemoMode == demo_Playback)\r
886         {\r
887                 dbyte = DemoBuffer[DemoOffset + 1];\r
888                 my = (dbyte & 3) - 1;\r
889                 mx = ((dbyte >> 2) & 3) - 1;\r
890                 buttons = (dbyte >> 4) & 3;\r
891 \r
892                 if (!(--DemoBuffer[DemoOffset]))\r
893                 {\r
894                         DemoOffset += 2;\r
895                         if (DemoOffset >= DemoSize)\r
896                                 DemoMode = demo_PlayDone;\r
897                 }\r
898 \r
899                 realdelta = false;\r
900         }\r
901         else if (DemoMode == demo_PlayDone)\r
902                 Quit("Demo playback exceeded");\r
903         else\r
904         {\r
905                 switch (type = Controls[player])\r
906                 {\r
907                 case ctrl_Keyboard1:\r
908                 case ctrl_Keyboard2:\r
909                         def = &KbdDefs[type - ctrl_Keyboard];\r
910 \r
911                         if (Keyboard[def->upleft])\r
912                                 mx = motion_Left,my = motion_Up;\r
913                         else if (Keyboard[def->upright])\r
914                                 mx = motion_Right,my = motion_Up;\r
915                         else if (Keyboard[def->downleft])\r
916                                 mx = motion_Left,my = motion_Down;\r
917                         else if (Keyboard[def->downright])\r
918                                 mx = motion_Right,my = motion_Down;\r
919 \r
920                         if (Keyboard[def->up])\r
921                                 my = motion_Up;\r
922                         else if (Keyboard[def->down])\r
923                                 my = motion_Down;\r
924 \r
925                         if (Keyboard[def->left])\r
926                                 mx = motion_Left;\r
927                         else if (Keyboard[def->right])\r
928                                 mx = motion_Right;\r
929 \r
930                         if (Keyboard[def->button0])\r
931                                 buttons += 1 << 0;\r
932                         if (Keyboard[def->button1])\r
933                                 buttons += 1 << 1;\r
934                         realdelta = false;\r
935                         break;\r
936                 case ctrl_Joystick1:\r
937                 case ctrl_Joystick2:\r
938                         INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
939                         if (GravisGamepad)\r
940                         {\r
941                                 buttons = INL_GetJoyButtons(2);\r
942                                 for (i=0; i<4; i++)\r
943                                 {\r
944                                         GravisAction[i] = buttons & (1 << GravisMap[i]);\r
945                                 }\r
946                         }\r
947                         buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
948                         realdelta = true;\r
949                         break;\r
950                 case ctrl_Mouse:\r
951                         INL_GetMouseDelta(&dx,&dy);\r
952                         buttons = INL_GetMouseButtons();\r
953                         realdelta = true;\r
954                         break;\r
955                 }\r
956         }\r
957 \r
958         if (realdelta)\r
959         {\r
960                 mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
961                 my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
962         }\r
963         else\r
964         {\r
965                 dx = mx * 127;\r
966                 dy = my * 127;\r
967         }\r
968 \r
969         info->x = dx;\r
970         info->xaxis = mx;\r
971         info->y = dy;\r
972         info->yaxis = my;\r
973         info->button0 = buttons & (1 << 0);\r
974         info->button1 = buttons & (1 << 1);\r
975         info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
976 \r
977         if (DemoMode == demo_Record)\r
978         {\r
979                 // Pack the control info into a byte\r
980                 dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
981 \r
982                 if\r
983                 (\r
984                         (DemoBuffer[DemoOffset + 1] == dbyte)\r
985                 &&      (DemoBuffer[DemoOffset] < 255)\r
986                 )\r
987                         (DemoBuffer[DemoOffset])++;\r
988                 else\r
989                 {\r
990                         if (DemoOffset || DemoBuffer[DemoOffset])\r
991                                 DemoOffset += 2;\r
992 \r
993                         if (DemoOffset >= DemoSize)\r
994                                 Quit("Demo buffer overflow");\r
995 \r
996                         DemoBuffer[DemoOffset] = 1;\r
997                         DemoBuffer[DemoOffset + 1] = dbyte;\r
998                 }\r
999         }\r
1000 }\r
1001 \r
1002 ///////////////////////////////////////////////////////////////////////////\r
1003 //\r
1004 //      IN_SetControlType() - Sets the control type to be used by the specified\r
1005 //              player\r
1006 //\r
1007 ///////////////////////////////////////////////////////////////////////////\r
1008 void\r
1009 IN_SetControlType(int player,ControlType type)\r
1010 {\r
1011         // DEBUG - check that requested type is present?\r
1012         Controls[player] = type;\r
1013 }\r
1014 \r
1015 ///////////////////////////////////////////////////////////////////////////\r
1016 //\r
1017 //      IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
1018 //              size passed. Returns if the buffer allocation was successful\r
1019 //\r
1020 ///////////////////////////////////////////////////////////////////////////\r
1021 boolean\r
1022 IN_StartDemoRecord(word bufsize)\r
1023 {\r
1024         if (!bufsize)\r
1025                 return(false);\r
1026 \r
1027         MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
1028         DemoMode = demo_Record;\r
1029         DemoSize = bufsize & ~1;\r
1030         DemoOffset = 0;\r
1031         DemoBuffer[0] = DemoBuffer[1] = 0;\r
1032 \r
1033         return(true);\r
1034 }\r
1035 \r
1036 ///////////////////////////////////////////////////////////////////////////\r
1037 //\r
1038 //      IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
1039 //\r
1040 ///////////////////////////////////////////////////////////////////////////\r
1041 void\r
1042 IN_StartDemoPlayback(byte _seg *buffer,word bufsize)\r
1043 {\r
1044         DemoBuffer = buffer;\r
1045         DemoMode = demo_Playback;\r
1046         DemoSize = bufsize & ~1;\r
1047         DemoOffset = 0;\r
1048 }\r
1049 \r
1050 ///////////////////////////////////////////////////////////////////////////\r
1051 //\r
1052 //      IN_StopDemo() - Turns off demo mode\r
1053 //\r
1054 ///////////////////////////////////////////////////////////////////////////\r
1055 void\r
1056 IN_StopDemo(void)\r
1057 {\r
1058         if ((DemoMode == demo_Record) && DemoOffset)\r
1059                 DemoOffset += 2;\r
1060 \r
1061         DemoMode = demo_Off;\r
1062 }\r
1063 \r
1064 ///////////////////////////////////////////////////////////////////////////\r
1065 //\r
1066 //      IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
1067 //\r
1068 ///////////////////////////////////////////////////////////////////////////\r
1069 void\r
1070 IN_FreeDemoBuffer(void)\r
1071 {\r
1072         if (DemoBuffer)\r
1073                 MM_FreePtr((memptr *)&DemoBuffer);\r
1074 }\r
1075 \r
1076 ///////////////////////////////////////////////////////////////////////////\r
1077 //\r
1078 //      IN_GetScanName() - Returns a string containing the name of the\r
1079 //              specified scan code\r
1080 //\r
1081 ///////////////////////////////////////////////////////////////////////////\r
1082 byte *\r
1083 IN_GetScanName(ScanCode scan)\r
1084 {\r
1085         byte            **p;\r
1086         ScanCode        far *s;\r
1087 \r
1088         for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
1089                 if (*s == scan)\r
1090                         return(*p);\r
1091 \r
1092         return(ScanNames[scan]);\r
1093 }\r
1094 \r
1095 ///////////////////////////////////////////////////////////////////////////\r
1096 //\r
1097 //      IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
1098 //              returns the scan code\r
1099 //\r
1100 ///////////////////////////////////////////////////////////////////////////\r
1101 ScanCode\r
1102 IN_WaitForKey(void)\r
1103 {\r
1104         ScanCode        result;\r
1105 \r
1106         while (!(result = LastScan))\r
1107                 ;\r
1108         LastScan = 0;\r
1109         return(result);\r
1110 }\r
1111 \r
1112 ///////////////////////////////////////////////////////////////////////////\r
1113 //\r
1114 //      IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
1115 //              returns the ASCII value\r
1116 //\r
1117 ///////////////////////////////////////////////////////////////////////////\r
1118 char\r
1119 IN_WaitForASCII(void)\r
1120 {\r
1121         char            result;\r
1122 \r
1123         while (!(result = LastASCII))\r
1124                 ;\r
1125         LastASCII = '\0';\r
1126         return(result);\r
1127 }\r
1128 \r
1129 ///////////////////////////////////////////////////////////////////////////\r
1130 //\r
1131 //      IN_AckBack() - Waits for either an ASCII keypress or a button press\r
1132 //\r
1133 ///////////////////////////////////////////////////////////////////////////\r
1134 void\r
1135 IN_AckBack(void)\r
1136 {\r
1137         word    i;\r
1138 \r
1139         while (!LastScan)\r
1140         {\r
1141                 if (MousePresent)\r
1142                 {\r
1143                         if (INL_GetMouseButtons())\r
1144                         {\r
1145                                 while (INL_GetMouseButtons())\r
1146                                         ;\r
1147                                 return;\r
1148                         }\r
1149                 }\r
1150 \r
1151                 for (i = 0;i < MaxJoys;i++)\r
1152                 {\r
1153                         if (JoysPresent[i] || GravisGamepad)\r
1154                         {\r
1155                                 if (IN_GetJoyButtonsDB(i))\r
1156                                 {\r
1157                                         while (IN_GetJoyButtonsDB(i))\r
1158                                                 ;\r
1159                                         return;\r
1160                                 }\r
1161                         }\r
1162                 }\r
1163         }\r
1164 \r
1165         IN_ClearKey(LastScan);\r
1166         LastScan = sc_None;\r
1167 }\r
1168 \r
1169 ///////////////////////////////////////////////////////////////////////////\r
1170 //\r
1171 //      IN_Ack() - Clears user input & then calls IN_AckBack()\r
1172 //\r
1173 ///////////////////////////////////////////////////////////////////////////\r
1174 void\r
1175 IN_Ack(void)\r
1176 {\r
1177         word    i;\r
1178 \r
1179         IN_ClearKey(LastScan);\r
1180         LastScan = sc_None;\r
1181 \r
1182         if (MousePresent)\r
1183                 while (INL_GetMouseButtons())\r
1184                                         ;\r
1185         for (i = 0;i < MaxJoys;i++)\r
1186                 if (JoysPresent[i] || GravisGamepad)\r
1187                         while (IN_GetJoyButtonsDB(i))\r
1188                                 ;\r
1189 \r
1190         IN_AckBack();\r
1191 }\r
1192 \r
1193 ///////////////////////////////////////////////////////////////////////////\r
1194 //\r
1195 //      IN_IsUserInput() - Returns true if a key has been pressed or a button\r
1196 //              is down\r
1197 //\r
1198 ///////////////////////////////////////////////////////////////////////////\r
1199 boolean\r
1200 IN_IsUserInput(void)\r
1201 {\r
1202         boolean result;\r
1203         word    i;\r
1204 \r
1205         result = LastScan;\r
1206 \r
1207         if (MousePresent)\r
1208                 if (INL_GetMouseButtons())\r
1209                         result = true;\r
1210 \r
1211         for (i = 0;i < MaxJoys;i++)\r
1212                 if (JoysPresent[i] || GravisGamepad)\r
1213                         if (INL_GetJoyButtons(i))\r
1214                                 result = true;\r
1215 \r
1216         return(result);\r
1217 }\r
1218 \r
1219 ///////////////////////////////////////////////////////////////////////////\r
1220 //\r
1221 //      IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
1222 //              user pressing a key or a mouse button. If the clear flag is set, it\r
1223 //              then either clears the key or waits for the user to let the mouse\r
1224 //              button up.\r
1225 //\r
1226 ///////////////////////////////////////////////////////////////////////////\r
1227 boolean\r
1228 IN_UserInput(longword delay,boolean clear)\r
1229 {\r
1230         longword        lasttime;\r
1231 \r
1232         lasttime = TimeCount;\r
1233         do\r
1234         {\r
1235                 if (IN_IsUserInput())\r
1236                 {\r
1237                         if (clear)\r
1238                                 IN_AckBack();\r
1239                         return(true);\r
1240                 }\r
1241         } while (TimeCount - lasttime < delay);\r
1242         return(false);\r
1243 }\r