DEADSOFTWARE

0fcad1aec0afa596a05959c9388e5de33f7ff502
[d2df-sdl.git] / src / game / sdl / g_system.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, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_system;
18 interface
20 uses Utils;
22 (* --- Utils --- *)
23 function sys_GetTicks (): Int64;
24 procedure sys_Delay (ms: Integer);
26 (* --- Graphics --- *)
27 function sys_GetDisplayModes (bpp: Integer): SSArray;
28 function sys_SetDisplayMode (w, h, bpp: Integer; fullscreen, maximized: Boolean): Boolean;
29 procedure sys_EnableVSync (yes: Boolean);
30 procedure sys_Repaint;
32 (* --- Input --- *)
33 function sys_HandleInput (): Boolean;
34 procedure sys_RequestQuit;
36 (* --- Init --- *)
37 procedure sys_Init;
38 procedure sys_Final;
40 implementation
42 uses
43 {$IF DEFINED(DARWIN) OR DEFINED(MACOS)}
44 Keyboards, Events,
45 {$IFDEF CPU64}
46 {$linkframework Carbon}
47 {$ENDIF}
48 {$ENDIF}
49 SysUtils, SDL, Math,
50 {$INCLUDE ../nogl/noGLuses.inc}
51 e_log, e_graphics, e_input, e_sound,
52 g_options, g_window, g_console, g_game, g_menu, g_gui, g_main, g_basic;
54 const
55 GameTitle = 'Doom 2D: Forever (SDL 1.2, %s, %s)';
57 var
58 userResize: Boolean;
59 modeResize: Integer;
60 useScancodes: Boolean;
61 screen: PSDL_Surface;
62 JoystickHandle: array [0..e_MaxJoys - 1] of PSDL_Joystick;
63 JoystickHatState: array [0..e_MaxJoys - 1, 0..e_MaxJoyHats - 1, HAT_LEFT..HAT_DOWN] of Boolean;
64 JoystickZeroAxes: array [0..e_MaxJoys - 1, 0..e_MaxJoyAxes - 1] of Integer;
66 (* --------- Utils --------- *)
68 function sys_GetTicks (): Int64;
69 begin
70 result := SDL_GetTicks()
71 end;
73 procedure sys_Delay (ms: Integer);
74 begin
75 SDL_Delay(ms)
76 end;
78 (* --------- Graphics --------- *)
80 function LoadGL: Boolean;
81 begin
82 result := true;
83 {$IFDEF NOGL_INIT}
84 nogl_Init;
85 if glRenderToFBO and (not nogl_ExtensionSupported('GL_OES_framebuffer_object')) then
86 {$ELSE}
87 if glRenderToFBO and (not Load_GL_ARB_framebuffer_object) then
88 {$ENDIF}
89 begin
90 e_LogWriteln('GL: framebuffer objects not supported; disabling FBO rendering');
91 glRenderToFBO := false;
92 end;
93 end;
95 procedure FreeGL;
96 begin
97 {$IFDEF NOGL_INIT}
98 nogl_Quit();
99 {$ENDIF}
100 end;
102 procedure UpdateSize (w, h: Integer);
103 begin
104 gWinSizeX := w;
105 gWinSizeY := h;
106 gRC_Width := w;
107 gRC_Height := h;
108 if glRenderToFBO then
109 begin
110 // store real window size in gWinSize, downscale resolution now
111 w := round(w / r_pixel_scale);
112 h := round(h / r_pixel_scale);
113 if not e_ResizeFramebuffer(w, h) then
114 begin
115 e_LogWriteln('GL: could not create framebuffer, falling back to --no-fbo');
116 glRenderToFBO := False;
117 w := gWinSizeX;
118 h := gWinSizeY;
119 end;
120 end;
121 gScreenWidth := w;
122 gScreenHeight := h;
123 {$IFDEF ENABLE_HOLMES}
124 fuiScrWdt := w;
125 fuiScrHgt := h;
126 {$ENDIF}
127 e_ResizeWindow(w, h);
128 e_InitGL;
129 g_Game_SetupScreenSize;
130 g_Menu_Reset;
131 g_Game_ClearLoading;
132 end;
134 function GetDriver (): AnsiString;
135 var buf: array [0..31] of AnsiChar;
136 begin
137 buf := '';
138 SDL_VideoDriverName(buf, Length(buf));
139 result := AnsiString(buf);
140 end;
142 function GetTitle (): AnsiString;
143 var info: AnsiString;
144 begin
145 info := g_GetBuildHash(false);
146 if info = 'custom build' then
147 info := info + ' by ' + g_GetBuilderName() + ' ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME;
148 result := Format(GameTitle, [GetDriver(), info]);
149 end;
151 function InitWindow (w, h, bpp: Integer; fullScreen: Boolean): Boolean;
152 var flags: Uint32; title: AnsiString;
153 begin
154 e_LogWritefln('InitWindow %s %s %s %s', [w, h, bpp, fullScreen]);
155 result := false;
156 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
157 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
158 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
159 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
160 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
161 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); // lights; it is enough to have 1-bit stencil buffer for lighting, but...
162 flags := SDL_OPENGL;
163 if fullScreen then flags := flags or SDL_FULLSCREEN;
164 if userResize then flags := flags or SDL_VIDEORESIZE;
165 if (screen = nil) or (SDL_VideoModeOk(w, h, bpp, flags) <> 0) then
166 begin
167 SDL_FreeSurface(screen);
168 screen := SDL_SetVideoMode(w, h, bpp, flags);
169 if screen <> nil then
170 begin
171 if not LoadGL then
172 begin
173 e_LogWriteln('GL: unable to load OpenGL functions', TMsgType.Fatal);
174 exit;
175 end;
176 title := GetTitle();
177 SDL_WM_SetCaption(PChar(title), nil);
178 gFullScreen := fullscreen;
179 gRC_FullScreen := fullscreen;
180 UpdateSize(w, h);
181 result := True
182 end
183 end
184 else
185 begin
186 e_LogWritefln('SDL: video mode not supported', [])
187 end
188 end;
190 procedure sys_Repaint;
191 begin
192 SDL_GL_SwapBuffers
193 end;
195 procedure sys_EnableVSync (yes: Boolean);
196 begin
197 (* ??? *)
198 end;
200 function sys_GetDisplayModes (bpp: Integer): SSArray;
201 var m: PPSDL_Rect; f: TSDL_PixelFormat; i, count: Integer;
202 begin
203 result := nil;
204 FillChar(f, sizeof(f), 0);
205 f.palette := nil;
206 f.BitsPerPixel := bpp;
207 f.BytesPerPixel := (bpp + 7) div 8;
208 m := SDL_ListModes(@f, SDL_OPENGL or SDL_FULLSCREEN);
209 if (m <> NIL) and (UIntPtr(m) <> UIntPtr(-1)) then
210 begin
211 count := 0;
212 while m[count] <> nil do inc(count);
213 SetLength(result, count);
214 for i := 0 to count - 1 do
215 result[i] := IntToStr(m[i].w) + 'x' + IntToStr(m[i].h);
216 end
217 end;
219 function sys_SetDisplayMode (w, h, bpp: Integer; fullscreen, maximized: Boolean): Boolean;
220 begin
221 result := InitWindow(w, h, bpp, fullscreen)
222 end;
224 (* --------- Joystick --------- *)
226 procedure HandleJoyButton (var ev: TSDL_JoyButtonEvent);
227 var down: Boolean; key: Integer;
228 begin
229 if (ev.which < e_MaxJoys) and (ev.button < e_MaxJoyBtns) then
230 begin
231 key := e_JoyButtonToKey(ev.which, ev.button);
232 down := ev.type_ = SDL_JOYBUTTONDOWN;
233 if g_dbg_input then
234 e_LogWritefln('Input Debug: jbutton, joy=%s, button=%s, keycode=%s, press=%s', [ev.which, ev.button, key, down]);
235 e_KeyUpDown(key, down);
236 g_Console_ProcessBind(key, down)
237 end
238 else
239 begin
240 if g_dbg_input then
241 begin
242 down := ev.type_ = SDL_JOYBUTTONDOWN;
243 e_LogWritefln('Input Debug: NOT IN RANGE! jbutton, joy=%s, button=%s, press=%s', [ev.which, ev.button, down])
244 end
245 end
246 end;
248 procedure HandleJoyAxis (var ev: TSDL_JoyAxisEvent);
249 var key, minuskey: Integer;
250 begin
251 if (ev.which < e_MaxJoys) and (ev.axis < e_MaxJoyAxes) then
252 begin
253 key := e_JoyAxisToKey(ev.which, ev.axis, AX_PLUS);
254 minuskey := e_JoyAxisToKey(ev.which, ev.axis, AX_MINUS);
256 if g_dbg_input then
257 e_LogWritefln('Input Debug: jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s', [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]]);
259 if ev.value < JoystickZeroAxes[ev.which, ev.axis] - e_JoystickDeadzones[ev.which] then
260 begin
261 if (e_KeyPressed(key)) then
262 begin
263 e_KeyUpDown(key, False);
264 g_Console_ProcessBind(key, False)
265 end;
266 e_KeyUpDown(minuskey, True);
267 g_Console_ProcessBind(minuskey, True)
268 end
269 else if ev.value > JoystickZeroAxes[ev.which, ev.axis] + e_JoystickDeadzones[ev.which] then
270 begin
271 if (e_KeyPressed(minuskey)) then
272 begin
273 e_KeyUpDown(minuskey, False);
274 g_Console_ProcessBind(minuskey, False)
275 end;
276 e_KeyUpDown(key, True);
277 g_Console_ProcessBind(key, True)
278 end
279 else
280 begin
281 if (e_KeyPressed(minuskey)) then
282 begin
283 e_KeyUpDown(minuskey, False);
284 g_Console_ProcessBind(minuskey, False)
285 end;
286 if (e_KeyPressed(key)) then
287 begin
288 e_KeyUpDown(key, False);
289 g_Console_ProcessBind(key, False)
290 end
291 end
292 end
293 else
294 begin
295 if g_dbg_input then
296 e_LogWritefln('Input Debug: NOT IN RANGE! jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s', [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]])
297 end
298 end;
300 procedure HandleJoyHat (var ev: TSDL_JoyHatEvent);
301 var
302 down: Boolean;
303 i, key: Integer;
304 hat: array [HAT_LEFT..HAT_DOWN] of Boolean;
305 begin
306 if (ev.which < e_MaxJoys) and (ev.hat < e_MaxJoyHats) then
307 begin
308 if g_dbg_input then
309 e_LogWritefln('Input Debug: jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value]);
310 hat[HAT_UP] := LongBool(ev.value and SDL_HAT_UP);
311 hat[HAT_DOWN] := LongBool(ev.value and SDL_HAT_DOWN);
312 hat[HAT_LEFT] := LongBool(ev.value and SDL_HAT_LEFT);
313 hat[HAT_RIGHT] := LongBool(ev.value and SDL_HAT_RIGHT);
314 for i := HAT_LEFT to HAT_DOWN do
315 begin
316 if JoystickHatState[ev.which, ev.hat, i] <> hat[i] then
317 begin
318 down := hat[i];
319 key := e_JoyHatToKey(ev.which, ev.hat, i);
320 e_KeyUpDown(key, down);
321 g_Console_ProcessBind(key, down)
322 end
323 end;
324 JoystickHatState[ev.which, ev.hat] := hat
325 end
326 else
327 begin
328 if g_dbg_input then
329 e_LogWritefln('Input Debug: NOT IN RANGE! jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value])
330 end
331 end;
333 procedure AddJoystick (which: Integer);
334 var i: Integer;
335 begin
336 assert(which < e_MaxJoys);
337 JoystickHandle[which] := SDL_JoystickOpen(which);
338 if JoystickHandle[which] <> nil then
339 begin
340 e_LogWritefln('Added Joystick %s', [which]);
341 e_JoystickAvailable[which] := True;
342 for i := 0 to Min(SDL_JoystickNumAxes(JoystickHandle[which]), e_MaxJoyAxes) - 1 do
343 JoystickZeroAxes[which, i] := SDL_JoystickGetAxis(JoystickHandle[which], i)
344 end
345 else
346 begin
347 e_LogWritefln('Failed to open Joystick %s', [which])
348 end
349 end;
351 procedure RemoveJoystick (which: Integer);
352 begin
353 assert(which < e_MaxJoys);
354 e_LogWritefln('Remove Joystick %s', [which]);
355 e_JoystickAvailable[which] := False;
356 if JoystickHandle[which] <> nil then
357 SDL_JoystickClose(JoystickHandle[which]);
358 JoystickHandle[which] := nil
359 end;
361 (* --------- Input --------- *)
363 {$IF DEFINED(DARWIN) OR DEFINED(MACOS)}
364 var
365 IsMacPlatform: Boolean;
367 function IsMacModSym (sym: Integer): Boolean;
368 begin
369 case sym of
370 SDLK_NUMLOCK,
371 SDLK_CAPSLOCK,
372 SDLK_RCTRL, SDLK_LCTRL,
373 SDLK_RSHIFT, SDLK_LSHIFT,
374 SDLK_RALT, SDLK_LALT,
375 SDLK_RSUPER, SDLK_LSUPER,
376 SDLK_RMETA, SDLK_LMETA:
377 result := true;
378 otherwise
379 result := false;
380 end;
381 end;
383 function Mac2Stub (scancode: Integer): Integer;
384 begin
385 (* SDL 2 swap tilda/grave and paragraph/plus-minus buttons on ISO keyboards *)
386 if ((scancode = 10) or (scancode = 50)) and (KBGetLayoutType(LMGetKbdType()) = kKeyboardISO) then
387 scancode := 60 - scancode;
388 case scancode of
389 0: result := IK_A;
390 1: result := IK_S;
391 2: result := IK_D;
392 3: result := IK_F;
393 4: result := IK_H;
394 5: result := IK_G;
395 6: result := IK_Z;
396 7: result := IK_X;
397 8: result := IK_C;
398 9: result := IK_V;
399 10: result := IK_NONUSBACKSLASH;
400 11: result := IK_B;
401 12: result := IK_Q;
402 13: result := IK_W;
403 14: result := IK_E;
404 15: result := IK_R;
405 16: result := IK_Y;
406 17: result := IK_T;
407 18: result := IK_1;
408 19: result := IK_2;
409 20: result := IK_3;
410 21: result := IK_4;
411 22: result := IK_6;
412 23: result := IK_5;
413 24: result := IK_EQUALS;
414 25: result := IK_9;
415 26: result := IK_7;
416 27: result := IK_MINUS;
417 28: result := IK_8;
418 29: result := IK_0;
419 30: result := {IK_RIGHTBRACKET} IK_RBRACKET;
420 31: result := IK_O;
421 32: result := IK_U;
422 33: result := {IK_LEFTBRACKET} IK_LBRACKET;
423 34: result := IK_I;
424 35: result := IK_P;
425 36: result := IK_RETURN;
426 37: result := IK_L;
427 38: result := IK_J;
428 39: result := {IK_APOSTROPHE} IK_QUOTE;
429 40: result := IK_K;
430 41: result := IK_SEMICOLON;
431 42: result := IK_BACKSLASH;
432 43: result := IK_COMMA;
433 44: result := IK_SLASH;
434 45: result := IK_N;
435 46: result := IK_M;
436 47: result := {IK_PERIOD} IK_DOT;
437 48: result := IK_TAB;
438 49: result := IK_SPACE;
439 50: result := IK_GRAVE;
440 51: result := IK_BACKSPACE;
441 52: result := {IK_KP_ENTER} IK_KPRETURN;
442 53: result := IK_ESCAPE;
443 54: result := {IK_RGUI} IK_RWIN;
444 55: result := {IK_LGUI} IK_WIN;
445 56: result := {IK_LSHIFT} IK_SHIFT;
446 57: result := IK_CAPSLOCK;
447 58: result := {IK_LALT} IK_ALT;
448 59: result := {IK_LCTRL} IK_CTRL;
449 60: result := IK_RSHIFT;
450 61: result := IK_RALT;
451 62: result := IK_RCTRL;
452 63: result := {IK_RGUI} IK_RWIN;
453 {64: result := IK_F17;}
454 65: result := {IK_KP_PERIOD} IK_KPDOT;
455 66: result := IK_INVALID; (* unused? *)
456 67: result := {IK_KP_MULTIPLY} IK_KPMULTIPLE;
457 68: result := IK_INVALID; (* unused? *)
458 69: result := {IK_KP_PLUS} IK_KPPLUS;
459 70: result := IK_INVALID; (* unused? *)
460 71: result := {IK_NUMLOCKCLEAR} IK_NUMLOCK;
461 {72: result := IK_VOLUMEUP;}
462 {73: result := IK_VOLUMEDOWN;}
463 {74: result := IK_MUTE;}
464 75: result := {IK_KP_DIVIDE} IK_KPDIVIDE;
465 76: result := {IK_KP_ENTER} IK_KPRETURN;
466 77: result := IK_INVALID; (* unused? *)
467 78: result := {IK_KP_MINUS} IK_KPMINUS;
468 {79: result := IK_F18;}
469 {80: result := IK_F19;}
470 {81: result := IK_KP_EQUALS;}
471 82: result := {IK_KP_0} IK_KPINSERT;
472 83: result := {IK_KP_1} IK_KPEND;
473 84: result := {IK_KP_2} IK_KPDOWN;
474 85: result := {IK_KP_3} IK_KPPAGEDN;
475 86: result := {IK_KP_4} IK_KPLEFT;
476 87: result := {IK_KP_5} IK_KP5;
477 88: result := {IK_KP_6} IK_KPRIGHT;
478 89: result := {IK_KP_7} IK_KPHOME;
479 90: result := IK_INVALID; (* unused? *)
480 91: result := {IK_KP_8} IK_KPUP;
481 92: result := {IK_KP_9} IK_KPPAGEUP;
482 {93: result := IK_INTERNATIONAL3;}
483 {94: result := IK_INTERNATIONAL1;}
484 {95: result := IK_KP_COMMA;}
485 96: result := IK_F5;
486 97: result := IK_F6;
487 98: result := IK_F7;
488 99: result := IK_F3;
489 100: result := IK_F8;
490 101: result := IK_F9;
491 {102: result := IK_LANG2;}
492 103: result := IK_F11;
493 {104: result := IK_LANG1;}
494 105: result := {IK_PRINTSCREEN} IK_PRINTSCR;
495 {106: result := IK_F16;}
496 107: result := IK_SCROLLLOCK;
497 108: result := IK_INVALID; (* unused? *)
498 109: result := IK_F10;
499 {110: result := IK_APPLICATION;}
500 111: result := IK_F12;
501 112: result := IK_INVALID; (* unused? *)
502 113: result := IK_PAUSE;
503 114: result := IK_INSERT;
504 115: result := IK_HOME;
505 116: result := IK_PAGEUP;
506 117: result := IK_DELETE;
507 118: result := IK_F4;
508 119: result := IK_END;
509 120: result := IK_F2;
510 121: result := {IK_PAGEDOWN} IK_PAGEDN;
511 122: result := IK_F1;
512 123: result := IK_LEFT;
513 124: result := IK_RIGHT;
514 125: result := IK_DOWN;
515 126: result := IK_UP;
516 {127: result := IK_POWER;}
517 otherwise result := IK_INVALID
518 end
519 end;
520 {$ENDIF}
522 function Key2Stub (key: Integer): Integer;
523 var x: Integer;
524 begin
525 case key of
526 SDLK_ESCAPE: x := IK_ESCAPE;
527 SDLK_RETURN: x := IK_RETURN;
528 SDLK_KP_ENTER: x := IK_KPRETURN;
529 SDLK_KP0: x := IK_KPINSERT;
530 SDLK_UP: x := IK_UP;
531 SDLK_KP8: x := IK_KPUP;
532 SDLK_DOWN: x := IK_DOWN;
533 SDLK_KP2: x := IK_KPDOWN;
534 SDLK_LEFT: x := IK_LEFT;
535 SDLK_KP4: x := IK_KPLEFT;
536 SDLK_RIGHT: x := IK_RIGHT;
537 SDLK_KP6: x := IK_KPRIGHT;
538 SDLK_DELETE: x := IK_DELETE;
539 SDLK_HOME: x := IK_HOME;
540 SDLK_KP7: x := IK_KPHOME;
541 SDLK_INSERT: x := IK_INSERT;
542 SDLK_SPACE: x := IK_SPACE;
543 SDLK_LSHIFT: x := IK_SHIFT;
544 SDLK_LALT: x := IK_ALT;
545 SDLK_TAB: x := IK_TAB;
546 SDLK_PAGEUP: x := IK_PAGEUP;
547 SDLK_KP9: x := IK_KPPAGEUP;
548 SDLK_PAGEDOWN: x := IK_PAGEDN;
549 SDLK_KP3: x := IK_KPPAGEDN;
550 SDLK_KP5: x := IK_KP5;
551 SDLK_NUMLOCK: x := IK_NUMLOCK;
552 SDLK_KP_DIVIDE: x := IK_KPDIVIDE;
553 SDLK_KP_MULTIPLY: x := IK_KPMULTIPLE;
554 SDLK_KP_MINUS: x := IK_KPMINUS;
555 SDLK_KP_PLUS: x := IK_KPPLUS;
556 SDLK_KP_PERIOD: x := IK_KPDOT;
557 SDLK_CAPSLOCK: x := IK_CAPSLOCK;
558 SDLK_RSHIFT: x := IK_RSHIFT;
559 SDLK_LCTRL: x := IK_CTRL;
560 SDLK_RCTRL: x := IK_RCTRL;
561 SDLK_RALT: x := IK_RALT;
562 SDLK_LSUPER: x := IK_WIN;
563 SDLK_RSUPER: x := IK_RWIN;
564 SDLK_MENU: x := IK_MENU;
565 SDLK_PRINT: x := IK_PRINTSCR;
566 SDLK_SCROLLOCK: x := IK_SCROLLLOCK;
567 SDLK_LEFTBRACKET: x := IK_LBRACKET;
568 SDLK_RIGHTBRACKET: x := IK_RBRACKET;
569 SDLK_SEMICOLON: x := IK_SEMICOLON;
570 SDLK_QUOTE: x := IK_QUOTE;
571 SDLK_BACKSLASH: x := IK_BACKSLASH;
572 SDLK_SLASH: x := IK_SLASH;
573 SDLK_COMMA: x := IK_COMMA;
574 SDLK_PERIOD: x := IK_DOT;
575 SDLK_EQUALS: x := IK_EQUALS;
576 SDLK_0: x := IK_0;
577 SDLK_1: x := IK_1;
578 SDLK_2: x := IK_2;
579 SDLK_3: x := IK_3;
580 SDLK_4: x := IK_4;
581 SDLK_5: x := IK_5;
582 SDLK_6: x := IK_6;
583 SDLK_7: x := IK_7;
584 SDLK_8: x := IK_8;
585 SDLK_9: x := IK_9;
586 SDLK_F1: x := IK_F1;
587 SDLK_F2: x := IK_F2;
588 SDLK_F3: x := IK_F3;
589 SDLK_F4: x := IK_F4;
590 SDLK_F5: x := IK_F5;
591 SDLK_F6: x := IK_F6;
592 SDLK_F7: x := IK_F7;
593 SDLK_F8: x := IK_F8;
594 SDLK_F9: x := IK_F9;
595 SDLK_F10: x := IK_F10;
596 SDLK_F11: x := IK_F11;
597 SDLK_F12: x := IK_F12;
598 SDLK_END: x := IK_END;
599 SDLK_KP1: x := IK_KPEND;
600 SDLK_BACKSPACE: x := IK_BACKSPACE;
601 SDLK_BACKQUOTE: x := IK_BACKQUOTE;
602 SDLK_PAUSE: x := IK_PAUSE;
603 SDLK_A..SDLK_Z: x := IK_A + (key - SDLK_A);
604 SDLK_MINUS: x := IK_MINUS;
605 else
606 x := IK_INVALID
607 end;
608 result := x
609 end;
611 procedure HandleKeyboard (var ev: TSDL_KeyboardEvent);
612 var down, repeated: Boolean; key: Integer; ch: Char;
613 begin
614 key := IK_INVALID;
615 if useScancodes then
616 begin
617 {$IF DEFINED(DARWIN) OR DEFINED(MACOS)}
618 if IsMacPlatform then
619 if IsMacModSym(ev.keysym.sym) = false then
620 key := Mac2Stub(ev.keysym.scancode);
621 {$ENDIF}
622 end;
623 if key = IK_INVALID then
624 key := Key2Stub(ev.keysym.sym);
625 down := (ev.type_ = SDL_KEYDOWN);
626 repeated := down and e_KeyPressed(key);
627 ch := wchar2win(WideChar(ev.keysym.unicode));
628 if g_dbg_input then
629 e_LogWritefln('Input Debug: keysym, scancode=%s, down=%s, sym=%s, state=%s, unicode=%s, stubsym=%s, cp1251=%s', [ev.keysym.scancode, down, ev.keysym.sym, ev.state, ev.keysym.unicode, key, Ord(ch)]);
630 if not repeated then
631 begin
632 e_KeyUpDown(key, down);
633 g_Console_ProcessBind(key, down);
634 end
635 else
636 begin
637 g_Console_ProcessBindRepeat(key)
638 end;
639 if down and IsValid1251(ev.keysym.unicode) and IsPrintable1251(ch) then
640 CharPress(ch)
641 end;
643 procedure HandleResize (var ev: TSDL_ResizeEvent);
644 begin
645 if g_dbg_input then
646 e_LogWritefln('Input Debug: SDL_VIDEORESIZE %s %s', [ev.w, ev.h]);
647 if modeResize = 1 then
648 UpdateSize(ev.w, ev.h)
649 else if modeResize > 1 then
650 InitWindow(ev.w, ev.h, gBPP, gFullscreen)
651 end;
653 function sys_HandleInput (): Boolean;
654 var ev: TSDL_Event;
655 begin
656 result := false;
657 while SDL_PollEvent(@ev) <> 0 do
658 begin
659 case ev.type_ of
660 SDL_QUITEV: result := true;
661 SDL_VIDEORESIZE: HandleResize(ev.resize);
662 SDL_KEYUP, SDL_KEYDOWN: HandleKeyboard(ev.key);
663 SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP: HandleJoyButton(ev.jbutton);
664 SDL_JOYAXISMOTION: HandleJoyAxis(ev.jaxis);
665 SDL_JOYHATMOTION: HandleJoyHat(ev.jhat);
666 SDL_VIDEOEXPOSE: sys_Repaint;
667 SDL_ACTIVEEVENT: e_MuteChannels((ev.active.gain = 0) and gMuteWhenInactive);
668 end
669 end
670 end;
672 procedure sys_RequestQuit;
673 var ev: TSDL_Event;
674 begin
675 ev.quit.type_ := SDL_QUITEV;
676 SDL_PushEvent(@ev)
677 end;
679 (* --------- Init --------- *)
681 procedure sys_Init;
682 var flags: Uint32; i: Integer; name: AnsiString;
683 begin
684 e_WriteLog('Init SDL', TMsgType.Notify);
685 flags := SDL_INIT_VIDEO or SDL_INIT_AUDIO or
686 SDL_INIT_TIMER or SDL_INIT_JOYSTICK
687 (*or SDL_INIT_NOPARACHUTE*);
688 if SDL_Init(flags) <> 0 then
689 raise Exception.Create('SDL: Init failed: ' + SDL_GetError);
690 name := GetDriver();
691 e_LogWritefln('SDL: Video Driver "%s"', [name]);
692 {$IF DEFINED(DARWIN) OR DEFINED(MACOS)}
693 IsMacPlatform := (name = 'Quartz') or (name = 'toolbox') or (name = 'DSp');
694 {$ENDIF}
695 SDL_EnableUNICODE(1);
696 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
697 for i := 0 to e_MaxJoys - 1 do
698 AddJoystick(i)
699 end;
701 procedure sys_Final;
702 var i: Integer;
703 begin
704 e_WriteLog('Releasing SDL', TMsgType.Notify);
705 for i := 0 to e_MaxJoys - 1 do
706 RemoveJoystick(i);
707 if screen <> nil then
708 begin
709 FreeGL;
710 SDL_FreeSurface(screen)
711 end;
712 SDL_Quit
713 end;
715 initialization
716 (* window resize are broken both on linux and osx, so disabled by default *)
717 conRegVar('sdl_allow_resize', @userResize, 'allow to resize window by user', 'allow to resize window by user');
718 conRegVar('sdl_resize_action', @modeResize, 'set window resize mode (0: ignore, 1: change, 2: reset)', '');
719 conRegVar('sdl_use_scancodes', @useScancodes, 'use platform-specific scancodes when possible', '');
720 userResize := false;
721 modeResize := 0;
722 useScancodes := true;
723 end.