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