comparison render_fbdev.c @ 1779:3a8c4ee68568

Added raw fbdev/evdev/ALSA render backend
author Michael Pavone <pavone@retrodev.com>
date Sun, 10 Mar 2019 21:30:09 -0700
parents
children fc9bea5ee079
comparison
equal deleted inserted replaced
1767:8a29c250f352 1779:3a8c4ee68568
1 /*
2 Copyright 2013 Michael Pavone
3 This file is part of BlastEm.
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
5 */
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <math.h>
10 #include <linux/fb.h>
11 #include <linux/input.h>
12 #include <linux/kd.h>
13 #include <alsa/asoundlib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/ioctl.h>
17 #include <sys/mman.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <pthread.h>
21 #include <dirent.h>
22 #include "render.h"
23 #include "blastem.h"
24 #include "genesis.h"
25 #include "bindings.h"
26 #include "util.h"
27 #include "paths.h"
28 #include "ppm.h"
29 #include "png.h"
30 #include "config.h"
31 #include "controller_info.h"
32
33 #ifndef DISABLE_OPENGL
34 #include <EGL/egl.h>
35 #include <GLES2/gl2.h>
36 #ifdef USE_MALI
37 //Mali GLES headers don't seem to define GLchar for some reason
38 typedef char GLchar;
39 #endif
40 #endif
41
42 #define MAX_EVENT_POLL_PER_FRAME 2
43
44 static EGLContext main_context;
45
46 static int main_width, main_height, windowed_width, windowed_height, is_fullscreen;
47
48 static uint8_t render_gl = 1;
49 static uint8_t scanlines = 0;
50
51 static uint32_t last_frame = 0;
52 static snd_pcm_uframes_t buffer_samples;
53 static unsigned int output_channels, sample_rate;
54 static uint32_t missing_count;
55
56
57 static uint8_t quitting = 0;
58
59 struct audio_source {
60 int16_t *front;
61 int16_t *back;
62 double dt;
63 uint64_t buffer_fraction;
64 uint64_t buffer_inc;
65 uint32_t buffer_pos;
66 uint32_t read_start;
67 uint32_t read_end;
68 uint32_t lowpass_alpha;
69 uint32_t mask;
70 int16_t last_left;
71 int16_t last_right;
72 uint8_t num_channels;
73 uint8_t front_populated;
74 };
75
76 static audio_source *audio_sources[8];
77 static audio_source *inactive_audio_sources[8];
78 static uint8_t num_audio_sources;
79 static uint8_t num_inactive_audio_sources;
80 static uint32_t min_buffered;
81
82 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len);
83
84 static int32_t mix_s16(audio_source *audio, void *vstream, int len)
85 {
86 int samples = len/(sizeof(int16_t)*output_channels);
87 int16_t *stream = vstream;
88 int16_t *end = stream + output_channels*samples;
89 int16_t *src = audio->front;
90 uint32_t i = audio->read_start;
91 uint32_t i_end = audio->read_end;
92 int16_t *cur = stream;
93 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1;
94 if (audio->num_channels == 1) {
95 while (cur < end && i != i_end)
96 {
97 *cur += src[i];
98 cur += first_add;
99 *cur += src[i++];
100 cur += second_add;
101 i &= audio->mask;
102 }
103 } else {
104 while (cur < end && i != i_end)
105 {
106 *cur += src[i++];
107 cur += first_add;
108 *cur += src[i++];
109 cur += second_add;
110 i &= audio->mask;
111 }
112 }
113
114 if (cur != end) {
115 printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
116 }
117 if (cur != end) {
118 //printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
119 return (cur-end)/2;
120 } else {
121 return ((i_end - i) & audio->mask) / audio->num_channels;
122 }
123 }
124
125 static int32_t mix_f32(audio_source *audio, void *vstream, int len)
126 {
127 int samples = len/(sizeof(float)*output_channels);
128 float *stream = vstream;
129 float *end = stream + output_channels*samples;
130 int16_t *src = audio->front;
131 uint32_t i = audio->read_start;
132 uint32_t i_end = audio->read_end;
133 float *cur = stream;
134 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1;
135 if (audio->num_channels == 1) {
136 while (cur < end && i != i_end)
137 {
138 *cur += ((float)src[i]) / 0x7FFF;
139 cur += first_add;
140 *cur += ((float)src[i++]) / 0x7FFF;
141 cur += second_add;
142 i &= audio->mask;
143 }
144 } else {
145 while(cur < end && i != i_end)
146 {
147 *cur += ((float)src[i++]) / 0x7FFF;
148 cur += first_add;
149 *cur += ((float)src[i++]) / 0x7FFF;
150 cur += second_add;
151 i &= audio->mask;
152 }
153 }
154 if (cur != end) {
155 printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask);
156 return (cur-end)/2;
157 } else {
158 return ((i_end - i) & audio->mask) / audio->num_channels;
159 }
160 }
161
162 static int32_t mix_null(audio_source *audio, void *vstream, int len)
163 {
164 return 0;
165 }
166
167 static mix_func mix;
168
169 static void render_close_audio()
170 {
171
172 }
173
174 #define BUFFER_INC_RES 0x40000000UL
175
176 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
177 {
178 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
179 }
180
181 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
182 {
183 audio_source *ret = NULL;
184 uint32_t alloc_size = channels * buffer_samples;
185 if (num_audio_sources < 8) {
186 ret = malloc(sizeof(audio_source));
187 ret->back = malloc(alloc_size * sizeof(int16_t));
188 ret->front = malloc(alloc_size * sizeof(int16_t));
189 ret->front_populated = 0;
190 ret->num_channels = channels;
191 audio_sources[num_audio_sources++] = ret;
192 }
193 if (!ret) {
194 fatal_error("Too many audio sources!");
195 } else {
196 render_audio_adjust_clock(ret, master_clock, sample_divider);
197 double lowpass_cutoff = get_lowpass_cutoff(config);
198 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
199 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
200 double alpha = ret->dt / (ret->dt + rc);
201 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
202 ret->buffer_pos = 0;
203 ret->buffer_fraction = 0;
204 ret->last_left = ret->last_right = 0;
205 ret->read_start = 0;
206 ret->read_end = buffer_samples * channels;
207 ret->mask = 0xFFFFFFFF;
208 }
209 return ret;
210 }
211
212 void render_pause_source(audio_source *src)
213 {
214 for (uint8_t i = 0; i < num_audio_sources; i++)
215 {
216 if (audio_sources[i] == src) {
217 audio_sources[i] = audio_sources[--num_audio_sources];
218 break;
219 }
220 }
221 inactive_audio_sources[num_inactive_audio_sources++] = src;
222 }
223
224 void render_resume_source(audio_source *src)
225 {
226 if (num_audio_sources < 8) {
227 audio_sources[num_audio_sources++] = src;
228 }
229 for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
230 {
231 if (inactive_audio_sources[i] == src) {
232 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
233 }
234 }
235 }
236
237 void render_free_source(audio_source *src)
238 {
239 render_pause_source(src);
240
241 free(src->front);
242 free(src->back);
243 free(src);
244 }
245 snd_pcm_t *audio_handle;
246 static void do_audio_ready(audio_source *src)
247 {
248 if (src->front_populated) {
249 fatal_error("Audio source filled up a buffer a second time before other sources finished their first\n");
250 }
251 int16_t *tmp = src->front;
252 src->front = src->back;
253 src->back = tmp;
254 src->front_populated = 1;
255 src->buffer_pos = 0;
256
257 for (uint8_t i = 0; i < num_audio_sources; i++)
258 {
259 if (!audio_sources[i]->front_populated) {
260 //at least one audio source is not ready yet.
261 return;
262 }
263 }
264
265 size_t bytes = (mix == mix_s16 ? sizeof(int16_t) : sizeof(float)) * output_channels * buffer_samples;
266 void *buffer = malloc(bytes);
267 for (uint8_t i = 0; i < num_audio_sources; i++)
268 {
269 mix(audio_sources[i], buffer, bytes);
270 audio_sources[i]->front_populated = 0;
271 }
272 int frames = snd_pcm_writei(audio_handle, buffer, buffer_samples);
273 if (frames < 0) {
274 frames = snd_pcm_recover(audio_handle, frames, 0);
275 }
276 if (frames < 0) {
277 fprintf(stderr, "Failed to write samples: %s\n", snd_strerror(frames));
278 }
279 }
280
281 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current)
282 {
283 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha);
284 current = tmp >> 16;
285 return current;
286 }
287
288 static void interp_sample(audio_source *src, int16_t last, int16_t current)
289 {
290 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc);
291 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc));
292 src->back[src->buffer_pos++] = tmp >> 16;
293 }
294
295 void render_put_mono_sample(audio_source *src, int16_t value)
296 {
297 value = lowpass_sample(src, src->last_left, value);
298 src->buffer_fraction += src->buffer_inc;
299 uint32_t base = 0;
300 while (src->buffer_fraction > BUFFER_INC_RES)
301 {
302 src->buffer_fraction -= BUFFER_INC_RES;
303 interp_sample(src, src->last_left, value);
304
305 if (((src->buffer_pos - base) & src->mask) >= buffer_samples) {
306 do_audio_ready(src);
307 }
308 src->buffer_pos &= src->mask;
309 }
310 src->last_left = value;
311 }
312
313 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right)
314 {
315 left = lowpass_sample(src, src->last_left, left);
316 right = lowpass_sample(src, src->last_right, right);
317 src->buffer_fraction += src->buffer_inc;
318 uint32_t base = 0;
319 while (src->buffer_fraction > BUFFER_INC_RES)
320 {
321 src->buffer_fraction -= BUFFER_INC_RES;
322
323 interp_sample(src, src->last_left, left);
324 interp_sample(src, src->last_right, right);
325
326 if (((src->buffer_pos - base) & src->mask)/2 >= buffer_samples) {
327 do_audio_ready(src);
328 }
329 src->buffer_pos &= src->mask;
330 }
331 src->last_left = left;
332 src->last_right = right;
333 }
334
335 int render_width()
336 {
337 return main_width;
338 }
339
340 int render_height()
341 {
342 return main_height;
343 }
344
345 int render_fullscreen()
346 {
347 return 1;
348 }
349
350 uint32_t red_shift, blue_shift, green_shift;
351 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
352 {
353 return r << red_shift | g << green_shift | b << blue_shift;
354 }
355
356 #ifndef DISABLE_OPENGL
357 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos;
358
359 static GLfloat vertex_data_default[] = {
360 -1.0f, -1.0f,
361 1.0f, -1.0f,
362 -1.0f, 1.0f,
363 1.0f, 1.0f
364 };
365
366 static GLfloat vertex_data[8];
367
368 static const GLushort element_data[] = {0, 1, 2, 3};
369
370 static const GLchar shader_prefix[] =
371 #ifdef USE_GLES
372 "#version 100\n";
373 #else
374 "#version 110\n"
375 "#define lowp\n"
376 "#define mediump\n"
377 "#define highp\n";
378 #endif
379
380 static GLuint load_shader(char * fname, GLenum shader_type)
381 {
382 char const * parts[] = {get_home_dir(), "/.config/blastem/shaders/", fname};
383 char * shader_path = alloc_concat_m(3, parts);
384 FILE * f = fopen(shader_path, "rb");
385 free(shader_path);
386 GLchar * text;
387 long fsize;
388 if (f) {
389 fsize = file_size(f);
390 text = malloc(fsize);
391 if (fread(text, 1, fsize, f) != fsize) {
392 warning("Error reading from shader file %s\n", fname);
393 free(text);
394 return 0;
395 }
396 } else {
397 shader_path = path_append("shaders", fname);
398 uint32_t fsize32;
399 text = read_bundled_file(shader_path, &fsize32);
400 free(shader_path);
401 if (!text) {
402 warning("Failed to open shader file %s for reading\n", fname);
403 return 0;
404 }
405 fsize = fsize32;
406 }
407
408 if (strncmp(text, "#version", strlen("#version"))) {
409 GLchar *tmp = text;
410 text = alloc_concat(shader_prefix, tmp);
411 free(tmp);
412 fsize += strlen(shader_prefix);
413 }
414 GLuint ret = glCreateShader(shader_type);
415 glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize);
416 free(text);
417 glCompileShader(ret);
418 GLint compile_status, loglen;
419 glGetShaderiv(ret, GL_COMPILE_STATUS, &compile_status);
420 if (!compile_status) {
421 glGetShaderiv(ret, GL_INFO_LOG_LENGTH, &loglen);
422 text = malloc(loglen);
423 glGetShaderInfoLog(ret, loglen, NULL, text);
424 warning("Shader %s failed to compile:\n%s\n", fname, text);
425 free(text);
426 glDeleteShader(ret);
427 return 0;
428 }
429 return ret;
430 }
431 #endif
432
433 static uint32_t texture_buf[513 * 512 * 2];
434 #ifdef DISABLE_OPENGL
435 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888
436 #else
437 #ifdef USE_GLES
438 #define INTERNAL_FORMAT GL_RGBA
439 #define SRC_FORMAT GL_RGBA
440 #define RENDER_FORMAT SDL_PIXELFORMAT_ABGR8888
441 #else
442 #define INTERNAL_FORMAT GL_RGBA8
443 #define SRC_FORMAT GL_BGRA
444 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888
445 #endif
446 static void gl_setup()
447 {
448 tern_val def = {.ptrval = "linear"};
449 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval;
450 GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR;
451 glGenTextures(3, textures);
452 for (int i = 0; i < 3; i++)
453 {
454 glBindTexture(GL_TEXTURE_2D, textures[i]);
455 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
456 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
457 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
458 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
459 if (i < 2) {
460 //TODO: Fixme for PAL + invalid display mode
461 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 512, 512, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf);
462 } else {
463 uint32_t blank = 255 << 24;
464 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank);
465 }
466 }
467 glGenBuffers(2, buffers);
468 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
469 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
470 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
471 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW);
472 def.ptrval = "default.v.glsl";
473 vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def, TVAL_PTR).ptrval, GL_VERTEX_SHADER);
474 def.ptrval = "default.f.glsl";
475 fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def, TVAL_PTR).ptrval, GL_FRAGMENT_SHADER);
476 program = glCreateProgram();
477 glAttachShader(program, vshader);
478 glAttachShader(program, fshader);
479 glLinkProgram(program);
480 GLint link_status;
481 glGetProgramiv(program, GL_LINK_STATUS, &link_status);
482 if (!link_status) {
483 fputs("Failed to link shader program\n", stderr);
484 exit(1);
485 }
486 un_textures[0] = glGetUniformLocation(program, "textures[0]");
487 un_textures[1] = glGetUniformLocation(program, "textures[1]");
488 un_width = glGetUniformLocation(program, "width");
489 un_height = glGetUniformLocation(program, "height");
490 at_pos = glGetAttribLocation(program, "pos");
491 }
492
493 static void gl_teardown()
494 {
495 glDeleteProgram(program);
496 glDeleteShader(vshader);
497 glDeleteShader(fshader);
498 glDeleteBuffers(2, buffers);
499 glDeleteTextures(3, textures);
500 }
501 #endif
502
503 static uint8_t texture_init;
504 static void render_alloc_surfaces()
505 {
506 if (texture_init) {
507 return;
508 }
509 texture_init = 1;
510 #ifndef DISABLE_OPENGL
511 if (render_gl) {
512 gl_setup();
513 }
514 #endif
515 }
516
517 static void free_surfaces(void)
518 {
519 texture_init = 0;
520 }
521
522 static char * caption = NULL;
523 static char * fps_caption = NULL;
524
525 static void render_quit()
526 {
527 render_close_audio();
528 free_surfaces();
529 #ifndef DISABLE_OPENGL
530 if (render_gl) {
531 gl_teardown();
532 //FIXME: replace with EGL equivalent
533 //SDL_GL_DeleteContext(main_context);
534 }
535 #endif
536 }
537
538 static float config_aspect()
539 {
540 static float aspect = 0.0f;
541 if (aspect == 0.0f) {
542 char *config_aspect = tern_find_path_default(config, "video\0aspect\0", (tern_val){.ptrval = "4:3"}, TVAL_PTR).ptrval;
543 if (strcmp("stretch", config_aspect)) {
544 aspect = 4.0f/3.0f;
545 char *end;
546 float aspect_numerator = strtof(config_aspect, &end);
547 if (aspect_numerator > 0.0f && *end == ':') {
548 float aspect_denominator = strtof(end+1, &end);
549 if (aspect_denominator > 0.0f && !*end) {
550 aspect = aspect_numerator / aspect_denominator;
551 }
552 }
553 } else {
554 aspect = -1.0f;
555 }
556 }
557 return aspect;
558 }
559
560 static void update_aspect()
561 {
562 //reset default values
563 #ifndef DISABLE_OPENGL
564 memcpy(vertex_data, vertex_data_default, sizeof(vertex_data));
565 #endif
566 if (config_aspect() > 0.0f) {
567 float aspect = (float)main_width / main_height;
568 if (fabs(aspect - config_aspect()) < 0.01f) {
569 //close enough for government work
570 return;
571 }
572 #ifndef DISABLE_OPENGL
573 if (render_gl) {
574 for (int i = 0; i < 4; i++)
575 {
576 if (aspect > config_aspect()) {
577 vertex_data[i*2] *= config_aspect()/aspect;
578 } else {
579 vertex_data[i*2+1] *= aspect/config_aspect();
580 }
581 }
582 } else {
583 #endif
584 //TODO: Maybe do some stuff for non-integer scaling in raw fbdev copy
585 #ifndef DISABLE_OPENGL
586 }
587 #endif
588 }
589 }
590
591 /*
592 static ui_render_fun on_context_destroyed, on_context_created;
593 void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create)
594 {
595 on_context_destroyed = destroy;
596 on_context_created = create;
597 }*/
598
599 static uint8_t scancode_map[128] = {
600 [KEY_A] = 0x1C,
601 [KEY_B] = 0x32,
602 [KEY_C] = 0x21,
603 [KEY_D] = 0x23,
604 [KEY_E] = 0x24,
605 [KEY_F] = 0x2B,
606 [KEY_G] = 0x34,
607 [KEY_H] = 0x33,
608 [KEY_I] = 0x43,
609 [KEY_J] = 0x3B,
610 [KEY_K] = 0x42,
611 [KEY_L] = 0x4B,
612 [KEY_M] = 0x3A,
613 [KEY_N] = 0x31,
614 [KEY_O] = 0x44,
615 [KEY_P] = 0x4D,
616 [KEY_Q] = 0x15,
617 [KEY_R] = 0x2D,
618 [KEY_S] = 0x1B,
619 [KEY_T] = 0x2C,
620 [KEY_U] = 0x3C,
621 [KEY_V] = 0x2A,
622 [KEY_W] = 0x1D,
623 [KEY_X] = 0x22,
624 [KEY_Y] = 0x35,
625 [KEY_Z] = 0x1A,
626 [KEY_1] = 0x16,
627 [KEY_2] = 0x1E,
628 [KEY_3] = 0x26,
629 [KEY_4] = 0x25,
630 [KEY_5] = 0x2E,
631 [KEY_6] = 0x36,
632 [KEY_7] = 0x3D,
633 [KEY_8] = 0x3E,
634 [KEY_9] = 0x46,
635 [KEY_0] = 0x45,
636 [KEY_ENTER] = 0x5A,
637 [KEY_ESC] = 0x76,
638 [KEY_SPACE] = 0x29,
639 [KEY_TAB] = 0x0D,
640 [KEY_BACKSPACE] = 0x66,
641 [KEY_MINUS] = 0x4E,
642 [KEY_EQUAL] = 0x55,
643 [KEY_LEFTBRACE] = 0x54,
644 [KEY_RIGHTBRACE] = 0x5B,
645 [KEY_BACKSLASH] = 0x5D,
646 [KEY_SEMICOLON] = 0x4C,
647 [KEY_APOSTROPHE] = 0x52,
648 [KEY_GRAVE] = 0x0E,
649 [KEY_COMMA] = 0x41,
650 [KEY_DOT] = 0x49,
651 [KEY_SLASH] = 0x4A,
652 [KEY_CAPSLOCK] = 0x58,
653 [KEY_F1] = 0x05,
654 [KEY_F2] = 0x06,
655 [KEY_F3] = 0x04,
656 [KEY_F4] = 0x0C,
657 [KEY_F5] = 0x03,
658 [KEY_F6] = 0x0B,
659 [KEY_F7] = 0x83,
660 [KEY_F8] = 0x0A,
661 [KEY_F9] = 0x01,
662 [KEY_F10] = 0x09,
663 [KEY_F11] = 0x78,
664 [KEY_F12] = 0x07,
665 [KEY_LEFTCTRL] = 0x14,
666 [KEY_LEFTSHIFT] = 0x12,
667 [KEY_LEFTALT] = 0x11,
668 [KEY_RIGHTCTRL] = 0x18,
669 [KEY_RIGHTSHIFT] = 0x59,
670 [KEY_RIGHTALT] = 0x17,
671 [KEY_INSERT] = 0x81,
672 [KEY_PAUSE] = 0x82,
673 [KEY_SYSRQ] = 0x84,
674 [KEY_SCROLLLOCK] = 0x7E,
675 [KEY_DELETE] = 0x85,
676 [KEY_LEFT] = 0x86,
677 [KEY_HOME] = 0x87,
678 [KEY_END] = 0x88,
679 [KEY_UP] = 0x89,
680 [KEY_DOWN] = 0x8A,
681 [KEY_PAGEUP] = 0x8B,
682 [KEY_PAGEDOWN] = 0x8C,
683 [KEY_RIGHT] = 0x8D,
684 [KEY_NUMLOCK] = 0x77,
685 [KEY_KPSLASH] = 0x80,
686 [KEY_KPASTERISK] = 0x7C,
687 [KEY_KPMINUS] = 0x7B,
688 [KEY_KPPLUS] = 0x79,
689 [KEY_KPENTER] = 0x19,
690 [KEY_KP1] = 0x69,
691 [KEY_KP2] = 0x72,
692 [KEY_KP3] = 0x7A,
693 [KEY_KP4] = 0x6B,
694 [KEY_KP5] = 0x73,
695 [KEY_KP6] = 0x74,
696 [KEY_KP7] = 0x6C,
697 [KEY_KP8] = 0x75,
698 [KEY_KP9] = 0x7D,
699 [KEY_KP0] = 0x70,
700 [KEY_KPDOT] = 0x71,
701 };
702
703 #include "special_keys_evdev.h"
704 static uint8_t sym_map[128] = {
705 [KEY_A] = 'a',
706 [KEY_B] = 'b',
707 [KEY_C] = 'c',
708 [KEY_D] = 'd',
709 [KEY_E] = 'e',
710 [KEY_F] = 'f',
711 [KEY_G] = 'g',
712 [KEY_H] = 'h',
713 [KEY_I] = 'i',
714 [KEY_J] = 'j',
715 [KEY_K] = 'k',
716 [KEY_L] = 'l',
717 [KEY_M] = 'm',
718 [KEY_N] = 'n',
719 [KEY_O] = 'o',
720 [KEY_P] = 'p',
721 [KEY_Q] = 'q',
722 [KEY_R] = 'r',
723 [KEY_S] = 's',
724 [KEY_T] = 't',
725 [KEY_U] = 'u',
726 [KEY_V] = 'v',
727 [KEY_W] = 'w',
728 [KEY_X] = 'x',
729 [KEY_Y] = 'y',
730 [KEY_Z] = 'z',
731 [KEY_1] = '1',
732 [KEY_2] = '2',
733 [KEY_3] = '3',
734 [KEY_4] = '4',
735 [KEY_5] = '5',
736 [KEY_6] = '6',
737 [KEY_7] = '7',
738 [KEY_8] = '8',
739 [KEY_9] = '9',
740 [KEY_0] = '0',
741 [KEY_ENTER] = '\r',
742 [KEY_SPACE] = ' ',
743 [KEY_TAB] = '\t',
744 [KEY_BACKSPACE] = '\b',
745 [KEY_MINUS] = '-',
746 [KEY_EQUAL] = '=',
747 [KEY_LEFTBRACE] = '[',
748 [KEY_RIGHTBRACE] = ']',
749 [KEY_BACKSLASH] = '\\',
750 [KEY_SEMICOLON] = ';',
751 [KEY_APOSTROPHE] = '\'',
752 [KEY_GRAVE] = '`',
753 [KEY_COMMA] = ',',
754 [KEY_DOT] = '.',
755 [KEY_SLASH] = '/',
756 [KEY_ESC] = RENDERKEY_ESC,
757 [KEY_F1] = RENDERKEY_F1,
758 [KEY_F2] = RENDERKEY_F2,
759 [KEY_F3] = RENDERKEY_F3,
760 [KEY_F4] = RENDERKEY_F4,
761 [KEY_F5] = RENDERKEY_F5,
762 [KEY_F6] = RENDERKEY_F6,
763 [KEY_F7] = RENDERKEY_F7,
764 [KEY_F8] = RENDERKEY_F8,
765 [KEY_F9] = RENDERKEY_F9,
766 [KEY_F10] = RENDERKEY_F10,
767 [KEY_F11] = RENDERKEY_F11,
768 [KEY_F12] = RENDERKEY_F12,
769 [KEY_LEFTCTRL] = RENDERKEY_LCTRL,
770 [KEY_LEFTSHIFT] = RENDERKEY_LSHIFT,
771 [KEY_LEFTALT] = RENDERKEY_LALT,
772 [KEY_RIGHTCTRL] = RENDERKEY_RCTRL,
773 [KEY_RIGHTSHIFT] = RENDERKEY_RSHIFT,
774 [KEY_RIGHTALT] = RENDERKEY_RALT,
775 [KEY_DELETE] = RENDERKEY_DEL,
776 [KEY_LEFT] = RENDERKEY_LEFT,
777 [KEY_HOME] = RENDERKEY_HOME,
778 [KEY_END] = RENDERKEY_END,
779 [KEY_UP] = RENDERKEY_UP,
780 [KEY_DOWN] = RENDERKEY_DOWN,
781 [KEY_PAGEUP] = RENDERKEY_PAGEUP,
782 [KEY_PAGEDOWN] = RENDERKEY_PAGEDOWN,
783 [KEY_RIGHT] = RENDERKEY_RIGHT,
784 [KEY_KPSLASH] = 0x80,
785 [KEY_KPASTERISK] = 0x7C,
786 [KEY_KPMINUS] = 0x7B,
787 [KEY_KPPLUS] = 0x79,
788 [KEY_KPENTER] = 0x19,
789 [KEY_KP1] = 0x69,
790 [KEY_KP2] = 0x72,
791 [KEY_KP3] = 0x7A,
792 [KEY_KP4] = 0x6B,
793 [KEY_KP5] = 0x73,
794 [KEY_KP6] = 0x74,
795 [KEY_KP7] = 0x6C,
796 [KEY_KP8] = 0x75,
797 [KEY_KP9] = 0x7D,
798 [KEY_KP0] = 0x70,
799 [KEY_KPDOT] = 0x71,
800 };
801
802 static drop_handler drag_drop_handler;
803 void render_set_drag_drop_handler(drop_handler handler)
804 {
805 drag_drop_handler = handler;
806 }
807
808 /*static event_handler custom_event_handler;
809 void render_set_event_handler(event_handler handler)
810 {
811 custom_event_handler = handler;
812 }*/
813
814 char* render_joystick_type_id(int index)
815 {
816 return "";
817 /*SDL_Joystick *stick = render_get_joystick(index);
818 if (!stick) {
819 return NULL;
820 }
821 char *guid_string = malloc(33);
822 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, 33);
823 return guid_string;*/
824 }
825
826 static uint32_t overscan_top[NUM_VID_STD] = {2, 21};
827 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17};
828 static uint32_t overscan_left[NUM_VID_STD] = {13, 13};
829 static uint32_t overscan_right[NUM_VID_STD] = {14, 14};
830 static vid_std video_standard = VID_NTSC;
831
832 typedef enum {
833 DEV_NONE,
834 DEV_KEYBOARD,
835 DEV_MOUSE,
836 DEV_GAMEPAD
837 } device_type;
838
839 static int32_t mouse_x, mouse_y, mouse_accum_x, mouse_accum_y;
840 static int32_t handle_event(device_type dtype, int device_index, struct input_event *event)
841 {
842 switch (event->type) {
843 case EV_KEY:
844 //code is key, value is 1 for keydown, 0 for keyup
845 if (dtype == DEV_KEYBOARD && event->code < 128) {
846 //keyboard key that we might have a mapping for
847 if (event->value) {
848 handle_keydown(sym_map[event->code], scancode_map[event->code]);
849 } else {
850 handle_keyup(sym_map[event->code], scancode_map[event->code]);
851 }
852 } else if (dtype == DEV_MOUSE && event->code >= BTN_MOUSE && event->code < BTN_JOYSTICK) {
853 //mosue button
854 if (event->value) {
855 handle_mousedown(device_index, event->code - BTN_LEFT);
856 } else {
857 handle_mouseup(device_index, event->code - BTN_LEFT);
858 }
859 } else if (dtype == DEV_GAMEPAD && event->code >= BTN_GAMEPAD && event->code < BTN_DIGI) {
860 //gamepad button
861 if (event->value) {
862 handle_joydown(device_index, event->code - BTN_SOUTH);
863 } else {
864 handle_joyup(device_index, event->code - BTN_SOUTH);
865 }
866 }
867 break;
868 case EV_REL:
869 if (dtype == DEV_MOUSE) {
870 switch(event->code)
871 {
872 case REL_X:
873 mouse_accum_x += event->value;
874 break;
875 case REL_Y:
876 mouse_accum_y += event->value;
877 break;
878 }
879 }
880 break;
881 case EV_ABS:
882 //TODO: Handle joystick axis/hat motion, absolute mouse movement
883 break;
884 case EV_SYN:
885 if (dtype == DEV_MOUSE && (mouse_accum_x || mouse_accum_y)) {
886 mouse_x += mouse_accum_x;
887 mouse_y += mouse_accum_y;
888 if (mouse_x < 0) {
889 mouse_x = 0;
890 } else if (mouse_x >= main_width) {
891 mouse_x = main_width - 1;
892 }
893 if (mouse_y < 0) {
894 mouse_y = 0;
895 } else if (mouse_y >= main_height) {
896 mouse_y = main_height - 1;
897 }
898 handle_mouse_moved(device_index, mouse_x, mouse_y, mouse_accum_x, mouse_accum_y);
899 mouse_accum_x = mouse_accum_y = 0;
900 }
901 break;
902 /*
903 case SDL_JOYHATMOTION:
904 handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value);
905 break;
906 case SDL_JOYAXISMOTION:
907 handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value);
908 break;*/
909 }
910 return 0;
911 }
912
913 #define MAX_DEVICES 16
914 static int device_fds[MAX_DEVICES];
915 static device_type device_types[MAX_DEVICES];
916 static int cur_devices;
917
918 static void drain_events()
919 {
920 struct input_event events[64];
921 int index_by_type[3] = {0,0,0};
922 for (int i = 0; i < cur_devices; i++)
923 {
924 int bytes = sizeof(events);
925 int device_index = index_by_type[device_types[i]-1]++;
926 while (bytes == sizeof(events))
927 {
928 bytes = read(device_fds[i], events, sizeof(events));
929 if (bytes > 0) {
930 int num_events = bytes / sizeof(events[0]);
931 for (int j = 0; j < num_events; j++)
932 {
933 handle_event(device_types[i], device_index, events + j);
934 }
935 } else if (bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
936 perror("Failed to read evdev events");
937 }
938 }
939 }
940 }
941
942 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
943
944 static void init_audio()
945 {
946 int res = snd_pcm_open(&audio_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
947 if (res < 0) {
948 fatal_error("Failed to open ALSA device: %s\n", snd_strerror(res));
949 }
950
951 snd_pcm_hw_params_t *params;
952 snd_pcm_hw_params_alloca(&params);
953 res = snd_pcm_hw_params_any(audio_handle, params);
954 if (res < 0) {
955 fatal_error("No playback configurations available: %s\n", snd_strerror(res));
956 }
957 res = snd_pcm_hw_params_set_access(audio_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
958 if (res < 0) {
959 fatal_error("Failed to set access type: %s\n", snd_strerror(res));
960 }
961 res = snd_pcm_hw_params_set_format(audio_handle, params, SND_PCM_FORMAT_S16_LE);
962 if (res < 0) {
963 //failed to set, signed 16-bit integer, try floating point
964 res = snd_pcm_hw_params_set_format(audio_handle, params, SND_PCM_FORMAT_FLOAT_LE);
965 if (res < 0) {
966 fatal_error("Failed to set an acceptable format: %s\n", snd_strerror(res));
967 }
968 mix = mix_f32;
969 } else {
970 mix = mix_s16;
971 }
972
973 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
974 sample_rate = rate_str ? atoi(rate_str) : 0;
975 if (!sample_rate) {
976 sample_rate = 48000;
977 }
978 snd_pcm_hw_params_set_rate_near(audio_handle, params, &sample_rate, NULL);
979 output_channels = 2;
980 snd_pcm_hw_params_set_channels_near(audio_handle, params, &output_channels);
981
982 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
983 buffer_samples = samples_str ? atoi(samples_str) : 0;
984 if (!buffer_samples) {
985 buffer_samples = 512;
986 }
987 snd_pcm_hw_params_set_period_size_near(audio_handle, params, &buffer_samples, NULL);
988
989 int dir = 1;
990 unsigned int periods = 2;
991 snd_pcm_hw_params_set_periods_near(audio_handle, params, &periods, &dir);
992
993 res = snd_pcm_hw_params(audio_handle, params);
994 if (res < 0) {
995 fatal_error("Failed to set ALSA hardware params: %s\n", snd_strerror(res));
996 }
997
998 printf("Initialized audio at frequency %d with a %d sample buffer, ", (int)sample_rate, (int)buffer_samples);
999 if (mix == mix_s16) {
1000 puts("signed 16-bit int format");
1001 } else {
1002 puts("32-bit float format");
1003 }
1004 }
1005
1006 int fbfd;
1007 uint32_t *framebuffer;
1008 uint32_t fb_stride;
1009 #ifndef DISABLE_OPENGL
1010 EGLDisplay egl_display;
1011 EGLSurface main_surface;
1012 uint8_t egl_setup(void)
1013 {
1014 //Mesa wants the fbdev file descriptor as the display
1015 egl_display = eglGetDisplay((EGLNativeDisplayType)fbfd);
1016 if (egl_display == EGL_NO_DISPLAY) {
1017 //Mali (and possibly others) seems to just want EGL_DEFAULT_DISPLAY
1018 egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1019 if (egl_display == EGL_NO_DISPLAY) {
1020 warning("eglGetDisplay failed with error %X\n", eglGetError());
1021 return 0;
1022 }
1023 }
1024 EGLint major, minor;
1025 if (!eglInitialize(egl_display, &major, &minor)) {
1026 warning("eglInitialize failed with error %X\n", eglGetError());
1027 return 0;
1028 }
1029 printf("EGL version %d.%d\n", major, minor);
1030 EGLint num_configs;
1031 EGLConfig config;
1032 EGLint const config_attribs[] = {
1033 EGL_RED_SIZE, 5,
1034 EGL_GREEN_SIZE, 5,
1035 EGL_BLUE_SIZE, 5,
1036 EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
1037 EGL_NONE
1038 };
1039 if (!eglChooseConfig(egl_display, config_attribs, &config, 1, &num_configs)) {
1040 num_configs = 0;
1041 warning("eglChooseConfig failed with error %X\n", eglGetError());
1042 }
1043 if (!num_configs) {
1044 warning("Failed to choose an EGL config\n");
1045 goto error;
1046 }
1047 EGLint const context_attribs[] = {
1048 #ifdef EGL_CONTEXT_MAJOR_VERSION
1049 EGL_CONTEXT_MAJOR_VERSION, 2,
1050 #endif
1051 EGL_NONE
1052 };
1053 main_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, context_attribs);
1054 if (main_context == EGL_NO_CONTEXT) {
1055 warning("Failed to create EGL context %X\n", eglGetError());
1056 goto error;
1057 }
1058 #ifdef USE_MALI
1059 struct mali_native_window native_window = {
1060 .width = main_width,
1061 .height = main_height
1062 };
1063 main_surface = eglCreateWindowSurface(egl_display, config, &native_window, NULL);
1064 #else
1065 main_surface = eglCreateWindowSurface(egl_display, config, (EGLNativeWindowType)NULL, NULL);
1066 #endif
1067 if (main_surface == EGL_NO_SURFACE) {
1068 warning("Failed to create EGL surface %X\n", eglGetError());
1069 goto post_context_error;
1070 }
1071 if (eglMakeCurrent(egl_display, main_surface, main_surface, main_context)) {
1072 return 1;
1073 }
1074 eglDestroySurface(egl_display, main_surface);
1075 post_context_error:
1076 eglDestroyContext(egl_display, main_context);
1077 error:
1078 eglTerminate(egl_display);
1079 return 0;
1080 }
1081 #endif
1082 static pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
1083 static pthread_cond_t buffer_cond = PTHREAD_COND_INITIALIZER;
1084 static uint8_t buffer_ready;
1085 static uint32_t *copy_buffer;
1086 static uint32_t last_width, last_height;
1087 static uint32_t max_multiple;
1088 static void do_buffer_copy(void)
1089 {
1090 uint32_t width_multiple = main_width / last_width;
1091 uint32_t height_multiple = main_height / last_height;
1092 uint32_t multiple = width_multiple < height_multiple ? width_multiple : height_multiple;
1093 if (max_multiple && multiple > max_multiple) {
1094 multiple = max_multiple;
1095 }
1096 uint32_t *cur_line = framebuffer + (main_width - last_width * multiple)/2;
1097 cur_line += fb_stride * (main_height - last_height * multiple) / (2 * sizeof(uint32_t));
1098 uint32_t *src_line = copy_buffer;
1099 for (uint32_t y = 0; y < last_height; y++)
1100 {
1101 for (uint32_t i = 0; i < multiple; i++)
1102 {
1103 uint32_t *cur = cur_line;
1104 uint32_t *src = src_line;
1105 for (uint32_t x = 0; x < last_width ; x++)
1106 {
1107 uint32_t pixel = *(src++);
1108 for (uint32_t j = 0; j < multiple; j++)
1109 {
1110 *(cur++) = pixel;
1111 }
1112 }
1113
1114 cur_line += fb_stride / sizeof(uint32_t);
1115 }
1116 src_line += LINEBUF_SIZE;
1117 }
1118 }
1119 static void *buffer_copy(void *data)
1120 {
1121 pthread_mutex_lock(&buffer_lock);
1122 for(;;)
1123 {
1124 while (!buffer_ready)
1125 {
1126 pthread_cond_wait(&buffer_cond, &buffer_lock);
1127 }
1128 buffer_ready = 0;
1129 do_buffer_copy();
1130 }
1131 return 0;
1132 }
1133
1134 static pthread_t buffer_copy_handle;
1135 static uint8_t copy_use_thread;
1136 void window_setup(void)
1137 {
1138 fbfd = open("/dev/fb0", O_RDWR);
1139 struct fb_fix_screeninfo fixInfo;
1140 struct fb_var_screeninfo varInfo;
1141 ioctl(fbfd, FBIOGET_FSCREENINFO, &fixInfo);
1142 ioctl(fbfd, FBIOGET_VSCREENINFO, &varInfo);
1143 printf("Resolution: %d x %d\n", varInfo.xres, varInfo.yres);
1144 printf("Framebuffer size: %d, line stride: %d\n", fixInfo.smem_len, fixInfo.line_length);
1145 main_width = varInfo.xres;
1146 main_height = varInfo.yres;
1147 fb_stride = fixInfo.line_length;
1148 tern_val def = {.ptrval = "audio"};
1149 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval;
1150
1151 const char *vsync;
1152 def.ptrval = "off";
1153 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
1154
1155
1156 tern_node *video = tern_find_node(config, "video");
1157 if (video)
1158 {
1159 for (int i = 0; i < NUM_VID_STD; i++)
1160 {
1161 tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
1162 if (std_settings) {
1163 char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1164 if (val) {
1165 overscan_top[i] = atoi(val);
1166 }
1167 val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1168 if (val) {
1169 overscan_bot[i] = atoi(val);
1170 }
1171 val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1172 if (val) {
1173 overscan_left[i] = atoi(val);
1174 }
1175 val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1176 if (val) {
1177 overscan_right[i] = atoi(val);
1178 }
1179 }
1180 }
1181 }
1182 render_gl = 0;
1183 #ifndef DISABLE_OPENGL
1184 char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
1185 uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
1186 if (gl_enabled)
1187 {
1188 render_gl = egl_setup();
1189 blue_shift = 16;
1190 green_shift = 8;
1191 red_shift = 0;
1192 }
1193 if (!render_gl) {
1194 #endif
1195 framebuffer = mmap(NULL, fixInfo.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fbfd, 0);
1196 red_shift = varInfo.red.offset;
1197 green_shift = varInfo.green.offset;
1198 blue_shift = varInfo.blue.offset;
1199 def.ptrval = "0";
1200 max_multiple = atoi(tern_find_path_default(config, "video\0fbdev\0max_multiple\0", def, TVAL_PTR).ptrval);
1201 def.ptrval = "true";
1202 copy_use_thread = strcmp(tern_find_path_default(config, "video\0fbdev\0use_thread\0", def, TVAL_PTR).ptrval, "false");
1203 if (copy_use_thread) {
1204 pthread_create(&buffer_copy_handle, NULL, buffer_copy, NULL);
1205 }
1206 #ifndef DISABLE_OPENGL
1207 }
1208 #endif
1209
1210 /*
1211 #ifndef DISABLE_OPENGL
1212 char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
1213 uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
1214 if (gl_enabled)
1215 {
1216 flags |= SDL_WINDOW_OPENGL;
1217 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
1218 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
1219 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
1220 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
1221 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
1222 #ifdef USE_GLES
1223 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
1224 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
1225 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
1226 #endif
1227 }
1228 #endif
1229 main_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, main_width, main_height, flags);
1230 if (!main_window) {
1231 fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
1232 }
1233 #ifndef DISABLE_OPENGL
1234 if (gl_enabled)
1235 {
1236 main_context = SDL_GL_CreateContext(main_window);
1237 #ifdef USE_GLES
1238 int major_version;
1239 if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major_version) == 0 && major_version >= 2) {
1240 #else
1241 GLenum res = glewInit();
1242 if (res != GLEW_OK) {
1243 warning("Initialization of GLEW failed with code %d\n", res);
1244 }
1245
1246 if (res == GLEW_OK && GLEW_VERSION_2_0) {
1247 #endif
1248 render_gl = 1;
1249 SDL_GL_MakeCurrent(main_window, main_context);
1250 if (!strcmp("tear", vsync)) {
1251 if (SDL_GL_SetSwapInterval(-1) < 0) {
1252 warning("late tear is not available (%s), using normal vsync\n", SDL_GetError());
1253 vsync = "on";
1254 } else {
1255 vsync = NULL;
1256 }
1257 }
1258 if (vsync) {
1259 if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) {
1260 warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError());
1261 }
1262 }
1263 } else {
1264 warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n");
1265 }
1266 }
1267 if (!render_gl) {
1268 #endif
1269 flags = SDL_RENDERER_ACCELERATED;
1270 if (!strcmp("on", vsync) || !strcmp("tear", vsync)) {
1271 flags |= SDL_RENDERER_PRESENTVSYNC;
1272 }
1273 main_renderer = SDL_CreateRenderer(main_window, -1, flags);
1274
1275 if (!main_renderer) {
1276 fatal_error("unable to create SDL renderer: %s\n", SDL_GetError());
1277 }
1278 SDL_RendererInfo rinfo;
1279 SDL_GetRendererInfo(main_renderer, &rinfo);
1280 printf("SDL2 Render Driver: %s\n", rinfo.name);
1281 main_clip.x = main_clip.y = 0;
1282 main_clip.w = main_width;
1283 main_clip.h = main_height;
1284 #ifndef DISABLE_OPENGL
1285 }
1286 #endif
1287
1288 SDL_GetWindowSize(main_window, &main_width, &main_height);
1289 printf("Window created with size: %d x %d\n", main_width, main_height);*/
1290 update_aspect();
1291 render_alloc_surfaces();
1292 def.ptrval = "off";
1293 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
1294 }
1295
1296 void restore_tty(void)
1297 {
1298 ioctl(STDIN_FILENO, KDSETMODE, KD_TEXT);
1299 }
1300
1301 void render_init(int width, int height, char * title, uint8_t fullscreen)
1302 {
1303 if (height <= 0) {
1304 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
1305 height = ((float)width / aspect) + 0.5f;
1306 }
1307 printf("width: %d, height: %d\n", width, height);
1308 windowed_width = width;
1309 windowed_height = height;
1310
1311 main_width = width;
1312 main_height = height;
1313
1314 caption = title;
1315
1316 if (isatty(STDIN_FILENO)) {
1317 ioctl(STDIN_FILENO, KDSETMODE, KD_GRAPHICS);
1318 atexit(restore_tty);
1319 }
1320
1321 window_setup();
1322
1323 init_audio();
1324
1325 render_set_video_standard(VID_NTSC);
1326
1327 DIR *d = opendir("/dev/input");
1328 struct dirent* entry;
1329 int joystick_counter = 0;
1330 while ((entry = readdir(d)) && cur_devices < MAX_DEVICES)
1331 {
1332 if (!strncmp("event", entry->d_name, strlen("event"))) {
1333 char *filename = alloc_concat("/dev/input/", entry->d_name);
1334 int fd = open(filename, O_RDONLY);
1335 if (fd == -1) {
1336 int errnum = errno;
1337 warning("Failed to open evdev device %s for reading: %s\n", filename, strerror(errnum));
1338 free(filename);
1339 continue;
1340 }
1341
1342 unsigned long bits;
1343 if (-1 == ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits)) {
1344 int errnum = errno;
1345 warning("Failed get capability bits from evdev device %s: %s\n", filename, strerror(errnum));
1346 free(filename);
1347 close(fd);
1348 continue;
1349 }
1350 if (!(1 & bits >> EV_KEY)) {
1351 //if it doesn't support key events we don't care about it
1352 free(filename);
1353 close(fd);
1354 continue;
1355 }
1356 unsigned long button_bits[(BTN_THUMBR+8*sizeof(long))/(8*sizeof(long))];
1357 int res = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(button_bits)), button_bits);
1358 if (-1 == res) {
1359 int errnum = errno;
1360 warning("Failed get capability bits from evdev device %s: %s\n", filename, strerror(errnum));
1361 free(filename);
1362 close(fd);
1363 continue;
1364 }
1365 int to_check[] = {KEY_ENTER, BTN_MOUSE, BTN_GAMEPAD};
1366 device_type dtype = DEV_NONE;
1367 for (int i = 0; i < 4; i++)
1368 {
1369 if (1 & button_bits[to_check[i]/(8*sizeof(button_bits[0]))] >> to_check[i]%(8*sizeof(button_bits[0]))) {
1370 dtype = i + 1;
1371 }
1372 }
1373 if (dtype == DEV_NONE) {
1374 close(fd);
1375 } else {
1376 device_fds[cur_devices] = fd;
1377 device_types[cur_devices] = dtype;
1378 char name[1024];
1379 char *names[] = {"Keyboard", "Mouse", "Gamepad"};
1380 ioctl(fd, EVIOCGNAME(sizeof(name)), name);
1381 printf("%s is a %s\n%s\n", filename, names[dtype - 1], name);
1382 //set FD to non-blocking mode for event polling
1383 fcntl(fd, F_SETFL, O_NONBLOCK);
1384 if (dtype == DEV_GAMEPAD) {
1385 handle_joy_added(joystick_counter++);
1386 }
1387 cur_devices++;
1388 }
1389 free(filename);
1390 }
1391 }
1392
1393 atexit(render_quit);
1394 }
1395 #include<unistd.h>
1396 static int in_toggle;
1397 static void update_source(audio_source *src, double rc, uint8_t sync_changed)
1398 {
1399 double alpha = src->dt / (src->dt + rc);
1400 int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
1401 src->lowpass_alpha = lowpass_alpha;
1402 }
1403
1404 void render_config_updated(void)
1405 {
1406
1407 free_surfaces();
1408 #ifndef DISABLE_OPENGL
1409 if (render_gl) {
1410 /*if (on_context_destroyed) {
1411 on_context_destroyed();
1412 }*/
1413 gl_teardown();
1414 //FIXME: EGL equivalent
1415 //SDL_GL_DeleteContext(main_context);
1416 } else {
1417 #endif
1418 #ifndef DISABLE_OPENGL
1419 }
1420 #endif
1421 //FIXME: EGL equivalent
1422 //SDL_DestroyWindow(main_window);
1423 drain_events();
1424
1425 char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
1426 if (config_width) {
1427 windowed_width = atoi(config_width);
1428 }
1429 char *config_height = tern_find_path(config, "video\0height\0", TVAL_PTR).ptrval;
1430 if (config_height) {
1431 windowed_height = atoi(config_height);
1432 } else {
1433 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
1434 windowed_height = ((float)windowed_width / aspect) + 0.5f;
1435 }
1436
1437 window_setup();
1438 update_aspect();
1439 #ifndef DISABLE_OPENGL
1440 //need to check render_gl again after window_setup as render option could have changed
1441 /*if (render_gl && on_context_created) {
1442 on_context_created();
1443 }*/
1444 #endif
1445
1446 render_close_audio();
1447 quitting = 0;
1448 init_audio();
1449 render_set_video_standard(video_standard);
1450
1451 double lowpass_cutoff = get_lowpass_cutoff(config);
1452 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
1453 for (uint8_t i = 0; i < num_audio_sources; i++)
1454 {
1455 update_source(audio_sources[i], rc, 0);
1456 }
1457 for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
1458 {
1459 update_source(inactive_audio_sources[i], rc, 0);
1460 }
1461 drain_events();
1462 }
1463
1464 void render_set_video_standard(vid_std std)
1465 {
1466 video_standard = std;
1467 }
1468
1469 void render_update_caption(char *title)
1470 {
1471 caption = title;
1472 free(fps_caption);
1473 fps_caption = NULL;
1474 }
1475
1476 static char *screenshot_path;
1477 void render_save_screenshot(char *path)
1478 {
1479 if (screenshot_path) {
1480 free(screenshot_path);
1481 }
1482 screenshot_path = path;
1483 }
1484
1485 uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler)
1486 {
1487 //not supported under fbdev
1488 return 0;
1489 }
1490
1491 void render_destroy_window(uint8_t which)
1492 {
1493 //not supported under fbdev
1494 }
1495
1496 uint32_t *locked_pixels;
1497 uint32_t locked_pitch;
1498 uint32_t texture_off;
1499 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
1500 {
1501 if (max_multiple == 1 && !render_gl) {
1502 *pitch = fb_stride;
1503 return framebuffer;
1504 }
1505 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1506 return texture_buf + texture_off;
1507 /*
1508 #ifndef DISABLE_OPENGL
1509 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1510 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1511 return texture_buf;
1512 } else {
1513 #endif
1514 if (which >= num_textures) {
1515 warning("Request for invalid framebuffer number %d\n", which);
1516 return NULL;
1517 }
1518 void *pixels;
1519 if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) {
1520 warning("Failed to lock texture: %s\n", SDL_GetError());
1521 return NULL;
1522 }
1523 static uint8_t last;
1524 if (which <= FRAMEBUFFER_EVEN) {
1525 locked_pixels = pixels;
1526 if (which == FRAMEBUFFER_EVEN) {
1527 pixels += *pitch;
1528 }
1529 locked_pitch = *pitch;
1530 if (which != last) {
1531 *pitch *= 2;
1532 }
1533 last = which;
1534 }
1535 return pixels;
1536 #ifndef DISABLE_OPENGL
1537 }
1538 #endif*/
1539 }
1540
1541 uint8_t events_processed;
1542 #ifdef __ANDROID__
1543 #define FPS_INTERVAL 10000
1544 #else
1545 #define FPS_INTERVAL 1000
1546 #endif
1547
1548 static uint8_t interlaced;
1549 void render_update_display();
1550 void render_framebuffer_updated(uint8_t which, int width)
1551 {
1552 uint32_t height = which <= FRAMEBUFFER_EVEN
1553 ? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
1554 : 240;
1555 width -= overscan_left[video_standard] + overscan_right[video_standard];
1556 #ifndef DISABLE_OPENGL
1557 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1558 last_width = width;
1559 glBindTexture(GL_TEXTURE_2D, textures[which]);
1560 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
1561 render_update_display();
1562 last_height = height;
1563 } else {
1564 #endif
1565 if (max_multiple != 1) {
1566 if (copy_use_thread) {
1567 pthread_mutex_lock(&buffer_lock);
1568 buffer_ready = 1;
1569 last_width = width;
1570 last_height = height;
1571 copy_buffer = texture_buf + texture_off + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard];
1572 texture_off = texture_off ? 0 : LINEBUF_SIZE * 512;
1573 pthread_cond_signal(&buffer_cond);
1574 pthread_mutex_unlock(&buffer_lock);
1575 } else {
1576 last_width = width;
1577 last_height = height;
1578 copy_buffer = texture_buf + texture_off + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard];
1579 do_buffer_copy();
1580 }
1581 }
1582 if (!events_processed) {
1583 process_events();
1584 }
1585 events_processed = 0;
1586 #ifndef DISABLE_OPENGL
1587 }
1588 #endif
1589 /*
1590 FILE *screenshot_file = NULL;
1591 uint32_t shot_height, shot_width;
1592 char *ext;
1593 if (screenshot_path && which == FRAMEBUFFER_ODD) {
1594 screenshot_file = fopen(screenshot_path, "wb");
1595 if (screenshot_file) {
1596 #ifndef DISABLE_ZLIB
1597 ext = path_extension(screenshot_path);
1598 #endif
1599 info_message("Saving screenshot to %s\n", screenshot_path);
1600 } else {
1601 warning("Failed to open screenshot file %s for writing\n", screenshot_path);
1602 }
1603 free(screenshot_path);
1604 screenshot_path = NULL;
1605 shot_height = video_standard == VID_NTSC ? 243 : 294;
1606 shot_width = width;
1607 }
1608 interlaced = last != which;
1609 width -= overscan_left[video_standard] + overscan_right[video_standard];
1610 #ifndef DISABLE_OPENGL
1611 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1612 SDL_GL_MakeCurrent(main_window, main_context);
1613 glBindTexture(GL_TEXTURE_2D, textures[which]);
1614 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
1615
1616 if (screenshot_file) {
1617 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now
1618 #ifndef DISABLE_ZLIB
1619 if (!strcasecmp(ext, "png")) {
1620 free(ext);
1621 save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1622 } else {
1623 free(ext);
1624 #endif
1625 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1626 #ifndef DISABLE_ZLIB
1627 }
1628 #endif
1629 }
1630 } else {
1631 #endif
1632 if (which <= FRAMEBUFFER_EVEN && last != which) {
1633 uint8_t *cur_dst = (uint8_t *)locked_pixels;
1634 uint8_t *cur_saved = (uint8_t *)texture_buf;
1635 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
1636 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
1637 for (int i = 0; i < height; ++i)
1638 {
1639 //copy saved line from other field
1640 memcpy(cur_dst + dst_off, cur_saved, locked_pitch);
1641 //save line from this field to buffer for next frame
1642 memcpy(cur_saved, cur_dst + src_off, locked_pitch);
1643 cur_dst += locked_pitch * 2;
1644 cur_saved += locked_pitch;
1645 }
1646 height = 480;
1647 }
1648 if (screenshot_file) {
1649 uint32_t shot_pitch = locked_pitch;
1650 if (which == FRAMEBUFFER_EVEN) {
1651 shot_height *= 2;
1652 } else {
1653 shot_pitch *= 2;
1654 }
1655 #ifndef DISABLE_ZLIB
1656 if (!strcasecmp(ext, "png")) {
1657 free(ext);
1658 save_png(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
1659 } else {
1660 free(ext);
1661 #endif
1662 save_ppm(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
1663 #ifndef DISABLE_ZLIB
1664 }
1665 #endif
1666 }
1667 SDL_UnlockTexture(sdl_textures[which]);
1668 #ifndef DISABLE_OPENGL
1669 }
1670 #endif
1671 last_height = height;
1672 if (which <= FRAMEBUFFER_EVEN) {
1673 render_update_display();
1674 } else {
1675 SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL);
1676 SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]);
1677 }
1678 if (screenshot_file) {
1679 fclose(screenshot_file);
1680 }
1681 if (which <= FRAMEBUFFER_EVEN) {
1682 last = which;
1683 static uint32_t frame_counter, start;
1684 frame_counter++;
1685 last_frame= SDL_GetTicks();
1686 if ((last_frame - start) > FPS_INTERVAL) {
1687 if (start && (last_frame-start)) {
1688 #ifdef __ANDROID__
1689 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
1690 #else
1691 if (!fps_caption) {
1692 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1);
1693 }
1694 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
1695 SDL_SetWindowTitle(main_window, fps_caption);
1696 #endif
1697 }
1698 start = last_frame;
1699 frame_counter = 0;
1700 }
1701 }
1702 if (!sync_to_audio) {
1703 int32_t local_cur_min, local_min_remaining;
1704 SDL_LockAudio();
1705 if (last_buffered > NO_LAST_BUFFERED) {
1706 average_change *= 0.9f;
1707 average_change += (cur_min_buffered - last_buffered) * 0.1f;
1708 }
1709 local_cur_min = cur_min_buffered;
1710 local_min_remaining = min_remaining_buffer;
1711 last_buffered = cur_min_buffered;
1712 SDL_UnlockAudio();
1713 float frames_to_problem;
1714 if (average_change < 0) {
1715 frames_to_problem = (float)local_cur_min / -average_change;
1716 } else {
1717 frames_to_problem = (float)local_min_remaining / average_change;
1718 }
1719 float adjust_ratio = 0.0f;
1720 if (
1721 frames_to_problem < BUFFER_FRAMES_THRESHOLD
1722 || (average_change < 0 && local_cur_min < 3*min_buffered / 4)
1723 || (average_change >0 && local_cur_min > 5 * min_buffered / 4)
1724 || cur_min_buffered < 0
1725 ) {
1726
1727 if (cur_min_buffered < 0) {
1728 adjust_ratio = max_adjust;
1729 SDL_PauseAudio(1);
1730 last_buffered = NO_LAST_BUFFERED;
1731 cur_min_buffered = 0;
1732 } else {
1733 adjust_ratio = -1.0 * average_change / ((float)sample_rate / (float)source_hz);
1734 adjust_ratio /= 2.5 * source_hz;
1735 if (fabsf(adjust_ratio) > max_adjust) {
1736 adjust_ratio = adjust_ratio > 0 ? max_adjust : -max_adjust;
1737 }
1738 }
1739 } else if (local_cur_min < min_buffered / 2) {
1740 adjust_ratio = max_adjust;
1741 }
1742 if (adjust_ratio != 0.0f) {
1743 average_change = 0;
1744 for (uint8_t i = 0; i < num_audio_sources; i++)
1745 {
1746 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5;
1747 }
1748 }
1749 while (source_frame_count > 0)
1750 {
1751 render_update_display();
1752 source_frame_count--;
1753 }
1754 source_frame++;
1755 if (source_frame >= source_hz) {
1756 source_frame = 0;
1757 }
1758 source_frame_count = frame_repeat[source_frame];
1759 }*/
1760 }
1761 /*
1762 static ui_render_fun render_ui;
1763 void render_set_ui_render_fun(ui_render_fun fun)
1764 {
1765 render_ui = fun;
1766 }
1767 */
1768 void render_update_display()
1769 {
1770 #ifndef DISABLE_OPENGL
1771 if (render_gl) {
1772 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1773 glClear(GL_COLOR_BUFFER_BIT);
1774
1775 glUseProgram(program);
1776 glActiveTexture(GL_TEXTURE0);
1777 glBindTexture(GL_TEXTURE_2D, textures[0]);
1778 glUniform1i(un_textures[0], 0);
1779
1780 glActiveTexture(GL_TEXTURE1);
1781 glBindTexture(GL_TEXTURE_2D, textures[interlaced ? 1 : scanlines ? 2 : 0]);
1782 glUniform1i(un_textures[1], 1);
1783
1784 glUniform1f(un_width, render_emulated_width());
1785 glUniform1f(un_height, last_height);
1786
1787 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
1788 glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
1789 glEnableVertexAttribArray(at_pos);
1790
1791 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
1792 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
1793
1794 glDisableVertexAttribArray(at_pos);
1795
1796 /*if (render_ui) {
1797 render_ui();
1798 }*/
1799
1800 eglSwapBuffers(egl_display, main_surface);
1801 }
1802 #endif
1803 if (!events_processed) {
1804 process_events();
1805 }
1806 events_processed = 0;
1807 }
1808
1809 uint32_t render_emulated_width()
1810 {
1811 return last_width - overscan_left[video_standard] - overscan_right[video_standard];
1812 }
1813
1814 uint32_t render_emulated_height()
1815 {
1816 return (video_standard == VID_NTSC ? 243 : 294) - overscan_top[video_standard] - overscan_bot[video_standard];
1817 }
1818
1819 uint32_t render_overscan_left()
1820 {
1821 return overscan_left[video_standard];
1822 }
1823
1824 uint32_t render_overscan_top()
1825 {
1826 return overscan_top[video_standard];
1827 }
1828
1829 void render_wait_quit(vdp_context * context)
1830 {
1831 for(;;)
1832 {
1833 drain_events();
1834 sleep(1);
1835 }
1836 }
1837
1838 int render_lookup_button(char *name)
1839 {
1840 static tern_node *button_lookup;
1841 if (!button_lookup) {
1842 //xbox/sdl style names
1843 button_lookup = tern_insert_int(button_lookup, "a", BTN_SOUTH);
1844 button_lookup = tern_insert_int(button_lookup, "b", BTN_EAST);
1845 button_lookup = tern_insert_int(button_lookup, "x", BTN_WEST);
1846 button_lookup = tern_insert_int(button_lookup, "y", BTN_NORTH);
1847 button_lookup = tern_insert_int(button_lookup, "back", BTN_SELECT);
1848 button_lookup = tern_insert_int(button_lookup, "start", BTN_START);
1849 button_lookup = tern_insert_int(button_lookup, "guid", BTN_MODE);
1850 button_lookup = tern_insert_int(button_lookup, "leftshoulder", BTN_TL);
1851 button_lookup = tern_insert_int(button_lookup, "rightshoulder", BTN_TR);
1852 button_lookup = tern_insert_int(button_lookup, "leftstick", BTN_THUMBL);
1853 button_lookup = tern_insert_int(button_lookup, "rightstick", BTN_THUMBR);
1854 //alternative Playstation-style names
1855 button_lookup = tern_insert_int(button_lookup, "cross", BTN_SOUTH);
1856 button_lookup = tern_insert_int(button_lookup, "circle", BTN_EAST);
1857 button_lookup = tern_insert_int(button_lookup, "square", BTN_WEST);
1858 button_lookup = tern_insert_int(button_lookup, "triangle", BTN_NORTH);
1859 button_lookup = tern_insert_int(button_lookup, "share", BTN_SELECT);
1860 button_lookup = tern_insert_int(button_lookup, "select", BTN_SELECT);
1861 button_lookup = tern_insert_int(button_lookup, "options", BTN_START);
1862 button_lookup = tern_insert_int(button_lookup, "l1", BTN_TL);
1863 button_lookup = tern_insert_int(button_lookup, "r1", BTN_TR);
1864 button_lookup = tern_insert_int(button_lookup, "l3", BTN_THUMBL);
1865 button_lookup = tern_insert_int(button_lookup, "r3", BTN_THUMBR);
1866 }
1867 return (int)tern_find_int(button_lookup, name, KEY_CNT);
1868 }
1869
1870 int render_lookup_axis(char *name)
1871 {
1872 static tern_node *axis_lookup;
1873 if (!axis_lookup) {
1874 //xbox/sdl style names
1875 axis_lookup = tern_insert_int(axis_lookup, "leftx", ABS_X);
1876 axis_lookup = tern_insert_int(axis_lookup, "lefty", ABS_Y);
1877 axis_lookup = tern_insert_int(axis_lookup, "lefttrigger", ABS_Z);
1878 axis_lookup = tern_insert_int(axis_lookup, "rightx", ABS_RX);
1879 axis_lookup = tern_insert_int(axis_lookup, "righty", ABS_RY);
1880 axis_lookup = tern_insert_int(axis_lookup, "righttrigger", ABS_RZ);
1881 //alternative Playstation-style names
1882 axis_lookup = tern_insert_int(axis_lookup, "l2", ABS_Z);
1883 axis_lookup = tern_insert_int(axis_lookup, "r2", ABS_RZ);
1884 }
1885 return (int)tern_find_int(axis_lookup, name, ABS_CNT);
1886 }
1887
1888 int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis)
1889 {
1890 tern_node *button_lookup, *axis_lookup;
1891 if (is_axis) {
1892 int axis = render_lookup_axis(name);
1893 if (axis == ABS_CNT) {
1894 return RENDER_INVALID_NAME;
1895 }
1896 return RENDER_AXIS_BIT | axis;
1897 } else {
1898 int button = render_lookup_button(name);
1899 if (button != KEY_CNT) {
1900 return button;
1901 }
1902 if (!strcmp("dpup", name)) {
1903 return RENDER_DPAD_BIT | 1;
1904 }
1905 if (!strcmp("dpdown", name)) {
1906 return RENDER_DPAD_BIT | 4;
1907 }
1908 if (!strcmp("dpdleft", name)) {
1909 return RENDER_DPAD_BIT | 8;
1910 }
1911 if (!strcmp("dpright", name)) {
1912 return RENDER_DPAD_BIT | 2;
1913 }
1914 return RENDER_INVALID_NAME;
1915 }
1916 }
1917
1918 int32_t render_dpad_part(int32_t input)
1919 {
1920 return input >> 4 & 0xFFFFFF;
1921 }
1922
1923 uint8_t render_direction_part(int32_t input)
1924 {
1925 return input & 0xF;
1926 }
1927
1928 int32_t render_axis_part(int32_t input)
1929 {
1930 return input & 0xFFFFFFF;
1931 }
1932
1933 void process_events()
1934 {
1935 if (events_processed > MAX_EVENT_POLL_PER_FRAME) {
1936 return;
1937 }
1938 drain_events();
1939 events_processed++;
1940 }
1941
1942 #define TOGGLE_MIN_DELAY 250
1943 void render_toggle_fullscreen()
1944 {
1945 //always fullscreen in fbdev
1946 }
1947
1948 uint32_t render_audio_buffer()
1949 {
1950 return buffer_samples;
1951 }
1952
1953 uint32_t render_sample_rate()
1954 {
1955 return sample_rate;
1956 }
1957
1958 void render_errorbox(char *title, char *message)
1959 {
1960
1961 }
1962
1963 void render_warnbox(char *title, char *message)
1964 {
1965
1966 }
1967
1968 void render_infobox(char *title, char *message)
1969 {
1970
1971 }
1972
1973 uint8_t render_has_gl(void)
1974 {
1975 return render_gl;
1976 }
1977
1978 uint8_t render_get_active_framebuffer(void)
1979 {
1980 return FRAMEBUFFER_ODD;
1981 }