diff --git a/AUTHORS b/AUTHORS index 4045c6066d..9753c268c6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,3 +14,7 @@ Daniel De Matteis - tukuyomi - - Cleanups in quickbuild +Michael Lelli - + - Raspberry Pi video driver + - Linux Raw keyboard input driver + diff --git a/Makefile b/Makefile index 1510406c89..863a2f607f 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ endif ifneq ($(findstring Linux,$(OS)),) LIBS += -lrt + DEFINES += -DIS_LINUX + OBJ += input/linuxraw_input.o endif ifeq ($(HAVE_THREADS), 1) @@ -114,6 +116,11 @@ endif endif endif +ifeq ($(HAVE_RPI), 1) + OBJ += gfx/rpi.o + LIBS += -lOpenVG -lGLESv2 -lEGL -lbcm_host +endif + ifeq ($(HAVE_XVIDEO), 1) OBJ += gfx/xvideo.o input/x11_input.o LIBS += $(XVIDEO_LIBS) $(X11_LIBS) $(XEXT_LIBS) @@ -195,7 +202,7 @@ ifeq ($(DEBUG), 1) OPTIMIZE_FLAG = -O0 endif -CFLAGS += -Wall $(OPTIMIZE_FLAG) -g -I. -pedantic +CFLAGS += -Wall $(OPTIMIZE_FLAG) $(INCLUDE_DIRS) -g -I. -pedantic ifeq ($(CXX_BUILD), 1) CFLAGS += -std=c++0x -xc++ -D__STDC_CONSTANT_MACROS else diff --git a/config.def.h b/config.def.h index fa29772910..8312bb3111 100644 --- a/config.def.h +++ b/config.def.h @@ -38,6 +38,7 @@ #define VIDEO_WII 24 #define VIDEO_XENON360 25 #define VIDEO_XDK360 28 +#define VIDEO_RPI 29 //////////////////////// #define AUDIO_RSOUND 1 #define AUDIO_OSS 2 @@ -62,9 +63,12 @@ #define INPUT_XENON360 21 #define INPUT_WII 23 #define INPUT_XDK360 26 +#define INPUT_LINUXRAW 30 //////////////////////// -#if defined(HAVE_OPENGL) || defined(__CELLOS_LV2__) +#if defined(HAVE_RPI) +#define VIDEO_DEFAULT_DRIVER VIDEO_RPI +#elif defined(HAVE_OPENGL) || defined(__CELLOS_LV2__) #define VIDEO_DEFAULT_DRIVER VIDEO_GL #elif defined(GEKKO) #define VIDEO_DEFAULT_DRIVER VIDEO_WII diff --git a/driver.c b/driver.c index 31e419fc7d..3cbf5f2d18 100644 --- a/driver.c +++ b/driver.c @@ -99,6 +99,9 @@ static const video_driver_t *video_drivers[] = { #ifdef GEKKO &video_wii, #endif +#ifdef HAVE_RPI + &video_rpi, +#endif }; static const input_driver_t *input_drivers[] = { @@ -120,6 +123,9 @@ static const input_driver_t *input_drivers[] = { #ifdef GEKKO &input_wii, #endif +#ifdef IS_LINUX + &input_linuxraw, +#endif }; static void find_audio_driver(void) diff --git a/driver.h b/driver.h index 4117f654a8..fb94d9afd5 100644 --- a/driver.h +++ b/driver.h @@ -220,6 +220,7 @@ extern const video_driver_t video_xenon360; extern const video_driver_t video_xvideo; extern const video_driver_t video_xdk360; extern const video_driver_t video_sdl; +extern const video_driver_t video_rpi; extern const video_driver_t video_ext; extern const input_driver_t input_sdl; extern const input_driver_t input_x; @@ -227,6 +228,7 @@ extern const input_driver_t input_ps3; extern const input_driver_t input_xenon360; extern const input_driver_t input_wii; extern const input_driver_t input_xdk360; +extern const input_driver_t input_linuxraw; //////////////////////////////////////////////// // Convenience macros. diff --git a/gfx/fonts/fonts.c b/gfx/fonts/fonts.c index c027a7133f..b6ac68e695 100644 --- a/gfx/fonts/fonts.c +++ b/gfx/fonts/fonts.c @@ -93,8 +93,12 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out tmp->width = slot->bitmap.width; tmp->height = slot->bitmap.rows; tmp->pitch = slot->bitmap.pitch; - tmp->off_x = off_x + slot->bitmap_left; - tmp->off_y = off_y + slot->bitmap_top - slot->bitmap.rows; + tmp->advance_x = slot->advance.x >> 6; + tmp->advance_y = slot->advance.y >> 6; + tmp->char_off_x = slot->bitmap_left; + tmp->char_off_y = slot->bitmap_top - slot->bitmap.rows; + tmp->off_x = off_x + tmp->char_off_x; + tmp->off_y = off_y + tmp->char_off_y; tmp->next = NULL; if (i == 0) @@ -147,6 +151,8 @@ static const char *font_paths[] = { static const char *font_paths[] = { "/usr/share/fonts/TTF/DejaVuSansMono.ttf", "/usr/share/fonts/TTF/DejaVuSans.ttf", + "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", + "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", #endif "osd-font.ttf", // Magic font to search for, useful for distribution. }; diff --git a/gfx/fonts/fonts.h b/gfx/fonts/fonts.h index 6f2abd6934..f99f94df2c 100644 --- a/gfx/fonts/fonts.h +++ b/gfx/fonts/fonts.h @@ -26,6 +26,7 @@ struct font_output uint8_t *output; // 8-bit intensity. unsigned width, height, pitch; int off_x, off_y; + int advance_x, advance_y, char_off_x, char_off_y; // for advanced font rendering struct font_output *next; // linked list. }; diff --git a/gfx/rpi.c b/gfx/rpi.c new file mode 100644 index 0000000000..19ea060323 --- /dev/null +++ b/gfx/rpi.c @@ -0,0 +1,435 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2012 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../libretro.h" +#include "../general.h" +#include "../input/linuxraw_input.h" +#include "../driver.h" + +#ifdef HAVE_FREETYPE +#include "fonts/fonts.h" +#include "../file.h" +#endif + + +typedef struct { + EGLDisplay mDisplay; + EGLSurface mSurface; + EGLContext mContext; + uint32_t mScreenWidth; + uint32_t mScreenHeight; + float mScreenAspect; + bool mKeepAspect; + unsigned mTextureWidth; + unsigned mTextureHeight; + unsigned mRenderWidth; + unsigned mRenderHeight; + unsigned x1, y1, x2, y2; + VGImageFormat mTexType; + VGImage mImage; + VGfloat mTransformMatrix[9]; + VGint scissor[4]; + +#ifdef HAVE_FREETYPE + char *mLastMsg; + uint32_t mFontHeight; + VGFont mFont; + font_renderer_t *mFontRenderer; + bool mFontsOn; + VGuint mMsgLength; + VGuint mGlyphIndices[1024]; + VGPaint mPaintFg; + VGPaint mPaintBg; +#endif +} rpi_t; + +static volatile sig_atomic_t rpi_shutdown = 0; + +static void rpi_kill(int sig) +{ + (void)sig; + rpi_shutdown = 1; +} + +static void rpi_set_nonblock_state(void *data, bool state) +{ + rpi_t *rpi = (rpi_t*)data; + eglSwapInterval(rpi->mDisplay, state ? 0 : 1); +} + +static void *rpi_init(const video_info_t *video, const input_driver_t **input, void **input_data) +{ + int32_t success; + EGLBoolean result; + EGLint num_config; + rpi_t *rpi = (rpi_t*)calloc(1, sizeof(rpi_t)); + *input = NULL; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + DISPMANX_MODEINFO_T dispman_modeinfo; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + bcm_host_init(); + + // get an EGL display connection + rpi->mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + rarch_assert(rpi->mDisplay != EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(rpi->mDisplay, NULL, NULL); + rarch_assert(result != EGL_FALSE); + eglBindAPI(EGL_OPENVG_API); + + // get an appropriate EGL frame buffer configuration + result = eglChooseConfig(rpi->mDisplay, attribute_list, &config, 1, &num_config); + rarch_assert(result != EGL_FALSE); + + // create an EGL rendering context + rpi->mContext = eglCreateContext(rpi->mDisplay, config, EGL_NO_CONTEXT, NULL); + rarch_assert(rpi->mContext != EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &rpi->mScreenWidth, &rpi->mScreenHeight); + rarch_assert(success >= 0); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = rpi->mScreenWidth; + dst_rect.height = rpi->mScreenHeight; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = rpi->mScreenWidth << 16; + src_rect.height = rpi->mScreenHeight << 16; + + dispman_display = vc_dispmanx_display_open(0 /* LCD */); + vc_dispmanx_display_get_info(dispman_display, &dispman_modeinfo); + dispman_update = vc_dispmanx_update_start(0); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, DISPMANX_NO_ROTATE); + + nativewindow.element = dispman_element; + nativewindow.width = rpi->mScreenWidth; + nativewindow.height = rpi->mScreenHeight; + vc_dispmanx_update_submit_sync(dispman_update); + + rpi->mSurface = eglCreateWindowSurface(rpi->mDisplay, config, &nativewindow, NULL); + rarch_assert(rpi->mSurface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(rpi->mDisplay, rpi->mSurface, rpi->mSurface, rpi->mContext); + rarch_assert(result != EGL_FALSE); + + rpi->mTexType = video->rgb32 ? VG_sABGR_8888 : VG_sARGB_1555; + rpi->mKeepAspect = video->force_aspect; + + // check for SD televisions: they should always be 4:3 + if (dispman_modeinfo.width == 720 && (dispman_modeinfo.height == 480 || dispman_modeinfo.height == 576)) + rpi->mScreenAspect = 4.0f / 3.0f; + else + rpi->mScreenAspect = (float) dispman_modeinfo.width / dispman_modeinfo.height; + + VGfloat clearColor[4] = {0, 0, 0, 1}; + vgSetfv(VG_CLEAR_COLOR, 4, clearColor); + + rpi->mTextureWidth = rpi->mTextureHeight = video->input_scale * RARCH_SCALE_BASE; + // We can't use the native format because there's no sXRGB_1555 type and + // emulation cores can send 0 in the top bit. We lose some speed on + // conversion but I doubt it has any real affect, since we are only drawing + // one image at the end of the day. Still keep the alpha channel for ABGR. + rpi->mImage = vgCreateImage(video->rgb32 ? VG_sABGR_8888 : VG_sXBGR_8888, rpi->mTextureWidth, rpi->mTextureHeight, video->smooth ? VG_IMAGE_QUALITY_BETTER : VG_IMAGE_QUALITY_NONANTIALIASED); + rpi_set_nonblock_state(rpi, !video->vsync); + + linuxraw_input_t *linuxraw_input = (linuxraw_input_t *)input_linuxraw.init(); + if (linuxraw_input) + { + *input = (const input_driver_t *)&input_linuxraw; + *input_data = linuxraw_input; + } + +#ifdef HAVE_FREETYPE + if (g_settings.video.font_enable) + { + rpi->mFont = vgCreateFont(0); + rpi->mFontHeight = g_settings.video.font_size * (g_settings.video.font_scale ? (float) rpi->mScreenWidth / 1280.0f : 1.0f); + + const char *path = g_settings.video.font_path; + if (!*path || !path_file_exists(path)) + path = font_renderer_get_default_font(); + + rpi->mFontRenderer = font_renderer_new(path, rpi->mFontHeight); + + if (rpi->mFont != VG_INVALID_HANDLE && rpi->mFontRenderer) + { + rpi->mFontsOn = true; + + rpi->mPaintFg = vgCreatePaint(); + rpi->mPaintBg = vgCreatePaint(); + VGfloat paintFg[] = { g_settings.video.msg_color_r, g_settings.video.msg_color_g, g_settings.video.msg_color_b, 1.0f }; + VGfloat paintBg[] = { g_settings.video.msg_color_r / 2.0f, g_settings.video.msg_color_g / 2.0f, g_settings.video.msg_color_b / 2.0f, 0.5f }; + + vgSetParameteri(rpi->mPaintFg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(rpi->mPaintFg, VG_PAINT_COLOR, 4, paintFg); + + vgSetParameteri(rpi->mPaintBg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(rpi->mPaintBg, VG_PAINT_COLOR, 4, paintBg); + } + } +#endif + + struct sigaction sa; + sa.sa_handler = rpi_kill; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + return rpi; +} + +static void rpi_free(void *data) +{ + rpi_t *rpi = (rpi_t*)data; + + vgDestroyImage(rpi->mImage); + +#ifdef HAVE_FREETYPE + if (rpi->mFontsOn) + { + vgDestroyFont(rpi->mFont); + font_renderer_free(rpi->mFontRenderer); + vgDestroyPaint(rpi->mPaintFg); + vgDestroyPaint(rpi->mPaintBg); + } +#endif + + // Release EGL resources + eglMakeCurrent(rpi->mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(rpi->mDisplay, rpi->mSurface); + eglDestroyContext(rpi->mDisplay, rpi->mContext); + eglTerminate(rpi->mDisplay); + + free(rpi); +} + +#ifdef HAVE_FREETYPE + +static void rpi_render_message(rpi_t *rpi, const char *msg) +{ + free(rpi->mLastMsg); + rpi->mLastMsg = strdup(msg); + + if(rpi->mMsgLength) + { + while (--rpi->mMsgLength) + vgClearGlyph(rpi->mFont, rpi->mMsgLength); + + vgClearGlyph(rpi->mFont, 0); + } + + struct font_output_list out; + font_renderer_msg(rpi->mFontRenderer, msg, &out); + struct font_output *head = out.head; + + while (head) + { + if (rpi->mMsgLength >= 1024) + break; + + VGfloat origin[2], escapement[2]; + VGImage img; + + escapement[0] = (VGfloat) (head->advance_x); + escapement[1] = (VGfloat) (head->advance_y); + origin[0] = (VGfloat) (-head->char_off_x); + origin[1] = (VGfloat) (head->char_off_y); + + img = vgCreateImage(VG_A_8, head->width, head->height, VG_IMAGE_QUALITY_NONANTIALIASED); + + // flip it + for (unsigned i = 0; i < head->height; i++) + vgImageSubData(img, head->output + head->pitch * i, head->pitch, VG_A_8, 0, head->height - i - 1, head->width, 1); + + vgSetGlyphToImage(rpi->mFont, rpi->mMsgLength, img, origin, escapement); + vgDestroyImage(img); + + rpi->mMsgLength++; + head = head->next; + } + + font_renderer_free_output(&out); + + for (unsigned i = 0; i < rpi->mMsgLength; i++) + rpi->mGlyphIndices[i] = i; +} + +static void rpi_draw_message(rpi_t *rpi, const char *msg) +{ + if (!rpi->mLastMsg || strcmp(rpi->mLastMsg, msg)) + rpi_render_message(rpi, msg); + + vgSeti(VG_SCISSORING, VG_FALSE); + vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL); + + VGfloat origins[] = { rpi->mScreenWidth * g_settings.video.msg_pos_x - 2.0f, rpi->mScreenHeight * g_settings.video.msg_pos_y - 2.0f }; + vgSetfv(VG_GLYPH_ORIGIN, 2, origins); + vgSetPaint(rpi->mPaintBg, VG_FILL_PATH); + vgDrawGlyphs(rpi->mFont, rpi->mMsgLength, rpi->mGlyphIndices, NULL, NULL, VG_FILL_PATH, VG_TRUE); + origins[0] += 2.0f; + origins[1] += 2.0f; + vgSetfv(VG_GLYPH_ORIGIN, 2, origins); + vgSetPaint(rpi->mPaintFg, VG_FILL_PATH); + vgDrawGlyphs(rpi->mFont, rpi->mMsgLength, rpi->mGlyphIndices, NULL, NULL, VG_FILL_PATH, VG_TRUE); + + vgSeti(VG_SCISSORING, VG_TRUE); + vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL); +} + +#endif + +static void rpi_calculate_quad(rpi_t *rpi) +{ + // set viewport for aspect ratio, taken from the OpenGL driver + if (rpi->mKeepAspect) + { + float desired_aspect = g_settings.video.aspect_ratio; + + // If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff), + // assume they are actually equal. + if (fabs(rpi->mScreenAspect - desired_aspect) < 0.0001) + { + rpi->x1 = 0; + rpi->y1 = 0; + rpi->x2 = rpi->mScreenWidth; + rpi->y2 = rpi->mScreenHeight; + } + else if (rpi->mScreenAspect > desired_aspect) + { + float delta = (desired_aspect / rpi->mScreenAspect - 1.0) / 2.0 + 0.5; + rpi->x1 = rpi->mScreenWidth * (0.5 - delta); + rpi->y1 = 0; + rpi->x2 = 2.0 * rpi->mScreenWidth * delta + rpi->x1; + rpi->y2 = rpi->mScreenHeight + rpi->y1; + } + else + { + float delta = (rpi->mScreenAspect / desired_aspect - 1.0) / 2.0 + 0.5; + rpi->x1 = 0; + rpi->y1 = rpi->mScreenHeight * (0.5 - delta); + rpi->x2 = rpi->mScreenWidth + rpi->x1; + rpi->y2 = 2.0 * rpi->mScreenHeight * delta + rpi->y1; + } + } + else + { + rpi->x1 = 0; + rpi->y1 = 0; + rpi->x2 = rpi->mScreenWidth; + rpi->y2 = rpi->mScreenHeight; + } + + rpi->scissor[0] = rpi->x1; + rpi->scissor[1] = rpi->y1; + rpi->scissor[2] = rpi->x2 - rpi->x1; + rpi->scissor[3] = rpi->y2 - rpi->y1; + + vgSetiv(VG_SCISSOR_RECTS, 4, rpi->scissor); +} + +static bool rpi_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg) +{ + rpi_t *rpi = (rpi_t*)data; + + if (width != rpi->mRenderWidth || height != rpi->mRenderHeight) + { + rpi->mRenderWidth = width; + rpi->mRenderHeight = height; + rpi_calculate_quad(rpi); + vguComputeWarpQuadToQuad( + rpi->x1, rpi->y1, rpi->x2, rpi->y1, rpi->x2, rpi->y2, rpi->x1, rpi->y2, + // needs to be flipped, Khronos loves their bottom-left origin + 0, height, width, height, width, 0, 0, 0, + rpi->mTransformMatrix); + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgLoadMatrix(rpi->mTransformMatrix); + } + vgSeti(VG_SCISSORING, VG_FALSE); + vgClear(0, 0, rpi->mScreenWidth, rpi->mScreenHeight); + vgSeti(VG_SCISSORING, VG_TRUE); + + vgImageSubData(rpi->mImage, frame, pitch, rpi->mTexType, 0, 0, width, height); + vgDrawImage(rpi->mImage); + +#ifdef HAVE_FREETYPE + if (msg && rpi->mFontsOn) + rpi_draw_message(rpi, msg); +#else + (void)msg; +#endif + + eglSwapBuffers(rpi->mDisplay, rpi->mSurface); + + return true; +} + +static bool rpi_alive(void *data) +{ + (void)data; + return !rpi_shutdown; +} + +static bool rpi_focus(void *data) +{ + (void)data; + return true; +} + +const video_driver_t video_rpi = { + rpi_init, + rpi_frame, + rpi_set_nonblock_state, + rpi_alive, + rpi_focus, + NULL, + rpi_free, + "rpi" +}; diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c new file mode 100644 index 0000000000..a0069c3eba --- /dev/null +++ b/input/linuxraw_input.c @@ -0,0 +1,306 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2012 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../driver.h" + +#include +#include +#include +#include +#include +#include +#include "../general.h" +#include "linuxraw_input.h" +#include "rarch_sdl_input.h" + +static long oldKbmd = 0xFFFF; +static struct termios oldTerm, newTerm; + +struct key_bind +{ + uint8_t x; + enum rarch_key sk; +}; + +static unsigned keysym_lut[SK_LAST]; +static const struct key_bind lut_binds[] = { + { KEY_ESC, SK_ESCAPE }, + { KEY_1, SK_1 }, + { KEY_2, SK_2 }, + { KEY_3, SK_3}, + { KEY_4, SK_4 }, + { KEY_5, SK_5 }, + { KEY_6, SK_6 }, + { KEY_7, SK_7 }, + { KEY_8, SK_8 }, + { KEY_9, SK_9 }, + { KEY_0, SK_0 }, + { KEY_MINUS, SK_MINUS }, + { KEY_EQUAL, SK_EQUALS }, + { KEY_BACKSPACE, SK_BACKSPACE }, + { KEY_TAB, SK_TAB }, + { KEY_Q, SK_q }, + { KEY_W, SK_w }, + { KEY_E, SK_e }, + { KEY_R, SK_r }, + { KEY_T, SK_t }, + { KEY_Y, SK_y }, + { KEY_U, SK_u }, + { KEY_I, SK_i }, + { KEY_O, SK_o }, + { KEY_P, SK_p }, + { KEY_LEFTBRACE, SK_LEFTBRACKET }, + { KEY_RIGHTBRACE, SK_RIGHTBRACKET }, + { KEY_ENTER, SK_RETURN }, + { KEY_LEFTCTRL, SK_LCTRL }, + { KEY_A, SK_a }, + { KEY_S, SK_s }, + { KEY_D, SK_d }, + { KEY_F, SK_f }, + { KEY_G, SK_g }, + { KEY_H, SK_h }, + { KEY_J, SK_j }, + { KEY_K, SK_k }, + { KEY_L, SK_l }, + { KEY_SEMICOLON, SK_SEMICOLON }, + { KEY_APOSTROPHE, SK_QUOTE }, + { KEY_GRAVE, SK_BACKQUOTE }, + { KEY_LEFTSHIFT, SK_LSHIFT }, + { KEY_BACKSLASH, SK_BACKSLASH }, + { KEY_Z, SK_z }, + { KEY_X, SK_x }, + { KEY_C, SK_c }, + { KEY_V, SK_v }, + { KEY_B, SK_b }, + { KEY_N, SK_n }, + { KEY_M, SK_m }, + { KEY_COMMA, SK_COMMA }, + { KEY_DOT, SK_PERIOD }, + { KEY_SLASH, SK_SLASH }, + { KEY_RIGHTSHIFT, SK_RSHIFT }, + { KEY_KPASTERISK, SK_KP_MULTIPLY }, + { KEY_LEFTALT, SK_LALT }, + { KEY_SPACE, SK_SPACE }, + { KEY_CAPSLOCK, SK_CAPSLOCK }, + { KEY_F1, SK_F1 }, + { KEY_F2, SK_F2 }, + { KEY_F3, SK_F3 }, + { KEY_F4, SK_F4 }, + { KEY_F5, SK_F5 }, + { KEY_F6, SK_F6 }, + { KEY_F7, SK_F7 }, + { KEY_F8, SK_F8 }, + { KEY_F9, SK_F9 }, + { KEY_F10, SK_F10 }, + { KEY_NUMLOCK, SK_NUMLOCK }, + { KEY_SCROLLLOCK, SK_SCROLLOCK }, + { KEY_KP7, SK_KP7 }, + { KEY_KP8, SK_KP8 }, + { KEY_KP9, SK_KP9 }, + { KEY_KPMINUS, SK_KP_MINUS }, + { KEY_KP4, SK_KP4 }, + { KEY_KP5, SK_KP5 }, + { KEY_KP6, SK_KP6 }, + { KEY_KPPLUS, SK_KP_PLUS }, + { KEY_KP1, SK_KP1 }, + { KEY_KP2, SK_KP2 }, + { KEY_KP3, SK_KP3 }, + { KEY_KP0, SK_KP0 }, + { KEY_KPDOT, SK_KP_PERIOD }, + + { KEY_F11, SK_F11 }, + { KEY_F12, SK_F12 }, + + { KEY_KPENTER, SK_KP_ENTER }, + { KEY_RIGHTCTRL, SK_RCTRL }, + { KEY_KPSLASH, SK_KP_DIVIDE }, + { KEY_SYSRQ, SK_PRINT }, + { KEY_RIGHTALT, SK_RALT }, + + { KEY_HOME, SK_HOME }, + { KEY_UP, SK_UP }, + { KEY_PAGEUP, SK_PAGEUP }, + { KEY_LEFT, SK_LEFT }, + { KEY_RIGHT, SK_RIGHT }, + { KEY_END, SK_END }, + { KEY_DOWN, SK_DOWN }, + { KEY_PAGEDOWN, SK_PAGEDOWN }, + { KEY_INSERT, SK_INSERT }, + { KEY_DELETE, SK_DELETE }, + + { KEY_PAUSE, SK_PAUSE }, +}; + +static void init_lut(void) +{ + memset(keysym_lut, 0, sizeof(keysym_lut)); + for (unsigned i = 0; i < sizeof(lut_binds) / sizeof(lut_binds[0]); i++) + keysym_lut[lut_binds[i].sk] = lut_binds[i].x; +} + +static void linuxraw_resetKbmd() +{ + if (oldKbmd != 0xFFFF) + { + ioctl(0, KDSKBMODE, oldKbmd); + tcsetattr(0, TCSAFLUSH, &oldTerm); + oldKbmd = 0xFFFF; + } +} + +static void linuxraw_exitGracefully(int sig) +{ + linuxraw_resetKbmd(); + kill(getpid(), sig); +} + +static void *linuxraw_input_init(void) +{ + // only work on terminals + if (!isatty(0)) + return NULL; + + linuxraw_input_t *linuxraw = (linuxraw_input_t*)calloc(1, sizeof(*linuxraw)); + if (!linuxraw) + return NULL; + + if (oldKbmd == 0xFFFF) + { + tcgetattr(0, &oldTerm); + newTerm = oldTerm; + newTerm.c_lflag &= ~(ECHO | ICANON | ISIG); + newTerm.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); + newTerm.c_cc[VMIN] = 0; + newTerm.c_cc[VTIME] = 0; + + if (ioctl(0, KDGKBMODE, &oldKbmd) != 0) + return NULL; + } + + tcsetattr(0, TCSAFLUSH, &newTerm); + + if (ioctl(0, KDSKBMODE, K_MEDIUMRAW) != 0) + { + linuxraw_resetKbmd(); + return NULL; + } + + struct sigaction sa; + sa.sa_handler = linuxraw_exitGracefully; + sa.sa_flags = SA_RESTART | SA_RESETHAND; + sigemptyset(&sa.sa_mask); + // trap some standard termination codes so we can restore the keyboard before we lose control + sigaction(SIGABRT, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); + sigaction(SIGFPE, &sa, NULL); + sigaction(SIGILL, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGSEGV, &sa, NULL); + + atexit(linuxraw_resetKbmd); + + linuxraw->sdl = (sdl_input_t*)input_sdl.init(); + if (!linuxraw->sdl) + { + linuxraw_resetKbmd(); + free(linuxraw); + return NULL; + } + + init_lut(); + + linuxraw->sdl->use_keyboard = false; + return linuxraw; +} + +static bool linuxraw_key_pressed(linuxraw_input_t *linuxraw, int key) +{ + return linuxraw->state[keysym_lut[key]]; +} + +static bool linuxraw_is_pressed(linuxraw_input_t *linuxraw, const struct snes_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct snes_keybind *bind = &binds[id]; + return bind->valid && linuxraw_key_pressed(linuxraw, binds[id].key); + } + else + return false; +} + +static bool linuxraw_bind_button_pressed(void *data, int key) +{ + linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; + return linuxraw_is_pressed(linuxraw, g_settings.input.binds[0], key) || + input_sdl.key_pressed(linuxraw->sdl, key); +} + +static int16_t linuxraw_input_state(void *data, const struct snes_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) +{ + linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return linuxraw_is_pressed(linuxraw, binds[port], id) || + input_sdl.input_state(linuxraw->sdl, binds, port, device, index, id); + + default: + return 0; + } +} + +static void linuxraw_input_free(void *data) +{ + linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; + input_sdl.free(linuxraw->sdl); + linuxraw_resetKbmd(); + free(data); +} + +static void linuxraw_input_poll(void *data) +{ + linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; + uint8_t c; + uint16_t t; + + while (read(0, &c, 1) > 0) + { + bool pressed = !(c & 0x80); + c &= ~0x80; + + // ignore extended scancodes + if (!c) + read(0, &t, 2); + else + linuxraw->state[c] = pressed; + } + + if (linuxraw->state[KEY_C] && (linuxraw->state[KEY_LEFTCTRL] || linuxraw->state[KEY_RIGHTCTRL])) + kill(getpid(), SIGINT); + + input_sdl.poll(linuxraw->sdl); +} + +const input_driver_t input_linuxraw = { + linuxraw_input_init, + linuxraw_input_poll, + linuxraw_input_state, + linuxraw_bind_button_pressed, + linuxraw_input_free, + "linuxraw" +}; diff --git a/input/linuxraw_input.h b/input/linuxraw_input.h new file mode 100644 index 0000000000..710ffdf5c6 --- /dev/null +++ b/input/linuxraw_input.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2012 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _LINUXRAW_INPUT_H +#define _LINUXRAW_INPUT_H + +#include "../general.h" +#include "rarch_sdl_input.h" + +typedef struct linuxraw_input +{ + sdl_input_t *sdl; + bool state[0x80]; +} linuxraw_input_t; + +#endif diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 8c352df57e..57afad9eac 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -10,6 +10,17 @@ add_define_make NOUNUSED "$HAVE_NOUNUSED" if [ "$OS" = 'BSD' ]; then DYLIB=-lc; else DYLIB=-ldl; fi +[ -d /opt/vc/lib ] && add_library_dirs /opt/vc/lib +check_lib RPI -lbcm_host bcm_host_init + +if [ "$HAVE_RPI" = 'yes' ]; then + [ -d /opt/vc/include ] && add_include_dirs /opt/vc/include + [ -d /opt/vc/include/interface/vcos/pthreads ] && add_include_dirs /opt/vc/include/interface/vcos/pthreads + + # the gles library gets messed up with the gl library if available, so turn it off + HAVE_OPENGL='no' +fi + if [ "$LIBRETRO" ]; then echo "Explicit libsnes used, disabling dynamic libsnes loading ..." HAVE_DYNAMIC='no' @@ -129,6 +140,6 @@ check_pkgconf PYTHON python3 add_define_make OS "$OS" # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC BSV_MOVIE" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC BSV_MOVIE RPI" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/settings.c b/settings.c index 7d6e97c50a..46a88dc17a 100644 --- a/settings.c +++ b/settings.c @@ -91,6 +91,8 @@ const char *config_get_default_video(void) return "sdl"; case VIDEO_EXT: return "ext"; + case VIDEO_RPI: + return "rpi"; default: return NULL; } @@ -112,6 +114,8 @@ const char *config_get_default_input(void) return "xdk360"; case INPUT_WII: return "wii"; + case INPUT_LINUXRAW: + return "linuxraw"; default: return NULL; }