QMK/lib/ugfx/drivers/multiple/SDL/gdisp_lld_SDL.c

478 lines
12 KiB
C

/*
* Created by Oleg Gerasimov <ogerasimov@gmail.com>
* 06.08.2016
*/
// We need to include stdio.h below. Turn off GFILE_NEED_STDIO just for this file to prevent conflicts
#define GFILE_NEED_STDIO_MUST_BE_OFF
#include "gfx.h"
#if GFX_USE_GDISP
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <SDL.h>
#define GDISP_DRIVER_VMT GDISPVMT_SDL
#include "gdisp_lld_config.h"
#include "../../../src/gdisp/gdisp_driver.h"
#ifndef GDISP_FORCE_24BIT
#define GDISP_FORCE_24BIT GFXOFF
#endif
#ifndef GDISP_SCREEN_WIDTH
#define GDISP_SCREEN_WIDTH 640
#endif
#ifndef GDISP_SCREEN_HEIGHT
#define GDISP_SCREEN_HEIGHT 480
#endif
#if GINPUT_NEED_MOUSE
// Include mouse support code
#define GMOUSE_DRIVER_VMT GMOUSEVMT_SDL
#include "../../../src/ginput/ginput_driver_mouse.h"
// Forward definitions
static gBool SDL_MouseInit(GMouse *m, unsigned driverinstance);
static gBool SDL_MouseRead(GMouse *m, GMouseReading *prd);
const GMouseVMT GMOUSE_DRIVER_VMT[1] = {{
{
GDRIVER_TYPE_MOUSE,
GMOUSE_VFLG_NOPOLL|GMOUSE_VFLG_DYNAMICONLY,
sizeof(GMouse),
_gmouseInitDriver, _gmousePostInitDriver, _gmouseDeInitDriver
},
1, // z_max
0, // z_min
1, // z_touchon
0, // z_touchoff
{ // pen_jitter
0, // calibrate
0, // click
0 // move
},
{ // finger_jitter
0, // calibrate
2, // click
2 // move
},
SDL_MouseInit, // init
0, // deinit
SDL_MouseRead, // get
0, // calsave
0 // calload
}};
static GMouse *mouse = 0;
#endif
#if GINPUT_NEED_KEYBOARD
// Include mouse support code
#define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_SDL
#include "../../../src/ginput/ginput_driver_keyboard.h"
// Forward definitions
static gBool SDL_KeyboardInit(GKeyboard *k, unsigned driverinstance);
static int SDL_KeyboardGetData(GKeyboard *k, gU8 *pch, int sz);
const GKeyboardVMT GKEYBOARD_DRIVER_VMT[1] = {{
{
GDRIVER_TYPE_KEYBOARD,
GKEYBOARD_VFLG_NOPOLL, // GKEYBOARD_VFLG_DYNAMICONLY
sizeof(GKeyboard),
_gkeyboardInitDriver, _gkeyboardPostInitDriver, _gkeyboardDeInitDriver
},
0,
SDL_KeyboardInit, // init
0, // deinit
SDL_KeyboardGetData, // getdata
0 // putdata void (*putdata)(GKeyboard *k, char ch); Optional
}};
static struct KeyMap {
SDL_Keycode k_sdl;
gU16 k_ugfx;
} SDL_keymap[] =
{
{SDLK_UP, GKEY_UP},
{SDLK_DOWN, GKEY_DOWN},
{SDLK_RIGHT, GKEY_RIGHT},
{SDLK_LEFT, GKEY_LEFT},
{SDLK_END, GKEY_END},
{SDLK_HOME, GKEY_HOME},
{SDLK_PAGEDOWN, GKEY_PAGEDOWN},
{SDLK_PAGEUP, GKEY_PAGEUP},
{SDLK_F1, GKEY_FN1},
{SDLK_F2, GKEY_FN2},
{SDLK_F3, GKEY_FN3},
{SDLK_F4, GKEY_FN4},
{SDLK_F5, GKEY_FN5},
{SDLK_F6, GKEY_FN6},
{SDLK_F7, GKEY_FN7},
{SDLK_F8, GKEY_FN8},
{SDLK_F9, GKEY_FN9},
{SDLK_F10, GKEY_FN10},
{SDLK_F11, GKEY_FN11},
{SDLK_F12, GKEY_FN12},
{SDLK_F13, GKEY_FN13},
{SDLK_F14, GKEY_FN14},
{SDLK_F15, GKEY_FN15},
{SDLK_BRIGHTNESSDOWN, GKEY_LIGHTDOWN},
{SDLK_BRIGHTNESSUP, GKEY_LIGHTUP},
{SDLK_AUDIONEXT, GKEY_MEDIANEXT},
{SDLK_AUDIOPREV, GKEY_MEDIAPREV},
{SDLK_AUDIOPLAY, GKEY_MEDIAPLAY},
{SDLK_AUDIOSTOP, GKEY_MEDIASTOP},
{SDLK_VOLUMEUP, GKEY_VOLUP},
{SDLK_VOLUMEDOWN, GKEY_VOLDOWN},
{SDLK_MUTE, GKEY_VOLMUTE},
{0,0}
};
static struct ModMap {
SDL_Keycode s_sdl;
gU32 s_ugfx;
} SDL_modmap[] = {
{KMOD_LSHIFT, GKEYSTATE_SHIFT_L},
{KMOD_RSHIFT, GKEYSTATE_SHIFT_R},
{KMOD_RCTRL, GKEYSTATE_CTRL_R},
{KMOD_LCTRL, GKEYSTATE_CTRL_L},
{KMOD_RALT, GKEYSTATE_ALT_R},
{KMOD_LALT, GKEYSTATE_ALT_L},
{KMOD_CAPS, GKEYSTATE_CAPSLOCK},
{KMOD_NUM, GKEYSTATE_NUMLOCK},
{0,0}
};
struct SDL_keymsg {
gU32 key;
gU32 keystate;
};
static GKeyboard *keyboard = 0;
#endif
// shared IPC context
struct SDL_UGFXContext {
gU32 framebuf[GDISP_SCREEN_WIDTH*GDISP_SCREEN_HEIGHT];
gI16 need_redraw;
int minx,miny,maxx,maxy;
#if GINPUT_NEED_MOUSE
gCoord mousex, mousey;
gU16 buttons;
#endif
#if GINPUT_NEED_KEYBOARD
gU16 keypos;
struct SDL_keymsg keybuffer[8];
#endif
};
static struct SDL_UGFXContext *context;
static sem_t *ctx_mutex;
static sem_t *input_event;
#define CTX_MUTEX_NAME "ugfx_ctx_mutex"
#define INPUT_EVENT_NAME "ugfx_input_event"
static int SDL_loop (void) {
SDL_Window *window = SDL_CreateWindow("uGFX", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT, 0);
SDL_Renderer *render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Texture *texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT);
int done = 0;
while (!done) {
if (context->need_redraw) {
context->need_redraw = 0;
SDL_Rect r;
r.x = context->minx;
r.y = context->miny;
r.w = context->maxx - context->minx + 1;
r.h = context->maxy - context->miny + 1;
context->minx = GDISP_SCREEN_WIDTH;
context->miny = GDISP_SCREEN_HEIGHT;
context->maxx = 0;
context->maxy = 0;
SDL_UpdateTexture(texture, &r, context->framebuf+r.y*GDISP_SCREEN_WIDTH+r.x, GDISP_SCREEN_WIDTH*sizeof(gU32));
SDL_RenderCopy(render, texture, 0, 0);
SDL_RenderPresent(render);
}
SDL_Event event;
for (; SDL_PollEvent(&event); ){
switch(event.type){
#if GINPUT_NEED_MOUSE
#if 0
// On osx event contains coordinates of touchpad. We can't use them, we screen coordinates.
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
context->mousex = (event.tfinger.x<1.0)?event.tfinger.x*GDISP_SCREEN_WIDTH:event.tfinger.x;
context->mousey = (event.tfinger.y<1.0)?event.tfinger.y*GDISP_SCREEN_HEIGHT:event.tfinger.y;
context->buttons = (event.type != SDL_FINGERUP)?GINPUT_MOUSE_BTN_LEFT:0;
sem_post (input_event);
break;
#endif
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
context->mousex = event.button.x;
context->mousey = event.button.y;
context->buttons = (event.type ==SDL_MOUSEBUTTONDOWN)?GINPUT_MOUSE_BTN_LEFT:0;
sem_post (input_event);
break;
case SDL_MOUSEMOTION:
if (event.motion.state & SDL_BUTTON_LMASK) {
context->mousex = event.motion.x;
context->mousey = event.motion.y;
context->buttons = GINPUT_MOUSE_BTN_LEFT;
sem_post (input_event);
}
break;
#endif
#if GINPUT_NEED_KEYBOARD
case SDL_TEXTINPUT: {
int i;
sem_wait (ctx_mutex);
for (i=0; context->keypos < sizeof (context->keybuffer) && event.text.text[i]; ++i) {
context->keybuffer[context->keypos].key = event.text.text[i];
context->keybuffer[context->keypos++].keystate = 0;
}
sem_post (ctx_mutex);
sem_post (input_event);
break;
}
case SDL_KEYDOWN:
case SDL_KEYUP: {
SDL_Keycode k_sdl = event.key.keysym.sym;
gU8 k_ugfx = 0;
gU32 s_ugfx = (event.type==SDL_KEYDOWN)?0:GKEYSTATE_KEYUP;
int i;
if (!(k_sdl & ~0x7f) && (k_sdl <32 || k_sdl == 127)) {
k_ugfx = k_sdl;
}
else
for (i = 0; SDL_keymap[i].k_sdl; ++i)
if (SDL_keymap[i].k_sdl == k_sdl) {
k_ugfx = SDL_keymap[i].k_ugfx;
s_ugfx |= GKEYSTATE_SPECIAL;
break;
}
for (i = 0; SDL_modmap[i].s_sdl; ++i)
if (SDL_modmap[i].s_sdl & event.key.keysym.mod)
s_ugfx |= SDL_modmap[i].s_ugfx;
sem_wait (ctx_mutex);
if (k_ugfx && context->keypos+1 < (int)sizeof (context->keybuffer)) {
context->keybuffer[context->keypos].key = k_ugfx;
context->keybuffer[context->keypos++].keystate = s_ugfx;
}
sem_post (ctx_mutex);
sem_post (input_event);
break;
}
#endif
case SDL_QUIT:
done = 1;
break;
default:
break;
}
}
SDL_Delay(40);
}
SDL_DestroyTexture (texture);
SDL_DestroyRenderer (render);
SDL_DestroyWindow (window);
return 0;
}
static void *SDL_input_event_loop (void *arg) {
(void)arg;
for (;;) {
sem_wait (input_event);
#if GINPUT_NEED_KEYBOARD
if (keyboard)
_gkeyboardWakeup (keyboard);
#endif
#if GINPUT_NEED_MOUSE
if (mouse)
_gmouseWakeup (mouse);
#endif
}
return 0;
}
// Init driver
// Must be executed on early stage of initialization: before threads and timer
void sdl_driver_init (void) {
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
exit (1) ;
}
if ((context = (struct SDL_UGFXContext*) mmap (0,sizeof (struct SDL_UGFXContext ),PROT_WRITE|PROT_READ,(MAP_ANONYMOUS | MAP_SHARED),0,0)) ==MAP_FAILED) {
perror("Failed to allocate shared memory");
exit(1);
}
// Create mutex for locking shared context
sem_unlink (CTX_MUTEX_NAME);
if((ctx_mutex = sem_open(CTX_MUTEX_NAME,O_CREAT,0666,1)) == SEM_FAILED) {
perror("Failed init semaphore");
exit(1);
}
// Create event for input notifications to ugfx process
sem_unlink (INPUT_EVENT_NAME);
if((input_event = sem_open(INPUT_EVENT_NAME,O_CREAT,0666,0)) == SEM_FAILED) {
perror("Failed init semaphore");
exit(1);
}
pid_t gui_pid = fork ();
if (gui_pid) {
// Main proccess. It's for host UI and SDL
int status;
memset (context,0,sizeof (*context));
context->need_redraw = 1;
context->maxx = GDISP_SCREEN_WIDTH-1;
context->maxy = GDISP_SCREEN_HEIGHT-1;
context->minx = 0;
context->miny = 0;
SDL_loop ();
// cleanup
kill(gui_pid,SIGKILL);
waitpid(gui_pid, &status, 0);
SDL_Quit ();
munmap (context,sizeof (*context));
sem_close (ctx_mutex);
sem_unlink (CTX_MUTEX_NAME);
sem_close (input_event);
sem_unlink (INPUT_EVENT_NAME);
exit (0);
}
// Create thread for input events processing
pthread_t thread;
pthread_create(&thread, NULL, SDL_input_event_loop, NULL);
pthread_detach (thread);
// Continue execution of ugfx UI in forked process
}
LLDSPEC gBool gdisp_lld_init(GDisplay *g) {
g->board = 0; // No board interface for this driver
#if GINPUT_NEED_MOUSE
gdriverRegister((const GDriverVMT *)GMOUSE_DRIVER_VMT, g);
#endif
g->g.Orientation = gOrientation0;
g->g.Powermode = gPowerOn;
g->g.Backlight = 100;
g->g.Contrast = 50;
g->g.Width = GDISP_SCREEN_WIDTH;
g->g.Height = GDISP_SCREEN_HEIGHT;
return gTrue;
}
static void SDL_extendUpdateRect (int x,int y) {
if (context->minx > x)
context->minx = x;
if (context->miny > y)
context->miny = y;
if (context->maxx < x)
context->maxx = x;
if (context->maxy < y)
context->maxy = y;
}
LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g)
{
if (context) {
context->framebuf[(g->p.y*GDISP_SCREEN_WIDTH)+g->p.x] = gdispColor2Native(g->p.color);
SDL_extendUpdateRect (g->p.x,g->p.y);
context->need_redraw = 1;
}
}
#if GDISP_HARDWARE_FILLS
LLDSPEC void gdisp_lld_fill_area(GDisplay *g) {
LLDCOLOR_TYPE c = gdispColor2Native(g->p.color);
if (context) {
int x,y;
gU32 *pbuf = context->framebuf + g->p.y*GDISP_SCREEN_WIDTH + g->p.x;
int dy = GDISP_SCREEN_WIDTH - g->p.cx;
for (y = 0; y < g->p.cy; ++y) {
for (x = 0; x < g->p.cx; ++x)
*pbuf++ = c;
pbuf += dy;
}
SDL_extendUpdateRect (g->p.x,g->p.y);
SDL_extendUpdateRect (g->p.x+g->p.cx-1,g->p.y+g->p.cy-1);
context->need_redraw = 1;
}
}
#endif
#if GDISP_HARDWARE_PIXELREAD
LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay *g) {
if (context)
return gdispNative2Color(context->framebuf[(g->p.y*GDISP_SCREEN_WIDTH)+g->p.x]);
return 0;
}
#endif
#if GINPUT_NEED_MOUSE
static gBool SDL_MouseInit(GMouse *m, unsigned driverinstance) {
mouse = m;
(void) driverinstance;
return gTrue;
}
static gBool SDL_MouseRead(GMouse *m, GMouseReading *pt) {
(void) m;
if (!context)
return gFalse;
pt->x = context->mousex;
pt->y = context->mousey;
pt->z = (context->buttons & GINPUT_MOUSE_BTN_LEFT) ? 1 : 0;
pt->buttons = context->buttons;
return gTrue;
}
#endif /* GINPUT_NEED_MOUSE */
#if GINPUT_NEED_KEYBOARD
static gBool SDL_KeyboardInit(GKeyboard *k, unsigned driverinstance) {
keyboard = k;
(void) driverinstance;
return gTrue;
}
static int SDL_KeyboardGetData(GKeyboard *k, gU8 *pch, int sz) {
int i = 0;
if (!context || !context->keypos || !sz)
return 0;
sem_wait (ctx_mutex);
k->keystate = context->keybuffer[0].keystate;
for (i = 0; i < sz && i < context->keypos && context->keybuffer[0].keystate == context->keybuffer[i].keystate; i++)
pch[i] = context->keybuffer[i].key;
context->keypos -= i;
memmove (context->keybuffer,context->keybuffer+i,context->keypos * sizeof (context->keybuffer[0]));
sem_post (ctx_mutex);
return i;
}
#endif /* GINPUT_NEED_KEYBOARD */
#endif /* GFX_USE_GDISP */