/* * 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 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, 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 gCoord 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", 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 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; 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 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, 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 */