1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 */
9 #include <rtthread.h>
10 #include <stdio.h>
11 #ifdef _WIN32
12 #include <sdl.h>
13 #else
14 #include <SDL2/SDL.h>
15 #endif
16 #include <rtdevice.h>
17 #include <rtgui/driver.h>
18
19 #define DBG_TAG "sdl.fb"
20 #define DBG_LVL DBG_WARNING
21 #include <rtdbg.h>
22
23 #define SDL_SCREEN_WIDTH 480
24 #define SDL_SCREEN_HEIGHT 320
25
26 extern void rt_hw_exit(void);
27
28 #define SDL_SCREEN_FORMAT SDL_PIXELFORMAT_RGB565
29
30 struct sdlfb_device
31 {
32 struct rt_device parent;
33
34 SDL_Renderer *renderer; /* window renderer */
35 SDL_Surface *surface; /* screen surface */
36
37 rt_uint16_t width;
38 rt_uint16_t height;
39 };
40 struct sdlfb_device _device;
41
42 /* common device interface */
sdlfb_init(rt_device_t dev)43 static rt_err_t sdlfb_init(rt_device_t dev)
44 {
45 return RT_EOK;
46 }
sdlfb_open(rt_device_t dev,rt_uint16_t oflag)47 static rt_err_t sdlfb_open(rt_device_t dev, rt_uint16_t oflag)
48 {
49 return RT_EOK;
50 }
sdlfb_close(rt_device_t dev)51 static rt_err_t sdlfb_close(rt_device_t dev)
52 {
53 SDL_Quit();
54 return RT_EOK;
55 }
56
sdlfb_info(int * format,int * bpp)57 int sdlfb_info(int *format, int *bpp)
58 {
59 int bit_pp; /* texture bits per pixel */
60 Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */
61
62 /* Grab info about format that will be passed into OpenGL */
63 SDL_PixelFormatEnumToMasks(SDL_SCREEN_FORMAT, &bit_pp, &Rmask, &Gmask,
64 &Bmask, &Amask);
65
66 *bpp = bit_pp;
67 switch (SDL_SCREEN_FORMAT)
68 {
69 case SDL_PIXELFORMAT_RGB565:
70 *format = RTGRAPHIC_PIXEL_FORMAT_RGB565;
71 break;
72 case SDL_PIXELFORMAT_RGB888:
73 *format = RTGRAPHIC_PIXEL_FORMAT_RGB888;
74 break;
75 case SDL_PIXELFORMAT_ARGB8888:
76 *format = RTGRAPHIC_PIXEL_FORMAT_ARGB888;
77 break;
78 case SDL_PIXELFORMAT_ABGR8888:
79 *format = RTGRAPHIC_PIXEL_FORMAT_ABGR888;
80 }
81
82 return 0;
83 }
84
85 static rt_mutex_t sdllock;
sdlfb_control(rt_device_t dev,int cmd,void * args)86 static rt_err_t sdlfb_control(rt_device_t dev, int cmd, void *args)
87 {
88 struct sdlfb_device *device;
89
90 rt_mutex_take(sdllock, RT_WAITING_FOREVER);
91 device = (struct sdlfb_device *)dev;
92 RT_ASSERT(device != RT_NULL);
93
94 switch (cmd)
95 {
96 case RTGRAPHIC_CTRL_GET_INFO:
97 {
98 int format, bpp;
99 struct rt_device_graphic_info *info;
100
101 sdlfb_info(&format, &bpp);
102 info = (struct rt_device_graphic_info *) args;
103 info->bits_per_pixel = bpp;
104 info->pixel_format = format;
105 info->framebuffer = (rt_uint8_t*)device->surface->pixels;
106 info->width = device->width;
107 info->height = device->height;
108 }
109 break;
110
111 case RTGRAPHIC_CTRL_RECT_UPDATE:
112 {
113 SDL_Texture * texture;
114 struct rt_device_rect_info *rect;
115 SDL_Rect _rect = { 0, 0, SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT };
116
117 rect = (struct rt_device_rect_info *)args;
118
119 SDL_RenderClear(_device.renderer);
120
121 texture = SDL_CreateTextureFromSurface(_device.renderer, _device.surface);
122 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
123 SDL_RenderCopy(_device.renderer, texture, NULL, &_rect);
124
125 SDL_RenderPresent(_device.renderer);
126
127 SDL_DestroyTexture(texture);
128 }
129 break;
130 case RTGRAPHIC_CTRL_SET_MODE:
131 {
132 break;
133 }
134 break;
135 }
136 rt_mutex_release(sdllock);
137 return RT_EOK;
138 }
139
140 Uint16 pixels[16 * 16] = { // ...or with raw pixel data:
141 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
142 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
143 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
144 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
145 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
146 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
147 0x0fff, 0x0aab, 0x0789, 0x0bcc, 0x0eee, 0x09aa, 0x099a, 0x0ddd,
148 0x0fff, 0x0eee, 0x0899, 0x0fff, 0x0fff, 0x1fff, 0x0dde, 0x0dee,
149 0x0fff, 0xabbc, 0xf779, 0x8cdd, 0x3fff, 0x9bbc, 0xaaab, 0x6fff,
150 0x0fff, 0x3fff, 0xbaab, 0x0fff, 0x0fff, 0x6689, 0x6fff, 0x0dee,
151 0xe678, 0xf134, 0x8abb, 0xf235, 0xf678, 0xf013, 0xf568, 0xf001,
152 0xd889, 0x7abc, 0xf001, 0x0fff, 0x0fff, 0x0bcc, 0x9124, 0x5fff,
153 0xf124, 0xf356, 0x3eee, 0x0fff, 0x7bbc, 0xf124, 0x0789, 0x2fff,
154 0xf002, 0xd789, 0xf024, 0x0fff, 0x0fff, 0x0002, 0x0134, 0xd79a,
155 0x1fff, 0xf023, 0xf000, 0xf124, 0xc99a, 0xf024, 0x0567, 0x0fff,
156 0xf002, 0xe678, 0xf013, 0x0fff, 0x0ddd, 0x0fff, 0x0fff, 0xb689,
157 0x8abb, 0x0fff, 0x0fff, 0xf001, 0xf235, 0xf013, 0x0fff, 0xd789,
158 0xf002, 0x9899, 0xf001, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0xe789,
159 0xf023, 0xf000, 0xf001, 0xe456, 0x8bcc, 0xf013, 0xf002, 0xf012,
160 0x1767, 0x5aaa, 0xf013, 0xf001, 0xf000, 0x0fff, 0x7fff, 0xf124,
161 0x0fff, 0x089a, 0x0578, 0x0fff, 0x089a, 0x0013, 0x0245, 0x0eff,
162 0x0223, 0x0dde, 0x0135, 0x0789, 0x0ddd, 0xbbbc, 0xf346, 0x0467,
163 0x0fff, 0x4eee, 0x3ddd, 0x0edd, 0x0dee, 0x0fff, 0x0fff, 0x0dee,
164 0x0def, 0x08ab, 0x0fff, 0x7fff, 0xfabc, 0xf356, 0x0457, 0x0467,
165 0x0fff, 0x0bcd, 0x4bde, 0x9bcc, 0x8dee, 0x8eff, 0x8fff, 0x9fff,
166 0xadee, 0xeccd, 0xf689, 0xc357, 0x2356, 0x0356, 0x0467, 0x0467,
167 0x0fff, 0x0ccd, 0x0bdd, 0x0cdd, 0x0aaa, 0x2234, 0x4135, 0x4346,
168 0x5356, 0x2246, 0x0346, 0x0356, 0x0467, 0x0356, 0x0467, 0x0467,
169 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
170 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
171 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
172 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff
173 };
174
sdlfb_hw_init(void)175 static void sdlfb_hw_init(void)
176 {
177 SDL_Window *win;
178
179 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
180 {
181 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
182 exit(1);
183 }
184
185 _device.parent.init = sdlfb_init;
186 _device.parent.open = sdlfb_open;
187 _device.parent.close = sdlfb_close;
188 _device.parent.read = RT_NULL;
189 _device.parent.write = RT_NULL;
190 _device.parent.control = sdlfb_control;
191
192 _device.width = SDL_SCREEN_WIDTH;
193 _device.height = SDL_SCREEN_HEIGHT;
194
195 {
196 int bpp; /* texture bits per pixel */
197 Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */
198
199 /* Grab info about format that will be passed into OpenGL */
200 SDL_PixelFormatEnumToMasks(SDL_SCREEN_FORMAT, &bpp, &Rmask, &Gmask,
201 &Bmask, &Amask);
202
203 _device.surface = SDL_CreateRGBSurface(0, SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT,
204 bpp, Rmask, Gmask, Bmask, Amask);
205 }
206
207 win = SDL_CreateWindow("RT-Thread/Persimmon",
208 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
209 SDL_SCREEN_WIDTH, SDL_SCREEN_HEIGHT, SDL_WINDOW_OPENGL);
210 if (win == NULL)
211 {
212 fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
213 exit(1);
214 }
215
216 /* set the icon of Window */
217 {
218 SDL_Surface *surface;
219 surface = SDL_CreateRGBSurfaceFrom(pixels, 16, 16, 16, 16 * 2, 0x0f00, 0x00f0, 0x000f, 0xf000);
220 SDL_SetWindowIcon(win, surface);
221 }
222
223 _device.renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
224
225 rt_device_register(RT_DEVICE(&_device), "sdl", RT_DEVICE_FLAG_RDWR);
226
227 sdllock = rt_mutex_create("fb", RT_IPC_FLAG_PRIO);
228 if (sdllock == RT_NULL)
229 {
230 LOG_E("Create mutex for sdlfb failed!");
231 }
232 }
233
234 #ifdef _WIN32
235 #include <windows.h>
236 #include <mmsystem.h>
237 #else
238 #include <pthread.h>
239 #include <signal.h>
240 #endif
241
242 #include <stdio.h>
243 #include <rtgui/event.h>
244 #include <rtgui/kbddef.h>
245 #include <rtgui/rtgui_server.h>
246 #include <rtgui/rtgui_system.h>
247
248 #ifdef _WIN32
249 static HANDLE sdl_ok_event = NULL;
250
sdl_loop(LPVOID lpParam)251 static DWORD WINAPI sdl_loop(LPVOID lpParam)
252 #else
253 static pthread_mutex_t sdl_ok_mutex;
254 static pthread_cond_t sdl_ok_event;
255
256 static void *sdl_loop(void *lpParam)
257 #endif
258 {
259 int quit = 0;
260 SDL_Event event;
261 int state = 0;
262 rt_device_t device;
263 int timeout = 1000000;
264
265 int last_x = -1, last_y = -1;
266
267 int motion_x, motion_y;
268 int motion_button = 0;
269 int motion_tick = 50;
270
271 int mouse_id = 1;
272
273 #ifndef _WIN32
274 sigset_t sigmask, oldmask;
275 /* set the getchar without buffer */
276 sigfillset(&sigmask);
277 pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
278 pthread_mutex_lock(&sdl_ok_mutex);
279 #endif
280
281 sdlfb_hw_init();
282
283 device = rt_device_find("sdl");
284 RT_ASSERT(device);
285 rtgui_graphic_set_device(device);
286 #ifdef _WIN32
287 SetEvent(sdl_ok_event);
288 #else
289 pthread_cond_signal(&sdl_ok_event);
290 pthread_mutex_unlock(&sdl_ok_mutex);
291 #endif
292 /* handle SDL event */
293 while (!quit)
294 {
295 if (motion_button & RTGUI_MOUSE_BUTTON_DOWN)
296 {
297 timeout = motion_tick - SDL_GetTicks();
298 }
299 else
300 {
301 timeout = 1000 * 1000;
302 }
303
304 int tick = SDL_GetTicks();
305 SDL_WaitEventTimeout(&event, timeout);
306 if (SDL_GetTicks() >= motion_tick && (motion_button & RTGUI_MOUSE_BUTTON_DOWN)) /* whether timeout */
307 {
308 /* sendout motion event */
309 struct rtgui_event_mouse emouse;
310
311 emouse.parent.type = RTGUI_EVENT_MOUSE_MOTION;
312 emouse.parent.sender = RT_NULL;
313 emouse.wid = RT_NULL;
314 emouse.id = mouse_id;
315 emouse.ts = rt_tick_get();
316
317 if ((motion_x > 0 && motion_x < SDL_SCREEN_WIDTH) &&
318 (motion_y > 0 && motion_y < SDL_SCREEN_HEIGHT))
319 {
320 emouse.x = motion_x;
321 emouse.y = motion_y;
322
323 /* init mouse button */
324 emouse.button = motion_button;
325
326 /* send event to server */
327 rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse));
328 }
329
330 /* reset motion tick */
331 motion_tick = 50 + SDL_GetTicks();
332 }
333
334 switch (event.type)
335 {
336 case SDL_MOUSEMOTION:
337 {
338 /* save to (x,y) in the motion */
339 motion_x = ((SDL_MouseMotionEvent *)&event)->x;
340 motion_y = ((SDL_MouseMotionEvent *)&event)->y;
341 }
342 break;
343
344 case SDL_MOUSEBUTTONDOWN:
345 case SDL_MOUSEBUTTONUP:
346 {
347 int x, y;
348 struct rtgui_event_mouse emouse;
349 SDL_MouseButtonEvent *mb;
350
351 emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;
352 emouse.parent.sender = RT_NULL;
353 emouse.wid = RT_NULL;
354 if (event.type == SDL_MOUSEBUTTONDOWN && state == 0)
355 mouse_id = rt_tick_get();
356
357 emouse.id = mouse_id;
358 emouse.ts = rt_tick_get();
359
360 mb = (SDL_MouseButtonEvent *)&event;
361
362 x = mb->x;
363 y = mb->y;
364 if ((x > 0 && x < SDL_SCREEN_WIDTH) &&
365 (y > 0 && y < SDL_SCREEN_HEIGHT))
366 {
367 emouse.x = x;
368 emouse.y = y;
369
370 /* init mouse button */
371 emouse.button = 0;
372
373 /* set emouse button */
374 if (mb->button & (1 << (SDL_BUTTON_LEFT - 1)))
375 {
376 emouse.button |= RTGUI_MOUSE_BUTTON_LEFT;
377 }
378 else if (mb->button & (1 << (SDL_BUTTON_RIGHT - 1)))
379 {
380 emouse.button |= RTGUI_MOUSE_BUTTON_RIGHT;
381 }
382 else if (mb->button & (1 << (SDL_BUTTON_MIDDLE - 1)))
383 {
384 emouse.button |= RTGUI_MOUSE_BUTTON_MIDDLE;
385 }
386
387 if (mb->type == SDL_MOUSEBUTTONDOWN)
388 {
389 emouse.button |= RTGUI_MOUSE_BUTTON_DOWN;
390 motion_button = emouse.button;
391
392 /* set motion timeout tick */
393 motion_tick = 50 + SDL_GetTicks();
394
395 if (state == 0)
396 {
397 /* send event to server */
398 rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse));
399 last_x = -1; last_y = -1;
400
401 state = 1;
402 }
403 }
404 else
405 {
406 emouse.button |= RTGUI_MOUSE_BUTTON_UP;
407 motion_button = 0;
408
409 if (state == 1)
410 {
411 /* send event to server */
412 rtgui_server_post_event(&emouse.parent, sizeof(struct rtgui_event_mouse));
413 last_x = -1; last_y = -1;
414
415 state = 0;
416 }
417 }
418 }
419 }
420 break;
421
422 case SDL_KEYUP:
423 {
424 struct rtgui_event_kbd ekbd;
425 ekbd.parent.type = RTGUI_EVENT_KBD;
426 ekbd.parent.sender = RT_NULL;
427 ekbd.type = RTGUI_KEYUP;
428 ekbd.wid = RT_NULL;
429 ekbd.mod = event.key.keysym.mod;
430 ekbd.key = event.key.keysym.sym;
431
432 /* FIXME: unicode */
433 ekbd.unicode = 0;
434
435 /* send event to server */
436 rtgui_server_post_event(&ekbd.parent, sizeof(struct rtgui_event_kbd));
437 }
438 break;
439
440 case SDL_KEYDOWN:
441 {
442 struct rtgui_event_kbd ekbd;
443 ekbd.parent.type = RTGUI_EVENT_KBD;
444 ekbd.parent.sender = RT_NULL;
445 ekbd.type = RTGUI_KEYDOWN;
446 ekbd.wid = RT_NULL;
447 ekbd.mod = event.key.keysym.mod;
448 ekbd.key = event.key.keysym.sym;
449
450 /* FIXME: unicode */
451 ekbd.unicode = 0;
452
453 /* send event to server */
454 rtgui_server_post_event(&ekbd.parent, sizeof(struct rtgui_event_kbd));
455 }
456 break;
457
458 case SDL_QUIT:
459 SDL_Quit();
460 quit = 1;
461 break;
462
463 default:
464 break;
465 }
466
467 if (quit)
468 {
469 exit(1);
470 break;
471 }
472
473 }
474 rt_hw_exit();
475 return 0;
476 }
477
478 /* start sdl thread */
rt_hw_sdl_start(void)479 void rt_hw_sdl_start(void)
480 {
481 #ifdef _WIN32
482 HANDLE thread;
483 DWORD thread_id;
484 sdl_ok_event = CreateEvent(NULL,
485 FALSE,
486 FALSE,
487 NULL);
488 if (sdl_ok_event == NULL)
489 {
490 printf("error, create SDL event failed\n");
491 exit(-1);
492 }
493 /* create thread that loop sdl event */
494 thread = CreateThread(NULL,
495 0,
496 (LPTHREAD_START_ROUTINE)sdl_loop,
497 0,
498 CREATE_SUSPENDED,
499 &thread_id);
500 if (thread == NULL)
501 {
502 //Display Error Message
503
504 return;
505 }
506 ResumeThread(thread);
507
508 /* wait until SDL LCD device is registered and seted */
509 WaitForSingleObject(sdl_ok_event, INFINITE);
510 #else
511 /* Linux */
512 pthread_t pid;
513 int res;
514
515 pthread_mutex_init(&sdl_ok_mutex, NULL);
516 pthread_cond_init(&sdl_ok_event, NULL);
517
518 res = pthread_create(&pid, NULL, &sdl_loop, NULL);
519 if (res)
520 {
521 printf("pthread create sdl thread faild, <%d>\n", res);
522 exit(EXIT_FAILURE);
523 }
524 pthread_mutex_lock(&sdl_ok_mutex);
525 pthread_cond_wait(&sdl_ok_event, &sdl_ok_mutex);
526
527 pthread_mutex_unlock(&sdl_ok_mutex);
528 pthread_mutex_destroy(&sdl_ok_mutex);
529 pthread_cond_destroy(&sdl_ok_event);
530 #endif
531 }
532