diff options
Diffstat (limited to 'minui')
-rw-r--r-- | minui/events.cpp | 207 | ||||
-rw-r--r-- | minui/graphics.cpp | 59 | ||||
-rw-r--r-- | minui/graphics_adf.cpp | 99 | ||||
-rw-r--r-- | minui/graphics_adf.h | 50 | ||||
-rw-r--r-- | minui/graphics_drm.cpp | 216 | ||||
-rw-r--r-- | minui/graphics_drm.h | 50 | ||||
-rw-r--r-- | minui/graphics_fbdev.cpp | 87 | ||||
-rw-r--r-- | minui/graphics_fbdev.h | 49 | ||||
-rw-r--r-- | minui/include/minui/minui.h | 72 | ||||
-rw-r--r-- | minui/resources.cpp | 172 |
10 files changed, 554 insertions, 507 deletions
diff --git a/minui/events.cpp b/minui/events.cpp index 2894c3b6b..7d0250e97 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -23,144 +23,148 @@ #include <string.h> #include <sys/epoll.h> #include <sys/ioctl.h> +#include <sys/types.h> #include <unistd.h> #include <functional> +#include <memory> + +#include <android-base/unique_fd.h> #include "minui/minui.h" -#define MAX_DEVICES 16 -#define MAX_MISC_FDS 16 +constexpr size_t MAX_DEVICES = 16; +constexpr size_t MAX_MISC_FDS = 16; -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG) +constexpr size_t BITS_PER_LONG = sizeof(unsigned long) * 8; +constexpr size_t BITS_TO_LONGS(size_t bits) { + return ((bits + BITS_PER_LONG - 1) / BITS_PER_LONG); +} -struct fd_info { - int fd; +struct FdInfo { + android::base::unique_fd fd; ev_callback cb; }; -static int g_epoll_fd; -static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS]; -static int npolledevents; +static android::base::unique_fd g_epoll_fd; +static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS]; +static int g_polled_events_count; -static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS]; +static FdInfo ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS]; -static unsigned ev_count = 0; -static unsigned ev_dev_count = 0; -static unsigned ev_misc_count = 0; +static size_t g_ev_count = 0; +static size_t g_ev_dev_count = 0; +static size_t g_ev_misc_count = 0; static bool test_bit(size_t bit, unsigned long* array) { // NOLINT - return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; + return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } int ev_init(ev_callback input_cb, bool allow_touch_inputs) { - g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); - if (g_epoll_fd == -1) { + g_epoll_fd.reset(); + + android::base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC)); + if (epoll_fd == -1) { return -1; } - bool epollctlfail = false; - DIR* dir = opendir("/dev/input"); - if (dir != nullptr) { - dirent* de; - while ((de = readdir(dir))) { - if (strncmp(de->d_name, "event", 5)) continue; - int fd = openat(dirfd(dir), de->d_name, O_RDONLY); - if (fd == -1) continue; - - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // Read the evbits of the input device. - if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { - close(fd); - continue; - } + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir); + if (!dir) { + return -1; + } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also - // allowed if allow_touch_inputs is set. - if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { - close(fd); - continue; - } - } + bool epoll_ctl_failed = false; + dirent* de; + while ((de = readdir(dir.get())) != nullptr) { + if (strncmp(de->d_name, "event", 5)) continue; + android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC)); + if (fd == -1) continue; + + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - epoll_event ev; - ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = &ev_fdinfo[ev_count]; - if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { - close(fd); - epollctlfail = true; + // Read the evbits of the input device. + if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. + if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { continue; } + } - ev_fdinfo[ev_count].fd = fd; - ev_fdinfo[ev_count].cb = std::move(input_cb); - ev_count++; - ev_dev_count++; - if (ev_dev_count == MAX_DEVICES) break; + epoll_event ev; + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = &ev_fdinfo[g_ev_count]; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { + epoll_ctl_failed = true; + continue; } - closedir(dir); + ev_fdinfo[g_ev_count].fd.reset(fd.release()); + ev_fdinfo[g_ev_count].cb = input_cb; + g_ev_count++; + g_ev_dev_count++; + if (g_ev_dev_count == MAX_DEVICES) break; } - if (epollctlfail && !ev_count) { - close(g_epoll_fd); - g_epoll_fd = -1; + if (epoll_ctl_failed && !g_ev_count) { return -1; } + g_epoll_fd.reset(epoll_fd.release()); return 0; } int ev_get_epollfd(void) { - return g_epoll_fd; + return g_epoll_fd.get(); } -int ev_add_fd(int fd, ev_callback cb) { - if (ev_misc_count == MAX_MISC_FDS || cb == NULL) { +int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb) { + if (g_ev_misc_count == MAX_MISC_FDS || cb == nullptr) { return -1; } epoll_event ev; ev.events = EPOLLIN | EPOLLWAKEUP; - ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]); + ev.data.ptr = static_cast<void*>(&ev_fdinfo[g_ev_count]); int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev); if (!ret) { - ev_fdinfo[ev_count].fd = fd; - ev_fdinfo[ev_count].cb = std::move(cb); - ev_count++; - ev_misc_count++; + ev_fdinfo[g_ev_count].fd.reset(fd.release()); + ev_fdinfo[g_ev_count].cb = std::move(cb); + g_ev_count++; + g_ev_misc_count++; } return ret; } void ev_exit(void) { - while (ev_count > 0) { - close(ev_fdinfo[--ev_count].fd); - } - ev_misc_count = 0; - ev_dev_count = 0; - close(g_epoll_fd); + while (g_ev_count > 0) { + ev_fdinfo[--g_ev_count].fd.reset(); + } + g_ev_misc_count = 0; + g_ev_dev_count = 0; + g_epoll_fd.reset(); } int ev_wait(int timeout) { - npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout); - if (npolledevents <= 0) { - return -1; - } - return 0; + g_polled_events_count = epoll_wait(g_epoll_fd, g_polled_events, g_ev_count, timeout); + if (g_polled_events_count <= 0) { + return -1; + } + return 0; } void ev_dispatch(void) { - for (int n = 0; n < npolledevents; n++) { - fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr); + for (int n = 0; n < g_polled_events_count; n++) { + FdInfo* fdi = static_cast<FdInfo*>(g_polled_events[n].data.ptr); const ev_callback& cb = fdi->cb; if (cb) { - cb(fdi->fd, polledevents[n].events); + cb(fdi->fd, g_polled_events[n].events); } } } @@ -180,7 +184,7 @@ int ev_sync_key_state(const ev_set_key_callback& set_key_cb) { unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT - for (size_t i = 0; i < ev_dev_count; ++i) { + for (size_t i = 0; i < g_ev_dev_count; ++i) { memset(ev_bits, 0, sizeof(ev_bits)); memset(key_bits, 0, sizeof(key_bits)); @@ -205,37 +209,36 @@ int ev_sync_key_state(const ev_set_key_callback& set_key_cb) { } void ev_iterate_available_keys(const std::function<void(int)>& f) { - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT - for (size_t i = 0; i < ev_dev_count; ++i) { - memset(ev_bits, 0, sizeof(ev_bits)); - memset(key_bits, 0, sizeof(key_bits)); + for (size_t i = 0; i < g_ev_dev_count; ++i) { + memset(ev_bits, 0, sizeof(ev_bits)); + memset(key_bits, 0, sizeof(key_bits)); - // Does this device even have keys? - if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { - continue; - } - if (!test_bit(EV_KEY, ev_bits)) { - continue; - } + // Does this device even have keys? + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_KEY, ev_bits)) { + continue; + } - int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits); - if (rc == -1) { - continue; - } + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits) == -1) { + continue; + } - for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { - if (test_bit(key_code, key_bits)) { - f(key_code); - } - } + for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { + if (test_bit(key_code, key_bits)) { + f(key_code); + } } + } } void ev_iterate_touch_inputs(const std::function<void(int)>& action) { - for (size_t i = 0; i < ev_dev_count; ++i) { + for (size_t i = 0; i < g_ev_dev_count; ++i) { // Use unsigned long to match ioctl's parameter type. unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { diff --git a/minui/graphics.cpp b/minui/graphics.cpp index e6367d950..4d1f9b2d2 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -40,7 +40,7 @@ static uint32_t gr_current = ~0; static constexpr uint32_t alpha_mask = 0xff000000; // gr_draw is owned by backends. -static const GRSurface* gr_draw = nullptr; +static GRSurface* gr_draw = nullptr; static GRRotation rotation = GRRotation::NONE; static PixelFormat pixel_format = PixelFormat::UNKNOWN; @@ -121,28 +121,29 @@ static void incr_y(uint32_t** p, int row_pixels) { } // Returns pixel pointer at given coordinates with rotation adjustment. -static uint32_t* pixel_at(const GRSurface* surf, int x, int y, int row_pixels) { +static uint32_t* PixelAt(GRSurface* surface, int x, int y, int row_pixels) { switch (rotation) { case GRRotation::NONE: - return reinterpret_cast<uint32_t*>(surf->data) + y * row_pixels + x; + return reinterpret_cast<uint32_t*>(surface->data()) + y * row_pixels + x; case GRRotation::RIGHT: - return reinterpret_cast<uint32_t*>(surf->data) + x * row_pixels + (surf->width - y); + return reinterpret_cast<uint32_t*>(surface->data()) + x * row_pixels + (surface->width - y); case GRRotation::DOWN: - return reinterpret_cast<uint32_t*>(surf->data) + (surf->height - 1 - y) * row_pixels + - (surf->width - 1 - x); + return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - y) * row_pixels + + (surface->width - 1 - x); case GRRotation::LEFT: - return reinterpret_cast<uint32_t*>(surf->data) + (surf->height - 1 - x) * row_pixels + y; + return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - x) * row_pixels + + y; default: printf("invalid rotation %d", static_cast<int>(rotation)); } return nullptr; } -static void text_blend(uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, - int width, int height) { +static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, + int width, int height) { uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24); for (int j = 0; j < height; ++j) { - uint8_t* sx = src_p; + const uint8_t* sx = src_p; uint32_t* px = dst_p; for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) { uint8_t a = *sx++; @@ -176,18 +177,18 @@ void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { } int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint8_t* src_p = font->texture->data + ((ch - ' ') * font->char_width) + - (bold ? font->char_height * font->texture->row_bytes : 0); - uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); + const uint8_t* src_p = font->texture->data() + ((ch - ' ') * font->char_width) + + (bold ? font->char_height * font->texture->row_bytes : 0); + uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); - text_blend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, - font->char_height); + TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, + font->char_height); x += font->char_width; } } -void gr_texticon(int x, int y, GRSurface* icon) { +void gr_texticon(int x, int y, const GRSurface* icon) { if (icon == nullptr) return; if (icon->pixel_bytes != 1) { @@ -201,10 +202,9 @@ void gr_texticon(int x, int y, GRSurface* icon) { if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint8_t* src_p = icon->data; - uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); - - text_blend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); + const uint8_t* src_p = icon->data(); + uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); + TextBlend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); } void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { @@ -221,9 +221,9 @@ void gr_clear() { (gr_current & 0xff) == ((gr_current >> 16) & 0xff) && (gr_current & 0xff) == ((gr_current >> 24) & 0xff) && gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) { - memset(gr_draw->data, gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); + memset(gr_draw->data(), gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); } else { - uint32_t* px = reinterpret_cast<uint32_t*>(gr_draw->data); + uint32_t* px = reinterpret_cast<uint32_t*>(gr_draw->data()); int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width; for (int y = 0; y < gr_draw->height; ++y) { for (int x = 0; x < gr_draw->width; ++x) { @@ -244,7 +244,7 @@ void gr_fill(int x1, int y1, int x2, int y2) { if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint32_t* p = pixel_at(gr_draw, x1, y1, row_pixels); + uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels); uint8_t alpha = static_cast<uint8_t>(((gr_current & alpha_mask) >> 24)); if (alpha > 0) { for (int y = y1; y < y2; ++y) { @@ -258,7 +258,7 @@ void gr_fill(int x1, int y1, int x2, int y2) { } } -void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { +void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { if (source == nullptr) return; if (gr_draw->pixel_bytes != source->pixel_bytes) { @@ -274,11 +274,12 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { if (rotation != GRRotation::NONE) { int src_row_pixels = source->row_bytes / source->pixel_bytes; int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; - uint32_t* src_py = reinterpret_cast<uint32_t*>(source->data) + sy * source->row_bytes / 4 + sx; - uint32_t* dst_py = pixel_at(gr_draw, dx, dy, row_pixels); + const uint32_t* src_py = + reinterpret_cast<const uint32_t*>(source->data()) + sy * source->row_bytes / 4 + sx; + uint32_t* dst_py = PixelAt(gr_draw, dx, dy, row_pixels); for (int y = 0; y < h; y += 1) { - uint32_t* src_px = src_py; + const uint32_t* src_px = src_py; uint32_t* dst_px = dst_py; for (int x = 0; x < w; x += 1) { *dst_px = *src_px++; @@ -288,8 +289,8 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { incr_y(&dst_py, row_pixels); } } else { - unsigned char* src_p = source->data + sy * source->row_bytes + sx * source->pixel_bytes; - unsigned char* dst_p = gr_draw->data + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; + const uint8_t* src_p = source->data() + sy * source->row_bytes + sx * source->pixel_bytes; + uint8_t* dst_p = gr_draw->data() + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; for (int i = 0; i < h; ++i) { memcpy(dst_p, src_p, w * source->pixel_bytes); diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp index 7439df9ac..10cd60709 100644 --- a/minui/graphics_adf.cpp +++ b/minui/graphics_adf.cpp @@ -20,6 +20,7 @@ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <sys/mman.h> #include <unistd.h> @@ -28,51 +29,60 @@ #include "minui/minui.h" -MinuiBackendAdf::MinuiBackendAdf() - : intf_fd(-1), dev(), current_surface(0), n_surfaces(0), surfaces() {} - -int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) { - *surf = {}; - surf->fence_fd = -1; - surf->fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format, - &surf->offset, &surf->pitch); - if (surf->fd < 0) { - return surf->fd; +GRSurfaceAdf::~GRSurfaceAdf() { + if (mmapped_buffer_) { + munmap(mmapped_buffer_, pitch * height); + } + if (fence_fd != -1) { + close(fence_fd); + } + if (fd != -1) { + close(fd); } +} + +std::unique_ptr<GRSurfaceAdf> GRSurfaceAdf::Create(int intf_fd, const drm_mode_modeinfo* mode, + __u32 format, int* err) { + __u32 offset; + __u32 pitch; + auto fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format, + &offset, &pitch); - surf->width = mode->hdisplay; - surf->height = mode->vdisplay; - surf->row_bytes = surf->pitch; - surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4; - - surf->data = static_cast<uint8_t*>( - mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset)); - if (surf->data == MAP_FAILED) { - int saved_errno = errno; - close(surf->fd); - return -saved_errno; + if (fd < 0) { + *err = fd; + return nullptr; } - return 0; + std::unique_ptr<GRSurfaceAdf> surf = std::unique_ptr<GRSurfaceAdf>( + new GRSurfaceAdf(mode->hdisplay, mode->vdisplay, pitch, (format == DRM_FORMAT_RGB565 ? 2 : 4), + offset, pitch, fd)); + + auto mmapped = + mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset); + if (mmapped == MAP_FAILED) { + *err = -errno; + return nullptr; + } + surf->mmapped_buffer_ = static_cast<uint8_t*>(mmapped); + return surf; } +MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), current_surface(0), n_surfaces(0) {} + int MinuiBackendAdf::InterfaceInit() { adf_interface_data intf_data; - int err = adf_get_interface_data(intf_fd, &intf_data); - if (err < 0) return err; + if (int err = adf_get_interface_data(intf_fd, &intf_data); err < 0) return err; - int ret = 0; - err = SurfaceInit(&intf_data.current_mode, &surfaces[0]); - if (err < 0) { - fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); - ret = err; + int result = 0; + surfaces[0] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result); + if (!surfaces[0]) { + fprintf(stderr, "Failed to allocate surface 0: %s\n", strerror(-result)); goto done; } - err = SurfaceInit(&intf_data.current_mode, &surfaces[1]); - if (err < 0) { - fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); - surfaces[1] = {}; + surfaces[1] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result); + if (!surfaces[1]) { + fprintf(stderr, "Failed to allocate surface 1: %s\n", strerror(-result)); n_surfaces = 1; } else { n_surfaces = 2; @@ -80,7 +90,7 @@ int MinuiBackendAdf::InterfaceInit() { done: adf_free_interface_data(&intf_data); - return ret; + return result; } int MinuiBackendAdf::DeviceInit(adf_device* dev) { @@ -91,7 +101,7 @@ int MinuiBackendAdf::DeviceInit(adf_device* dev) { err = adf_device_attach(dev, eng_id, intf_id); if (err < 0 && err != -EALREADY) return err; - intf_fd = adf_interface_open(dev, intf_id, O_RDWR); + intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC); if (intf_fd < 0) return intf_fd; err = InterfaceInit(); @@ -153,12 +163,12 @@ GRSurface* MinuiBackendAdf::Init() { } void MinuiBackendAdf::Sync(GRSurfaceAdf* surf) { - static constexpr unsigned int warningTimeout = 3000; + static constexpr unsigned int kWarningTimeout = 3000; if (surf == nullptr) return; if (surf->fence_fd >= 0) { - int err = sync_wait(surf->fence_fd, warningTimeout); + int err = sync_wait(surf->fence_fd, kWarningTimeout); if (err < 0) { perror("adf sync fence wait error\n"); } @@ -169,31 +179,22 @@ void MinuiBackendAdf::Sync(GRSurfaceAdf* surf) { } GRSurface* MinuiBackendAdf::Flip() { - GRSurfaceAdf* surf = &surfaces[current_surface]; + const auto& surf = surfaces[current_surface]; int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format, surf->fd, surf->offset, surf->pitch, -1); if (fence_fd >= 0) surf->fence_fd = fence_fd; current_surface = (current_surface + 1) % n_surfaces; - Sync(&surfaces[current_surface]); - return &surfaces[current_surface]; + Sync(surfaces[current_surface].get()); + return surfaces[current_surface].get(); } void MinuiBackendAdf::Blank(bool blank) { adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); } -void MinuiBackendAdf::SurfaceDestroy(GRSurfaceAdf* surf) { - munmap(surf->data, surf->pitch * surf->height); - close(surf->fence_fd); - close(surf->fd); -} - MinuiBackendAdf::~MinuiBackendAdf() { adf_device_close(&dev); - for (unsigned int i = 0; i < n_surfaces; i++) { - SurfaceDestroy(&surfaces[i]); - } if (intf_fd >= 0) close(intf_fd); } diff --git a/minui/graphics_adf.h b/minui/graphics_adf.h index 2f019ed0b..79d8d2acb 100644 --- a/minui/graphics_adf.h +++ b/minui/graphics_adf.h @@ -14,45 +14,63 @@ * limitations under the License. */ -#ifndef _GRAPHICS_ADF_H_ -#define _GRAPHICS_ADF_H_ +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +#include <memory> #include <adf/adf.h> #include "graphics.h" +#include "minui/minui.h" class GRSurfaceAdf : public GRSurface { - private: - int fence_fd; - int fd; - __u32 offset; - __u32 pitch; + public: + ~GRSurfaceAdf() override; + static std::unique_ptr<GRSurfaceAdf> Create(int intf_fd, const drm_mode_modeinfo* mode, + __u32 format, int* err); + + uint8_t* data() override { + return mmapped_buffer_; + } + + private: friend class MinuiBackendAdf; + + GRSurfaceAdf(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes, __u32 offset, + __u32 pitch, int fd) + : GRSurface(width, height, row_bytes, pixel_bytes), offset(offset), pitch(pitch), fd(fd) {} + + const __u32 offset; + const __u32 pitch; + + int fd; + int fence_fd{ -1 }; + uint8_t* mmapped_buffer_{ nullptr }; }; class MinuiBackendAdf : public MinuiBackend { public: + MinuiBackendAdf(); + ~MinuiBackendAdf() override; GRSurface* Init() override; GRSurface* Flip() override; void Blank(bool) override; - ~MinuiBackendAdf() override; - MinuiBackendAdf(); private: - int SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf); int InterfaceInit(); int DeviceInit(adf_device* dev); - void SurfaceDestroy(GRSurfaceAdf* surf); void Sync(GRSurfaceAdf* surf); int intf_fd; adf_id_t eng_id; __u32 format; adf_device dev; - unsigned int current_surface; - unsigned int n_surfaces; - GRSurfaceAdf surfaces[2]; + size_t current_surface; + size_t n_surfaces; + std::unique_ptr<GRSurfaceAdf> surfaces[2]; }; - -#endif // _GRAPHICS_ADF_H_ diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 630b80180..7b2eed15d 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -24,74 +24,37 @@ #include <sys/types.h> #include <unistd.h> +#include <memory> + +#include <android-base/macros.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> #include <drm_fourcc.h> #include <xf86drm.h> #include <xf86drmMode.h> #include "minui/minui.h" -#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A))) - -MinuiBackendDrm::MinuiBackendDrm() - : GRSurfaceDrms(), main_monitor_crtc(nullptr), main_monitor_connector(nullptr), drm_fd(-1) {} - -void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) { - if (crtc) { - drmModeSetCrtc(drm_fd, crtc->crtc_id, - 0, // fb_id - 0, 0, // x,y - nullptr, // connectors - 0, // connector_count - nullptr); // mode - } -} - -int MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface) { - int ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y - &main_monitor_connector->connector_id, - 1, // connector_count - &main_monitor_crtc->mode); - - if (ret) { - printf("drmModeSetCrtc failed ret=%d\n", ret); - } - - return ret; -} - -void MinuiBackendDrm::Blank(bool blank) { - if (blank) { - DrmDisableCrtc(drm_fd, main_monitor_crtc); - } else { - DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]); - } -} - -void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) { - if (!surface) return; - - if (surface->data) { - munmap(surface->data, surface->row_bytes * surface->height); +GRSurfaceDrm::~GRSurfaceDrm() { + if (mmapped_buffer_) { + munmap(mmapped_buffer_, row_bytes * height); } - if (surface->fb_id) { - int ret = drmModeRmFB(drm_fd, surface->fb_id); - if (ret) { - printf("drmModeRmFB failed ret=%d\n", ret); + if (fb_id) { + if (drmModeRmFB(drm_fd_, fb_id) != 0) { + perror("Failed to drmModeRmFB"); + // Falling through to free other resources. } } - if (surface->handle) { + if (handle) { drm_gem_close gem_close = {}; - gem_close.handle = surface->handle; + gem_close.handle = handle; - int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); - if (ret) { - printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); + if (drmIoctl(drm_fd_, DRM_IOCTL_GEM_CLOSE, &gem_close) != 0) { + perror("Failed to DRM_IOCTL_GEM_CLOSE"); } } - - delete surface; } static int drm_format_to_bpp(uint32_t format) { @@ -111,10 +74,7 @@ static int drm_format_to_bpp(uint32_t format) { } } -GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) { - GRSurfaceDrm* surface = new GRSurfaceDrm; - *surface = {}; - +std::unique_ptr<GRSurfaceDrm> GRSurfaceDrm::Create(int drm_fd, int width, int height) { uint32_t format; PixelFormat pixel_format = gr_pixel_format(); // PixelFormat comes in byte order, whereas DRM_FORMAT_* uses little-endian @@ -137,53 +97,74 @@ GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) { create_dumb.bpp = drm_format_to_bpp(format); create_dumb.flags = 0; - int ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); - if (ret) { - printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n", ret); - DrmDestroySurface(surface); + if (drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) != 0) { + perror("Failed to DRM_IOCTL_MODE_CREATE_DUMB"); return nullptr; } - surface->handle = create_dumb.handle; + + // Cannot use std::make_unique to access non-public ctor. + auto surface = std::unique_ptr<GRSurfaceDrm>(new GRSurfaceDrm( + width, height, create_dumb.pitch, create_dumb.bpp / 8, drm_fd, create_dumb.handle)); uint32_t handles[4], pitches[4], offsets[4]; handles[0] = surface->handle; pitches[0] = create_dumb.pitch; offsets[0] = 0; - - ret = - drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &(surface->fb_id), 0); - if (ret) { - printf("drmModeAddFB2 failed ret=%d\n", ret); - DrmDestroySurface(surface); + if (drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &surface->fb_id, 0) != + 0) { + perror("Failed to drmModeAddFB2"); return nullptr; } drm_mode_map_dumb map_dumb = {}; map_dumb.handle = create_dumb.handle; - ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); - if (ret) { - printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n", ret); - DrmDestroySurface(surface); + if (drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) != 0) { + perror("Failed to DRM_IOCTL_MODE_MAP_DUMB"); return nullptr; } - surface->height = height; - surface->width = width; - surface->row_bytes = create_dumb.pitch; - surface->pixel_bytes = create_dumb.bpp / 8; - surface->data = static_cast<unsigned char*>(mmap(nullptr, surface->height * surface->row_bytes, - PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, - map_dumb.offset)); - if (surface->data == MAP_FAILED) { - perror("mmap() failed"); - DrmDestroySurface(surface); + auto mmapped = mmap(nullptr, surface->height * surface->row_bytes, PROT_READ | PROT_WRITE, + MAP_SHARED, drm_fd, map_dumb.offset); + if (mmapped == MAP_FAILED) { + perror("Failed to mmap()"); return nullptr; } - + surface->mmapped_buffer_ = static_cast<uint8_t*>(mmapped); return surface; } +void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) { + if (crtc) { + drmModeSetCrtc(drm_fd, crtc->crtc_id, + 0, // fb_id + 0, 0, // x,y + nullptr, // connectors + 0, // connector_count + nullptr); // mode + } +} + +bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, + const std::unique_ptr<GRSurfaceDrm>& surface) { + if (drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y + &main_monitor_connector->connector_id, + 1, // connector_count + &main_monitor_crtc->mode) != 0) { + perror("Failed to drmModeSetCrtc"); + return false; + } + return true; +} + +void MinuiBackendDrm::Blank(bool blank) { + if (blank) { + DrmDisableCrtc(drm_fd, main_monitor_crtc); + } else { + DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]); + } +} + static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources, drmModeConnector* connector) { // Find the encoder. If we already have one, just use it. @@ -265,7 +246,7 @@ drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources do { main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]); i++; - } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority)); + } while (!main_monitor_connector && i < arraysize(kConnectorPriority)); /* If we didn't find a connector, grab the first one that is connected. */ if (!main_monitor_connector) { @@ -299,60 +280,53 @@ void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmMode GRSurface* MinuiBackendDrm::Init() { drmModeRes* res = nullptr; + drm_fd = -1; /* Consider DRM devices in order. */ for (int i = 0; i < DRM_MAX_MINOR; i++) { - char* dev_name; - int ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret < 0) continue; + auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i); + android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR | O_CLOEXEC)); + if (fd == -1) continue; - drm_fd = open(dev_name, O_RDWR, 0); - free(dev_name); - if (drm_fd < 0) continue; - - uint64_t cap = 0; /* We need dumb buffers. */ - ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap); - if (ret || cap == 0) { - close(drm_fd); + if (uint64_t cap = 0; drmGetCap(fd.get(), DRM_CAP_DUMB_BUFFER, &cap) != 0 || cap == 0) { continue; } - res = drmModeGetResources(drm_fd); + res = drmModeGetResources(fd.get()); if (!res) { - close(drm_fd); continue; } /* Use this device if it has at least one connected monitor. */ if (res->count_crtcs > 0 && res->count_connectors > 0) { - if (find_first_connected_connector(drm_fd, res)) break; + if (find_first_connected_connector(fd.get(), res)) { + drm_fd = fd.release(); + break; + } } drmModeFreeResources(res); - close(drm_fd); res = nullptr; } - if (drm_fd < 0 || res == nullptr) { - perror("cannot find/open a drm device"); + if (drm_fd == -1 || res == nullptr) { + perror("Failed to find/open a drm device"); return nullptr; } uint32_t selected_mode; main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode); - if (!main_monitor_connector) { - printf("main_monitor_connector not found\n"); + fprintf(stderr, "Failed to find main_monitor_connector\n"); drmModeFreeResources(res); close(drm_fd); return nullptr; } main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector); - if (!main_monitor_crtc) { - printf("main_monitor_crtc not found\n"); + fprintf(stderr, "Failed to find main_monitor_crtc\n"); drmModeFreeResources(res); close(drm_fd); return nullptr; @@ -367,21 +341,20 @@ GRSurface* MinuiBackendDrm::Init() { drmModeFreeResources(res); - GRSurfaceDrms[0] = DrmCreateSurface(width, height); - GRSurfaceDrms[1] = DrmCreateSurface(width, height); + GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height); + GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height); if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) { - // GRSurfaceDrms and drm_fd should be freed in d'tor. return nullptr; } current_buffer = 0; // We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails. - if (DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1]) != 0) { + if (!DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1])) { return nullptr; } - return GRSurfaceDrms[0]; + return GRSurfaceDrms[0].get(); } static void page_flip_complete(__unused int fd, @@ -394,12 +367,9 @@ static void page_flip_complete(__unused int fd, GRSurface* MinuiBackendDrm::Flip() { bool ongoing_flip = true; - - int ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, - GRSurfaceDrms[current_buffer]->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip); - if (ret < 0) { - printf("drmModePageFlip failed ret=%d\n", ret); + if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, GRSurfaceDrms[current_buffer]->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) { + perror("Failed to drmModePageFlip"); return nullptr; } @@ -409,9 +379,8 @@ GRSurface* MinuiBackendDrm::Flip() { .events = POLLIN }; - ret = poll(&fds, 1, -1); - if (ret == -1 || !(fds.revents & POLLIN)) { - printf("poll() failed on drm fd\n"); + if (poll(&fds, 1, -1) == -1 || !(fds.revents & POLLIN)) { + perror("Failed to poll() on drm fd"); break; } @@ -420,21 +389,18 @@ GRSurface* MinuiBackendDrm::Flip() { .page_flip_handler = page_flip_complete }; - ret = drmHandleEvent(drm_fd, &evctx); - if (ret != 0) { - printf("drmHandleEvent failed ret=%d\n", ret); + if (drmHandleEvent(drm_fd, &evctx) != 0) { + perror("Failed to drmHandleEvent"); break; } } current_buffer = 1 - current_buffer; - return GRSurfaceDrms[current_buffer]; + return GRSurfaceDrms[current_buffer].get(); } MinuiBackendDrm::~MinuiBackendDrm() { DrmDisableCrtc(drm_fd, main_monitor_crtc); - DrmDestroySurface(GRSurfaceDrms[0]); - DrmDestroySurface(GRSurfaceDrms[1]); drmModeFreeCrtc(main_monitor_crtc); drmModeFreeConnector(main_monitor_connector); close(drm_fd); diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h index 756625b03..57ba39b83 100644 --- a/minui/graphics_drm.h +++ b/minui/graphics_drm.h @@ -14,45 +14,61 @@ * limitations under the License. */ -#ifndef _GRAPHICS_DRM_H_ -#define _GRAPHICS_DRM_H_ +#pragma once +#include <stddef.h> #include <stdint.h> +#include <memory> + #include <xf86drmMode.h> #include "graphics.h" #include "minui/minui.h" class GRSurfaceDrm : public GRSurface { - private: - uint32_t fb_id; - uint32_t handle; + public: + ~GRSurfaceDrm() override; + + // Creates a GRSurfaceDrm instance. + static std::unique_ptr<GRSurfaceDrm> Create(int drm_fd, int width, int height); + uint8_t* data() override { + return mmapped_buffer_; + } + + private: friend class MinuiBackendDrm; + + GRSurfaceDrm(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes, int drm_fd, + uint32_t handle) + : GRSurface(width, height, row_bytes, pixel_bytes), drm_fd_(drm_fd), handle(handle) {} + + const int drm_fd_; + + uint32_t fb_id{ 0 }; + uint32_t handle{ 0 }; + uint8_t* mmapped_buffer_{ nullptr }; }; class MinuiBackendDrm : public MinuiBackend { public: + MinuiBackendDrm() = default; + ~MinuiBackendDrm() override; + GRSurface* Init() override; GRSurface* Flip() override; void Blank(bool) override; - ~MinuiBackendDrm() override; - MinuiBackendDrm(); private: void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc); - int DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface); - GRSurfaceDrm* DrmCreateSurface(int width, int height); - void DrmDestroySurface(GRSurfaceDrm* surface); + bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr<GRSurfaceDrm>& surface); void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc); drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index); - GRSurfaceDrm* GRSurfaceDrms[2]; - int current_buffer; - drmModeCrtc* main_monitor_crtc; - drmModeConnector* main_monitor_connector; - int drm_fd; + std::unique_ptr<GRSurfaceDrm> GRSurfaceDrms[2]; + int current_buffer{ 0 }; + drmModeCrtc* main_monitor_crtc{ nullptr }; + drmModeConnector* main_monitor_connector{ nullptr }; + int drm_fd{ -1 }; }; - -#endif // _GRAPHICS_DRM_H_ diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp index 746f42aaa..2584017d6 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -26,21 +26,29 @@ #include <sys/types.h> #include <unistd.h> +#include <memory> + +#include <android-base/unique_fd.h> + #include "minui/minui.h" -MinuiBackendFbdev::MinuiBackendFbdev() : gr_draw(nullptr), fb_fd(-1) {} +std::unique_ptr<GRSurfaceFbdev> GRSurfaceFbdev::Create(size_t width, size_t height, + size_t row_bytes, size_t pixel_bytes) { + // Cannot use std::make_unique to access non-public ctor. + return std::unique_ptr<GRSurfaceFbdev>(new GRSurfaceFbdev(width, height, row_bytes, pixel_bytes)); +} void MinuiBackendFbdev::Blank(bool blank) { int ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); if (ret < 0) perror("ioctl(): blank"); } -void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) { +void MinuiBackendFbdev::SetDisplayedFramebuffer(size_t 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; + 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"); } @@ -48,7 +56,7 @@ void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) { } GRSurface* MinuiBackendFbdev::Init() { - int fd = open("/dev/graphics/fb0", O_RDWR); + android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR | O_CLOEXEC)); if (fd == -1) { perror("cannot open fb0"); return nullptr; @@ -57,13 +65,11 @@ GRSurface* MinuiBackendFbdev::Init() { fb_fix_screeninfo fi; if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { perror("failed to get fb0 info"); - close(fd); return nullptr; } if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { perror("failed to get fb0 info"); - close(fd); return nullptr; } @@ -90,50 +96,41 @@ GRSurface* MinuiBackendFbdev::Init() { void* 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 nullptr; } memset(bits, 0, fi.smem_len); - 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 = static_cast<uint8_t*>(bits); - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + gr_framebuffer[0] = + GRSurfaceFbdev::Create(vi.xres, vi.yres, fi.line_length, vi.bits_per_pixel / 8); + gr_framebuffer[0]->buffer_ = static_cast<uint8_t*>(bits); + memset(gr_framebuffer[0]->buffer_, 0, gr_framebuffer[0]->height * gr_framebuffer[0]->row_bytes); + + gr_framebuffer[1] = + GRSurfaceFbdev::Create(gr_framebuffer[0]->width, gr_framebuffer[0]->height, + gr_framebuffer[0]->row_bytes, gr_framebuffer[0]->pixel_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; - + gr_framebuffer[1]->buffer_ = + gr_framebuffer[0]->buffer_ + gr_framebuffer[0]->height * gr_framebuffer[0]->row_bytes; } 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 = static_cast<GRSurface*>(malloc(sizeof(GRSurface))); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes)); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return nullptr; - } + // 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. + memory_buffer.resize(gr_framebuffer[1]->height * gr_framebuffer[1]->row_bytes); + gr_framebuffer[1]->buffer_ = memory_buffer.data(); } - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - fb_fd = fd; + gr_draw = gr_framebuffer[1].get(); + memset(gr_draw->buffer_, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = std::move(fd); SetDisplayedFramebuffer(0); - printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + printf("framebuffer: %d (%zu x %zu)\n", fb_fd.get(), gr_draw->width, gr_draw->height); Blank(true); Blank(false); @@ -143,25 +140,13 @@ GRSurface* MinuiBackendFbdev::Init() { GRSurface* MinuiBackendFbdev::Flip() { 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; + // 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].get(); SetDisplayedFramebuffer(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); + memcpy(gr_framebuffer[0]->buffer_, gr_draw->buffer_, gr_draw->height * gr_draw->row_bytes); } return gr_draw; } - -MinuiBackendFbdev::~MinuiBackendFbdev() { - close(fb_fd); - fb_fd = -1; - - if (!double_buffered && gr_draw) { - free(gr_draw->data); - free(gr_draw); - } - gr_draw = nullptr; -} diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h index 107e19567..596ba74ea 100644 --- a/minui/graphics_fbdev.h +++ b/minui/graphics_fbdev.h @@ -14,31 +14,58 @@ * limitations under the License. */ -#ifndef _GRAPHICS_FBDEV_H_ -#define _GRAPHICS_FBDEV_H_ +#pragma once #include <linux/fb.h> +#include <stddef.h> +#include <stdint.h> + +#include <memory> +#include <vector> + +#include <android-base/unique_fd.h> #include "graphics.h" #include "minui/minui.h" +class GRSurfaceFbdev : public GRSurface { + public: + // Creates and returns a GRSurfaceFbdev instance, or nullptr on error. + static std::unique_ptr<GRSurfaceFbdev> Create(size_t width, size_t height, size_t row_bytes, + size_t pixel_bytes); + + uint8_t* data() override { + return buffer_; + } + + protected: + using GRSurface::GRSurface; + + private: + friend class MinuiBackendFbdev; + + // Points to the start of the buffer: either the mmap'd framebuffer or one allocated in-memory. + uint8_t* buffer_{ nullptr }; +}; + class MinuiBackendFbdev : public MinuiBackend { public: + MinuiBackendFbdev() = default; + ~MinuiBackendFbdev() override = default; + GRSurface* Init() override; GRSurface* Flip() override; void Blank(bool) override; - ~MinuiBackendFbdev() override; - MinuiBackendFbdev(); private: - void SetDisplayedFramebuffer(unsigned n); + void SetDisplayedFramebuffer(size_t n); - GRSurface gr_framebuffer[2]; + std::unique_ptr<GRSurfaceFbdev> gr_framebuffer[2]; + // Points to the current surface (i.e. one of the two gr_framebuffer's). + GRSurfaceFbdev* gr_draw{ nullptr }; bool double_buffered; - GRSurface* gr_draw; - int displayed_buffer; + std::vector<uint8_t> memory_buffer; + size_t displayed_buffer{ 0 }; fb_var_screeninfo vi; - int fb_fd; + android::base::unique_fd fb_fd; }; - -#endif // _GRAPHICS_FBDEV_H_ diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index fa13ecdff..36bdcf103 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -14,25 +14,73 @@ * limitations under the License. */ -#ifndef _MINUI_H_ -#define _MINUI_H_ +#pragma once +#include <stdint.h> +#include <stdlib.h> #include <sys/types.h> #include <functional> +#include <memory> #include <string> #include <vector> +#include <android-base/macros.h> +#include <android-base/unique_fd.h> + // // Graphics. // -struct GRSurface { - int width; - int height; - int row_bytes; - int pixel_bytes; - unsigned char* data; +class GRSurface { + public: + static constexpr size_t kSurfaceDataAlignment = 8; + + virtual ~GRSurface() = default; + + // Creates and returns a GRSurface instance that's sufficient for storing an image of the given + // size (i.e. row_bytes * height). The starting address of the surface data is aligned to + // kSurfaceDataAlignment. Returns the created GRSurface instance (in std::unique_ptr), or nullptr + // on error. + static std::unique_ptr<GRSurface> Create(size_t width, size_t height, size_t row_bytes, + size_t pixel_bytes); + + // Clones the current GRSurface instance (i.e. an image). + std::unique_ptr<GRSurface> Clone() const; + + virtual uint8_t* data() { + return data_.get(); + } + + const uint8_t* data() const { + return const_cast<const uint8_t*>(const_cast<GRSurface*>(this)->data()); + } + + size_t data_size() const { + return data_size_; + } + + size_t width; + size_t height; + size_t row_bytes; + size_t pixel_bytes; + + protected: + GRSurface(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes) + : width(width), height(height), row_bytes(row_bytes), pixel_bytes(pixel_bytes) {} + + private: + // The deleter for data_, whose data is allocated via aligned_alloc(3). + struct DataDeleter { + void operator()(uint8_t* data) { + free(data); + } + }; + + std::unique_ptr<uint8_t, DataDeleter> data_; + size_t data_size_; + + DISALLOW_COPY_AND_ASSIGN(GRSurface); }; struct GRFont { @@ -75,7 +123,7 @@ void gr_clear(); void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void gr_fill(int x1, int y1, int x2, int y2); -void gr_texticon(int x, int y, GRSurface* icon); +void gr_texticon(int x, int y, const GRSurface* icon); const GRFont* gr_sys_font(); int gr_init_font(const char* name, GRFont** dest); @@ -85,7 +133,7 @@ int gr_measure(const GRFont* font, const char* s); // Returns -1 if font is nullptr. int gr_font_size(const GRFont* font, int* x, int* y); -void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); +void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); unsigned int gr_get_width(const GRSurface* surface); unsigned int gr_get_height(const GRSurface* surface); @@ -106,7 +154,7 @@ using ev_set_key_callback = std::function<int(int code, int value)>; int ev_init(ev_callback input_cb, bool allow_touch_inputs = false); void ev_exit(); -int ev_add_fd(int fd, ev_callback cb); +int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb); void ev_iterate_available_keys(const std::function<void(int)>& f); void ev_iterate_touch_inputs(const std::function<void(int)>& action); int ev_sync_key_state(const ev_set_key_callback& set_key_cb); @@ -165,5 +213,3 @@ std::vector<std::string> get_locales_in_png(const std::string& png_name); // Free a surface allocated by any of the res_create_*_surface() // functions. void res_free_surface(GRSurface* surface); - -#endif diff --git a/minui/resources.cpp b/minui/resources.cpp index 477fbe2a2..069a49529 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -27,6 +27,7 @@ #include <sys/types.h> #include <unistd.h> +#include <limits> #include <memory> #include <regex> #include <string> @@ -37,18 +38,29 @@ #include "minui/minui.h" -#define SURFACE_DATA_ALIGNMENT 8 - static std::string g_resource_dir{ "/res/images" }; -static GRSurface* malloc_surface(size_t data_size) { - size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT; - unsigned char* temp = static_cast<unsigned char*>(malloc(size)); - if (temp == NULL) return NULL; - GRSurface* surface = reinterpret_cast<GRSurface*>(temp); - surface->data = temp + sizeof(GRSurface) + - (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT)); - return surface; +std::unique_ptr<GRSurface> GRSurface::Create(size_t width, size_t height, size_t row_bytes, + size_t pixel_bytes) { + if (width == 0 || row_bytes == 0 || height == 0 || pixel_bytes == 0) return nullptr; + if (std::numeric_limits<size_t>::max() / row_bytes < height) return nullptr; + + // Cannot use std::make_unique to access non-public ctor. + auto result = std::unique_ptr<GRSurface>(new GRSurface(width, height, row_bytes, pixel_bytes)); + size_t data_size = row_bytes * height; + result->data_size_ = + (data_size + kSurfaceDataAlignment - 1) / kSurfaceDataAlignment * kSurfaceDataAlignment; + result->data_.reset( + static_cast<uint8_t*>(aligned_alloc(kSurfaceDataAlignment, result->data_size_))); + if (!result->data_) return nullptr; + return result; +} + +std::unique_ptr<GRSurface> GRSurface::Clone() const { + auto result = GRSurface::Create(width, height, row_bytes, pixel_bytes); + if (!result) return nullptr; + memcpy(result->data(), data(), data_size_); + return result; } PngHandler::PngHandler(const std::string& name) { @@ -63,7 +75,7 @@ PngHandler::PngHandler(const std::string& name) { return; } - unsigned char header[8]; + uint8_t header[8]; size_t bytesRead = fread(header, 1, sizeof(header), png_fp_.get()); if (bytesRead != sizeof(header)) { error_code_ = -2; @@ -126,70 +138,49 @@ PngHandler::~PngHandler() { } } -// "display" surfaces are transformed into the framebuffer's required -// pixel format (currently only RGBX is supported) at load time, so -// gr_blit() can be nothing more than a memcpy() for each row. The -// next two functions are the only ones that know anything about the -// framebuffer pixel format; they need to be modified if the -// framebuffer format changes (but nothing else should). - -// Allocate and return a GRSurface* sufficient for storing an image of -// the indicated size in the framebuffer pixel format. -static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) { - GRSurface* surface = malloc_surface(width * height * 4); - if (surface == NULL) return NULL; - - surface->width = width; - surface->height = height; - surface->row_bytes = width * 4; - surface->pixel_bytes = 4; - - return surface; -} +// "display" surfaces are transformed into the framebuffer's required pixel format (currently only +// RGBX is supported) at load time, so gr_blit() can be nothing more than a memcpy() for each row. -// Copy 'input_row' to 'output_row', transforming it to the -// framebuffer pixel format. The input format depends on the value of -// 'channels': +// Copies 'input_row' to 'output_row', transforming it to the framebuffer pixel format. The input +// format depends on the value of 'channels': // // 1 - input is 8-bit grayscale // 3 - input is 24-bit RGB // 4 - input is 32-bit RGBA/RGBX // // 'width' is the number of pixels in the row. -static void transform_rgb_to_draw(unsigned char* input_row, - unsigned char* output_row, - int channels, int width) { - int x; - unsigned char* ip = input_row; - unsigned char* op = output_row; - - switch (channels) { - case 1: - // expand gray level to RGBX - for (x = 0; x < width; ++x) { - *op++ = *ip; - *op++ = *ip; - *op++ = *ip; - *op++ = 0xff; - ip++; - } - break; - - case 3: - // expand RGBA to RGBX - for (x = 0; x < width; ++x) { - *op++ = *ip++; - *op++ = *ip++; - *op++ = *ip++; - *op++ = 0xff; - } - break; - - case 4: - // copy RGBA to RGBX - memcpy(output_row, input_row, width*4); - break; - } +static void TransformRgbToDraw(const uint8_t* input_row, uint8_t* output_row, int channels, + int width) { + const uint8_t* ip = input_row; + uint8_t* op = output_row; + + switch (channels) { + case 1: + // expand gray level to RGBX + for (int x = 0; x < width; ++x) { + *op++ = *ip; + *op++ = *ip; + *op++ = *ip; + *op++ = 0xff; + ip++; + } + break; + + case 3: + // expand RGBA to RGBX + for (int x = 0; x < width; ++x) { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + *op++ = 0xff; + } + break; + + case 4: + // copy RGBA to RGBX + memcpy(output_row, input_row, width * 4); + break; + } } int res_create_display_surface(const char* name, GRSurface** pSurface) { @@ -202,7 +193,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { png_uint_32 width = png_handler.width(); png_uint_32 height = png_handler.height(); - GRSurface* surface = init_display_surface(width, height); + auto surface = GRSurface::Create(width, height, width * 4, 4); if (!surface) { return -8; } @@ -213,13 +204,13 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) { } for (png_uint_32 y = 0; y < height; ++y) { - std::vector<unsigned char> p_row(width * 4); + std::vector<uint8_t> p_row(width * 4); png_read_row(png_ptr, p_row.data(), nullptr); - transform_rgb_to_draw(p_row.data(), surface->data + y * surface->row_bytes, - png_handler.channels(), width); + TransformRgbToDraw(p_row.data(), surface->data() + y * surface->row_bytes, + png_handler.channels(), width); } - *pSurface = surface; + *pSurface = surface.release(); return 0; } @@ -272,11 +263,12 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps, goto exit; } for (int i = 0; i < *frames; ++i) { - surface[i] = init_display_surface(width, height / *frames); - if (!surface[i]) { + auto created_surface = GRSurface::Create(width, height / *frames, width * 4, 4); + if (!created_surface) { result = -8; goto exit; } + surface[i] = created_surface.release(); } if (gr_pixel_format() == PixelFormat::ABGR || gr_pixel_format() == PixelFormat::BGRA) { @@ -284,11 +276,11 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps, } for (png_uint_32 y = 0; y < height; ++y) { - std::vector<unsigned char> p_row(width * 4); + std::vector<uint8_t> p_row(width * 4); png_read_row(png_ptr, p_row.data(), nullptr); int frame = y % *frames; - unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes; - transform_rgb_to_draw(p_row.data(), out_row, png_handler.channels(), width); + uint8_t* out_row = surface[frame]->data() + (y / *frames) * surface[frame]->row_bytes; + TransformRgbToDraw(p_row.data(), out_row, png_handler.channels(), width); } *pSurface = surface; @@ -319,14 +311,10 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { png_uint_32 width = png_handler.width(); png_uint_32 height = png_handler.height(); - GRSurface* surface = malloc_surface(width * height); + auto surface = GRSurface::Create(width, height, width, 1); if (!surface) { return -8; } - surface->width = width; - surface->height = height; - surface->row_bytes = width; - surface->pixel_bytes = 1; PixelFormat pixel_format = gr_pixel_format(); if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) { @@ -334,11 +322,11 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { } for (png_uint_32 y = 0; y < height; ++y) { - unsigned char* p_row = surface->data + y * surface->row_bytes; + uint8_t* p_row = surface->data() + y * surface->row_bytes; png_read_row(png_ptr, p_row, nullptr); } - *pSurface = surface; + *pSurface = surface.release(); return 0; } @@ -383,7 +371,7 @@ std::vector<std::string> get_locales_in_png(const std::string& png_name) { } std::vector<std::string> result; - std::vector<unsigned char> row(png_handler.width()); + std::vector<uint8_t> row(png_handler.width()); for (png_uint_32 y = 0; y < png_handler.height(); ++y) { png_read_row(png_handler.png_ptr(), row.data(), nullptr); int h = (row[3] << 8) | row[2]; @@ -419,7 +407,7 @@ int res_create_localized_alpha_surface(const char* name, png_uint_32 height = png_handler.height(); for (png_uint_32 y = 0; y < height; ++y) { - std::vector<unsigned char> row(width); + std::vector<uint8_t> row(width); png_read_row(png_ptr, row.data(), nullptr); int w = (row[1] << 8) | row[0]; int h = (row[3] << 8) | row[2]; @@ -429,21 +417,17 @@ int res_create_localized_alpha_surface(const char* name, if (y + 1 + h >= height || matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); - GRSurface* surface = malloc_surface(w * h); + auto surface = GRSurface::Create(w, h, w, 1); if (!surface) { return -8; } - surface->width = w; - surface->height = h; - surface->row_bytes = w; - surface->pixel_bytes = 1; for (int i = 0; i < h; ++i, ++y) { png_read_row(png_ptr, row.data(), nullptr); - memcpy(surface->data + i * w, row.data(), w); + memcpy(surface->data() + i * w, row.data(), w); } - *pSurface = surface; + *pSurface = surface.release(); break; } |