DEADSOFTWARE

key binds and vars are stored in dfconfig.cfg
[d2df-sdl.git] / src / engine / e_input.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit e_input;
19 interface
21 uses
22 SysUtils,
23 e_log,
24 SDL2;
26 const
27 e_MaxKbdKeys = SDL_NUM_SCANCODES;
28 e_MaxJoys = 4;
29 e_MaxJoyBtns = 32;
30 e_MaxJoyAxes = 8;
31 e_MaxJoyHats = 8;
32 e_MaxVirtKeys = 48;
34 e_MaxJoyKeys = e_MaxJoyBtns + e_MaxJoyAxes*2 + e_MaxJoyHats*4;
36 e_MaxInputKeys = e_MaxKbdKeys + e_MaxJoys*e_MaxJoyKeys + e_MaxVirtKeys - 1;
37 // $$$..$$$ - 321 Keyboard buttons/keys
38 // $$$..$$$ - 4*32 Joystick buttons
39 // $$$..$$$ - 8*2 Joystick axes (- and +)
40 // $$$..$$$ - 4*4 Joystick hats (L U R D)
41 // $$$..$$$ - 48 Virtual buttons/keys
43 // these are apparently used in g_gui and g_game and elsewhere
44 IK_INVALID = 65535;
45 IK_ESCAPE = SDL_SCANCODE_ESCAPE;
46 IK_RETURN = SDL_SCANCODE_RETURN;
47 IK_KPRETURN= SDL_SCANCODE_KP_ENTER;
48 IK_ENTER = SDL_SCANCODE_RETURN;
49 IK_UP = SDL_SCANCODE_UP;
50 IK_KPUP = SDL_SCANCODE_KP_8;
51 IK_DOWN = SDL_SCANCODE_DOWN;
52 IK_KPDOWN = SDL_SCANCODE_KP_2;
53 IK_LEFT = SDL_SCANCODE_LEFT;
54 IK_KPLEFT = SDL_SCANCODE_KP_4;
55 IK_RIGHT = SDL_SCANCODE_RIGHT;
56 IK_KPRIGHT = SDL_SCANCODE_KP_6;
57 IK_DELETE = SDL_SCANCODE_DELETE;
58 IK_HOME = SDL_SCANCODE_HOME;
59 IK_KPHOME = SDL_SCANCODE_KP_7;
60 IK_INSERT = SDL_SCANCODE_INSERT;
61 IK_SPACE = SDL_SCANCODE_SPACE;
62 IK_CONTROL = SDL_SCANCODE_LCTRL;
63 IK_SHIFT = SDL_SCANCODE_LSHIFT;
64 IK_ALT = SDL_SCANCODE_LALT;
65 IK_TAB = SDL_SCANCODE_TAB;
66 IK_PAGEUP = SDL_SCANCODE_PAGEUP;
67 IK_KPPAGEUP= SDL_SCANCODE_KP_9;
68 IK_PAGEDN = SDL_SCANCODE_PAGEDOWN;
69 IK_KPPAGEDN= SDL_SCANCODE_KP_3;
70 IK_0 = SDL_SCANCODE_0;
71 IK_1 = SDL_SCANCODE_1;
72 IK_2 = SDL_SCANCODE_2;
73 IK_3 = SDL_SCANCODE_3;
74 IK_4 = SDL_SCANCODE_4;
75 IK_5 = SDL_SCANCODE_5;
76 IK_6 = SDL_SCANCODE_6;
77 IK_7 = SDL_SCANCODE_7;
78 IK_8 = SDL_SCANCODE_8;
79 IK_9 = SDL_SCANCODE_9;
80 IK_F1 = SDL_SCANCODE_F1;
81 IK_F2 = SDL_SCANCODE_F2;
82 IK_F3 = SDL_SCANCODE_F3;
83 IK_F4 = SDL_SCANCODE_F4;
84 IK_F5 = SDL_SCANCODE_F5;
85 IK_F6 = SDL_SCANCODE_F6;
86 IK_F7 = SDL_SCANCODE_F7;
87 IK_F8 = SDL_SCANCODE_F8;
88 IK_F9 = SDL_SCANCODE_F9;
89 IK_F10 = SDL_SCANCODE_F10;
90 IK_F11 = SDL_SCANCODE_F11;
91 IK_F12 = SDL_SCANCODE_F12;
92 IK_END = SDL_SCANCODE_END;
93 IK_KPEND = SDL_SCANCODE_KP_1;
94 IK_BACKSPACE = SDL_SCANCODE_BACKSPACE;
95 IK_BACKQUOTE = SDL_SCANCODE_GRAVE;
96 IK_GRAVE = SDL_SCANCODE_GRAVE;
97 IK_PAUSE = SDL_SCANCODE_PAUSE;
98 IK_Y = SDL_SCANCODE_Y;
99 IK_N = SDL_SCANCODE_N;
100 IK_W = SDL_SCANCODE_W;
101 IK_A = SDL_SCANCODE_A;
102 IK_S = SDL_SCANCODE_S;
103 IK_D = SDL_SCANCODE_D;
104 IK_Q = SDL_SCANCODE_Q;
105 IK_E = SDL_SCANCODE_E;
106 IK_H = SDL_SCANCODE_H;
107 IK_J = SDL_SCANCODE_J;
108 IK_T = SDL_SCANCODE_T;
109 IK_MINUS = SDL_SCANCODE_MINUS;
110 // TODO: think of something better than this shit
111 IK_LASTKEY = SDL_NUM_SCANCODES-1;
113 VK_FIRSTKEY = e_MaxKbdKeys + e_MaxJoys*e_MaxJoyKeys;
114 VK_LEFT = VK_FIRSTKEY + 0;
115 VK_RIGHT = VK_FIRSTKEY + 1;
116 VK_UP = VK_FIRSTKEY + 2;
117 VK_DOWN = VK_FIRSTKEY + 3;
118 VK_FIRE = VK_FIRSTKEY + 4;
119 VK_OPEN = VK_FIRSTKEY + 5;
120 VK_JUMP = VK_FIRSTKEY + 6;
121 VK_CHAT = VK_FIRSTKEY + 7;
122 VK_ESCAPE = VK_FIRSTKEY + 8;
123 VK_0 = VK_FIRSTKEY + 9;
124 VK_1 = VK_FIRSTKEY + 10;
125 VK_2 = VK_FIRSTKEY + 11;
126 VK_3 = VK_FIRSTKEY + 12;
127 VK_4 = VK_FIRSTKEY + 13;
128 VK_5 = VK_FIRSTKEY + 14;
129 VK_6 = VK_FIRSTKEY + 15;
130 VK_7 = VK_FIRSTKEY + 16;
131 VK_8 = VK_FIRSTKEY + 17;
132 VK_9 = VK_FIRSTKEY + 18;
133 VK_A = VK_FIRSTKEY + 19;
134 VK_B = VK_FIRSTKEY + 20;
135 VK_C = VK_FIRSTKEY + 21;
136 VK_D = VK_FIRSTKEY + 22;
137 VK_E = VK_FIRSTKEY + 23;
138 VK_F = VK_FIRSTKEY + 24;
139 VK_CONSOLE = VK_FIRSTKEY + 25;
140 VK_STATUS = VK_FIRSTKEY + 26;
141 VK_TEAM = VK_FIRSTKEY + 27;
142 VK_PREV = VK_FIRSTKEY + 28;
143 VK_NEXT = VK_FIRSTKEY + 29;
144 VK_STRAFE = VK_FIRSTKEY + 30;
145 VK_LSTRAFE = VK_FIRSTKEY + 31;
146 VK_RSTRAFE = VK_FIRSTKEY + 32;
147 VK_PRINTSCR = VK_FIRSTKEY + 33;
148 VK_LASTKEY = e_MaxKbdKeys + e_MaxJoys*e_MaxJoyKeys + e_MaxVirtKeys - 1;
150 AX_MINUS = 0;
151 AX_PLUS = 1;
152 HAT_LEFT = 0;
153 HAT_UP = 1;
154 HAT_RIGHT = 2;
155 HAT_DOWN = 3;
157 function e_InitInput(): Boolean;
158 procedure e_ReleaseInput();
159 procedure e_ClearInputBuffer();
160 //function e_PollInput(): Boolean;
161 procedure e_PollJoysticks(); // call this from message loop to update joysticks
162 function e_KeyPressed(Key: Word): Boolean;
163 function e_AnyKeyPressed(): Boolean;
164 function e_GetFirstKeyPressed(): Word;
165 function e_JoystickStateToString(mode: Integer): String;
166 function e_JoyByHandle(handle: Word): Integer;
167 function e_JoyButtonToKey(id: Word; btn: Byte): Word;
168 function e_JoyAxisToKey(id: Word; ax: Byte; dir: Byte): Word;
169 function e_JoyHatToKey(id: Word; hat: Byte; dir: Byte): Word;
170 procedure e_SetKeyState(key: Word; state: Integer);
172 procedure e_UnpressAllKeys ();
173 procedure e_KeyUpDown (key: Word; down: Boolean);
175 var
176 {e_MouseInfo: TMouseInfo;}
177 e_EnableInput: Boolean = False;
178 e_JoysticksAvailable: Byte = 0;
179 e_JoystickDeadzones: array [0..e_MaxJoys-1] of Integer = (8192, 8192, 8192, 8192);
180 e_KeyNames: array [0..e_MaxInputKeys] of String;
182 implementation
184 uses Math;
186 const
187 KBRD_END = e_MaxKbdKeys;
188 JOYK_BEG = KBRD_END;
189 JOYK_END = JOYK_BEG + e_MaxJoyBtns*e_MaxJoys;
190 JOYA_BEG = JOYK_END;
191 JOYA_END = JOYA_BEG + e_MaxJoyAxes*2*e_MaxJoys;
192 JOYH_BEG = JOYA_END;
193 JOYH_END = JOYH_BEG + e_MaxJoyHats*4*e_MaxJoys;
194 VIRT_BEG = JOYH_END;
195 VIRT_END = VIRT_BEG + e_MaxVirtKeys;
197 type
198 TJoystick = record
199 ID: Byte;
200 Handle: PSDL_Joystick;
201 Axes: Byte;
202 Buttons: Byte;
203 Hats: Byte;
204 ButtBuf: array [0..e_MaxJoyBtns] of Boolean;
205 AxisBuf: array [0..e_MaxJoyAxes] of Integer;
206 AxisZero: array [0..e_MaxJoyAxes] of Integer;
207 HatBuf: array [0..e_MaxJoyHats] of array [HAT_LEFT..HAT_DOWN] of Boolean;
208 end;
210 var
211 KeyBuffer: array [0..e_MaxKbdKeys] of Boolean;
212 VirtBuffer: array [0..e_MaxVirtKeys] of Boolean;
213 Joysticks: array of TJoystick = nil;
215 function OpenJoysticks(): Byte;
216 var
217 i, j, k, c: Integer;
218 joy: PSDL_Joystick;
219 begin
220 Result := 0;
221 k := Min(e_MaxJoys, SDL_NumJoysticks());
222 if k = 0 then Exit;
223 c := 0;
224 for i := 0 to k do
225 begin
226 joy := SDL_JoystickOpen(i);
227 if joy <> nil then
228 begin
229 Inc(c);
230 e_WriteLog('Input: Opened SDL joystick ' + IntToStr(i) + ' (' + SDL_JoystickName(joy) +
231 ') as joystick ' + IntToStr(c) + ':', TMsgType.Notify);
232 SetLength(Joysticks, c);
233 with Joysticks[c-1] do
234 begin
235 ID := i;
236 Handle := joy;
237 Axes := Min(e_MaxJoyAxes, SDL_JoystickNumAxes(joy));
238 Buttons := Min(e_MaxJoyBtns, SDL_JoystickNumButtons(joy));
239 Hats := Min(e_MaxJoyHats, SDL_JoystickNumHats(joy));
240 // TODO: find proper solution for this xbox trigger shit
241 for j := 0 to Axes do AxisZero[j] := SDL_JoystickGetAxis(joy, j);
242 e_WriteLog(' ' + IntToStr(Axes) + ' axes, ' + IntToStr(Buttons) + ' buttons, ' +
243 IntToStr(Hats) + ' hats.', TMsgType.Notify);
244 end;
245 end;
246 end;
247 Result := c;
248 end;
250 procedure ReleaseJoysticks();
251 var
252 i: Integer;
253 begin
254 if (Joysticks = nil) or (e_JoysticksAvailable = 0) then Exit;
255 for i := Low(Joysticks) to High(Joysticks) do
256 with Joysticks[i] do
257 SDL_JoystickClose(Handle);
258 SetLength(Joysticks, 0);
259 end;
262 procedure e_UnpressAllKeys ();
263 var
264 i: Integer;
265 begin
266 for i := 0 to High(KeyBuffer) do KeyBuffer[i] := False;
267 for i := 0 to High(VirtBuffer) do VirtBuffer[i] := False;
268 end;
271 procedure e_KeyUpDown (key: Word; down: Boolean);
272 begin
273 if (key > 0) and (key < Length(KeyBuffer)) then KeyBuffer[key] := down
274 else if (key >= VIRT_BEG) and (key < VIRT_END) then VirtBuffer[key - VIRT_BEG] := down
275 end;
278 function PollKeyboard(): Boolean;
280 var
281 Keys: PByte;
282 NKeys: Integer;
283 i: NativeUInt;
285 begin
286 Result := False;
288 Keys := SDL_GetKeyboardState(@NKeys);
289 if (Keys = nil) or (NKeys < 1) then Exit;
290 for i := 0 to NKeys do
291 begin
292 if ((PByte(NativeUInt(Keys) + i)^) <> 0) then KeyBuffer[i] := false;
293 end;
294 for i := NKeys to High(KeyBuffer) do KeyBuffer[i] := False;
296 end;
298 procedure e_PollJoysticks();
299 var
300 i, j: Word;
301 hat: Byte;
302 begin
303 //Result := False;
304 if (Joysticks = nil) or (e_JoysticksAvailable = 0) then Exit;
305 SDL_JoystickUpdate();
306 for j := Low(Joysticks) to High(Joysticks) do
307 begin
308 with Joysticks[j] do
309 begin
310 for i := 0 to Buttons do ButtBuf[i] := SDL_JoystickGetButton(Handle, i) <> 0;
311 for i := 0 to Axes do AxisBuf[i] := SDL_JoystickGetAxis(Handle, i);
312 for i := 0 to Hats do
313 begin
314 hat := SDL_JoystickGetHat(Handle, i);
315 HatBuf[i, HAT_UP] := LongBool(hat and SDL_HAT_UP);
316 HatBuf[i, HAT_DOWN] := LongBool(hat and SDL_HAT_DOWN);
317 HatBuf[i, HAT_LEFT] := LongBool(hat and SDL_HAT_LEFT);
318 HatBuf[i, HAT_RIGHT] := LongBool(hat and SDL_HAT_RIGHT);
319 end;
320 end;
321 end;
322 end;
324 procedure GenerateKeyNames();
325 var
326 i, j, k: LongWord;
327 begin
328 // keyboard key names
329 for i := 0 to IK_LASTKEY do
330 e_KeyNames[i] := SDL_GetScancodeName(i);
332 // joysticks
333 for j := 0 to e_MaxJoys-1 do
334 begin
335 k := JOYK_BEG + j * e_MaxJoyBtns;
336 // buttons
337 for i := 0 to e_MaxJoyBtns-1 do
338 e_KeyNames[k + i] := Format('JOY%d B%d', [j, i]);
339 k := JOYA_BEG + j * e_MaxJoyAxes * 2;
340 // axes
341 for i := 0 to e_MaxJoyAxes-1 do
342 begin
343 e_KeyNames[k + i*2 ] := Format('JOY%d A%d+', [j, i]);
344 e_KeyNames[k + i*2 + 1] := Format('JOY%d A%d-', [j, i]);
345 end;
346 k := JOYH_BEG + j * e_MaxJoyHats * 4;
347 // hats
348 for i := 0 to e_MaxJoyHats-1 do
349 begin
350 e_KeyNames[k + i*4 ] := Format('JOY%d D%dL', [j, i]);
351 e_KeyNames[k + i*4 + 1] := Format('JOY%d D%dU', [j, i]);
352 e_KeyNames[k + i*4 + 2] := Format('JOY%d D%dR', [j, i]);
353 e_KeyNames[k + i*4 + 3] := Format('JOY%d D%dD', [j, i]);
354 end;
355 end;
357 // vitrual keys
358 for i := 0 to e_MaxVirtKeys-1 do
359 e_KeyNames[VIRT_BEG + i] := 'VIRTUAL ' + IntToStr(i);
360 end;
362 function e_InitInput(): Boolean;
363 begin
364 Result := False;
366 e_JoysticksAvailable := OpenJoysticks();
367 e_EnableInput := True;
368 GenerateKeyNames();
370 Result := True;
371 end;
373 procedure e_ReleaseInput();
374 begin
375 ReleaseJoysticks();
376 e_JoysticksAvailable := 0;
377 end;
379 procedure e_ClearInputBuffer();
380 var
381 i, j, d: Integer;
382 begin
383 for i := Low(KeyBuffer) to High(KeyBuffer) do
384 KeyBuffer[i] := False;
385 if (Joysticks = nil) or (e_JoysticksAvailable = 0) then
386 for i := Low(Joysticks) to High(Joysticks) do
387 begin
388 for j := Low(Joysticks[i].ButtBuf) to High(Joysticks[i].ButtBuf) do
389 Joysticks[i].ButtBuf[j] := False;
390 for j := Low(Joysticks[i].AxisBuf) to High(Joysticks[i].AxisBuf) do
391 Joysticks[i].AxisBuf[j] := 0;
392 for j := Low(Joysticks[i].HatBuf) to High(Joysticks[i].HatBuf) do
393 for d := Low(Joysticks[i].HatBuf[j]) to High(Joysticks[i].HatBuf[j]) do
394 Joysticks[i].HatBuf[j, d] := False;
395 end;
396 for i := Low(VirtBuffer) to High(VirtBuffer) do
397 VirtBuffer[i] := False;
398 end;
401 function e_PollInput(): Boolean;
402 var
403 kb, js: Boolean;
404 begin
405 kb := PollKeyboard();
406 js := e_PollJoysticks();
408 Result := kb or js;
409 end;
412 function e_KeyPressed(Key: Word): Boolean;
413 var
414 joyi, dir: Integer;
415 begin
416 Result := False;
417 if (Key = IK_INVALID) or (Key = 0) then Exit;
419 if (Key < KBRD_END) then
420 begin // Keyboard buttons/keys
421 Result := KeyBuffer[Key];
422 end
424 else if (Key >= JOYK_BEG) and (Key < JOYK_END) then
425 begin // Joystick buttons
426 JoyI := (Key - JOYK_BEG) div e_MaxJoyBtns;
427 if JoyI >= e_JoysticksAvailable then
428 Result := False
429 else
430 begin
431 Key := (Key - JOYK_BEG) mod e_MaxJoyBtns;
432 Result := Joysticks[JoyI].ButtBuf[Key];
433 end;
434 end
436 else if (Key >= JOYA_BEG) and (Key < JOYA_END) then
437 begin // Joystick axes
438 JoyI := (Key - JOYA_BEG) div (e_MaxJoyAxes*2);
439 if JoyI >= e_JoysticksAvailable then
440 Result := False
441 else
442 begin
443 Key := (Key - JOYA_BEG) mod (e_MaxJoyAxes*2);
444 dir := Key mod 2;
445 if dir = AX_MINUS then
446 Result := Joysticks[JoyI].AxisBuf[Key div 2] <
447 Joysticks[JoyI].AxisZero[Key div 2] - e_JoystickDeadzones[JoyI]
448 else
449 Result := Joysticks[JoyI].AxisBuf[Key div 2] >
450 Joysticks[JoyI].AxisZero[Key div 2] + e_JoystickDeadzones[JoyI]
451 end;
452 end
454 else if (Key >= JOYH_BEG) and (Key < JOYH_END) then
455 begin // Joystick hats
456 JoyI := (Key - JOYH_BEG) div (e_MaxJoyHats*4);
457 if JoyI >= e_JoysticksAvailable then
458 Result := False
459 else
460 begin
461 Key := (Key - JOYH_BEG) mod (e_MaxJoyHats*4);
462 dir := Key mod 4;
463 Result := Joysticks[JoyI].HatBuf[Key div 4, dir];
464 end;
465 end
467 else if (Key >= VIRT_BEG) and (Key < VIRT_END) then
468 Result := VirtBuffer[Key - VIRT_BEG]
469 end;
471 procedure e_SetKeyState(key: Word; state: Integer);
472 var
473 JoyI, dir: Integer;
474 begin
475 if (Key = IK_INVALID) or (Key = 0) then Exit;
477 if (Key < KBRD_END) then
478 begin // Keyboard buttons/keys
479 keyBuffer[key] := (state <> 0);
480 end
482 else if (Key >= JOYK_BEG) and (Key < JOYK_END) then
483 begin // Joystick buttons
484 JoyI := (Key - JOYK_BEG) div e_MaxJoyBtns;
485 if JoyI >= e_JoysticksAvailable then
486 Exit
487 else
488 begin
489 Key := (Key - JOYK_BEG) mod e_MaxJoyBtns;
490 Joysticks[JoyI].ButtBuf[Key] := (state <> 0);
491 end;
492 end
494 else if (Key >= JOYA_BEG) and (Key < JOYA_END) then
495 begin // Joystick axes
496 JoyI := (Key - JOYA_BEG) div (e_MaxJoyAxes*2);
497 if JoyI >= e_JoysticksAvailable then
498 Exit
499 else
500 begin
501 Key := (Key - JOYA_BEG) mod (e_MaxJoyAxes*2);
502 Joysticks[JoyI].AxisBuf[Key div 2] := state;
503 end;
504 end
506 else if (Key >= JOYH_BEG) and (Key < JOYH_END) then
507 begin // Joystick hats
508 JoyI := (Key - JOYH_BEG) div (e_MaxJoyHats*4);
509 if JoyI >= e_JoysticksAvailable then
510 Exit
511 else
512 begin
513 Key := (Key - JOYH_BEG) mod (e_MaxJoyHats*4);
514 dir := Key mod 4;
515 Joysticks[JoyI].HatBuf[Key div 4, dir] := (state <> 0);
516 end;
517 end
519 else if (Key >= VIRT_BEG) and (Key < VIRT_END) then
520 VirtBuffer[Key - VIRT_BEG] := (state <> 0)
521 end;
523 function e_AnyKeyPressed(): Boolean;
524 var
525 k: Word;
526 begin
527 Result := False;
529 for k := 1 to e_MaxInputKeys do
530 if e_KeyPressed(k) then
531 begin
532 Result := True;
533 Break;
534 end;
535 end;
537 function e_GetFirstKeyPressed(): Word;
538 var
539 k: Word;
540 begin
541 Result := IK_INVALID;
543 for k := 1 to e_MaxInputKeys do
544 if e_KeyPressed(k) then
545 begin
546 Result := k;
547 Break;
548 end;
549 end;
551 ////////////////////////////////////////////////////////////////////////////////
553 function e_JoystickStateToString(mode: Integer): String;
554 begin
555 Result := '';
556 end;
558 function e_JoyByHandle(handle: Word): Integer;
559 var
560 i: Integer;
561 begin
562 Result := -1;
563 if Joysticks = nil then Exit;
564 for i := Low(Joysticks) to High(Joysticks) do
565 if Joysticks[i].ID = handle then
566 begin
567 Result := i;
568 Exit;
569 end;
570 end;
572 function e_JoyButtonToKey(id: Word; btn: Byte): Word;
573 begin
574 Result := 0;
575 if id >= e_MaxJoys then Exit;
576 Result := JOYK_BEG + id*e_MaxJoyBtns + btn;
577 end;
579 function e_JoyAxisToKey(id: Word; ax: Byte; dir: Byte): Word;
580 begin
581 Result := 0;
582 if id >= e_MaxJoys then Exit;
583 Result := JOYA_BEG + id*e_MaxJoyAxes*2 + ax*2 + dir;
584 end;
586 function e_JoyHatToKey(id: Word; hat: Byte; dir: Byte): Word;
587 begin
588 Result := 0;
589 if id >= e_MaxJoys then Exit;
590 Result := JOYH_BEG + id*e_MaxJoyHats*4 + hat*4 + dir;
591 end;
593 end.