/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes */ #include #include #ifdef _WIN32 #include #else #include #endif #include #include #define DBG_TAG "sdl.fb" #define DBG_LVL DBG_WARNING #include #define SDL_SCREEN_WIDTH 480 #define SDL_SCREEN_HEIGHT 320 extern void rt_hw_exit(void); #define SDL_SCREEN_FORMAT SDL_PIXELFORMAT_RGB565 struct sdlfb_device { struct rt_device parent; SDL_Renderer *renderer; /* window renderer */ SDL_Surface *surface; /* screen surface */ rt_uint16_t width; rt_uint16_t height; }; struct sdlfb_device _device; /* common device interface */ static rt_err_t sdlfb_init(rt_device_t dev) { return RT_EOK; } static rt_err_t sdlfb_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t sdlfb_close(rt_device_t dev) { SDL_Quit(); return RT_EOK; } int sdlfb_info(int *format, int *bpp) { int bit_pp; /* texture bits per pixel */ Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */ /* Grab info about format that will be passed into OpenGL */ SDL_PixelFormatEnumToMasks(SDL_SCREEN_FORMAT, &bit_pp, &Rmask, &Gmask, &Bmask, &Amask); *bpp = bit_pp; switch (SDL_SCREEN_FORMAT) { case SDL_PIXELFORMAT_RGB565: *format = RTGRAPHIC_PIXEL_FORMAT_RGB565; break; case SDL_PIXELFORMAT_RGB888: *format = RTGRAPHIC_PIXEL_FORMAT_RGB888; break; case SDL_PIXELFORMAT_ARGB8888: *format = RTGRAPHIC_PIXEL_FORMAT_ARGB888; break; case SDL_PIXELFORMAT_ABGR8888: *format = RTGRAPHIC_PIXEL_FORMAT_ABGR888; } return 0; } static rt_mutex_t sdllock; static rt_err_t sdlfb_control(rt_device_t dev, int cmd, void *args) { struct sdlfb_device *device; rt_mutex_take(sdllock, RT_WAITING_FOREVER); device = (struct sdlfb_device *)dev; RT_ASSERT(device != RT_NULL); switch (cmd) { case RTGRAPHIC_CTRL_GET_INFO: { int format, bpp; struct rt_device_graphic_info *info; sdlfb_info(&format, &bpp); info = (struct rt_device_graphic_info *) args; info->bits_per_pixel = bpp; info->pixel_format = format; info->framebuffer = (rt_uint8_t*)device->surface->pixels; info->width = device->width; info->height = device->height; } break; case RTGRAPHIC_CTRL_RECT_UPDATE: { SDL_Texture * texture; struct rt_device_rect_info *rect; SDL_Rect _rect = { 0, 0, SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT }; rect = (struct rt_device_rect_info *)args; SDL_RenderClear(_device.renderer); texture = SDL_CreateTextureFromSurface(_device.renderer, _device.surface); SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); SDL_RenderCopy(_device.renderer, texture, NULL, &_rect); SDL_RenderPresent(_device.renderer); SDL_DestroyTexture(texture); } break; case RTGRAPHIC_CTRL_SET_MODE: { break; } break; } rt_mutex_release(sdllock); return RT_EOK; } Uint16 pixels[16 * 16] = { // ...or with raw pixel data: 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0aab, 0x0789, 0x0bcc, 0x0eee, 0x09aa, 0x099a, 0x0ddd, 0x0fff, 0x0eee, 0x0899, 0x0fff, 0x0fff, 0x1fff, 0x0dde, 0x0dee, 0x0fff, 0xabbc, 0xf779, 0x8cdd, 0x3fff, 0x9bbc, 0xaaab, 0x6fff, 0x0fff, 0x3fff, 0xbaab, 0x0fff, 0x0fff, 0x6689, 0x6fff, 0x0dee, 0xe678, 0xf134, 0x8abb, 0xf235, 0xf678, 0xf013, 0xf568, 0xf001, 0xd889, 0x7abc, 0xf001, 0x0fff, 0x0fff, 0x0bcc, 0x9124, 0x5fff, 0xf124, 0xf356, 0x3eee, 0x0fff, 0x7bbc, 0xf124, 0x0789, 0x2fff, 0xf002, 0xd789, 0xf024, 0x0fff, 0x0fff, 0x0002, 0x0134, 0xd79a, 0x1fff, 0xf023, 0xf000, 0xf124, 0xc99a, 0xf024, 0x0567, 0x0fff, 0xf002, 0xe678, 0xf013, 0x0fff, 0x0ddd, 0x0fff, 0x0fff, 0xb689, 0x8abb, 0x0fff, 0x0fff, 0xf001, 0xf235, 0xf013, 0x0fff, 0xd789, 0xf002, 0x9899, 0xf001, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0xe789, 0xf023, 0xf000, 0xf001, 0xe456, 0x8bcc, 0xf013, 0xf002, 0xf012, 0x1767, 0x5aaa, 0xf013, 0xf001, 0xf000, 0x0fff, 0x7fff, 0xf124, 0x0fff, 0x089a, 0x0578, 0x0fff, 0x089a, 0x0013, 0x0245, 0x0eff, 0x0223, 0x0dde, 0x0135, 0x0789, 0x0ddd, 0xbbbc, 0xf346, 0x0467, 0x0fff, 0x4eee, 0x3ddd, 0x0edd, 0x0dee, 0x0fff, 0x0fff, 0x0dee, 0x0def, 0x08ab, 0x0fff, 0x7fff, 0xfabc, 0xf356, 0x0457, 0x0467, 0x0fff, 0x0bcd, 0x4bde, 0x9bcc, 0x8dee, 0x8eff, 0x8fff, 0x9fff, 0xadee, 0xeccd, 0xf689, 0xc357, 0x2356, 0x0356, 0x0467, 0x0467, 0x0fff, 0x0ccd, 0x0bdd, 0x0cdd, 0x0aaa, 0x2234, 0x4135, 0x4346, 0x5356, 0x2246, 0x0346, 0x0356, 0x0467, 0x0356, 0x0467, 0x0467, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff }; static void sdlfb_hw_init(void) { SDL_Window *win; if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); exit(1); } _device.parent.init = sdlfb_init; _device.parent.open = sdlfb_open; _device.parent.close = sdlfb_close; _device.parent.read = RT_NULL; _device.parent.write = RT_NULL; _device.parent.control = sdlfb_control; _device.width = SDL_SCREEN_WIDTH; _device.height = SDL_SCREEN_HEIGHT; { int bpp; /* texture bits per pixel */ Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */ /* Grab info about format that will be passed into OpenGL */ SDL_PixelFormatEnumToMasks(SDL_SCREEN_FORMAT, &bpp, &Rmask, &Gmask, &Bmask, &Amask); _device.surface = SDL_CreateRGBSurface(0, SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT, bpp, Rmask, Gmask, Bmask, Amask); } win = SDL_CreateWindow("RT-Thread/Persimmon", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT, SDL_WINDOW_OPENGL); if (win == NULL) { fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError()); exit(1); } /* set the icon of Window */ { SDL_Surface *surface; surface = SDL_CreateRGBSurfaceFrom(pixels, 16, 16, 16, 16 * 2, 0x0f00, 0x00f0, 0x000f, 0xf000); SDL_SetWindowIcon(win, surface); } _device.renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); rt_device_register(RT_DEVICE(&_device), "sdl", RT_DEVICE_FLAG_RDWR); sdllock = rt_mutex_create("fb", RT_IPC_FLAG_PRIO); if (sdllock == RT_NULL) { LOG_E("Create mutex for sdlfb failed!"); } } #ifdef _WIN32 #include #include #else #include #include #endif #include #include #include #include #include #ifdef _WIN32 static HANDLE sdl_ok_event = NULL; static DWORD WINAPI sdl_loop(LPVOID lpParam) #else static pthread_mutex_t sdl_ok_mutex; static pthread_cond_t sdl_ok_event; static void *sdl_loop(void *lpParam) #endif { int quit = 0; SDL_Event event; int state = 0; rt_device_t device; int timeout = 1000000; int last_x = -1, last_y = -1; int motion_x, motion_y; int motion_button = 0; int motion_tick = 50; int mouse_id = 1; #ifndef _WIN32 sigset_t sigmask, oldmask; /* set the getchar without buffer */ sigfillset(&sigmask); pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask); pthread_mutex_lock(&sdl_ok_mutex); #endif sdlfb_hw_init(); device = rt_device_find("sdl"); RT_ASSERT(device); rtgui_graphic_set_device(device); #ifdef _WIN32 SetEvent(sdl_ok_event); #else pthread_cond_signal(&sdl_ok_event); pthread_mutex_unlock(&sdl_ok_mutex); #endif /* handle SDL event */ while (!quit) { if (motion_button & RTGUI_MOUSE_BUTTON_DOWN) { timeout = motion_tick - SDL_GetTicks(); } else { timeout = 1000 * 1000; } int tick = SDL_GetTicks(); SDL_WaitEventTimeout(&event, timeout); if (SDL_GetTicks() >= motion_tick && (motion_button & RTGUI_MOUSE_BUTTON_DOWN)) /* whether timeout */ { /* sendout motion event */ struct rtgui_event_mouse emouse; emouse.parent.type = RTGUI_EVENT_MOUSE_MOTION; emouse.parent.sender = RT_NULL; emouse.wid = RT_NULL; emouse.id = mouse_id; emouse.ts = rt_tick_get(); if ((motion_x > 0 && motion_x < SDL_SCREEN_WIDTH) && (motion_y > 0 && motion_y < SDL_SCREEN_HEIGHT)) { emouse.x = motion_x; emouse.y = motion_y; /* init mouse button */ emouse.button = motion_button; /* send event to server */ rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse)); } /* reset motion tick */ motion_tick = 50 + SDL_GetTicks(); } switch (event.type) { case SDL_MOUSEMOTION: { /* save to (x,y) in the motion */ motion_x = ((SDL_MouseMotionEvent *)&event)->x; motion_y = ((SDL_MouseMotionEvent *)&event)->y; } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { int x, y; struct rtgui_event_mouse emouse; SDL_MouseButtonEvent *mb; emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON; emouse.parent.sender = RT_NULL; emouse.wid = RT_NULL; if (event.type == SDL_MOUSEBUTTONDOWN && state == 0) mouse_id = rt_tick_get(); emouse.id = mouse_id; emouse.ts = rt_tick_get(); mb = (SDL_MouseButtonEvent *)&event; x = mb->x; y = mb->y; if ((x > 0 && x < SDL_SCREEN_WIDTH) && (y > 0 && y < SDL_SCREEN_HEIGHT)) { emouse.x = x; emouse.y = y; /* init mouse button */ emouse.button = 0; /* set emouse button */ if (mb->button & (1 << (SDL_BUTTON_LEFT - 1))) { emouse.button |= RTGUI_MOUSE_BUTTON_LEFT; } else if (mb->button & (1 << (SDL_BUTTON_RIGHT - 1))) { emouse.button |= RTGUI_MOUSE_BUTTON_RIGHT; } else if (mb->button & (1 << (SDL_BUTTON_MIDDLE - 1))) { emouse.button |= RTGUI_MOUSE_BUTTON_MIDDLE; } if (mb->type == SDL_MOUSEBUTTONDOWN) { emouse.button |= RTGUI_MOUSE_BUTTON_DOWN; motion_button = emouse.button; /* set motion timeout tick */ motion_tick = 50 + SDL_GetTicks(); if (state == 0) { /* send event to server */ rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse)); last_x = -1; last_y = -1; state = 1; } } else { emouse.button |= RTGUI_MOUSE_BUTTON_UP; motion_button = 0; if (state == 1) { /* send event to server */ rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse)); last_x = -1; last_y = -1; state = 0; } } } } break; case SDL_KEYUP: { struct rtgui_event_kbd ekbd; ekbd.parent.type = RTGUI_EVENT_KBD; ekbd.parent.sender = RT_NULL; ekbd.type = RTGUI_KEYUP; ekbd.wid = RT_NULL; ekbd.mod = event.key.keysym.mod; ekbd.key = event.key.keysym.sym; /* FIXME: unicode */ ekbd.unicode = 0; /* send event to server */ rtgui_server_post_event(&ekbd.parent, sizeof(struct rtgui_event_kbd)); } break; case SDL_KEYDOWN: { struct rtgui_event_kbd ekbd; ekbd.parent.type = RTGUI_EVENT_KBD; ekbd.parent.sender = RT_NULL; ekbd.type = RTGUI_KEYDOWN; ekbd.wid = RT_NULL; ekbd.mod = event.key.keysym.mod; ekbd.key = event.key.keysym.sym; /* FIXME: unicode */ ekbd.unicode = 0; /* send event to server */ rtgui_server_post_event(&ekbd.parent, sizeof(struct rtgui_event_kbd)); } break; case SDL_QUIT: SDL_Quit(); quit = 1; break; default: break; } if (quit) { exit(1); break; } } rt_hw_exit(); return 0; } /* start sdl thread */ void rt_hw_sdl_start(void) { #ifdef _WIN32 HANDLE thread; DWORD thread_id; sdl_ok_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (sdl_ok_event == NULL) { printf("error, create SDL event failed\n"); exit(-1); } /* create thread that loop sdl event */ thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)sdl_loop, 0, CREATE_SUSPENDED, &thread_id); if (thread == NULL) { //Display Error Message return; } ResumeThread(thread); /* wait until SDL LCD device is registered and seted */ WaitForSingleObject(sdl_ok_event, INFINITE); #else /* Linux */ pthread_t pid; int res; pthread_mutex_init(&sdl_ok_mutex, NULL); pthread_cond_init(&sdl_ok_event, NULL); res = pthread_create(&pid, NULL, &sdl_loop, NULL); if (res) { printf("pthread create sdl thread faild, <%d>\n", res); exit(EXIT_FAILURE); } pthread_mutex_lock(&sdl_ok_mutex); pthread_cond_wait(&sdl_ok_event, &sdl_ok_mutex); pthread_mutex_unlock(&sdl_ok_mutex); pthread_mutex_destroy(&sdl_ok_mutex); pthread_cond_destroy(&sdl_ok_event); #endif }