diff options
-rw-r--r-- | minui/Android.mk | 2 | ||||
-rw-r--r-- | minui/graphics.c | 166 | ||||
-rw-r--r-- | minui/graphics.h | 49 | ||||
-rw-r--r-- | minui/graphics_fbdev.c | 201 | ||||
-rw-r--r-- | minui/minui.h | 2 |
5 files changed, 268 insertions, 152 deletions
diff --git a/minui/Android.mk b/minui/Android.mk index 43e0ad33b..5cc84ac7a 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := graphics.c events.c resources.c +LOCAL_SRC_FILES := graphics.c graphics_fbdev.c events.c resources.c LOCAL_C_INCLUDES +=\ external/libpng\ diff --git a/minui/graphics.c b/minui/graphics.c index c2fc9ea9e..f8ffa8941 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -32,6 +32,7 @@ #include "font_10x18.h" #include "minui.h" +#include "graphics.h" typedef struct { GRSurface* texture; @@ -40,155 +41,26 @@ typedef struct { } GRFont; static GRFont* gr_font = NULL; +static minui_backend* gr_backend = NULL; static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; -static int gr_fb_fd = -1; static int gr_vt_fd = -1; -static GRSurface gr_framebuffer[2]; -static bool double_buffered; -static GRSurface* gr_draw = NULL; -static int displayed_buffer; - static unsigned char gr_current_r = 255; static unsigned char gr_current_g = 255; static unsigned char gr_current_b = 255; static unsigned char gr_current_a = 255; -static struct fb_var_screeninfo vi; +static GRSurface* gr_draw = NULL; static bool outside(int x, int y) { return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; } -static void set_displayed_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffered) return; - - vi.yres_virtual = gr_framebuffer[0].height * 2; - vi.yoffset = n * gr_framebuffer[0].height; - vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); - } - displayed_buffer = n; -} - -static int get_framebuffer() -{ - int fd; - void *bits; - - struct fb_fix_screeninfo fi; - - fd = open("/dev/graphics/fb0", O_RDWR); - if (fd < 0) { - perror("cannot open fb0"); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - // We print this out for informational purposes only, but - // throughout we assume that the framebuffer device uses an RGBX - // pixel format. This is the case for every development device I - // have access to. For some of those devices (eg, hammerhead aka - // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a - // different format (XBGR) but actually produces the correct - // results on the display when you write RGBX. - // - // If you have a device that actually *needs* another pixel format - // (ie, BGRX, or 565), patches welcome... - - printf("fb0 reports (possibly inaccurate):\n" - " vi.bits_per_pixel = %d\n" - " vi.red.offset = %3d .length = %3d\n" - " vi.green.offset = %3d .length = %3d\n" - " vi.blue.offset = %3d .length = %3d\n", - vi.bits_per_pixel, - vi.red.offset, vi.red.length, - vi.green.offset, vi.green.length, - vi.blue.offset, vi.blue.length); - - bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bits == MAP_FAILED) { - perror("failed to mmap framebuffer"); - close(fd); - return -1; - } - - overscan_offset_x = vi.xres * overscan_percent / 100; - overscan_offset_y = vi.yres * overscan_percent / 100; - - gr_framebuffer[0].width = vi.xres; - gr_framebuffer[0].height = vi.yres; - gr_framebuffer[0].row_bytes = fi.line_length; - gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; - gr_framebuffer[0].data = bits; - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); - - /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 <= fi.smem_len) { - double_buffered = true; - - memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); - gr_framebuffer[1].data = gr_framebuffer[0].data + - gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - - gr_draw = gr_framebuffer+1; - - } else { - double_buffered = false; - - // Without double-buffering, we allocate RAM for a buffer to - // draw in, and then "flipping" the buffer consists of a - // memcpy from the buffer we allocated to the framebuffer. - - gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return -1; - } - } - - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - gr_fb_fd = fd; - set_displayed_framebuffer(0); - - return fd; -} - -void gr_flip(void) -{ - if (double_buffered) { - // Change gr_draw to point to the buffer currently displayed, - // then flip the driver so we're displaying the other buffer - // instead. - gr_draw = gr_framebuffer + displayed_buffer; - set_displayed_framebuffer(1-displayed_buffer); - } else { - // Copy from the in-memory surface to the framebuffer. - memcpy(gr_framebuffer[0].data, gr_draw->data, - gr_draw->height * gr_draw->row_bytes); - } -} - int gr_measure(const char *s) { return gr_font->cwidth * strlen(s); @@ -466,7 +338,7 @@ static void gr_test() { gr_color(0, 0, 255, 128); gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); - gr_flip(); + gr_draw = gr_backend->flip(gr_backend); } printf("getting end time\n"); time_t end = time(NULL); @@ -478,6 +350,10 @@ static void gr_test() { } #endif +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); +} + int gr_init(void) { gr_init_font(); @@ -493,36 +369,28 @@ int gr_init(void) return -1; } - if (get_framebuffer(&gr_framebuffer) < 0) { - gr_exit(); + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { return -1; } - printf("framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_draw->width, gr_draw->height); + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; gr_flip(); gr_flip(); - gr_fb_blank(true); - gr_fb_blank(false); - return 0; } void gr_exit(void) { - close(gr_fb_fd); - gr_fb_fd = -1; + gr_backend->exit(gr_backend); ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); close(gr_vt_fd); gr_vt_fd = -1; - - if (!double_buffered) { - if (gr_draw) free(gr_draw->data); - free(gr_draw); - } } int gr_fb_width(void) @@ -537,9 +405,5 @@ int gr_fb_height(void) void gr_fb_blank(bool blank) { - int ret; - - ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - if (ret < 0) - perror("ioctl(): blank"); + gr_backend->blank(gr_backend, blank); } diff --git a/minui/graphics.h b/minui/graphics.h new file mode 100644 index 000000000..94b89a554 --- /dev/null +++ b/minui/graphics.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GRAPHICS_H_ +#define _GRAPHICS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include "minui.h" + +typedef struct minui_backend { + // Initializes the backend and returns a gr_surface to draw into. + gr_surface (*init)(struct minui_backend*); + + // Causes the current drawing surface (returned by the most recent + // call to flip() or init()) to be displayed, and returns a new + // drawing surface. + gr_surface (*flip)(struct minui_backend*); + + // Blank (or unblank) the screen. + void (*blank)(struct minui_backend*, bool); + + // Device cleanup when drawing is done. + void (*exit)(struct minui_backend*); +} minui_backend; + +minui_backend* open_fbdev(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c new file mode 100644 index 000000000..6a6330b22 --- /dev/null +++ b/minui/graphics_fbdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#include "minui.h" +#include "graphics.h" + +static gr_surface fbdev_init(minui_backend*); +static gr_surface fbdev_flip(minui_backend*); +static void fbdev_blank(minui_backend*, bool); +static void fbdev_exit(minui_backend*); + +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static struct fb_var_screeninfo vi; +static int fb_fd = -1; + +static minui_backend my_backend = { + .init = fbdev_init, + .flip = fbdev_flip, + .blank = fbdev_blank, + .exit = fbdev_exit, +}; + +minui_backend* open_fbdev() { + return &my_backend; +} + +static void fbdev_blank(minui_backend* backend, bool blank) +{ + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static gr_surface fbdev_init(minui_backend* backend) { + int fd; + void *bits; + + struct fb_fix_screeninfo fi; + + fd = open("/dev/graphics/fb0", O_RDWR); + if (fd < 0) { + perror("cannot open fb0"); + return NULL; + } + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + + bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (bits == MAP_FAILED) { + perror("failed to mmap framebuffer"); + close(fd); + return NULL; + } + + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = bits; + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + + /* check if we can use double buffering */ + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + + gr_draw = gr_framebuffer+1; + + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return NULL; + } + } + + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = fd; + set_displayed_framebuffer(0); + + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + + fbdev_blank(backend, true); + fbdev_blank(backend, false); + + return gr_draw; +} + +static gr_surface fbdev_flip(minui_backend* backend) { + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } + return gr_draw; +} + +static void fbdev_exit(minui_backend* backend) { + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; +} diff --git a/minui/minui.h b/minui/minui.h index 83c121e26..b4b57e1db 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -17,6 +17,8 @@ #ifndef _MINUI_H_ #define _MINUI_H_ +#include <sys/types.h> + #include <stdbool.h> #ifdef __cplusplus |