1471 lines
44 KiB
C
1471 lines
44 KiB
C
// =======================================================================
|
|
// GUIslice library (driver layer for SDL 1.2 & 2.0)
|
|
// - Calvin Hass
|
|
// - https://www.impulseadventure.com/elec/guislice-gui.html
|
|
// - https://github.com/ImpulseAdventure/GUIslice
|
|
// =======================================================================
|
|
//
|
|
// The MIT License
|
|
//
|
|
// Copyright 2016-2020 Calvin Hass
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
// =======================================================================
|
|
/// \file GUIslice_drv_sdl.c
|
|
|
|
// Compiler guard for requested driver
|
|
#include "GUIslice_config.h" // Sets DRV_DISP_*
|
|
#if defined(DRV_DISP_SDL1) || defined(DRV_DISP_SDL2)
|
|
|
|
// =======================================================================
|
|
// Driver Layer for SDL
|
|
// =======================================================================
|
|
|
|
|
|
// GUIslice library
|
|
#include "GUIslice_drv_sdl.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Load display & touch drivers
|
|
// ------------------------------------------------------------------------
|
|
|
|
// Optionally enable SDL clean start VT workaround
|
|
#if (DRV_SDL_FIX_START)
|
|
#include <fcntl.h> // For O_RDONLY
|
|
#include <errno.h> // For errno
|
|
#include <unistd.h> // For close()
|
|
#include <sys/ioctl.h> // For ioctl()
|
|
#include <sys/kd.h> // for KDSETMODE
|
|
#include <sys/vt.h> // for VT_UNLOCKSWITCH
|
|
#define DRV_SDL_FIX_TTY "/dev/tty0"
|
|
#endif
|
|
|
|
// Define driver names
|
|
#if defined(DRV_DISP_SDL1)
|
|
const char* m_acDrvDisp = "SDL1";
|
|
#elif defined(DRV_DISP_SDL2)
|
|
const char* m_acDrvDisp = "SDL2";
|
|
#endif
|
|
|
|
#if defined(DRV_TOUCH_TSLIB)
|
|
const char* m_acDrvTouch = "TSLIB";
|
|
#else
|
|
const char* m_acDrvTouch = "SDL";
|
|
#endif
|
|
|
|
|
|
// =======================================================================
|
|
// Public APIs to GUIslice core library
|
|
// =======================================================================
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Configuration Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool gslc_DrvInit(gslc_tsGui* pGui)
|
|
{
|
|
// Report any debug info (before init) if enabled
|
|
#if defined(DBG_DRIVER)
|
|
gslc_DrvReportInfoPre();
|
|
#endif
|
|
|
|
// Primary surface definitions
|
|
pGui->sImgRefBkgnd = gslc_ResetImage();
|
|
|
|
// Initialize any SDL version-specific members
|
|
if (pGui->pvDriver) {
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
pDriver->pSurfScreen = NULL;
|
|
pGui->bRedrawPartialEn = true;
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
pDriver->pWind = NULL;
|
|
pDriver->pRender = NULL;
|
|
// In SDL2, always need full page redraw since backbuffer
|
|
// is treated as invalidated after every RenderPresent()
|
|
pGui->bRedrawPartialEn = false;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if (DRV_SDL_FIX_START)
|
|
// Force a clean start to SDL to workaround any bad state
|
|
// left behind by a previous SDL application's failure to
|
|
// clean up.
|
|
// TODO: Allow compiler option to skip this workaround
|
|
// TODO: Allow determination of the TTY to use
|
|
gslc_DrvCleanStart(DRV_SDL_FIX_TTY);
|
|
#endif
|
|
|
|
// Setup SDL
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvInit() error in SDL_Init(): %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
// Now that we have successfully initialized SDL
|
|
// we need to register an exit handler so that SDL_Quit()
|
|
// gets called at program termination. If we don't do this
|
|
// then some types of errors/aborts will result in
|
|
// the SDL environment being left in a bad state that
|
|
// affects the next SDL program execution.
|
|
atexit(SDL_Quit);
|
|
|
|
// Report any debug info (after init) if enabled
|
|
#if defined(DBG_DRIVER)
|
|
gslc_DrvReportInfoPost();
|
|
#endif
|
|
|
|
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
// Set up pGui->pSurfScreen
|
|
const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
|
|
int16_t nSystemX = videoInfo->current_w;
|
|
int16_t nSystemY = videoInfo->current_h;
|
|
uint8_t nBpp = videoInfo->vfmt->BitsPerPixel ;
|
|
|
|
// Save a copy of the display dimensions
|
|
pGui->nDispW = nSystemX;
|
|
pGui->nDispH = nSystemY;
|
|
pGui->nDispDepth = nBpp;
|
|
|
|
#if defined(DBG_DRIVER)
|
|
GSLC_DEBUG_PRINT("DBG: Video mode: %u x %u x %u bit/pixel\n",
|
|
pGui->nDispW,pGui->nDispH,pGui->nDispDepth);
|
|
#endif
|
|
|
|
// SDL_SWSURFACE is apparently more reliable
|
|
pDriver->pSurfScreen = SDL_SetVideoMode(nSystemX,nSystemY,nBpp,SDL_SWSURFACE | SDL_FULLSCREEN);
|
|
if (!pDriver->pSurfScreen) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvInit() error in SDL_SetVideoMode(): %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
// Default to using OpenGL rendering engine and full-screen
|
|
// When using SDL_WINDOW_FULLSCREEN, the width & height dimensions are ignored
|
|
pDriver->pWind = SDL_CreateWindow("GUIslice",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,
|
|
0,0,SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
|
|
if (!pDriver->pWind) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvInit() error in SDL_CreateWindow(): %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Save a copy of the display dimensions from the created fullscreen window
|
|
int nSystemX,nSystemY;
|
|
SDL_GetWindowSize(pDriver->pWind,&nSystemX,&nSystemY);
|
|
pGui->nDispW = nSystemX;
|
|
pGui->nDispH = nSystemY;
|
|
|
|
// Fetch the bit depth of the first display
|
|
SDL_DisplayMode sDispMode;
|
|
int nRet = SDL_GetCurrentDisplayMode(0,&sDispMode);
|
|
if (nRet != 0) {
|
|
// TODO: ERROR
|
|
return false;
|
|
}
|
|
pGui->nDispDepth = SDL_BITSPERPIXEL(sDispMode.format);
|
|
|
|
#if defined(DBG_DRIVER)
|
|
GSLC_DEBUG_PRINT("DBG: Video mode: %u x %u x %u bit/pixel\n",
|
|
pGui->nDispW,pGui->nDispH,pGui->nDispDepth);
|
|
#endif
|
|
|
|
// Create renderer
|
|
// TODO: Resolve performance with "accelerated" renderer (SDL_RENDERER_ACCELERATED.
|
|
// For now, use software renderer (SDL_RENDERER_SOFTWARE) which appears
|
|
// to be much faster.
|
|
|
|
// If we are sure that the OpenGL ES 2.0 driver has been installed, we
|
|
// can request that SDL use this as a renderer. Disable this for now.
|
|
// SDL_SetHint(SDL_HINT_RENDER_DRIVER,"opengles2");
|
|
|
|
#if (DRV_SDL_RENDER_ACCEL)
|
|
pDriver->pRender = SDL_CreateRenderer(pDriver->pWind,-1,SDL_RENDERER_ACCELERATED);
|
|
#else
|
|
pDriver->pRender = SDL_CreateRenderer(pDriver->pWind,-1,SDL_RENDERER_SOFTWARE);
|
|
#endif
|
|
if (!pDriver->pRender) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvInit() error in SDL_CreateRenderer(): %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
#if defined(DBG_DRIVER)
|
|
SDL_RendererInfo sRendInfo;
|
|
SDL_GetRendererInfo(pDriver->pRender,&sRendInfo);
|
|
GSLC_DEBUG_PRINT("DBG: Renderer selected: [%s]\n",
|
|
sRendInfo.name);
|
|
#endif
|
|
|
|
// If we wanted to support scaling of the renderer, we would call
|
|
// SDL_RenderSetLogicalSize() here. For now, don't scale.
|
|
|
|
#endif
|
|
|
|
// Initialize font engine
|
|
if (TTF_Init() == -1) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvInit(%s) error in TTF_Init()\n","");
|
|
return false;
|
|
}
|
|
|
|
// Since the mouse cursor is based on SDL coords which are badly
|
|
// scaled when in touch mode, we will disable the mouse pointer.
|
|
// Note that the SDL coords seem to be OK when in actual mouse mode.
|
|
#if (!DRV_SDL_MOUSE_SHOW)
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui)
|
|
{
|
|
return (pGui->pvDriver);
|
|
}
|
|
|
|
void gslc_DrvDestruct(gslc_tsGui* pGui)
|
|
{
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
if (pDriver->pRender) {
|
|
SDL_DestroyRenderer(pDriver->pRender);
|
|
pDriver->pRender = NULL;
|
|
}
|
|
if (pDriver->pWind) {
|
|
SDL_DestroyWindow(pDriver->pWind);
|
|
pDriver->pWind = NULL;
|
|
}
|
|
#endif
|
|
|
|
// Close down SDL
|
|
SDL_Quit();
|
|
}
|
|
|
|
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui)
|
|
{
|
|
return m_acDrvDisp;
|
|
}
|
|
|
|
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui)
|
|
{
|
|
return m_acDrvTouch;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Image/surface handling Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef)
|
|
{
|
|
|
|
if (sImgRef.eImgFlags == GSLC_IMGREF_NONE) {
|
|
return NULL;
|
|
} else if ((sImgRef.eImgFlags & GSLC_IMGREF_SRC) == GSLC_IMGREF_SRC_SD) {
|
|
// Load image from SD card
|
|
// TODO: Not yet supported
|
|
return NULL;
|
|
} else if ((sImgRef.eImgFlags & GSLC_IMGREF_SRC) == GSLC_IMGREF_SRC_RAM) {
|
|
// Load image from RAM
|
|
// TODO: Not yet supported
|
|
return NULL;
|
|
} else if ((sImgRef.eImgFlags & GSLC_IMGREF_SRC) == GSLC_IMGREF_SRC_PROG) {
|
|
// Load image from FLASH
|
|
// TODO: Not yet supported
|
|
return NULL;
|
|
} else if ((sImgRef.eImgFlags & GSLC_IMGREF_SRC) == GSLC_IMGREF_SRC_FILE) {
|
|
// Load image from file system
|
|
const char* pStrFname = sImgRef.pFname;
|
|
|
|
// Pointer to the surface image that was loaded
|
|
SDL_Surface* pSurfLoaded = NULL;
|
|
|
|
// Load the image
|
|
// - The SDL_LoadBMP() routine should be able to handle a multitude of
|
|
// BMP format types.
|
|
// - TODO: Check (eImgFlags & GSLC_IMGREF_FMT) to ensure type is supported
|
|
pSurfLoaded = SDL_LoadBMP(pStrFname);
|
|
|
|
// Confirm that the image loaded correctly
|
|
if (pSurfLoaded == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvLoadBmpFile(%s) failed: %s\n",pStrFname,SDL_GetError());
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
|
|
//Create an optimized surface
|
|
|
|
//The optimized surface that will be used
|
|
SDL_Surface* pSurfOptimized = SDL_DisplayFormat( pSurfLoaded );
|
|
|
|
//Free the old surface
|
|
SDL_FreeSurface( pSurfLoaded );
|
|
|
|
//If the surface was optimized
|
|
if( pSurfOptimized != NULL ) {
|
|
|
|
// Support optional transparency
|
|
if (GSLC_BMP_TRANS_EN) {
|
|
// Color key surface
|
|
// - Use transparency color key defined in BMP_TRANS_RGB
|
|
SDL_SetColorKey( pSurfOptimized, SDL_SRCCOLORKEY,
|
|
SDL_MapRGB( pSurfOptimized->format, GSLC_BMP_TRANS_RGB ) );
|
|
} // GSLC_BMP_TRANS_EN
|
|
}
|
|
|
|
//Return the optimized surface
|
|
return (void*)(pSurfOptimized);
|
|
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Texture* pTex = NULL;
|
|
|
|
if( pSurfLoaded != NULL ) {
|
|
|
|
// Support optional transparency
|
|
if (GSLC_BMP_TRANS_EN) {
|
|
// Color key surface
|
|
// - Use transparency color key defined in BMP_TRANS_RGB
|
|
// - SDL2 passes SDL_TRUE instead of SDL_SRCCOLORKEY
|
|
SDL_SetColorKey( pSurfLoaded, SDL_TRUE,
|
|
SDL_MapRGB( pSurfLoaded->format, GSLC_BMP_TRANS_RGB ) );
|
|
} // GSLC_BMP_TRANS_EN
|
|
}
|
|
|
|
pTex = (void*)SDL_CreateTextureFromSurface(pDriver->pRender,pSurfLoaded);
|
|
if (pTex == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvLoadBmp(%s) SDL_CreateTextureFromSurface() failed: %s\n",pStrFname,SDL_GetError());
|
|
return NULL;
|
|
}
|
|
|
|
// Dispose of surface
|
|
SDL_FreeSurface(pSurfLoaded);
|
|
pSurfLoaded = NULL;
|
|
|
|
//Return the texture
|
|
return (void*)pTex;
|
|
|
|
#endif
|
|
|
|
} // eImgFlags
|
|
|
|
// If reached here, it is an error
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef)
|
|
{
|
|
// Dispose of previous background
|
|
if (pGui->sImgRefBkgnd.eImgFlags != GSLC_IMGREF_NONE) {
|
|
gslc_DrvImageDestruct(pGui->sImgRefBkgnd.pvImgRaw);
|
|
pGui->sImgRefBkgnd = gslc_ResetImage();
|
|
}
|
|
|
|
pGui->sImgRefBkgnd = sImgRef;
|
|
pGui->sImgRefBkgnd.pvImgRaw = gslc_DrvLoadImage(pGui,sImgRef);
|
|
if (pGui->sImgRefBkgnd.pvImgRaw == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvSetBkgndImage(%s) failed\n","");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol)
|
|
{
|
|
|
|
// Dispose of previous background
|
|
if (pGui->sImgRefBkgnd.eImgFlags != GSLC_IMGREF_NONE) {
|
|
gslc_DrvImageDestruct(pGui->sImgRefBkgnd.pvImgRaw);
|
|
pGui->sImgRefBkgnd = gslc_ResetImage();
|
|
}
|
|
|
|
SDL_Surface* pSurfBkgnd = NULL;
|
|
|
|
uint16_t nScreenW = pGui->nDispW;
|
|
uint16_t nScreenH = pGui->nDispH;
|
|
uint8_t nBpp = pGui->nDispDepth;
|
|
|
|
// Create surface that we can draw into
|
|
// - For the masks, we can pass 0 to get defaults
|
|
#if defined(DRV_DISP_SDL1)
|
|
pSurfBkgnd = SDL_CreateRGBSurface(SDL_SWSURFACE,nScreenW,nScreenH,nBpp,0,0,0,0);
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
// - In SDL2, the flags field is ignored, so set to 0
|
|
pSurfBkgnd = SDL_CreateRGBSurface(0,nScreenW,nScreenH,nBpp,0,0,0,0);
|
|
#endif
|
|
|
|
if (pSurfBkgnd == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvSetBkgndColor() SDL_CreateRGBSurface failed: %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Fill with requested color
|
|
SDL_FillRect(pSurfBkgnd,NULL,
|
|
SDL_MapRGB(pSurfBkgnd->format,nCol.r,nCol.g,nCol.b));
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
// Save surface into GUI struct
|
|
pGui->sImgRefBkgnd.pvImgRaw = (void*)(pSurfBkgnd);
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
// Convert to texture and save into GUI struct
|
|
pGui->sImgRefBkgnd.pvImgRaw = (void*)SDL_CreateTextureFromSurface(pDriver->pRender,pSurfBkgnd);
|
|
if (pGui->sImgRefBkgnd.pvImgRaw == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvSetBkgndColor() SDL_CreateTextureFromSurface failed: %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
// Dispose of temporary surface
|
|
SDL_FreeSurface(pSurfBkgnd);
|
|
pSurfBkgnd = NULL;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef)
|
|
{
|
|
// Dispose of previous image
|
|
if (pElem->sImgRefNorm.eImgFlags != GSLC_IMGREF_NONE) {
|
|
gslc_DrvImageDestruct(pElem->sImgRefNorm.pvImgRaw);
|
|
pElem->sImgRefNorm = gslc_ResetImage();
|
|
}
|
|
|
|
pElem->sImgRefNorm = sImgRef;
|
|
pElem->sImgRefNorm.pvImgRaw = gslc_DrvLoadImage(pGui,sImgRef);
|
|
if (pElem->sImgRefNorm.pvImgRaw == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvSetElemImageNorm(%s) failed\n","");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef)
|
|
{
|
|
// Dispose of previous image
|
|
if (pElem->sImgRefGlow.eImgFlags != GSLC_IMGREF_NONE) {
|
|
gslc_DrvImageDestruct(pElem->sImgRefGlow.pvImgRaw);
|
|
pElem->sImgRefGlow = gslc_ResetImage();
|
|
}
|
|
|
|
pElem->sImgRefGlow = sImgRef;
|
|
pElem->sImgRefGlow.pvImgRaw = gslc_DrvLoadImage(pGui,sImgRef);
|
|
if (pElem->sImgRefGlow.pvImgRaw == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvSetElemImageGlow(%s) failed\n","");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void gslc_DrvImageDestruct(void* pvImg)
|
|
{
|
|
if (pvImg == NULL) {
|
|
return;
|
|
}
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_FreeSurface((SDL_Surface*)pvImg);
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_DestroyTexture((SDL_Texture*)pvImg);
|
|
#endif
|
|
}
|
|
|
|
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect)
|
|
{
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
if (pRect == NULL) {
|
|
SDL_SetClipRect(pScreen,NULL);
|
|
} else {
|
|
SDL_Rect rSRect = gslc_DrvAdaptRect(*pRect);
|
|
SDL_SetClipRect(pScreen,&rSRect);
|
|
}
|
|
return true;
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
if (pRect == NULL) {
|
|
SDL_RenderSetClipRect(pRender,NULL);
|
|
} else {
|
|
SDL_Rect rSRect = gslc_DrvAdaptRect(*pRect);
|
|
SDL_RenderSetClipRect(pRender,&rSRect);
|
|
}
|
|
return true;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Font handling Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz)
|
|
{
|
|
// UNIX/SDL mode currently only supports font definitions from files
|
|
if (eFontRefType != GSLC_FONTREF_FNAME) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvFontAdd(%s) failed - SDL mode only supports file-based fonts\n","");
|
|
return NULL;
|
|
}
|
|
TTF_Font* pFont;
|
|
// In the case of SDL, the pvFontRef is a string that defines the
|
|
// file path to the font file
|
|
pFont = TTF_OpenFont((const char*)pvFontRef,nFontSz);
|
|
if (pFont == NULL) {
|
|
GSLC_DEBUG_PRINT("ERROR: DrvFontAdd(%s) failed in TTF_OpenFont\n",pvFontRef);
|
|
return NULL;
|
|
}
|
|
return (const void*)pFont;
|
|
}
|
|
|
|
void gslc_DrvFontsDestruct(gslc_tsGui* pGui)
|
|
{
|
|
uint16_t nFontInd;
|
|
TTF_Font* pFont = NULL;
|
|
for (nFontInd=0;nFontInd<pGui->nFontCnt;nFontInd++) {
|
|
if (pGui->asFont[nFontInd].pvFont != NULL) {
|
|
pFont = (TTF_Font*)(pGui->asFont[nFontInd].pvFont);
|
|
TTF_CloseFont(pFont);
|
|
pGui->asFont[nFontInd].pvFont = NULL;
|
|
}
|
|
}
|
|
pGui->nFontCnt = 0;
|
|
TTF_Quit();
|
|
}
|
|
|
|
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
|
|
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH)
|
|
{
|
|
// NOTE: Shouldn't need to process eTxtFlags
|
|
int32_t nTxtSzW,nTxtSzH;
|
|
TTF_Font* pDrvFont = (TTF_Font*)(pFont->pvFont);
|
|
if (!pDrvFont) { return false; }
|
|
if ((eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8) {
|
|
TTF_SizeUTF8(pDrvFont,pStr,&nTxtSzW,&nTxtSzH);
|
|
} else {
|
|
TTF_SizeText(pDrvFont,pStr,&nTxtSzW,&nTxtSzH);
|
|
}
|
|
*pnTxtSzW = (uint16_t)nTxtSzW;
|
|
*pnTxtSzH = (uint16_t)nTxtSzH;
|
|
// No offset coordinates used
|
|
*pnTxtX = 0;
|
|
*pnTxtY = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
// NOTE: SDL driver is compiled as pure C, so can't use default parameters.
|
|
// Other drivers have specified colBg as a default, but so far no callers
|
|
// are depending on the default.
|
|
// TODO: Update DrvDrawTxt() and DrvDrawTxtAlign() APIs for all drivers to
|
|
// no longer use the default param for consistency.
|
|
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt, gslc_tsColor colBg)
|
|
{
|
|
if ((pGui == NULL) || (pFont == NULL)) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawTxt(%s) with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
if ((pStr == NULL) || (pStr[0] == '\0')) {
|
|
return true;
|
|
}
|
|
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Surface* pSurfTxt = NULL;
|
|
TTF_Font* pDrvFont = (TTF_Font*)(pFont->pvFont);
|
|
if (!pDrvFont) { return false; }
|
|
if ((eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8) {
|
|
pSurfTxt = TTF_RenderUTF8_Blended(pDrvFont,pStr,gslc_DrvAdaptColor(colTxt));
|
|
} else {
|
|
pSurfTxt = TTF_RenderText_Blended(pDrvFont,pStr,gslc_DrvAdaptColor(colTxt));
|
|
}
|
|
if (pSurfTxt == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawTxt() failed in TTF_RenderText_Solid() (%s)\n",pStr);
|
|
return false;
|
|
}
|
|
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
gslc_DrvPasteSurface(pGui,nTxtX,nTxtY,pSurfTxt,pDriver->pSurfScreen);
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Rect rRect = (SDL_Rect){nTxtX,nTxtY,pSurfTxt->w,pSurfTxt->h};
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_Texture* pTex = SDL_CreateTextureFromSurface(pRender,pSurfTxt);
|
|
if (pTex == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawTxt() error in SDL_CreateTextureFromSurface(): %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
SDL_RenderCopy(pRender,pTex,NULL,&rRect);
|
|
|
|
// Destroy texture
|
|
// TODO: Consider if worth optimizing by retaining texture in case
|
|
// we need to redraw it without changing content
|
|
SDL_DestroyTexture(pTex);
|
|
pTex = NULL;
|
|
#endif
|
|
|
|
// Dispose of temporary surface
|
|
if (pSurfTxt != NULL) {
|
|
SDL_FreeSurface(pSurfTxt);
|
|
pSurfTxt = NULL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Screen Management Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
void gslc_DrvPageFlipNow(gslc_tsGui* pGui)
|
|
{
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
SDL_Flip(pScreen);
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
if (pRender) {
|
|
// Flip the offscreen buffer so we can display our drawing output
|
|
SDL_RenderPresent(pRender);
|
|
// Clear the drawing before any new drawing occurs
|
|
SDL_SetRenderDrawColor(pRender,0x00,0x00,0x00,0xFF);
|
|
SDL_RenderClear(pRender);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Graphics Primitives Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol)
|
|
{
|
|
#if defined(DRV_DISP_SDL1)
|
|
if (gslc_DrvScreenLock(pGui)) {
|
|
uint32_t nColRaw = gslc_DrvAdaptColorRaw(pGui,nCol);
|
|
gslc_DrvDrawSetPixelRaw(pGui,nX,nY,nColRaw);
|
|
gslc_DrvScreenUnlock(pGui);
|
|
}
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_SetRenderDrawColor(pRender,nCol.r,nCol.g,nCol.b,255);
|
|
|
|
// Call SDL optimized routine
|
|
SDL_RenderDrawPoint(pRender,nX,nY);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|
|
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol)
|
|
{
|
|
#if defined(DRV_DISP_SDL1)
|
|
uint16_t nIndPt;
|
|
if (gslc_DrvScreenLock(pGui)) {
|
|
uint32_t nColRaw = gslc_DrvAdaptColorRaw(pGui,nCol);
|
|
for (nIndPt=0;nIndPt<nNumPt;nIndPt++) {
|
|
gslc_DrvDrawSetPixelRaw(pGui,asPt[nIndPt].x,asPt[nIndPt].y,nColRaw);
|
|
}
|
|
gslc_DrvScreenUnlock(pGui);
|
|
}
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
// NOTE: gslc_tsPt is defined to have the same layout as SDL_Point
|
|
// so we simply typecast it here. This saves us from having
|
|
// to perform any malloc() and type conversion.
|
|
SDL_SetRenderDrawColor(pRender,nCol.r,nCol.g,nCol.b,255);
|
|
|
|
// Call SDL optimized routine
|
|
SDL_RenderDrawPoints(pRender,(SDL_Point*)asPt,(int)nNumPt);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol)
|
|
{
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
#if defined(DRV_DISP_SDL1)
|
|
// Typecast
|
|
SDL_Rect rSRect = gslc_DrvAdaptRect(rRect);
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
|
|
// Call SDL optimized routine
|
|
SDL_FillRect(pScreen,&rSRect,
|
|
SDL_MapRGB(pScreen->format,nCol.r,nCol.g,nCol.b));
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_SetRenderDrawColor(pRender,nCol.r,nCol.g,nCol.b,255);
|
|
|
|
// Call SDL optimized routine
|
|
SDL_Rect rRectSdl;
|
|
rRectSdl = gslc_DrvAdaptRect(rRect);
|
|
SDL_RenderFillRect(pRender,&rRectSdl);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol)
|
|
{
|
|
#if defined(DRV_DISP_SDL1)
|
|
return false;
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_SetRenderDrawColor(pRender,nCol.r,nCol.g,nCol.b,255);
|
|
|
|
// Call SDL optimized routine
|
|
SDL_Rect rRectSdl;
|
|
rRectSdl = gslc_DrvAdaptRect(rRect);
|
|
SDL_RenderDrawRect(pRender,&rRectSdl);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol)
|
|
{
|
|
#if defined(DRV_DISP_SDL1)
|
|
// ERROR
|
|
return false;
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_SetRenderDrawColor(pRender,nCol.r,nCol.g,nCol.b,255);
|
|
|
|
// Call SDL optimized routine
|
|
SDL_RenderDrawLine(pRender,nX0,nY0,nX1,nY1);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawImage(%s) with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
// GUIslice adapter library for SDL always pre-loads
|
|
// surfaces / textures before calling DrvDrawImage(), so
|
|
// we just need to confirm that the raw image data is defined.
|
|
void* pImage = sImgRef.pvImgRaw;
|
|
if (pImage == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawImage(%s) with NULL pvImgRaw\n","");
|
|
return false;
|
|
}
|
|
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
gslc_DrvPasteSurface(pGui,nDstX,nDstY,pImage,pDriver->pSurfScreen);
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_Texture* pTex = (SDL_Texture*)pImage;
|
|
|
|
// Determine dest rect based on source texture dimensions and parameterized offset
|
|
SDL_Rect rDest;
|
|
rDest.x = nDstX;
|
|
rDest.y = nDstY;
|
|
SDL_QueryTexture(pTex,NULL,NULL,&rDest.w,&rDest.h);
|
|
|
|
// Default to copying all of source texture rect by specifying NULL
|
|
SDL_RenderCopy(pRender,pTex,NULL,&rDest);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/// NOTE: Background image is stored in pGui->sImgRefBkgnd
|
|
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawBkgnd(%s) with NULL ptr\n","");
|
|
return;
|
|
}
|
|
// GUIslice adapter library for SDL always pre-loads
|
|
// surfaces / textures before calling DrvDrawBkgnd(), so
|
|
// we just need to confirm that the raw image data is defined.
|
|
void* pImage = pGui->sImgRefBkgnd.pvImgRaw;
|
|
if (pImage == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawBkgnd(%s) with NULL pvImgRaw\n","");
|
|
|
|
// Since the image load failed, resort to black background
|
|
gslc_DrvSetBkgndColor(pGui,GSLC_COL_BLACK);
|
|
return;
|
|
}
|
|
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
gslc_DrvPasteSurface(pGui,0,0,pGui->sImgRefBkgnd.pvImgRaw,pDriver->pSurfScreen);
|
|
#endif
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
SDL_Renderer* pRender = pDriver->pRender;
|
|
SDL_Texture* pTex = (SDL_Texture*)(pGui->sImgRefBkgnd.pvImgRaw);
|
|
|
|
// Determine destination rect
|
|
|
|
// TODO: Support other background modes such as:
|
|
// - Single Corner Unscaled
|
|
// - Single Centered Unscaled
|
|
// - Single Centered Scaled
|
|
// - Tiled Corner Unscaled
|
|
|
|
// For "single centered scaled", we fetch entire viewport
|
|
SDL_Rect rDest;
|
|
SDL_RenderGetViewport(pDriver->pRender,&rDest);
|
|
rDest.x = 0;
|
|
rDest.y = 0;
|
|
|
|
// Default to copying all of source texture rect by specifying NULL
|
|
SDL_RenderCopy(pRender,pTex,NULL,&rDest);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Touch Functions (via SDL)
|
|
// ------------------------------------------------------------------------
|
|
|
|
// POST:
|
|
// - pDriver->pTsDev mapped to touchscreen device
|
|
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvInitTouch(%s) called with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
|
|
// Perform any driver-specific touchscreen init here
|
|
|
|
#if defined(DRV_TOUCH_TSLIB)
|
|
// Assign default
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
pDriver->pTsDev = NULL;
|
|
#endif
|
|
|
|
// Nothing further to do with SDL driver
|
|
return true;
|
|
}
|
|
|
|
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvGetTouch(%s) called with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
|
|
// Use SDL for touch events
|
|
bool bRet = false;
|
|
SDL_Event sEvent;
|
|
int32_t nX,nY;
|
|
int16_t nKeyVal;
|
|
|
|
*peInputEvent = GSLC_INPUT_NONE;
|
|
|
|
if (SDL_PollEvent(&sEvent)) {
|
|
nKeyVal = (int16_t)(sEvent.key.keysym.sym);
|
|
|
|
// Handle Key presses
|
|
if (sEvent.type == SDL_KEYDOWN) {
|
|
#if (GSLC_FEATURE_INPUT)
|
|
*peInputEvent = GSLC_INPUT_KEY_DOWN;
|
|
*pnInputVal = nKeyVal;
|
|
bRet = true;
|
|
#endif
|
|
} else if (sEvent.type == SDL_KEYUP) {
|
|
#if (GSLC_FEATURE_INPUT)
|
|
*peInputEvent = GSLC_INPUT_KEY_UP;
|
|
*pnInputVal = nKeyVal;
|
|
bRet = true;
|
|
#endif
|
|
|
|
// Handle Touch / Mouse presses
|
|
} else if (sEvent.type == SDL_MOUSEMOTION) {
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_GetMouseState(&nX,&nY);
|
|
#elif defined(DRV_DISP_SDL2)
|
|
nX = sEvent.button.x;
|
|
nY = sEvent.button.y;
|
|
#endif
|
|
*pnX = (int16_t)nX;
|
|
*pnY = (int16_t)nY;
|
|
// For MOUSEMOTION, we want to return the previous state of
|
|
// the touch pressure in pnPress as MOUSEMOTION is called during
|
|
// both mouse up and mouse down states.
|
|
// Note that we can't simply leave pnPress as-is since it
|
|
// doesn't retain its state in the caller.
|
|
*pnPress = pGui->nTouchLastPress;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
} else if (sEvent.type == SDL_MOUSEBUTTONDOWN) {
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_GetMouseState(&nX,&nY);
|
|
#elif defined(DRV_DISP_SDL2)
|
|
nX = sEvent.button.x;
|
|
nY = sEvent.button.y;
|
|
#endif
|
|
*pnX = (int16_t)nX;
|
|
*pnY = (int16_t)nY;
|
|
(*pnPress) = 1;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
} else if (sEvent.type == SDL_MOUSEBUTTONUP) {
|
|
#if defined(DRV_DISP_SDL1)
|
|
SDL_GetMouseState(&nX,&nY);
|
|
#elif defined(DRV_DISP_SDL2)
|
|
nX = sEvent.motion.x;
|
|
nY = sEvent.motion.y;
|
|
#endif
|
|
*pnX = (int16_t)nX;
|
|
*pnY = (int16_t)nY;
|
|
(*pnPress) = 0;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
|
|
|
|
// SDL2 defines touch events instead of reusing mouse events
|
|
#if defined(DRV_DISP_SDL2)
|
|
|
|
} else if (sEvent.type == SDL_FINGERMOTION) {
|
|
*pnX = (int16_t)(sEvent.tfinger.x);
|
|
*pnY = (int16_t)(sEvent.tfinger.y);
|
|
// For FINGERMOTION, we want to return the previous state of
|
|
// the touch pressure in pnPress as FINGERMOTION is called during
|
|
// both up and down states.
|
|
// Note that we can't simply leave pnPress as-is since it
|
|
// doesn't retain its state in the caller.
|
|
*pnPress = pGui->nTouchLastPress;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
} else if (sEvent.type == SDL_FINGERDOWN) {
|
|
*pnX = (int16_t)(sEvent.tfinger.x);
|
|
*pnY = (int16_t)(sEvent.tfinger.y);
|
|
(*pnPress) = 1;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
} else if (sEvent.type == SDL_FINGERUP) {
|
|
*pnX = (int16_t)(sEvent.tfinger.x);
|
|
*pnY = (int16_t)(sEvent.tfinger.y);
|
|
(*pnPress) = 0;
|
|
*peInputEvent = GSLC_INPUT_TOUCH;
|
|
bRet = true;
|
|
#endif // DRV_DISP_SDL2
|
|
|
|
}
|
|
} // SDL_PollEvent()
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/// Change display rotation and any associated touch orientation
|
|
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation)
|
|
{
|
|
// TODO: Implement support for display rotation
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvRotate(%s) not supported in current DRV_DISP_SDL* mode yet\n","");
|
|
return false;
|
|
}
|
|
|
|
// =======================================================================
|
|
// Private Functions
|
|
// =======================================================================
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Configuration Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Unfortunately, SDL has an issue wherein if a previous
|
|
// SDL program execution aborts before cleaning up properly (eg.
|
|
// with SDL_Quit) then the next time SDL_SetVideoMode
|
|
// is called, it may hang.
|
|
//
|
|
// The following code attempts to work around this sensitivity
|
|
// in SDL, making the SDL_SetVideoMode call more robust.
|
|
//
|
|
// DETAILS:
|
|
// - If an SDL program exits without first calling the cleanup
|
|
// routines (ie. in SDL_Quit() ), the terminal may be left in
|
|
// KD_GRAPHICS mode.
|
|
// - When another SDL program is started and attempts to call
|
|
// SDL_SetVideoMode(), program execution may hang in
|
|
// FB_EnterGraphicsMode() during the wait on switch to
|
|
// KD_GRAPHICS mode. Since we are already in KD_GRAPHICS
|
|
// this event never occurs.
|
|
// - This workaround unlocks the switch (which was set at the
|
|
// end of a previous call to FB_EnterGraphicsMode) and then
|
|
// forces the terminal to KD_TEXT mode.
|
|
// - By starting in text mode, we should then observe a VT
|
|
// switch event as we attempt to transition to graphics mode
|
|
// in the call to SDL_SetVideoMode().
|
|
bool gslc_DrvCleanStart(const char* sTTY)
|
|
{
|
|
#if (DRV_SDL_FIX_START)
|
|
int nFD = -1;
|
|
int nRet = false;
|
|
|
|
nFD = open(sTTY, O_RDONLY, 0);
|
|
if (nFD < 0) {
|
|
GSLC_DEBUG2_PRINT( "ERROR: DrvCleanStart() failed to open console (%s): %s\n",
|
|
sTTY,strerror(errno));
|
|
return false;
|
|
}
|
|
nRet = ioctl(nFD, VT_UNLOCKSWITCH, 1);
|
|
if (nRet != false) {
|
|
GSLC_DEBUG2_PRINT( "ERROR: DrvCleanStart() failed to unlock console (%s): %s\n",
|
|
sTTY,strerror(errno));
|
|
return false;
|
|
}
|
|
nRet = ioctl(nFD, KDSETMODE, KD_TEXT);
|
|
if (nRet != 0) {
|
|
GSLC_DEBUG2_PRINT( "ERROR: DrvCleanStart() failed to set text mode (%s): %s\n",
|
|
sTTY,strerror(errno));
|
|
return false;
|
|
}
|
|
close(nFD);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Report some SDL debug info
|
|
void gslc_DrvReportInfoPre()
|
|
{
|
|
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
|
|
#elif defined(DRV_DISP_SDL2)
|
|
int16_t nDriverInd = 0;
|
|
int16_t nNumDrivers = 0;
|
|
|
|
// Video driver info
|
|
const char* pDriverName = NULL;
|
|
bool bDriverOk = false;
|
|
nNumDrivers = SDL_GetNumVideoDrivers();
|
|
for (nDriverInd=0;nDriverInd<nNumDrivers;nDriverInd++) {
|
|
pDriverName = SDL_GetVideoDriver(nDriverInd);
|
|
GSLC_DEBUG_PRINT("DBG: Video driver #%d: [%s]\n",nDriverInd,pDriverName);
|
|
|
|
bDriverOk = false;
|
|
#if defined(DRV_DISP_SDL1)
|
|
if (SDL_VideoInit(pDriverName,0) == 0) {
|
|
SDL_VideoQuit();
|
|
bDriverOk = true;
|
|
}
|
|
#elif defined(DRV_DISP_SDL2)
|
|
if (SDL_VideoInit(pDriverName) == 0) {
|
|
SDL_VideoQuit();
|
|
bDriverOk = true;
|
|
}
|
|
#endif
|
|
if (bDriverOk) {
|
|
GSLC_DEBUG_PRINT("DBG: - OK\n");
|
|
} else {
|
|
GSLC_DEBUG_PRINT("DBG: - Failed [%s]\n",SDL_GetError());
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(DRV_DISP_SDL2)
|
|
// Renderer info
|
|
SDL_RendererInfo sRendInfo;
|
|
nNumDrivers = SDL_GetNumRenderDrivers();
|
|
char acStr[10];
|
|
for (nDriverInd=0;nDriverInd<nNumDrivers;nDriverInd++) {
|
|
SDL_GetRenderDriverInfo(nDriverInd,&sRendInfo);
|
|
|
|
GSLC_DEBUG_PRINT("DBG: Render driver #%d: [%s]\n",nDriverInd,sRendInfo.name);
|
|
sprintf(acStr,"%08X",sRendInfo.flags);
|
|
GSLC_DEBUG_PRINT("DBG: - Flags = 0x%s\n", acStr);
|
|
GSLC_DEBUG_PRINT("DBG: - Software = %u\n",(sRendInfo.flags & SDL_RENDERER_SOFTWARE)?1:0);
|
|
GSLC_DEBUG_PRINT("DBG: - Accelerated = %u\n",(sRendInfo.flags & SDL_RENDERER_ACCELERATED)?1:0);
|
|
GSLC_DEBUG_PRINT("DBG: - PresentVSync = %u\n",(sRendInfo.flags & SDL_RENDERER_PRESENTVSYNC)?1:0);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
// Report some SDL debug info
|
|
void gslc_DrvReportInfoPost()
|
|
{
|
|
#if defined(DRV_DISP_SDL1)
|
|
// Video driver info
|
|
char acDriverName[40];
|
|
SDL_VideoDriverName(acDriverName,40);
|
|
GSLC_DEBUG_PRINT("DBG: Video Driver [%s]\n",acDriverName);
|
|
|
|
#elif defined(DRV_DISP_SDL2)
|
|
// Display Modes
|
|
int16_t nDispCnt,nDispInd;
|
|
int16_t nModeInd = 0;
|
|
bool bModeOk;
|
|
SDL_DisplayMode sMode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
|
|
|
|
nDispCnt = SDL_GetNumVideoDisplays();
|
|
GSLC_DEBUG_PRINT("DBG: Display count = %u\n",nDispCnt);
|
|
for (nDispInd=0;nDispInd<nDispCnt;nDispInd++) {
|
|
const char* pDispName = NULL;
|
|
bModeOk = (SDL_GetDisplayMode(nDispInd, nModeInd, &sMode) == 0)? true : false;
|
|
pDispName = SDL_GetDisplayName(nDispInd);
|
|
GSLC_DEBUG_PRINT("DBG: Display #%d: [%s]\n",nDispInd,pDispName);
|
|
GSLC_DEBUG_PRINT("DBG: - OK = %u\n",bModeOk?1:0);
|
|
if (bModeOk) {
|
|
GSLC_DEBUG_PRINT("DBG: - %d x %d, %u bpp\n",sMode.w,sMode.h,SDL_BITSPERPIXEL(sMode.format));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Conversion Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Simple typecasting for abstraction layer
|
|
SDL_Rect gslc_DrvAdaptRect(gslc_tsRect rRect)
|
|
{
|
|
SDL_Rect rSRect;
|
|
rSRect.x = rRect.x;
|
|
rSRect.y = rRect.y;
|
|
rSRect.h = rRect.h;
|
|
rSRect.w = rRect.w;
|
|
return rSRect;
|
|
}
|
|
|
|
// Simple typecasting for abstraction layer
|
|
SDL_Color gslc_DrvAdaptColor(gslc_tsColor sCol)
|
|
{
|
|
SDL_Color sSCol;
|
|
sSCol.r = sCol.r;
|
|
sSCol.g = sCol.g;
|
|
sSCol.b = sCol.b;
|
|
#if defined(DRV_DISP_SDL1)
|
|
sSCol.unused = 0;
|
|
#endif
|
|
#if defined(DRV_DISP_SDL2)
|
|
//???
|
|
#endif
|
|
return sSCol;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Private Drawing Functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
#if defined(DRV_DISP_SDL1)
|
|
uint32_t gslc_DrvAdaptColorRaw(gslc_tsGui* pGui,gslc_tsColor nCol)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvAdaptColorRaw(%s) called with NULL ptr\n","");
|
|
return 0;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
return SDL_MapRGB(pScreen->format,nCol.r,nCol.g,nCol.b);
|
|
}
|
|
|
|
|
|
// SDL1 requires direct pixel access as there is no "draw point / pixel"
|
|
// function. SDL2 has native access for point/pixel drawing so there is
|
|
// no need to access the pixel map directly.
|
|
|
|
bool gslc_DrvScreenLock(gslc_tsGui* pGui)
|
|
{
|
|
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvScreenLock(%s) called with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
// Typecast
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
|
|
if (SDL_MUSTLOCK(pScreen)) {
|
|
if (SDL_LockSurface(pScreen) < 0) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvScreenLock() can't lock screen: %s\n",SDL_GetError());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void gslc_DrvScreenUnlock(gslc_tsGui* pGui)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvScreenUnlock(%s) called with NULL ptr\n","");
|
|
return;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
// Typecast
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
|
|
if (SDL_MUSTLOCK(pScreen)) {
|
|
SDL_UnlockSurface(pScreen);
|
|
}
|
|
}
|
|
|
|
|
|
// - Based on code from:
|
|
// - https://www.libsdl.org/release/SDL-1.2.15/docs/html/guidevideo.html
|
|
uint32_t gslc_DrvDrawGetPixelRaw(gslc_tsGui* pGui, int16_t nX, int16_t nY)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawGetPixelRaw(%s) called with NULL ptr\n","");
|
|
return 0;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
if (pScreen == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawGetPixelRaw(%s) screen surface NULL\n","");
|
|
return 0;
|
|
}
|
|
int nBpp = pScreen->format->BytesPerPixel;
|
|
|
|
// Handle any range violations for entire surface
|
|
if ( (nX < 0) || (nX >= pScreen->w) ||
|
|
(nY < 0) || (nY >= pScreen->h) ) {
|
|
// ERROR
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawGetPixelRaw() out of range (%i,%i)\n",nX,nY);
|
|
return 0;
|
|
}
|
|
|
|
// Here pPixel is the address to the pixel we want to get
|
|
uint8_t *pPixel = (uint8_t *)pScreen->pixels + nY * pScreen->pitch + nX * nBpp;
|
|
|
|
switch(nBpp) {
|
|
case 1:
|
|
return *pPixel;
|
|
|
|
case 2:
|
|
return *(uint16_t *)pPixel;
|
|
|
|
case 3:
|
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
return pPixel[0] << 16 | pPixel[1] << 8 | pPixel[2];
|
|
else
|
|
return pPixel[0] | pPixel[1] << 8 | pPixel[2] << 16;
|
|
|
|
case 4:
|
|
return *(uint32_t *)pPixel;
|
|
|
|
default:
|
|
return 0; // shouldn't happen, but avoids warnings
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// - Based on code from:
|
|
// - https://www.libsdl.org/release/SDL-1.2.15/docs/html/guidevideo.html
|
|
// - Added range checks from surface clipping rect
|
|
void gslc_DrvDrawSetPixelRaw(gslc_tsGui* pGui,int16_t nX, int16_t nY, uint32_t nPixelVal)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawSetPixelRaw(%s) called with NULL ptr\n","");
|
|
return;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
SDL_Surface* pScreen = pDriver->pSurfScreen;
|
|
if (pScreen == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvDrawSetPixelRaw(%s) screen surface NULL\n","");
|
|
return;
|
|
}
|
|
uint8_t nBpp = pScreen->format->BytesPerPixel;
|
|
|
|
// Handle any clipping
|
|
if ( (nX < pScreen->clip_rect.x) || (nX >= pScreen->clip_rect.x+pScreen->clip_rect.w) ||
|
|
(nY < pScreen->clip_rect.y) || (nY >= pScreen->clip_rect.y+pScreen->clip_rect.h) ) {
|
|
return;
|
|
}
|
|
|
|
// Here pPixel is the address to the pixel we want to set
|
|
uint8_t *pPixel = (uint8_t *)pScreen->pixels + nY * pScreen->pitch + nX * nBpp;
|
|
|
|
switch(nBpp) {
|
|
case 1:
|
|
*pPixel = nPixelVal;
|
|
break;
|
|
|
|
case 2:
|
|
*(uint16_t *)pPixel = nPixelVal;
|
|
break;
|
|
|
|
case 3:
|
|
if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
|
|
pPixel[0] = (nPixelVal >> 16) & 0xff;
|
|
pPixel[1] = (nPixelVal >> 8) & 0xff;
|
|
pPixel[2] = nPixelVal & 0xff;
|
|
} else {
|
|
pPixel[0] = nPixelVal & 0xff;
|
|
pPixel[1] = (nPixelVal >> 8) & 0xff;
|
|
pPixel[2] = (nPixelVal >> 16) & 0xff;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
*(uint32_t *)pPixel = nPixelVal;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void gslc_DrvPasteSurface(gslc_tsGui* pGui,int16_t nX, int16_t nY, void* pvSrc, void* pvDest)
|
|
{
|
|
if ((pGui == NULL) || (pvSrc == NULL) || (pvDest == NULL)) {
|
|
GSLC_DEBUG2_PRINT("ERROR: DrvPasteSurface(%s) called with NULL ptr\n","");
|
|
return;
|
|
}
|
|
SDL_Surface* pSrc = (SDL_Surface*)(pvSrc);
|
|
SDL_Surface* pDest = (SDL_Surface*)(pvDest);
|
|
SDL_Rect offset;
|
|
offset.x = nX;
|
|
offset.y = nY;
|
|
SDL_BlitSurface(pSrc,NULL,pDest,&offset);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Touch Functions (via external tslib)
|
|
// ------------------------------------------------------------------------
|
|
|
|
#if defined(DRV_TOUCH_TSLIB)
|
|
|
|
// POST:
|
|
// - pDriver->pTsDev mapped to touchscreen device
|
|
bool gslc_TDrvInitTouch(gslc_tsGui* pGui,const char* acDev)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: TDrvInitTouch(%s) called with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
|
|
// Perform any driver-specific touchscreen init here
|
|
|
|
// Assign default
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
pDriver->pTsDev = NULL;
|
|
|
|
// TODO: Consider using env "TSLIB_TSDEVICE" instead
|
|
//char* pDevName = NULL;
|
|
//pDevName = getenv("TSLIB_TSDEVICE");
|
|
//pDriver->pTsDev = ts_open(pDevName,1);
|
|
|
|
// Open in non-blocking mode
|
|
pDriver->pTsDev = ts_open(acDev,1);
|
|
if (!pDriver->pTsDev) {
|
|
GSLC_DEBUG2_PRINT("ERROR: TsOpen(%s) failed\n","");
|
|
return false;
|
|
}
|
|
|
|
if (ts_config(pDriver->pTsDev)) {
|
|
GSLC_DEBUG2_PRINT("ERROR: ts_config(%s) failed\n","");
|
|
// Clear the tslib pointer so we don't try to call it again
|
|
pDriver->pTsDev = NULL;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool gslc_TDrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal)
|
|
{
|
|
if (pGui == NULL) {
|
|
GSLC_DEBUG2_PRINT("ERROR: TDrvGetTouch(%s) called with NULL ptr\n","");
|
|
return false;
|
|
}
|
|
gslc_tsDriver* pDriver = (gslc_tsDriver*)(pGui->pvDriver);
|
|
// In case tslib was not loaded, exit now
|
|
if (pDriver->pTsDev == NULL) {
|
|
return false;
|
|
}
|
|
struct ts_sample pSamp;
|
|
int32_t nRet = ts_read(pDriver->pTsDev,&pSamp,1);
|
|
// ts_read returns the number of samples actually fetched
|
|
// Since we are only requesting at most 1 sample, the return
|
|
// value should either be 0 (no samples) or 1 (sample success)
|
|
|
|
if (nRet > 0) {
|
|
// Sample successfully fetched
|
|
(*pnX) = pSamp.x;
|
|
(*pnY) = pSamp.y;
|
|
(*pnPress) = pSamp.pressure;
|
|
(*peInputEvent) = GSLC_INPUT_TOUCH;
|
|
(*pnInputVal) = 0;
|
|
return true;
|
|
} else {
|
|
// No sample returned
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif // DRV_TOUCH_TSLIB
|
|
|
|
#endif // Compiler guard for requested driver
|