From c5a2fbc6e6fbcad9232c5102306e809af152d71a Mon Sep 17 00:00:00 2001 From: Toad King Date: Tue, 19 Jun 2012 15:01:34 -0400 Subject: [PATCH] EOL fixes --- gfx/rpi.c | 870 ++++++++++++++++++++--------------------- input/linuxraw_input.c | 612 ++++++++++++++--------------- 2 files changed, 741 insertions(+), 741 deletions(-) diff --git a/gfx/rpi.c b/gfx/rpi.c index b718428c8e..19ea060323 100644 --- a/gfx/rpi.c +++ b/gfx/rpi.c @@ -1,435 +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" -}; +/* 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 index 97fec4e17e..a0069c3eba 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -1,306 +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" -}; +/* 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" +};