Home | History | Annotate | Download | only in minui
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdbool.h>
     18 #include <stdlib.h>
     19 #include <unistd.h>
     20 
     21 #include <fcntl.h>
     22 #include <stdio.h>
     23 
     24 #include <sys/ioctl.h>
     25 #include <sys/mman.h>
     26 #include <sys/types.h>
     27 
     28 #include <linux/fb.h>
     29 #include <linux/kd.h>
     30 
     31 #include <pixelflinger/pixelflinger.h>
     32 
     33 #include "font_10x18.h"
     34 #include "minui.h"
     35 
     36 #if defined(RECOVERY_BGRA)
     37 #define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
     38 #define PIXEL_SIZE   4
     39 #elif defined(RECOVERY_RGBX)
     40 #define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
     41 #define PIXEL_SIZE   4
     42 #else
     43 #define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
     44 #define PIXEL_SIZE   2
     45 #endif
     46 
     47 #define NUM_BUFFERS 2
     48 
     49 typedef struct {
     50     GGLSurface* texture;
     51     unsigned cwidth;
     52     unsigned cheight;
     53 } GRFont;
     54 
     55 static GRFont *gr_font = 0;
     56 static GGLContext *gr_context = 0;
     57 static GGLSurface gr_font_texture;
     58 static GGLSurface gr_framebuffer[NUM_BUFFERS];
     59 static GGLSurface gr_mem_surface;
     60 static unsigned gr_active_fb = 0;
     61 static unsigned double_buffering = 0;
     62 static int overscan_percent = OVERSCAN_PERCENT;
     63 static int overscan_offset_x = 0;
     64 static int overscan_offset_y = 0;
     65 
     66 static int gr_fb_fd = -1;
     67 static int gr_vt_fd = -1;
     68 
     69 static struct fb_var_screeninfo vi;
     70 static struct fb_fix_screeninfo fi;
     71 
     72 static int get_framebuffer(GGLSurface *fb)
     73 {
     74     int fd;
     75     void *bits;
     76 
     77     fd = open("/dev/graphics/fb0", O_RDWR);
     78     if (fd < 0) {
     79         perror("cannot open fb0");
     80         return -1;
     81     }
     82 
     83     if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
     84         perror("failed to get fb0 info");
     85         close(fd);
     86         return -1;
     87     }
     88 
     89     vi.bits_per_pixel = PIXEL_SIZE * 8;
     90     if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) {
     91       vi.red.offset     = 8;
     92       vi.red.length     = 8;
     93       vi.green.offset   = 16;
     94       vi.green.length   = 8;
     95       vi.blue.offset    = 24;
     96       vi.blue.length    = 8;
     97       vi.transp.offset  = 0;
     98       vi.transp.length  = 8;
     99     } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) {
    100       vi.red.offset     = 24;
    101       vi.red.length     = 8;
    102       vi.green.offset   = 16;
    103       vi.green.length   = 8;
    104       vi.blue.offset    = 8;
    105       vi.blue.length    = 8;
    106       vi.transp.offset  = 0;
    107       vi.transp.length  = 8;
    108     } else { /* RGB565*/
    109       vi.red.offset     = 11;
    110       vi.red.length     = 5;
    111       vi.green.offset   = 5;
    112       vi.green.length   = 6;
    113       vi.blue.offset    = 0;
    114       vi.blue.length    = 5;
    115       vi.transp.offset  = 0;
    116       vi.transp.length  = 0;
    117     }
    118     if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
    119         perror("failed to put fb0 info");
    120         close(fd);
    121         return -1;
    122     }
    123 
    124     if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
    125         perror("failed to get fb0 info");
    126         close(fd);
    127         return -1;
    128     }
    129 
    130     bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    131     if (bits == MAP_FAILED) {
    132         perror("failed to mmap framebuffer");
    133         close(fd);
    134         return -1;
    135     }
    136 
    137     overscan_offset_x = vi.xres * overscan_percent / 100;
    138     overscan_offset_y = vi.yres * overscan_percent / 100;
    139 
    140     fb->version = sizeof(*fb);
    141     fb->width = vi.xres;
    142     fb->height = vi.yres;
    143     fb->stride = fi.line_length/PIXEL_SIZE;
    144     fb->data = bits;
    145     fb->format = PIXEL_FORMAT;
    146     memset(fb->data, 0, vi.yres * fi.line_length);
    147 
    148     fb++;
    149 
    150     /* check if we can use double buffering */
    151     if (vi.yres * fi.line_length * 2 > fi.smem_len)
    152         return fd;
    153 
    154     double_buffering = 1;
    155 
    156     fb->version = sizeof(*fb);
    157     fb->width = vi.xres;
    158     fb->height = vi.yres;
    159     fb->stride = fi.line_length/PIXEL_SIZE;
    160     fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length);
    161     fb->format = PIXEL_FORMAT;
    162     memset(fb->data, 0, vi.yres * fi.line_length);
    163 
    164     return fd;
    165 }
    166 
    167 static void get_memory_surface(GGLSurface* ms) {
    168   ms->version = sizeof(*ms);
    169   ms->width = vi.xres;
    170   ms->height = vi.yres;
    171   ms->stride = fi.line_length/PIXEL_SIZE;
    172   ms->data = malloc(fi.line_length * vi.yres);
    173   ms->format = PIXEL_FORMAT;
    174 }
    175 
    176 static void set_active_framebuffer(unsigned n)
    177 {
    178     if (n > 1 || !double_buffering) return;
    179     vi.yres_virtual = vi.yres * NUM_BUFFERS;
    180     vi.yoffset = n * vi.yres;
    181     vi.bits_per_pixel = PIXEL_SIZE * 8;
    182     if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
    183         perror("active fb swap failed");
    184     }
    185 }
    186 
    187 void gr_flip(void)
    188 {
    189     GGLContext *gl = gr_context;
    190 
    191     /* swap front and back buffers */
    192     if (double_buffering)
    193         gr_active_fb = (gr_active_fb + 1) & 1;
    194 
    195     /* copy data from the in-memory surface to the buffer we're about
    196      * to make active. */
    197     memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
    198            fi.line_length * vi.yres);
    199 
    200     /* inform the display driver */
    201     set_active_framebuffer(gr_active_fb);
    202 }
    203 
    204 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
    205 {
    206     GGLContext *gl = gr_context;
    207     GGLint color[4];
    208     color[0] = ((r << 8) | r) + 1;
    209     color[1] = ((g << 8) | g) + 1;
    210     color[2] = ((b << 8) | b) + 1;
    211     color[3] = ((a << 8) | a) + 1;
    212     gl->color4xv(gl, color);
    213 }
    214 
    215 int gr_measure(const char *s)
    216 {
    217     return gr_font->cwidth * strlen(s);
    218 }
    219 
    220 void gr_font_size(int *x, int *y)
    221 {
    222     *x = gr_font->cwidth;
    223     *y = gr_font->cheight;
    224 }
    225 
    226 int gr_text(int x, int y, const char *s, int bold)
    227 {
    228     GGLContext *gl = gr_context;
    229     GRFont *font = gr_font;
    230     unsigned off;
    231 
    232     if (!font->texture) return x;
    233 
    234     bold = bold && (font->texture->height != font->cheight);
    235 
    236     x += overscan_offset_x;
    237     y += overscan_offset_y;
    238 
    239     gl->bindTexture(gl, font->texture);
    240     gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
    241     gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    242     gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    243     gl->enable(gl, GGL_TEXTURE_2D);
    244 
    245     while((off = *s++)) {
    246         off -= 32;
    247         if (off < 96) {
    248             gl->texCoord2i(gl, (off * font->cwidth) - x,
    249                            (bold ? font->cheight : 0) - y);
    250             gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
    251         }
    252         x += font->cwidth;
    253     }
    254 
    255     return x;
    256 }
    257 
    258 void gr_texticon(int x, int y, gr_surface icon) {
    259     if (gr_context == NULL || icon == NULL) {
    260         return;
    261     }
    262     GGLContext* gl = gr_context;
    263 
    264     x += overscan_offset_x;
    265     y += overscan_offset_y;
    266 
    267     gl->bindTexture(gl, (GGLSurface*) icon);
    268     gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
    269     gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    270     gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    271     gl->enable(gl, GGL_TEXTURE_2D);
    272 
    273     int w = gr_get_width(icon);
    274     int h = gr_get_height(icon);
    275 
    276     gl->texCoord2i(gl, -x, -y);
    277     gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon));
    278 }
    279 
    280 void gr_fill(int x1, int y1, int x2, int y2)
    281 {
    282     x1 += overscan_offset_x;
    283     y1 += overscan_offset_y;
    284 
    285     x2 += overscan_offset_x;
    286     y2 += overscan_offset_y;
    287 
    288     GGLContext *gl = gr_context;
    289     gl->disable(gl, GGL_TEXTURE_2D);
    290     gl->recti(gl, x1, y1, x2, y2);
    291 }
    292 
    293 void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
    294     if (gr_context == NULL || source == NULL) {
    295         return;
    296     }
    297     GGLContext *gl = gr_context;
    298 
    299     dx += overscan_offset_x;
    300     dy += overscan_offset_y;
    301 
    302     gl->bindTexture(gl, (GGLSurface*) source);
    303     gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
    304     gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    305     gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
    306     gl->enable(gl, GGL_TEXTURE_2D);
    307     gl->texCoord2i(gl, sx - dx, sy - dy);
    308     gl->recti(gl, dx, dy, dx + w, dy + h);
    309 }
    310 
    311 unsigned int gr_get_width(gr_surface surface) {
    312     if (surface == NULL) {
    313         return 0;
    314     }
    315     return ((GGLSurface*) surface)->width;
    316 }
    317 
    318 unsigned int gr_get_height(gr_surface surface) {
    319     if (surface == NULL) {
    320         return 0;
    321     }
    322     return ((GGLSurface*) surface)->height;
    323 }
    324 
    325 static void gr_init_font(void)
    326 {
    327     gr_font = calloc(sizeof(*gr_font), 1);
    328 
    329     int res = res_create_surface("font", (void**)&(gr_font->texture));
    330     if (res == 0) {
    331         // The font image should be a 96x2 array of character images.  The
    332         // columns are the printable ASCII characters 0x20 - 0x7f.  The
    333         // top row is regular text; the bottom row is bold.
    334         gr_font->cwidth = gr_font->texture->width / 96;
    335         gr_font->cheight = gr_font->texture->height / 2;
    336     } else {
    337         printf("failed to read font: res=%d\n", res);
    338 
    339         // fall back to the compiled-in font.
    340         gr_font->texture = malloc(sizeof(*gr_font->texture));
    341         gr_font->texture->width = font.width;
    342         gr_font->texture->height = font.height;
    343         gr_font->texture->stride = font.width;
    344 
    345         unsigned char* bits = malloc(font.width * font.height);
    346         gr_font->texture->data = (void*) bits;
    347 
    348         unsigned char data;
    349         unsigned char* in = font.rundata;
    350         while((data = *in++)) {
    351             memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
    352             bits += (data & 0x7f);
    353         }
    354 
    355         gr_font->cwidth = font.cwidth;
    356         gr_font->cheight = font.cheight;
    357     }
    358 
    359     // interpret the grayscale as alpha
    360     gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
    361 }
    362 
    363 int gr_init(void)
    364 {
    365     gglInit(&gr_context);
    366     GGLContext *gl = gr_context;
    367 
    368     gr_init_font();
    369     gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
    370     if (gr_vt_fd < 0) {
    371         // This is non-fatal; post-Cupcake kernels don't have tty0.
    372         perror("can't open /dev/tty0");
    373     } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
    374         // However, if we do open tty0, we expect the ioctl to work.
    375         perror("failed KDSETMODE to KD_GRAPHICS on tty0");
    376         gr_exit();
    377         return -1;
    378     }
    379 
    380     gr_fb_fd = get_framebuffer(gr_framebuffer);
    381     if (gr_fb_fd < 0) {
    382         gr_exit();
    383         return -1;
    384     }
    385 
    386     get_memory_surface(&gr_mem_surface);
    387 
    388     fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",
    389             gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
    390 
    391         /* start with 0 as front (displayed) and 1 as back (drawing) */
    392     gr_active_fb = 0;
    393     set_active_framebuffer(0);
    394     gl->colorBuffer(gl, &gr_mem_surface);
    395 
    396     gl->activeTexture(gl, 0);
    397     gl->enable(gl, GGL_BLEND);
    398     gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
    399 
    400     gr_fb_blank(true);
    401     gr_fb_blank(false);
    402 
    403     return 0;
    404 }
    405 
    406 void gr_exit(void)
    407 {
    408     close(gr_fb_fd);
    409     gr_fb_fd = -1;
    410 
    411     free(gr_mem_surface.data);
    412 
    413     ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
    414     close(gr_vt_fd);
    415     gr_vt_fd = -1;
    416 }
    417 
    418 int gr_fb_width(void)
    419 {
    420     return gr_framebuffer[0].width - 2*overscan_offset_x;
    421 }
    422 
    423 int gr_fb_height(void)
    424 {
    425     return gr_framebuffer[0].height - 2*overscan_offset_y;
    426 }
    427 
    428 gr_pixel *gr_fb_data(void)
    429 {
    430     return (unsigned short *) gr_mem_surface.data;
    431 }
    432 
    433 void gr_fb_blank(bool blank)
    434 {
    435     int ret;
    436 
    437     ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
    438     if (ret < 0)
    439         perror("ioctl(): blank");
    440 }
    441