2222#define FBDEV_DEFAULT "/dev/fb0"
2323#define SCREEN (x ) ((twin_context_t *) x)->screen
2424#define PRIV (x ) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
25+ #define ARGB32_TO_RGB565 (pixel ) \
26+ (((pixel & 0x00f80000) >> 8) | ((pixel & 0x0000fc00) >> 5) | \
27+ ((pixel & 0x000000f8) >> 3))
28+
29+ /* Requires validation in 24-bit per pixel environments. */
30+ #define ARGB32_TO_RGB888 (pixel ) (0xff000000 | (pixel))
31+ #define GET_TWIN_FBDEV (left , top , closure , dest ) \
32+ do { \
33+ twin_screen_t *screen = SCREEN(closure); \
34+ twin_fbdev_t *tx = PRIV(closure); \
35+ off_t off = (top) * (screen->width) + (left); \
36+ *(dest) = \
37+ (uint32_t *) ((uintptr_t) tx->fb_base + (off * sizeof(uint32_t))); \
38+ } while (0)
2539
2640typedef struct {
2741 twin_screen_t * screen ;
@@ -43,22 +57,43 @@ typedef struct {
4357 size_t fb_len ;
4458} twin_fbdev_t ;
4559
46- static void _twin_fbdev_put_span (twin_coord_t left ,
47- twin_coord_t top ,
48- twin_coord_t right ,
49- twin_argb32_t * pixels ,
50- void * closure )
60+ static void _twin_fbdev_put_span16 (twin_coord_t left ,
61+ twin_coord_t top ,
62+ twin_coord_t right ,
63+ twin_argb32_t * pixels ,
64+ void * closure )
5165{
52- twin_screen_t * screen = SCREEN (closure );
53- twin_fbdev_t * tx = PRIV (closure );
66+ uint32_t * dest ;
67+ GET_TWIN_FBDEV (left , top , closure , & dest );
68+ twin_coord_t width = right - left ;
69+ for (int i = 0 ; i < width ; i ++ ) {
70+ dest [i ] = ARGB32_TO_RGB565 (pixels [i ]);
71+ }
72+ }
5473
55- if (tx -> fb_base == MAP_FAILED )
56- return ;
74+ static void _twin_fbdev_put_span24 (twin_coord_t left ,
75+ twin_coord_t top ,
76+ twin_coord_t right ,
77+ twin_argb32_t * pixels ,
78+ void * closure )
79+ {
80+ uint32_t * dest ;
81+ GET_TWIN_FBDEV (left , top , closure , & dest );
82+ twin_coord_t width = right - left ;
83+ for (int i = 0 ; i < width ; i ++ ) {
84+ dest [i ] = ARGB32_TO_RGB888 (pixels [i ]);
85+ }
86+ }
5787
88+ static void _twin_fbdev_put_span32 (twin_coord_t left ,
89+ twin_coord_t top ,
90+ twin_coord_t right ,
91+ twin_argb32_t * pixels ,
92+ void * closure )
93+ {
94+ uint32_t * dest ;
95+ GET_TWIN_FBDEV (left , top , closure , & dest );
5896 twin_coord_t width = right - left ;
59- off_t off = top * screen -> width + left ;
60- uint32_t * dest =
61- (uint32_t * ) ((uintptr_t ) tx -> fb_base + (off * sizeof (* dest )));
6297 memcpy (dest , pixels , width * sizeof (* dest ));
6398}
6499
@@ -88,6 +123,27 @@ static bool twin_fbdev_work(void *closure)
88123 return true;
89124}
90125
126+ static inline bool twin_fbdev_is_rgb565 (twin_fbdev_t * tx )
127+ {
128+ return tx -> fb_var .red .offset == 11 && tx -> fb_var .red .length == 5 &&
129+ tx -> fb_var .green .offset == 5 && tx -> fb_var .green .length == 6 &&
130+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 5 ;
131+ }
132+
133+ static inline bool twin_fbdev_is_rgb888 (twin_fbdev_t * tx )
134+ {
135+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
136+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
137+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
138+ }
139+
140+ static inline bool twin_fbdev_is_argb32 (twin_fbdev_t * tx )
141+ {
142+ return tx -> fb_var .red .offset == 16 && tx -> fb_var .red .length == 8 &&
143+ tx -> fb_var .green .offset == 8 && tx -> fb_var .green .length == 8 &&
144+ tx -> fb_var .blue .offset == 0 && tx -> fb_var .blue .length == 8 ;
145+ }
146+
91147static bool twin_fbdev_apply_config (twin_fbdev_t * tx )
92148{
93149 /* Read changable information of the framebuffer */
@@ -99,7 +155,6 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
99155 /* Set the virtual screen size to be the same as the physical screen */
100156 tx -> fb_var .xres_virtual = tx -> fb_var .xres ;
101157 tx -> fb_var .yres_virtual = tx -> fb_var .yres ;
102- tx -> fb_var .bits_per_pixel = 32 ;
103158 if (ioctl (tx -> fb_fd , FBIOPUT_VSCREENINFO , & tx -> fb_var ) < 0 ) {
104159 log_error ("Failed to set framebuffer mode" );
105160 return false;
@@ -111,10 +166,29 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
111166 return false;
112167 }
113168
114- /* Check bits per pixel */
115- if (tx -> fb_var .bits_per_pixel != 32 ) {
116- log_error ("Failed to set framebuffer bpp to 32" );
117- return false;
169+ /* Examine the framebuffer format */
170+ switch (tx -> fb_var .bits_per_pixel ) {
171+ case 16 : /* RGB565 */
172+ if (!twin_fbdev_is_rgb565 (tx )) {
173+ log_error ("Invalid framebuffer format for 16 bpp" );
174+ return false;
175+ }
176+ break ;
177+ case 24 : /* RGB888 */
178+ if (!twin_fbdev_is_rgb888 (tx )) {
179+ log_error ("Invalid framebuffer format for 24 bpp" );
180+ return false;
181+ }
182+ break ;
183+ case 32 : /* ARGB32 */
184+ if (!twin_fbdev_is_argb32 (tx )) {
185+ log_error ("Invalid framebuffer format for 32 bpp" );
186+ return false;
187+ }
188+ break ;
189+ default :
190+ log_error ("Unsupported bits per pixel: %d" , tx -> fb_var .bits_per_pixel );
191+ break ;
118192 }
119193
120194 /* Read unchangable information of the framebuffer */
@@ -220,9 +294,21 @@ twin_context_t *twin_fbdev_init(int width, int height)
220294 goto bail_vt_fd ;
221295 }
222296
297+ /* Examine if framebuffer mapping is valid */
298+ if (tx -> fb_base == MAP_FAILED ) {
299+ log_error ("Failed to map framebuffer memory" );
300+ return ;
301+ }
302+
303+ const twin_put_span_t fbdev_put_spans [] = {
304+ _twin_fbdev_put_span16 ,
305+ _twin_fbdev_put_span24 ,
306+ _twin_fbdev_put_span32 ,
307+ };
223308 /* Create TWIN screen */
224- ctx -> screen =
225- twin_screen_create (width , height , NULL , _twin_fbdev_put_span , ctx );
309+ ctx -> screen = twin_screen_create (
310+ width , height , NULL , fbdev_put_spans [tx -> fb_var .bits_per_pixel / 8 - 2 ],
311+ ctx );
226312
227313 /* Create Linux input system object */
228314 tx -> input = twin_linux_input_create (ctx -> screen );
0 commit comments