fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/drivers/video/sdl2.c *
7 * Created: 2015-06-15 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2015-2025 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <config.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27
28#include <SDL.h>
29#include <SDL_syswm.h>
30
31#if !(defined(PCE_HOST_WINDOWS) || defined(PCE_HOST_MACOS))
32#include <X11/Xlib.h>
33#include <X11/Xutil.h>
34#endif
35
36#include <drivers/video/terminal.h>
37#include <drivers/video/keys.h>
38#include <drivers/video/sdl2.h>
39
40
41static sdl2_keymap_t keymap[] = {
42 { SDL_SCANCODE_ESCAPE, PCE_KEY_ESC },
43 { SDL_SCANCODE_F1, PCE_KEY_F1 },
44 { SDL_SCANCODE_F2, PCE_KEY_F2 },
45 { SDL_SCANCODE_F3, PCE_KEY_F3 },
46 { SDL_SCANCODE_F4, PCE_KEY_F4 },
47 { SDL_SCANCODE_F5, PCE_KEY_F5 },
48 { SDL_SCANCODE_F6, PCE_KEY_F6 },
49 { SDL_SCANCODE_F7, PCE_KEY_F7 },
50 { SDL_SCANCODE_F8, PCE_KEY_F8 },
51 { SDL_SCANCODE_F9, PCE_KEY_F9 },
52 { SDL_SCANCODE_F10, PCE_KEY_F10 },
53 { SDL_SCANCODE_F11, PCE_KEY_F11 },
54 { SDL_SCANCODE_F12, PCE_KEY_F12 },
55
56 { SDL_SCANCODE_PRINTSCREEN, PCE_KEY_PRTSCN },
57 { SDL_SCANCODE_SCROLLLOCK, PCE_KEY_SCRLK },
58 { SDL_SCANCODE_PAUSE, PCE_KEY_PAUSE },
59
60 { SDL_SCANCODE_GRAVE, PCE_KEY_BACKQUOTE },
61 { SDL_SCANCODE_1, PCE_KEY_1 },
62 { SDL_SCANCODE_2, PCE_KEY_2 },
63 { SDL_SCANCODE_3, PCE_KEY_3 },
64 { SDL_SCANCODE_4, PCE_KEY_4 },
65 { SDL_SCANCODE_5, PCE_KEY_5 },
66 { SDL_SCANCODE_6, PCE_KEY_6 },
67 { SDL_SCANCODE_7, PCE_KEY_7 },
68 { SDL_SCANCODE_8, PCE_KEY_8 },
69 { SDL_SCANCODE_9, PCE_KEY_9 },
70 { SDL_SCANCODE_0, PCE_KEY_0 },
71 { SDL_SCANCODE_MINUS, PCE_KEY_MINUS },
72 { SDL_SCANCODE_EQUALS, PCE_KEY_EQUAL },
73 { SDL_SCANCODE_BACKSPACE, PCE_KEY_BACKSPACE },
74
75 { SDL_SCANCODE_TAB, PCE_KEY_TAB },
76 { SDL_SCANCODE_Q, PCE_KEY_Q },
77 { SDL_SCANCODE_W, PCE_KEY_W },
78 { SDL_SCANCODE_E, PCE_KEY_E },
79 { SDL_SCANCODE_R, PCE_KEY_R },
80 { SDL_SCANCODE_T, PCE_KEY_T },
81 { SDL_SCANCODE_Y, PCE_KEY_Y },
82 { SDL_SCANCODE_U, PCE_KEY_U },
83 { SDL_SCANCODE_I, PCE_KEY_I },
84 { SDL_SCANCODE_O, PCE_KEY_O },
85 { SDL_SCANCODE_P, PCE_KEY_P },
86 { SDL_SCANCODE_LEFTBRACKET, PCE_KEY_LBRACKET },
87 { SDL_SCANCODE_RIGHTBRACKET, PCE_KEY_RBRACKET },
88 { SDL_SCANCODE_RETURN, PCE_KEY_RETURN },
89
90 { SDL_SCANCODE_CAPSLOCK, PCE_KEY_CAPSLOCK },
91 { SDL_SCANCODE_A, PCE_KEY_A },
92 { SDL_SCANCODE_S, PCE_KEY_S },
93 { SDL_SCANCODE_D, PCE_KEY_D },
94 { SDL_SCANCODE_F, PCE_KEY_F },
95 { SDL_SCANCODE_G, PCE_KEY_G },
96 { SDL_SCANCODE_H, PCE_KEY_H },
97 { SDL_SCANCODE_J, PCE_KEY_J },
98 { SDL_SCANCODE_K, PCE_KEY_K },
99 { SDL_SCANCODE_L, PCE_KEY_L },
100 { SDL_SCANCODE_SEMICOLON, PCE_KEY_SEMICOLON },
101 { SDL_SCANCODE_APOSTROPHE, PCE_KEY_QUOTE },
102 { SDL_SCANCODE_BACKSLASH, PCE_KEY_BACKSLASH },
103
104 { SDL_SCANCODE_LSHIFT, PCE_KEY_LSHIFT },
105 { SDL_SCANCODE_NONUSBACKSLASH, PCE_KEY_LESS },
106 { SDL_SCANCODE_Z, PCE_KEY_Z },
107 { SDL_SCANCODE_X, PCE_KEY_X },
108 { SDL_SCANCODE_C, PCE_KEY_C },
109 { SDL_SCANCODE_V, PCE_KEY_V },
110 { SDL_SCANCODE_B, PCE_KEY_B },
111 { SDL_SCANCODE_N, PCE_KEY_N },
112 { SDL_SCANCODE_M, PCE_KEY_M },
113 { SDL_SCANCODE_COMMA, PCE_KEY_COMMA },
114 { SDL_SCANCODE_PERIOD, PCE_KEY_PERIOD },
115 { SDL_SCANCODE_SLASH, PCE_KEY_SLASH },
116 { SDL_SCANCODE_RSHIFT, PCE_KEY_RSHIFT },
117
118 { SDL_SCANCODE_LCTRL, PCE_KEY_LCTRL },
119 { SDL_SCANCODE_LGUI, PCE_KEY_LSUPER },
120 { SDL_SCANCODE_LALT, PCE_KEY_LALT },
121 { SDL_SCANCODE_MODE, PCE_KEY_MODE },
122 { SDL_SCANCODE_SPACE, PCE_KEY_SPACE },
123 { SDL_SCANCODE_RALT, PCE_KEY_RALT },
124 { SDL_SCANCODE_RGUI, PCE_KEY_RSUPER },
125 { SDL_SCANCODE_MENU, PCE_KEY_MENU },
126 { SDL_SCANCODE_RCTRL, PCE_KEY_RCTRL },
127
128 { SDL_SCANCODE_NUMLOCKCLEAR, PCE_KEY_NUMLOCK },
129 { SDL_SCANCODE_KP_DIVIDE, PCE_KEY_KP_SLASH },
130 { SDL_SCANCODE_KP_MULTIPLY, PCE_KEY_KP_STAR },
131 { SDL_SCANCODE_KP_MINUS, PCE_KEY_KP_MINUS },
132 { SDL_SCANCODE_KP_7, PCE_KEY_KP_7 },
133 { SDL_SCANCODE_KP_8, PCE_KEY_KP_8 },
134 { SDL_SCANCODE_KP_9, PCE_KEY_KP_9 },
135 { SDL_SCANCODE_KP_PLUS, PCE_KEY_KP_PLUS },
136 { SDL_SCANCODE_KP_4, PCE_KEY_KP_4 },
137 { SDL_SCANCODE_KP_5, PCE_KEY_KP_5 },
138 { SDL_SCANCODE_KP_6, PCE_KEY_KP_6 },
139 { SDL_SCANCODE_KP_1, PCE_KEY_KP_1 },
140 { SDL_SCANCODE_KP_2, PCE_KEY_KP_2 },
141 { SDL_SCANCODE_KP_3, PCE_KEY_KP_3 },
142 { SDL_SCANCODE_KP_ENTER, PCE_KEY_KP_ENTER },
143 { SDL_SCANCODE_KP_0, PCE_KEY_KP_0 },
144 { SDL_SCANCODE_KP_PERIOD, PCE_KEY_KP_PERIOD },
145 { SDL_SCANCODE_INSERT, PCE_KEY_INS },
146 { SDL_SCANCODE_HOME, PCE_KEY_HOME },
147 { SDL_SCANCODE_PAGEUP, PCE_KEY_PAGEUP },
148 { SDL_SCANCODE_DELETE, PCE_KEY_DEL },
149 { SDL_SCANCODE_END, PCE_KEY_END },
150 { SDL_SCANCODE_PAGEDOWN, PCE_KEY_PAGEDN },
151 { SDL_SCANCODE_UP, PCE_KEY_UP },
152 { SDL_SCANCODE_LEFT, PCE_KEY_LEFT },
153 { SDL_SCANCODE_DOWN, PCE_KEY_DOWN },
154 { SDL_SCANCODE_RIGHT, PCE_KEY_RIGHT },
155 { 0, PCE_KEY_NONE }
156};
157
158
159static
160void sdl2_set_keymap (sdl2_t *sdl, SDL_Scancode src, pce_key_t dst)
161{
162 unsigned i;
163 sdl2_keymap_t *tmp;
164
165 for (i = 0; i < sdl->keymap_cnt; i++) {
166 if (sdl->keymap[i].sdlkey == src) {
167 sdl->keymap[i].pcekey = dst;
168 return;
169 }
170 }
171
172 tmp = realloc (sdl->keymap, (sdl->keymap_cnt + 1) * sizeof (sdl2_keymap_t));
173
174 if (tmp == NULL) {
175 return;
176 }
177
178 tmp[sdl->keymap_cnt].sdlkey = src;
179 tmp[sdl->keymap_cnt].pcekey = dst;
180
181 sdl->keymap = tmp;
182 sdl->keymap_cnt += 1;
183}
184
185static
186void sdl2_init_keymap_default (sdl2_t *sdl)
187{
188 unsigned i, n;
189
190 sdl->keymap_cnt = 0;
191 sdl->keymap = NULL;
192
193 n = 0;
194 while (keymap[n].pcekey != PCE_KEY_NONE) {
195 n += 1;
196 }
197
198 sdl->keymap = malloc (n * sizeof (sdl2_keymap_t));
199
200 if (sdl->keymap == NULL) {
201 return;
202 }
203
204 for (i = 0; i < n; i++) {
205 sdl->keymap[i] = keymap[i];
206 }
207
208 sdl->keymap_cnt = n;
209}
210
211static
212void sdl2_init_keymap_user (sdl2_t *sdl, ini_sct_t *sct)
213{
214 const char *str;
215 ini_val_t *val;
216 unsigned long sdlkey;
217 pce_key_t pcekey;
218
219 val = NULL;
220
221 while (1) {
222 val = ini_next_val (sct, val, "keymap");
223
224 if (val == NULL) {
225 break;
226 }
227
228 str = ini_val_get_str (val);
229
230 if (str == NULL) {
231 continue;
232 }
233
234 if (pce_key_get_map (str, &sdlkey, &pcekey)) {
235 continue;
236 }
237
238 sdl2_set_keymap (sdl, (SDL_Scancode) sdlkey, pcekey);
239 }
240}
241
242static
243void sdl2_grab (sdl2_t *sdl, int grab)
244{
245 sdl->grab = (grab != 0);
246
247 if (sdl->window != NULL) {
248 if (sdl->grab_keyboard) {
249 SDL_SetWindowKeyboardGrab (sdl->window, sdl->grab ? SDL_TRUE : SDL_FALSE);
250 }
251 SDL_SetWindowMouseGrab (sdl->window, sdl->grab ? SDL_TRUE : SDL_FALSE);
252 SDL_SetRelativeMouseMode (sdl->grab ? SDL_TRUE : SDL_FALSE);
253 }
254}
255
256static
257void sdl2_set_fullscreen (sdl2_t *sdl, int val)
258{
259 if ((val != 0) == (sdl->fullscreen != 0)) {
260 return;
261 }
262
263 sdl->fullscreen = (val != 0);
264
265 if (sdl->window != NULL) {
266 SDL_SetWindowFullscreen (sdl->window, val ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
267 }
268}
269
270static
271int sdl2_set_window_size (sdl2_t *sdl, unsigned w, unsigned h)
272{
273#if !(defined(PCE_HOST_WINDOWS) || defined(PCE_HOST_MACOS))
274 XSizeHints hints;
275 SDL_SysWMinfo info;
276 long r;
277#endif
278
279 if ((w == 0) || (h == 0)) {
280 return (1);
281 }
282
283 if ((sdl->wdw_w == w) && (sdl->wdw_h == h)) {
284 return (0);
285 }
286
287#if !(defined(PCE_HOST_WINDOWS) || defined(PCE_HOST_MACOS))
288 if (sdl->autosize == 2) {
289 SDL_VERSION(&info.version);
290 if (SDL_GetWindowWMInfo(sdl->window, &info) &&
291 info.subsystem == SDL_SYSWM_X11 &&
292 XGetWMSizeHints(info.info.x11.display, info.info.x11.window, &hints, &r, XA_WM_SIZE_HINTS) == 0) {
293 hints.flags = PMinSize | PMaxSize | PAspect;
294 hints.max_width = w;
295 hints.min_width = w;
296 hints.max_height = h;
297 hints.min_height = h;
298 XSetWMNormalHints (info.info.x11.display, info.info.x11.window, &hints);
299 }
300 }
301#endif
302
303 SDL_SetWindowSize (sdl->window, w, h);
304
305 sdl->wdw_w = w;
306 sdl->wdw_h = h;
307
308 return (0);
309}
310
311static
312int sdl2_set_window_size_auto (sdl2_t *sdl)
313{
314 unsigned fx, fy, tw, th, ww, wh;
315
316 tw = sdl->trm.w;
317 th = sdl->trm.h;
318
319 trm_get_scale (&sdl->trm, tw, th, &fx, &fy);
320
321 ww = fx * tw;
322 wh = fy * th;
323
324 if (sdl2_set_window_size (sdl, ww, wh)) {
325 return (1);
326 }
327
328 return (0);
329}
330
331static
332int sdl2_set_frame_size (sdl2_t *sdl)
333{
334 unsigned tw, th;
335
336 tw = sdl->trm.w;
337 th = sdl->trm.h;
338
339 if (sdl->autosize) {
340 sdl2_set_window_size_auto (sdl);
341 }
342
343 if ((sdl->txt_w == tw) && (sdl->txt_h == th)) {
344 return (0);
345 }
346
347 if (sdl->texture != NULL) {
348 SDL_DestroyTexture (sdl->texture);
349 sdl->texture = NULL;
350 }
351
352 sdl->texture = SDL_CreateTexture (sdl->render,
353 SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, tw, th
354 );
355
356 if (sdl->texture == NULL) {
357 fprintf (stderr, "sdl2: texture\n");
358 return (1);
359 }
360
361 sdl->txt_w = tw;
362 sdl->txt_h = th;
363
364 return (0);
365}
366
367static
368unsigned sdl2_map_key (sdl2_t *sdl, SDL_Scancode key)
369{
370 unsigned i;
371
372 for (i = 0; i < sdl->keymap_cnt; i++) {
373 if (sdl->keymap[i].sdlkey == key) {
374 return (sdl->keymap[i].pcekey);
375 }
376 }
377
378 return (PCE_KEY_NONE);
379}
380
381static
382void sdl2_update (sdl2_t *sdl)
383{
384 terminal_t *trm;
385 void *pixels;
386 int pitch;
387
388 trm = &sdl->trm;
389
390 sdl->update = 0;
391
392 if ((trm->w == 0) || (trm->h == 0)) {
393 return;
394 }
395
396 if (sdl2_set_frame_size (sdl)) {
397 return;
398 }
399
400 if ((sdl->texture == NULL) || (sdl->render == NULL)) {
401 return;
402 }
403
404 SDL_LockTexture (sdl->texture, NULL, &pixels, &pitch);
405 memcpy (pixels, trm->buf, 3UL * trm->w * trm->h);
406 SDL_UnlockTexture (sdl->texture);
407
408 SDL_RenderCopy (sdl->render, sdl->texture, NULL, NULL);
409 SDL_RenderPresent (sdl->render);
410}
411
412static
413void sdl2_event_keydown (sdl2_t *sdl, SDL_Scancode key, SDL_Keymod mod)
414{
415 pce_key_t pcekey;
416
417 if (sdl->ignore_keys || !sdl->grab) {
418 return;
419 }
420
421 if (key == SDL_SCANCODE_GRAVE) {
422 if (sdl->grave_down) {
423 return;
424 }
425 else if (mod & KMOD_LCTRL) {
426 sdl->grave_down = 1;
427 sdl2_grab (sdl, 0);
428 sdl2_set_fullscreen (sdl, 0);
429 trm_set_msg_emu (&sdl->trm, "emu.stop", "1");
430 return;
431 }
432 }
433 else if (key == SDL_SCANCODE_PRINTSCREEN) {
434 trm_screenshot (&sdl->trm, NULL);
435 return;
436 }
437
438 pcekey = sdl2_map_key (sdl, key);
439
440 if (sdl->report_keys || (pcekey == PCE_KEY_NONE)) {
441 fprintf (stderr, "sdl: key = 0x%04x (%s)\n",
442 (unsigned) key, SDL_GetScancodeName (key)
443 );
444 }
445
446 if (pcekey == PCE_KEY_NONE) {
447 return;
448 }
449
450 trm_set_key (&sdl->trm, PCE_KEY_EVENT_DOWN, pcekey);
451
452 if (key == SDL_SCANCODE_NUMLOCKCLEAR) {
453 trm_set_key (&sdl->trm, PCE_KEY_EVENT_UP, pcekey);
454 }
455}
456
457static
458void sdl2_event_keyup (sdl2_t *sdl, SDL_Scancode key, SDL_Keymod mod)
459{
460 pce_key_t pcekey;
461
462 if (sdl->ignore_keys) {
463 return;
464 }
465
466 pcekey = sdl2_map_key (sdl, key);
467
468 if (key == SDL_SCANCODE_GRAVE) {
469 if (sdl->grave_down) {
470 sdl->grave_down = 0;
471 return;
472 }
473 }
474 else if (key == SDL_SCANCODE_PRINTSCREEN) {
475 return;
476 }
477
478 if (pcekey != PCE_KEY_NONE) {
479 if (key == SDL_SCANCODE_NUMLOCKCLEAR) {
480 trm_set_key (&sdl->trm, PCE_KEY_EVENT_DOWN, pcekey);
481 }
482
483 trm_set_key (&sdl->trm, PCE_KEY_EVENT_UP, pcekey);
484 }
485}
486
487static
488void sdl2_event_mouse_button (sdl2_t *sdl, int down, unsigned button)
489{
490 if (button == 0) {
491 return;
492 }
493
494 if (button == 2) {
495 button = 3;
496 }
497 else if (button == 3) {
498 button = 2;
499 }
500
501 button -= 1;
502
503 if (down) {
504 sdl->button |= 1U << button;
505 }
506 else {
507 sdl->button &= ~(1U << button);
508 }
509
510 if (sdl->grab == 0) {
511 if (down == 0) {
512 sdl2_grab (sdl, 1);
513 }
514 return;
515 }
516
517 if (sdl->trm.set_mouse == NULL) {
518 return;
519 }
520
521 trm_set_mouse (&sdl->trm, 0, 0, sdl->button);
522}
523
524static
525void sdl2_event_mouse_motion (sdl2_t *sdl, int dx, int dy)
526{
527 int ax, ay, fx, fy;
528
529 if (sdl->grab == 0) {
530 return;
531 }
532
533 if (sdl->trm.set_mouse == NULL) {
534 return;
535 }
536
537 trm_set_mouse (&sdl->trm, dx, dy, sdl->button);
538
539 if (!sdl->autograb || sdl->trm.get_mouse == NULL || sdl->fullscreen) {
540 return;
541 }
542
543 trm_get_mouse (&sdl->trm, &ax, &ay);
544
545 /* if at screen edge and still moving that direction, unbind */
546 if ((ax == 0 && dx < 0) || (ay == 0 && dy < 0) ||
547 (ax == sdl->trm.w && dx > 0) || (ay == sdl->trm.h && dy > 0)) {
548 trm_get_scale (&sdl->trm, sdl->trm.w, sdl->trm.h, &fx, &fy);
549 SDL_WarpMouseInWindow (sdl->window, ax * fx, ay * fy);
550 sdl2_grab (sdl, 0);
551 }
552}
553
554static
555void sdl2_event_window (sdl2_t *sdl, SDL_WindowEvent *evt)
556{
557 if (sdl->window == NULL) {
558 return;
559 }
560
561 if (evt->windowID != SDL_GetWindowID (sdl->window)) {
562 return;
563 }
564
565 switch (evt->event) {
566 case SDL_WINDOWEVENT_SIZE_CHANGED:
567 sdl->update = 1;
568 break;
569
570 case SDL_WINDOWEVENT_RESIZED:
571 if ((sdl->wdw_w != evt->data1) || (sdl->wdw_h != evt->data2)) {
572 sdl->wdw_w = evt->data1;
573 sdl->wdw_h = evt->data2;
574 sdl->autosize = 0;
575 if (sdl->autosize != 2)
576 sdl->autosize = 0;
577 }
578 sdl->update = 1;
579 break;
580
581 case SDL_WINDOWEVENT_RESTORED:
582 sdl->update = 1;
583 break;
584
585 case SDL_WINDOWEVENT_EXPOSED:
586 sdl->update = 1;
587 break;
588
589 case SDL_WINDOWEVENT_MOVED:
590 break;
591
592 case SDL_WINDOWEVENT_ENTER:
593 if (sdl->autograb && sdl->grab == 0) {
594 sdl2_grab (sdl, 1);
595 }
596 break;
597
598 case SDL_WINDOWEVENT_LEAVE:
599 break;
600
601 case SDL_WINDOWEVENT_FOCUS_GAINED:
602 sdl->ignore_keys = 1;
603 break;
604
605 case SDL_WINDOWEVENT_FOCUS_LOST:
606 break;
607
608 case SDL_WINDOWEVENT_SHOWN:
609 sdl->update = 1;
610 break;
611
612 case SDL_WINDOWEVENT_HIDDEN:
613 break;
614
615 case SDL_WINDOWEVENT_MINIMIZED:
616 break;
617
618 case SDL_WINDOWEVENT_MAXIMIZED:
619 sdl->update = 1;
620 break;
621
622 case SDL_WINDOWEVENT_TAKE_FOCUS:
623 break;
624
625 default:
626#ifdef DEBUG_SDL2
627 fprintf (stderr, "sdl2: window event %u\n", evt->event);
628#endif
629 break;
630 }
631}
632
633static
634void sdl2_check (sdl2_t *sdl)
635{
636 static SDL_Event cur_keydown_evt = { 0 };
637 SDL_Event evt;
638 SDL_Event peek_evt;
639 unsigned int now;
640
641 if (sdl->mouse_button_delay) {
642 now = SDL_GetTicks();
643
644 SDL_PumpEvents();
645 if (SDL_PeepEvents(&peek_evt, 1, SDL_PEEKEVENT, SDL_MOUSEMOTION,
646 SDL_MOUSEBUTTONUP) > 0 &&
647 now - sdl->last_mouse_button_at <= sdl->mouse_button_delay) {
648 goto done;
649 }
650 }
651
652 while (SDL_PollEvent (&evt)) {
653 switch (evt.type) {
654 case SDL_KEYDOWN:
655 cur_keydown_evt = evt;
656 sdl2_event_keydown (sdl, evt.key.keysym.scancode, evt.key.keysym.mod);
657 break;
658
659 case SDL_KEYUP:
660 memset(&cur_keydown_evt, 0, sizeof(cur_keydown_evt));
661 sdl2_event_keyup (sdl, evt.key.keysym.scancode, evt.key.keysym.mod);
662 break;
663
664 case SDL_TEXTINPUT:
665 case SDL_KEYMAPCHANGED:
666 break;
667
668 case SDL_MOUSEBUTTONDOWN:
669 if (sdl->mouse_button_delay) {
670 sdl->last_mouse_button_at = now;
671 }
672 sdl2_event_mouse_button (sdl, 1, evt.button.button);
673 goto done;
674
675 case SDL_MOUSEBUTTONUP:
676 if (sdl->mouse_button_delay) {
677 sdl->last_mouse_button_at = now;
678 }
679 sdl2_event_mouse_button (sdl, 0, evt.button.button);
680 goto done;
681
682 case SDL_MOUSEMOTION:
683 sdl2_event_mouse_motion (sdl, evt.motion.xrel, evt.motion.yrel);
684 break;
685
686 case SDL_WINDOWEVENT:
687 sdl2_event_window (sdl, &evt.window);
688 break;
689
690 case SDL_QUIT:
691 if (cur_keydown_evt.key.keysym.sym && sdl->grab)
692 break;
693 sdl2_grab (sdl, 0);
694 trm_set_msg_emu (&sdl->trm, "emu.exit", "1");
695 break;
696
697 case SDL_AUDIODEVICEADDED:
698 break;
699
700 default:
701#ifdef DEBUG_SDL2
702 fprintf (stderr, "sdl2: event %u\n", evt.type);
703#endif
704 break;
705 }
706 }
707done:
708
709 if (sdl->update) {
710 sdl2_update (sdl);
711 }
712
713 if (sdl->ignore_keys) {
714 sdl->ignore_keys = 0;
715 }
716}
717
718static
719int sdl2_set_msg_trm (sdl2_t *sdl, const char *msg, const char *val)
720{
721 if (val == NULL) {
722 val = "";
723 }
724
725 if (strcmp (msg, "term.grab") == 0) {
726 sdl2_grab (sdl, 1);
727 return (0);
728 }
729 else if (strcmp (msg, "term.release") == 0) {
730 sdl2_grab (sdl, 0);
731 return (0);
732 }
733 else if (strcmp (msg, "term.title") == 0) {
734 if (sdl->window != NULL) {
735 SDL_SetWindowTitle (sdl->window, val);
736 }
737 return (0);
738 }
739 else if (strcmp (msg, "term.fullscreen.toggle") == 0) {
740 sdl2_set_fullscreen (sdl, !sdl->fullscreen);
741 return (0);
742 }
743 else if (strcmp (msg, "term.fullscreen") == 0) {
744 int v;
745
746 v = strtol (val, NULL, 0);
747
748 sdl2_set_fullscreen (sdl, v != 0);
749
750 return (0);
751 }
752 else if (strcmp (msg, "term.autosize") == 0) {
753 sdl->autosize = 1;
754 sdl->update = 1;
755 return (0);
756 }
757
758 return (-1);
759}
760
761static
762void sdl2_del (sdl2_t *sdl)
763{
764 free (sdl);
765}
766
767static
768int sdl2_open (sdl2_t *sdl, unsigned w, unsigned h)
769{
770 unsigned x, y;
771 unsigned fx, fy;
772 unsigned flags;
773
774 trm_get_scale (&sdl->trm, w, h, &fx, &fy);
775
776 w *= fx;
777 h *= fy;
778
779 if ((w == 0) || (h == 0)) {
780 w = 512;
781 h = 384;
782 }
783
784 if (SDL_WasInit (SDL_INIT_VIDEO) == 0) {
785 if (SDL_InitSubSystem (SDL_INIT_VIDEO) < 0) {
786 return (1);
787 }
788 }
789
790 sdl->window = NULL;
791 sdl->render = NULL;
792 sdl->texture = NULL;
793
794 SDL_EventState (SDL_MOUSEMOTION, SDL_ENABLE);
795
796 SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, sdl->scale_quality);
797 SDL_SetHint (SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
798
799 flags = SDL_WINDOW_RESIZABLE;
800
801 if (sdl->fullscreen) {
802 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
803 }
804
805 x = SDL_WINDOWPOS_UNDEFINED;
806 y = SDL_WINDOWPOS_UNDEFINED;
807
808 sdl->window = SDL_CreateWindow ("pce", x, y, w, h, flags);
809
810 if (sdl->window == NULL) {
811 fprintf (stderr, "sdl2: window\n");
812 return (1);
813 }
814
815 SDL_ShowWindow (sdl->window);
816
817 sdl->wdw_w = w;
818 sdl->wdw_h = h;
819
820 sdl->render = SDL_CreateRenderer (sdl->window, -1, 0);
821
822 if (sdl->render == NULL) {
823 fprintf (stderr, "sdl2: renderer\n");
824 return (1);
825 }
826
827 return (0);
828}
829
830static
831int sdl2_close (sdl2_t *sdl)
832{
833 sdl2_grab (sdl, 0);
834
835 if (sdl->texture != NULL) {
836 SDL_DestroyTexture (sdl->texture);
837 sdl->texture = NULL;
838 }
839
840 if (sdl->render != NULL) {
841 SDL_DestroyRenderer (sdl->render);
842 sdl->render = NULL;
843 }
844
845 if (sdl->window != NULL) {
846 SDL_DestroyWindow (sdl->window);
847 sdl->window = NULL;
848 }
849
850 return (0);
851}
852
853static
854void sdl2_init (sdl2_t *sdl, ini_sct_t *sct)
855{
856 int val;
857 unsigned int autosize;
858 const char *str;
859
860 trm_init (&sdl->trm, sdl);
861
862 sdl->trm.del = (void *) sdl2_del;
863 sdl->trm.open = (void *) sdl2_open;
864 sdl->trm.close = (void *) sdl2_close;
865 sdl->trm.set_msg_trm = (void *) sdl2_set_msg_trm;
866 sdl->trm.update = (void *) sdl2_update;
867 sdl->trm.check = (void *) sdl2_check;
868
869 sdl->window = NULL;
870 sdl->render = NULL;
871 sdl->texture = NULL;
872
873 sdl->txt_w = 0;
874 sdl->txt_h = 0;
875
876 sdl->wdw_w = 0;
877 sdl->wdw_h = 0;
878
879 sdl->update = 0;
880
881 sdl->button = 0;
882
883 ini_get_bool (sct, "fullscreen", &val, 0);
884 sdl->fullscreen = (val != 0);
885
886 ini_get_string (sct, "scale_quality", &sdl->scale_quality, "linear");
887
888 sdl->grab = 0;
889
890 ini_get_bool (sct, "grab_keyboard", &val, 1);
891 sdl->grab_keyboard = (val != 0);
892
893 ini_get_bool (sct, "autograb", &val, 0);
894 sdl->autograb = (val != 0);
895
896 ini_get_bool (sct, "report_keys", &val, 0);
897 sdl->report_keys = (val != 0);
898
899 if (ini_get_string (sct, "scale_quality", &str, NULL) == 0) {
900 SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, str);
901 }
902
903 ini_get_uint16 (sct, "autosize", &autosize, 1);
904 sdl->autosize = (autosize & 0xff);
905
906 sdl->grave_down = 0;
907 sdl->ignore_keys = 0;
908
909 ini_get_uint16 (sct, "mouse_button_delay", &sdl->mouse_button_delay, 0);
910
911 sdl2_init_keymap_default (sdl);
912 sdl2_init_keymap_user (sdl, sct);
913
914 SDL_SetHint (SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
915}
916
917terminal_t *sdl2_new (ini_sct_t *sct)
918{
919 sdl2_t *sdl;
920
921 if ((sdl = malloc (sizeof (sdl2_t))) == NULL) {
922 return (NULL);
923 }
924
925 sdl2_init (sdl, sct);
926
927 return (&sdl->trm);
928}