/** * @file sdl.h * */ /********************* * INCLUDES *********************/ #include "sdl.h" #if USE_MONITOR || USE_SDL #if LV_USE_DRAW_SDL # error "LV_USE_DRAW_SDL must not be enabled" #endif #if USE_MONITOR # warning "MONITOR is deprecated, use SDL instead. See lv_drivers/sdl/sdl.c" #endif #if USE_KEYBOARD # warning "KEYBOARD is deprecated, use SDL instead. See lv_drivers/sdl/sdl.c" #endif #if USE_MOUSE # warning "MOUSE is deprecated, use SDL instead. See lv_drivers/sdl/sdl.c" #endif #if USE_MOUSEWHEEL # warning "MOUSEWHEEL is deprecated, use SDL instead that. See lv_drivers/sdl/sdl.c" #endif #if USE_MONITOR && USE_SDL # error "Cannot enable both MONITOR and SDL at the same time. " #endif #if USE_MONITOR # define SDL_HOR_RES MONITOR_HOR_RES # define SDL_VER_RES MONITOR_VER_RES # define SDL_ZOOM MONITOR_ZOOM # define SDL_DOUBLE_BUFFERED MONITOR_DOUBLE_BUFFERED # define SDL_INCLUDE_PATH MONITOR_SDL_INCLUDE_PATH # define SDL_VIRTUAL_MACHINE MONITOR_VIRTUAL_MACHINE # define SDL_DUAL_DISPLAY MONITOR_DUAL #endif #ifndef SDL_FULLSCREEN # define SDL_FULLSCREEN 0 #endif #include "sdl_common_internal.h" #include #include #include #include SDL_INCLUDE_PATH /********************* * DEFINES *********************/ #ifndef KEYBOARD_BUFFER_SIZE #define KEYBOARD_BUFFER_SIZE SDL_TEXTINPUTEVENT_TEXT_SIZE #endif #ifndef SDL_WINDOW_TITLE #define SDL_WINDOW_TITLE "TFT Simulator" #endif /********************** * TYPEDEFS **********************/ typedef struct { SDL_Window * window; SDL_Renderer * renderer; SDL_Texture * texture; volatile bool sdl_refr_qry; #if SDL_DOUBLE_BUFFERED uint32_t * tft_fb_act; #else uint32_t * tft_fb; #endif }monitor_t; /********************** * STATIC PROTOTYPES **********************/ static void window_create(monitor_t * m); static void window_update(monitor_t * m); static void monitor_sdl_clean_up(void); static void sdl_event_handler(lv_timer_t * t); static void monitor_sdl_refr(lv_timer_t * t); /*********************** * GLOBAL PROTOTYPES ***********************/ /********************** * STATIC VARIABLES **********************/ monitor_t monitor; #if SDL_DUAL_DISPLAY monitor_t monitor2; #endif /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void sdl_init(void) { /*Initialize the SDL*/ SDL_Init(SDL_INIT_VIDEO); SDL_SetEventFilter(quit_filter, NULL); window_create(&monitor); #if SDL_DUAL_DISPLAY window_create(&monitor2); int x, y; SDL_GetWindowPosition(monitor2.window, &x, &y); SDL_SetWindowPosition(monitor.window, x + (SDL_HOR_RES * SDL_ZOOM) / 2 + 10, y); SDL_SetWindowPosition(monitor2.window, x - (SDL_HOR_RES * SDL_ZOOM) / 2 - 10, y); #endif SDL_StartTextInput(); lv_timer_create(sdl_event_handler, 10, NULL); } /** * Flush a buffer to the marked area * @param disp_drv pointer to driver where this function belongs * @param area an area where to copy `color_p` * @param color_p an array of pixels to copy to the `area` part of the screen */ void sdl_display_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { const lv_coord_t hres = disp_drv->physical_hor_res == -1 ? disp_drv->hor_res : disp_drv->physical_hor_res; const lv_coord_t vres = disp_drv->physical_ver_res == -1 ? disp_drv->ver_res : disp_drv->physical_ver_res; // printf("x1:%d,y1:%d,x2:%d,y2:%d\n", area->x1, area->y1, area->x2, area->y2); /*Return if the area is out the screen*/ if(area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1) { lv_disp_flush_ready(disp_drv); return; } #if SDL_DOUBLE_BUFFERED monitor.tft_fb_act = (uint32_t *)color_p; #else /*SDL_DOUBLE_BUFFERED*/ int32_t y; #if LV_COLOR_DEPTH != 24 && LV_COLOR_DEPTH != 32 /*32 is valid but support 24 for backward compatibility too*/ int32_t x; for(y = area->y1; y <= area->y2 && y < vres; y++) { for(x = area->x1; x <= area->x2; x++) { monitor.tft_fb[y * hres + x] = lv_color_to32(*color_p); color_p++; } } #else uint32_t w = lv_area_get_width(area); for(y = area->y1; y <= area->y2 && y < vres; y++) { memcpy(&monitor.tft_fb[y * hres + area->x1], color_p, w * sizeof(lv_color_t)); color_p += w; } #endif #endif /*SDL_DOUBLE_BUFFERED*/ monitor.sdl_refr_qry = true; /* TYPICALLY YOU DO NOT NEED THIS * If it was the last part to refresh update the texture of the window.*/ if(lv_disp_flush_is_last(disp_drv)) { monitor_sdl_refr(NULL); } /*IMPORTANT! It must be called to tell the system the flush is ready*/ lv_disp_flush_ready(disp_drv); } #if SDL_DUAL_DISPLAY /** * Flush a buffer to the marked area * @param disp_drv pointer to driver where this function belongs * @param area an area where to copy `color_p` * @param color_p an array of pixels to copy to the `area` part of the screen */ void sdl_display_flush2(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { const lv_coord_t hres = disp_drv->physical_hor_res == -1 ? disp_drv->hor_res : disp_drv->physical_hor_res; const lv_coord_t vres = disp_drv->physical_ver_res == -1 ? disp_drv->ver_res : disp_drv->physical_ver_res; /*Return if the area is out the screen*/ if(area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1) { lv_disp_flush_ready(disp_drv); return; } #if SDL_DOUBLE_BUFFERED monitor2.tft_fb_act = (uint32_t *)color_p; monitor2.sdl_refr_qry = true; /*IMPORTANT! It must be called to tell the system the flush is ready*/ lv_disp_flush_ready(disp_drv); #else int32_t y; #if LV_COLOR_DEPTH != 24 && LV_COLOR_DEPTH != 32 /*32 is valid but support 24 for backward compatibility too*/ int32_t x; for(y = area->y1; y <= area->y2 && y < vres; y++) { for(x = area->x1; x <= area->x2; x++) { monitor2.tft_fb[y * hres + x] = lv_color_to32(*color_p); color_p++; } } #else uint32_t w = lv_area_get_width(area); for(y = area->y1; y <= area->y2 && y < vres; y++) { memcpy(&monitor2.tft_fb[y * hres + area->x1], color_p, w * sizeof(lv_color_t)); color_p += w; } #endif monitor2.sdl_refr_qry = true; /* TYPICALLY YOU DO NOT NEED THIS * If it was the last part to refresh update the texture of the window.*/ if(lv_disp_flush_is_last(disp_drv)) { monitor_sdl_refr(NULL); } /*IMPORTANT! It must be called to tell the system the flush is ready*/ lv_disp_flush_ready(disp_drv); #endif } #endif /********************** * STATIC FUNCTIONS **********************/ /** * SDL main thread. All SDL related task have to be handled here! * It initializes SDL, handles drawing and the mouse. */ static void sdl_event_handler(lv_timer_t * t) { (void)t; /*Refresh handling*/ SDL_Event event; while(SDL_PollEvent(&event)) { mouse_handler(&event); mousewheel_handler(&event); keyboard_handler(&event); if((&event)->type == SDL_WINDOWEVENT) { switch((&event)->window.event) { #if SDL_VERSION_ATLEAST(2, 0, 5) case SDL_WINDOWEVENT_TAKE_FOCUS: #endif case SDL_WINDOWEVENT_EXPOSED: window_update(&monitor); #if SDL_DUAL_DISPLAY window_update(&monitor2); #endif break; default: break; } } } /*Run until quit event not arrives*/ if(sdl_quit_qry) { monitor_sdl_clean_up(); exit(0); } } /** * SDL main thread. All SDL related task have to be handled here! * It initializes SDL, handles drawing and the mouse. */ static void monitor_sdl_refr(lv_timer_t * t) { (void)t; /*Refresh handling*/ if(monitor.sdl_refr_qry != false) { monitor.sdl_refr_qry = false; window_update(&monitor); } #if SDL_DUAL_DISPLAY if(monitor2.sdl_refr_qry != false) { monitor2.sdl_refr_qry = false; window_update(&monitor2); } #endif } static void monitor_sdl_clean_up(void) { SDL_DestroyTexture(monitor.texture); SDL_DestroyRenderer(monitor.renderer); SDL_DestroyWindow(monitor.window); #if SDL_DUAL_DISPLAY SDL_DestroyTexture(monitor2.texture); SDL_DestroyRenderer(monitor2.renderer); SDL_DestroyWindow(monitor2.window); #endif SDL_Quit(); } static void window_create(monitor_t * m) { int flag = 0; #if SDL_FULLSCREEN flag |= SDL_WINDOW_FULLSCREEN; #endif m->window = SDL_CreateWindow(SDL_WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_HOR_RES * SDL_ZOOM, SDL_VER_RES * SDL_ZOOM, flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ m->renderer = SDL_CreateRenderer(m->window, -1, SDL_RENDERER_SOFTWARE); m->texture = SDL_CreateTexture(m->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, SDL_HOR_RES, SDL_VER_RES); SDL_SetTextureBlendMode(m->texture, SDL_BLENDMODE_BLEND); /*Initialize the frame buffer to gray (77 is an empirical value) */ #if SDL_DOUBLE_BUFFERED SDL_UpdateTexture(m->texture, NULL, m->tft_fb_act, SDL_HOR_RES * sizeof(uint32_t)); #else m->tft_fb = (uint32_t *)malloc(sizeof(uint32_t) * SDL_HOR_RES * SDL_VER_RES); memset(m->tft_fb, 0x44, SDL_HOR_RES * SDL_VER_RES * sizeof(uint32_t)); #endif m->sdl_refr_qry = true; } static void window_update(monitor_t * m) { #if SDL_DOUBLE_BUFFERED == 0 SDL_UpdateTexture(m->texture, NULL, m->tft_fb, SDL_HOR_RES * sizeof(uint32_t)); #else if(m->tft_fb_act == NULL) return; SDL_UpdateTexture(m->texture, NULL, m->tft_fb_act, SDL_HOR_RES * sizeof(uint32_t)); #endif SDL_RenderClear(m->renderer); lv_disp_t * d = _lv_refr_get_disp_refreshing(); if(d && d->driver->screen_transp) { SDL_SetRenderDrawColor(m->renderer, 0xff, 0, 0, 0xff); SDL_Rect r; r.x = 0; r.y = 0; r.w = SDL_HOR_RES; r.h = SDL_VER_RES; SDL_RenderDrawRect(m->renderer, &r); } /*Update the renderer with the texture containing the rendered image*/ SDL_RenderCopy(m->renderer, m->texture, NULL, NULL); SDL_RenderPresent(m->renderer); } #endif /*USE_MONITOR || USE_SDL*/