diff --git a/boards/base/Linux/board.mk b/boards/base/Linux/board.mk index 49004bdd..591d09d0 100644 --- a/boards/base/Linux/board.mk +++ b/boards/base/Linux/board.mk @@ -3,5 +3,5 @@ GFXSRC += GFXDEFS += -DGFX_USE_OS_LINUX=TRUE GFXLIBS += rt -include $(GFXLIB)/drivers/multiple/X/driver.mk +include $(GFXLIB)/drivers/multiple/SDL/driver.mk diff --git a/boards/base/OSX/board.mk b/boards/base/OSX/board.mk index 9381890d..b2f46341 100644 --- a/boards/base/OSX/board.mk +++ b/boards/base/OSX/board.mk @@ -3,5 +3,5 @@ GFXSRC += GFXDEFS += -DGFX_USE_OS_OSX=TRUE GFXLIBS += -include $(GFXLIB)/drivers/multiple/X/driver.mk +include $(GFXLIB)/drivers/multiple/SDL/driver.mk diff --git a/drivers/multiple/SDL/driver.mk b/drivers/multiple/SDL/driver.mk new file mode 100644 index 00000000..85cdee7d --- /dev/null +++ b/drivers/multiple/SDL/driver.mk @@ -0,0 +1,5 @@ +GFXINC += $(GFXLIB)/drivers/multiple/SDL +GFXSRC += $(GFXLIB)/drivers/multiple/SDL/gdisp_lld_SDL.c +GFXDEFS += GFX_OS_PRE_INIT_FUNCTION=sdl_driver_init +CFLAGS += `sdl2-config --cflags || /usr/local/bin/sdl2-config --cflags` +LDFLAGS += `sdl2-config --libs --cflags || /usr/local/bin/sdl2-config --libs --cflags` diff --git a/drivers/multiple/SDL/gdisp_lld_SDL.c b/drivers/multiple/SDL/gdisp_lld_SDL.c new file mode 100644 index 00000000..6c7748fa --- /dev/null +++ b/drivers/multiple/SDL/gdisp_lld_SDL.c @@ -0,0 +1,477 @@ +/* + * Created by Oleg Gerasimov + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 FALSE +#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 bool_t SDL_MouseInit(GMouse *m, unsigned driverinstance); + static bool_t 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 bool_t SDL_KeyboardInit(GKeyboard *k, unsigned driverinstance); + static int SDL_KeyboardGetData(GKeyboard *k, uint8_t *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; + uint16_t 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; + uint32_t 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 { + uint32_t key; + uint32_t keystate; + }; + static GKeyboard *keyboard = 0; +#endif + +// shared IPC context +struct SDL_UGFXContext { + uint32_t framebuf[GDISP_SCREEN_WIDTH*GDISP_SCREEN_HEIGHT]; + int16_t need_redraw; + int minx,miny,maxx,maxy; +#if GINPUT_NEED_MOUSE + coord_t mousex, mousey; + uint16_t buttons; +#endif +#if GINPUT_NEED_KEYBOARD + uint16_t 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 simulator", 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(uint32_t)); + 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; + uint8_t k_ugfx = 0; + uint32_t 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 bool_t 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 = GDISP_ROTATE_0; + g->g.Powermode = powerOn; + g->g.Backlight = 100; + + g->g.Contrast = 50; + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + + return TRUE; +} + + +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; + uint32_t *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 color_t 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 bool_t SDL_MouseInit(GMouse *m, unsigned driverinstance) { + mouse = m; + (void) driverinstance; + return TRUE; + } + + static bool_t SDL_MouseRead(GMouse *m, GMouseReading *pt) { + (void) m; + if (!context) + return FALSE; + pt->x = context->mousex; + pt->y = context->mousey; + pt->z = (context->buttons & GINPUT_MOUSE_BTN_LEFT) ? 1 : 0; + pt->buttons = context->buttons; + return TRUE; + } +#endif /* GINPUT_NEED_MOUSE */ + +#if GINPUT_NEED_KEYBOARD + static bool_t SDL_KeyboardInit(GKeyboard *k, unsigned driverinstance) { + keyboard = k; + (void) driverinstance; + return TRUE; + } + + static int SDL_KeyboardGetData(GKeyboard *k, uint8_t *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 */ diff --git a/drivers/multiple/SDL/gdisp_lld_config.h b/drivers/multiple/SDL/gdisp_lld_config.h new file mode 100644 index 00000000..22fc4180 --- /dev/null +++ b/drivers/multiple/SDL/gdisp_lld_config.h @@ -0,0 +1,26 @@ +/* + * Created by Oleg Gerasimov + * 06.08.2016 + */ + +#ifndef _GDISP_LLD_CONFIG_H +#define _GDISP_LLD_CONFIG_H + +#if GFX_USE_GDISP + +/*===========================================================================*/ +/* Driver hardware support. */ +/*===========================================================================*/ + +#define GDISP_HARDWARE_DRAWPIXEL TRUE +#define GDISP_HARDWARE_FILLS TRUE +#define GDISP_HARDWARE_BITFILLS FALSE +#define GDISP_HARDWARE_SCROLL FALSE +#define GDISP_HARDWARE_PIXELREAD TRUE +#define GDISP_HARDWARE_CONTROL FALSE + +#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB888 + +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_LLD_CONFIG_H */ diff --git a/drivers/multiple/SDL/readme.txt b/drivers/multiple/SDL/readme.txt new file mode 100644 index 00000000..3bbaec59 --- /dev/null +++ b/drivers/multiple/SDL/readme.txt @@ -0,0 +1,26 @@ +To use this driver: + +This driver is special in that it implements both the gdisp low level driver +and a touchscreen driver. + +1. Add in your gfxconf.h: + a) #define GFX_USE_GDISP TRUE + b) #define GFX_USE_GINPUT TRUE + #define GINPUT_USE_MOUSE TRUE + c) Any optional high level driver defines (see gdisp.h) eg: GDISP_NEED_MULTITHREAD + d) Optionally the following (with appropriate values): + #define GDISP_SCREEN_WIDTH 640 + #define GDISP_SCREEN_HEIGHT 480 + +2. To your makefile add the following lines: + include $(GFXLIB)/gfx.mk + include $(GFXLIB)/drivers/multiple/SDL/gdisp_lld.mk + +3. Install sdl2 + on Linux: + sudo apt-get install libsdl2-dev + on OSX: + brew install sdl2 --universal + +4. Modify your makefile to add `sdl2-config --libs --cflags` to the CFLAGS line. i.e. + CFLAGS = `sdl2-config --libs --cflags`