forked from fernandotcl/TinyEMU
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sdl.c
426 lines (393 loc) · 14.5 KB
/
sdl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/*
* SDL display driver
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <SDL2/SDL.h>
#include "cutils.h"
#include "virtio.h"
#include "machine.h"
#define KEYCODE_MAX 127
static SDL_Window *window;
static SDL_Renderer *renderer;
static SDL_Texture *fb_texture;
static int window_width, window_height, fb_width, fb_height;
static SDL_Cursor *sdl_cursor_hidden;
static uint8_t key_pressed[KEYCODE_MAX + 1];
static void sdl_update_fb_texture(FBDevice *fb_dev)
{
if (!fb_texture ||
fb_width != fb_dev->width ||
fb_height != fb_dev->height) {
if (fb_texture != NULL)
SDL_DestroyTexture(fb_texture);
fb_width = fb_dev->width;
fb_height = fb_dev->height;
fb_texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
fb_dev->width,
fb_dev->height);
if (!fb_texture) {
fprintf(stderr, "Could not create SDL framebuffer texture\n");
exit(1);
}
}
}
static void sdl_update(FBDevice *fb_dev, void *opaque,
int x, int y, int w, int h)
{
int *dirty = (int *)opaque;
*dirty = 1;
}
static int sdl_get_keycode(const SDL_KeyboardEvent *ev)
{
switch (ev->keysym.scancode) {
case SDL_SCANCODE_ESCAPE: return 1;
case SDL_SCANCODE_1: return 2;
case SDL_SCANCODE_2: return 3;
case SDL_SCANCODE_3: return 4;
case SDL_SCANCODE_4: return 5;
case SDL_SCANCODE_5: return 6;
case SDL_SCANCODE_6: return 7;
case SDL_SCANCODE_7: return 8;
case SDL_SCANCODE_8: return 9;
case SDL_SCANCODE_9: return 10;
case SDL_SCANCODE_0: return 11;
case SDL_SCANCODE_MINUS: return 12;
case SDL_SCANCODE_EQUALS: return 13;
case SDL_SCANCODE_BACKSPACE: return 14;
case SDL_SCANCODE_TAB: return 15;
case SDL_SCANCODE_Q: return 16;
case SDL_SCANCODE_W: return 17;
case SDL_SCANCODE_E: return 18;
case SDL_SCANCODE_R: return 19;
case SDL_SCANCODE_T: return 20;
case SDL_SCANCODE_Y: return 21;
case SDL_SCANCODE_U: return 22;
case SDL_SCANCODE_I: return 23;
case SDL_SCANCODE_O: return 24;
case SDL_SCANCODE_P: return 25;
case SDL_SCANCODE_LEFTBRACKET: return 26;
case SDL_SCANCODE_RIGHTBRACKET: return 27;
case SDL_SCANCODE_RETURN: return 28;
case SDL_SCANCODE_LCTRL: return 29;
case SDL_SCANCODE_A: return 30;
case SDL_SCANCODE_S: return 31;
case SDL_SCANCODE_D: return 32;
case SDL_SCANCODE_F: return 33;
case SDL_SCANCODE_G: return 34;
case SDL_SCANCODE_H: return 35;
case SDL_SCANCODE_J: return 36;
case SDL_SCANCODE_K: return 37;
case SDL_SCANCODE_L: return 38;
case SDL_SCANCODE_SEMICOLON: return 39;
case SDL_SCANCODE_APOSTROPHE: return 40;
case SDL_SCANCODE_GRAVE: return 41;
case SDL_SCANCODE_LSHIFT: return 42;
case SDL_SCANCODE_BACKSLASH: return 43;
case SDL_SCANCODE_Z: return 44;
case SDL_SCANCODE_X: return 45;
case SDL_SCANCODE_C: return 46;
case SDL_SCANCODE_V: return 47;
case SDL_SCANCODE_B: return 48;
case SDL_SCANCODE_N: return 49;
case SDL_SCANCODE_M: return 50;
case SDL_SCANCODE_COMMA: return 51;
case SDL_SCANCODE_PERIOD: return 52;
case SDL_SCANCODE_SLASH: return 53;
case SDL_SCANCODE_RSHIFT: return 54;
case SDL_SCANCODE_KP_MULTIPLY: return 55;
case SDL_SCANCODE_LALT: return 56;
case SDL_SCANCODE_SPACE: return 57;
case SDL_SCANCODE_CAPSLOCK: return 58;
case SDL_SCANCODE_F1: return 59;
case SDL_SCANCODE_F2: return 60;
case SDL_SCANCODE_F3: return 61;
case SDL_SCANCODE_F4: return 62;
case SDL_SCANCODE_F5: return 63;
case SDL_SCANCODE_F6: return 64;
case SDL_SCANCODE_F7: return 65;
case SDL_SCANCODE_F8: return 66;
case SDL_SCANCODE_F9: return 67;
case SDL_SCANCODE_F10: return 68;
case SDL_SCANCODE_NUMLOCKCLEAR: return 69;
case SDL_SCANCODE_SCROLLLOCK: return 70;
case SDL_SCANCODE_KP_7: return 71;
case SDL_SCANCODE_KP_8: return 72;
case SDL_SCANCODE_KP_9: return 73;
case SDL_SCANCODE_KP_MINUS: return 74;
case SDL_SCANCODE_KP_4: return 75;
case SDL_SCANCODE_KP_5: return 76;
case SDL_SCANCODE_KP_6: return 77;
case SDL_SCANCODE_KP_PLUS: return 78;
case SDL_SCANCODE_KP_1: return 79;
case SDL_SCANCODE_KP_2: return 80;
case SDL_SCANCODE_KP_3: return 81;
case SDL_SCANCODE_KP_0: return 82;
case SDL_SCANCODE_KP_PERIOD: return 83;
case SDL_SCANCODE_LANG5: return 85;
case SDL_SCANCODE_NONUSBACKSLASH: return 86;
case SDL_SCANCODE_F11: return 87;
case SDL_SCANCODE_F12: return 88;
case SDL_SCANCODE_INTERNATIONAL1: return 89;
case SDL_SCANCODE_LANG3: return 90;
case SDL_SCANCODE_LANG4: return 91;
case SDL_SCANCODE_INTERNATIONAL4: return 92;
case SDL_SCANCODE_INTERNATIONAL2: return 93;
case SDL_SCANCODE_INTERNATIONAL5: return 94;
case SDL_SCANCODE_KP_ENTER: return 96;
case SDL_SCANCODE_RCTRL: return 97;
case SDL_SCANCODE_KP_DIVIDE: return 98;
case SDL_SCANCODE_SYSREQ: return 99;
case SDL_SCANCODE_RALT: return 100;
case SDL_SCANCODE_HOME: return 102;
case SDL_SCANCODE_UP: return 103;
case SDL_SCANCODE_PAGEUP: return 104;
case SDL_SCANCODE_LEFT: return 105;
case SDL_SCANCODE_RIGHT: return 106;
case SDL_SCANCODE_END: return 107;
case SDL_SCANCODE_DOWN: return 108;
case SDL_SCANCODE_PAGEDOWN: return 109;
case SDL_SCANCODE_INSERT: return 110;
case SDL_SCANCODE_DELETE: return 111;
case SDL_SCANCODE_MUTE: return 113;
case SDL_SCANCODE_VOLUMEDOWN: return 114;
case SDL_SCANCODE_VOLUMEUP: return 115;
case SDL_SCANCODE_POWER: return 116;
case SDL_SCANCODE_KP_EQUALS: return 117;
case SDL_SCANCODE_KP_PLUSMINUS: return 118;
case SDL_SCANCODE_PAUSE: return 119;
case SDL_SCANCODE_KP_COMMA: return 121;
case SDL_SCANCODE_LANG1: return 122;
case SDL_SCANCODE_LANG2: return 123;
case SDL_SCANCODE_INTERNATIONAL3: return 124;
case SDL_SCANCODE_LGUI: return 125;
case SDL_SCANCODE_RGUI: return 126;
case SDL_SCANCODE_APPLICATION: return 127;
case SDL_SCANCODE_STOP: return 128;
case SDL_SCANCODE_AGAIN: return 129;
case SDL_SCANCODE_UNDO: return 131;
case SDL_SCANCODE_COPY: return 133;
case SDL_SCANCODE_PASTE: return 135;
case SDL_SCANCODE_FIND: return 136;
case SDL_SCANCODE_CUT: return 137;
case SDL_SCANCODE_HELP: return 138;
case SDL_SCANCODE_MENU: return 139;
case SDL_SCANCODE_CALCULATOR: return 140;
case SDL_SCANCODE_SLEEP: return 142;
case SDL_SCANCODE_APP1: return 148;
case SDL_SCANCODE_APP2: return 149;
case SDL_SCANCODE_WWW: return 150;
case SDL_SCANCODE_MAIL: return 155;
case SDL_SCANCODE_AC_BOOKMARKS: return 156;
case SDL_SCANCODE_COMPUTER: return 157;
case SDL_SCANCODE_AC_BACK: return 158;
case SDL_SCANCODE_AC_FORWARD: return 159;
case SDL_SCANCODE_EJECT: return 161;
case SDL_SCANCODE_AUDIONEXT: return 163;
case SDL_SCANCODE_AUDIOPLAY: return 164;
case SDL_SCANCODE_AUDIOPREV: return 165;
case SDL_SCANCODE_AUDIOSTOP: return 166;
#if SDL_VERSION_ATLEAST(2, 0, 6)
case SDL_SCANCODE_AUDIOREWIND: return 168;
#endif
case SDL_SCANCODE_AC_HOME: return 172;
case SDL_SCANCODE_AC_REFRESH: return 173;
case SDL_SCANCODE_KP_LEFTPAREN: return 179;
case SDL_SCANCODE_KP_RIGHTPAREN: return 180;
case SDL_SCANCODE_F13: return 183;
case SDL_SCANCODE_F14: return 184;
case SDL_SCANCODE_F15: return 185;
case SDL_SCANCODE_F16: return 186;
case SDL_SCANCODE_F17: return 187;
case SDL_SCANCODE_F18: return 188;
case SDL_SCANCODE_F19: return 189;
case SDL_SCANCODE_F20: return 190;
case SDL_SCANCODE_F21: return 191;
case SDL_SCANCODE_F22: return 192;
case SDL_SCANCODE_F23: return 193;
case SDL_SCANCODE_F24: return 194;
#if SDL_VERSION_ATLEAST(2, 0, 6)
case SDL_SCANCODE_AUDIOFASTFORWARD: return 208;
#endif
case SDL_SCANCODE_AC_SEARCH: return 217;
case SDL_SCANCODE_ALTERASE: return 222;
case SDL_SCANCODE_CANCEL: return 223;
case SDL_SCANCODE_BRIGHTNESSDOWN: return 224;
case SDL_SCANCODE_BRIGHTNESSUP: return 225;
case SDL_SCANCODE_DISPLAYSWITCH: return 227;
case SDL_SCANCODE_KBDILLUMTOGGLE: return 228;
case SDL_SCANCODE_KBDILLUMDOWN: return 229;
case SDL_SCANCODE_KBDILLUMUP: return 230;
default: return 0;
}
}
/* release all pressed keys */
static void sdl_reset_keys(VirtMachine *m)
{
int i;
for(i = 1; i <= KEYCODE_MAX; i++) {
if (key_pressed[i]) {
vm_send_key_event(m, FALSE, i);
key_pressed[i] = FALSE;
}
}
}
static void sdl_handle_key_event(const SDL_KeyboardEvent *ev, VirtMachine *m)
{
int keycode, keypress;
keycode = sdl_get_keycode(ev);
if (keycode) {
if (keycode == 0x3a || keycode ==0x45) {
/* SDL does not generate key up for numlock & caps lock */
vm_send_key_event(m, TRUE, keycode);
vm_send_key_event(m, FALSE, keycode);
} else {
keypress = (ev->type == SDL_KEYDOWN);
if (keycode <= KEYCODE_MAX)
key_pressed[keycode] = keypress;
vm_send_key_event(m, keypress, keycode);
}
} else if (ev->type == SDL_KEYUP) {
/* workaround to reset the keyboard state (used when changing
desktop with ctrl-alt-x on Linux) */
sdl_reset_keys(m);
}
}
static void sdl_send_mouse_event(VirtMachine *m, int x1, int y1,
int dz, int state, BOOL is_absolute)
{
int buttons, x, y;
buttons = 0;
if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
buttons |= (1 << 0);
if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
buttons |= (1 << 1);
if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
buttons |= (1 << 2);
if (is_absolute) {
x = (x1 * 32768) / window_width;
y = (y1 * 32768) / window_height;
} else {
x = x1;
y = y1;
}
vm_send_mouse_event(m, x, y, dz, buttons);
}
static void sdl_handle_mouse_motion_event(const SDL_Event *ev, VirtMachine *m)
{
BOOL is_absolute = vm_mouse_is_absolute(m);
int x, y;
if (is_absolute) {
x = ev->motion.x;
y = ev->motion.y;
} else {
x = ev->motion.xrel;
y = ev->motion.yrel;
}
sdl_send_mouse_event(m, x, y, 0, ev->motion.state, is_absolute);
}
static void sdl_handle_mouse_button_event(const SDL_Event *ev, VirtMachine *m)
{
BOOL is_absolute = vm_mouse_is_absolute(m);
int state, dz;
dz = 0;
if (ev->type == SDL_MOUSEWHEEL)
dz = ev->wheel.y;
state = SDL_GetMouseState(NULL, NULL);
/* just in case */
if (ev->type == SDL_MOUSEBUTTONDOWN)
state |= SDL_BUTTON(ev->button.button);
else
state &= ~SDL_BUTTON(ev->button.button);
if (is_absolute) {
sdl_send_mouse_event(m, ev->button.x, ev->button.y,
dz, state, is_absolute);
} else {
sdl_send_mouse_event(m, 0, 0, dz, state, is_absolute);
}
}
void sdl_refresh(VirtMachine *m)
{
SDL_Event ev_s, *ev = &ev_s;
if (!m->fb_dev)
return;
sdl_update_fb_texture(m->fb_dev);
int dirty = 0;
m->fb_dev->refresh(m->fb_dev, sdl_update, &dirty);
if (dirty) {
SDL_UpdateTexture(fb_texture, NULL,
m->fb_dev->fb_data,
m->fb_dev->stride);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, fb_texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
while (SDL_PollEvent(ev)) {
switch (ev->type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
sdl_handle_key_event(&ev->key, m);
break;
case SDL_MOUSEMOTION:
sdl_handle_mouse_motion_event(ev, m);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
sdl_handle_mouse_button_event(ev, m);
break;
case SDL_QUIT:
exit(0);
}
}
}
static void sdl_hide_cursor(void)
{
uint8_t data = 0;
sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
SDL_ShowCursor(1);
SDL_SetCursor(sdl_cursor_hidden);
}
void sdl_init(int width, int height)
{
window_width = width;
window_height = height;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE)) {
fprintf(stderr, "Could not initialize SDL - exiting\n");
exit(1);
}
int result = SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer);
if (result == -1) {
fprintf(stderr, "Could not create SDL window\n");
exit(1);
}
SDL_SetWindowTitle(window, "TinyEMU");
sdl_hide_cursor();
}