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,20 @@ 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+ 
223303    /* Create TWIN screen */ 
224-     ctx -> screen  = 
225-         twin_screen_create (width , height , NULL , _twin_fbdev_put_span , ctx );
304+     ctx -> screen  =  twin_screen_create (
305+         width , height , NULL ,
306+         (tx -> fb_var .bits_per_pixel  ==  16 )   ? _twin_fbdev_put_span16 
307+         : (tx -> fb_var .bits_per_pixel  ==  24 ) ? _twin_fbdev_put_span24 
308+         : (tx -> fb_var .bits_per_pixel  ==  32 ) ? _twin_fbdev_put_span32 
309+                                             : NULL ,
310+         ctx );
226311
227312    /* Create Linux input system object */ 
228313    tx -> input  =  twin_linux_input_create (ctx -> screen );
0 commit comments