diff options
Diffstat (limited to 'minuitwrp')
-rw-r--r-- | minuitwrp/Android.mk | 186 | ||||
-rw-r--r-- | minuitwrp/events.cpp | 794 | ||||
-rw-r--r-- | minuitwrp/graphics.cpp | 422 | ||||
-rw-r--r-- | minuitwrp/graphics.h | 44 | ||||
-rw-r--r-- | minuitwrp/graphics_adf.cpp | 285 | ||||
-rw-r--r-- | minuitwrp/graphics_drm.cpp | 492 | ||||
-rw-r--r-- | minuitwrp/graphics_fbdev.cpp | 348 | ||||
-rw-r--r-- | minuitwrp/graphics_overlay.cpp | 673 | ||||
-rw-r--r-- | minuitwrp/graphics_utils.cpp | 118 | ||||
-rw-r--r-- | minuitwrp/include/linux/msm_ion.h | 157 | ||||
-rw-r--r-- | minuitwrp/include/linux/msm_mdp.h | 992 | ||||
-rw-r--r-- | minuitwrp/minui.h | 94 | ||||
-rw-r--r-- | minuitwrp/resources.cpp | 455 | ||||
-rw-r--r-- | minuitwrp/truetype.cpp | 817 |
14 files changed, 5877 insertions, 0 deletions
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk new file mode 100644 index 000000000..51246660b --- /dev/null +++ b/minuitwrp/Android.mk @@ -0,0 +1,186 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + graphics.cpp \ + graphics_fbdev.cpp \ + resources.cpp \ + graphics_overlay.cpp \ + truetype.cpp \ + graphics_utils.cpp \ + events.cpp + +ifneq ($(TW_BOARD_CUSTOM_GRAPHICS),) + $(warning ****************************************************************************) + $(warning * TW_BOARD_CUSTOM_GRAPHICS support has been deprecated in TWRP. *) + $(warning ****************************************************************************) + $(error stopping) +endif + +ifeq ($(TW_TARGET_USES_QCOM_BSP), true) + LOCAL_CFLAGS += -DMSM_BSP + ifeq ($(TARGET_PREBUILT_KERNEL),) + LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + else + ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),) + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include + else + LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS) + endif + endif +else + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include + # The header files required for adf graphics can cause compile errors + # with adf graphics. + ifneq ($(wildcard system/core/adf/Android.mk),) + LOCAL_CFLAGS += -DHAS_ADF + LOCAL_SRC_FILES += graphics_adf.cpp + LOCAL_WHOLE_STATIC_LIBRARIES += libadf + endif +endif + +ifeq ($(TW_NEW_ION_HEAP), true) + LOCAL_CFLAGS += -DNEW_ION_HEAP +endif + +ifneq ($(wildcard external/libdrm/Android.mk),) + LOCAL_CFLAGS += -DHAS_DRM + LOCAL_SRC_FILES += graphics_drm.cpp + LOCAL_WHOLE_STATIC_LIBRARIES += libdrm +endif + +LOCAL_C_INCLUDES += \ + external/libpng \ + external/zlib \ + system/core/include \ + external/freetype/include \ + external/libcxx/include + +ifneq ($(TW_INCLUDE_JPEG),) + LOCAL_C_INCLUDES += \ + external/jpeg + LOCAL_CFLAGS += -DTW_INCLUDE_JPEG +endif + +ifeq ($(RECOVERY_TOUCHSCREEN_SWAP_XY), true) +LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_SWAP_XY +endif + +ifeq ($(RECOVERY_TOUCHSCREEN_FLIP_X), true) +LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_FLIP_X +endif + +ifeq ($(RECOVERY_TOUCHSCREEN_FLIP_Y), true) +LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_FLIP_Y +endif + +ifeq ($(RECOVERY_GRAPHICS_FORCE_USE_LINELENGTH), true) +LOCAL_CFLAGS += -DRECOVERY_GRAPHICS_FORCE_USE_LINELENGTH +endif + +ifeq ($(RECOVERY_GRAPHICS_FORCE_SINGLE_BUFFER), true) +LOCAL_CFLAGS += -DRECOVERY_GRAPHICS_FORCE_SINGLE_BUFFER +endif + +#Remove the # from the line below to enable event logging +#TWRP_EVENT_LOGGING := true +ifeq ($(TWRP_EVENT_LOGGING), true) +LOCAL_CFLAGS += -D_EVENT_LOGGING +endif + +ifeq ($(subst ",,$(TARGET_RECOVERY_FORCE_PIXEL_FORMAT)),RGBA_8888) + $(warning ****************************************************************************) + $(warning * TARGET_RECOVERY_FORCE_PIXEL_FORMAT := RGBA_8888 not implemented yet *) + $(warning ****************************************************************************) + $(error stopping) + LOCAL_CFLAGS += -DRECOVERY_RGBA +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_FORCE_PIXEL_FORMAT)),RGBX_8888) + $(warning ****************************************************************************) + $(warning * TARGET_RECOVERY_FORCE_PIXEL_FORMAT := RGBX_8888 not implemented yet *) + $(warning ****************************************************************************) + $(error stopping) + LOCAL_CFLAGS += -DRECOVERY_RGBX +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_FORCE_PIXEL_FORMAT)),BGRA_8888) + $(warning ****************************************************************************) + $(warning * TARGET_RECOVERY_FORCE_PIXEL_FORMAT := BGRA_8888 not implemented yet *) + $(warning ****************************************************************************) + $(error stopping) + LOCAL_CFLAGS += -DRECOVERY_BGRA +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_FORCE_PIXEL_FORMAT)),RGB_565) + LOCAL_CFLAGS += -DRECOVERY_FORCE_RGB_565 +endif + +# This used to compare against values in double-quotes (which are just +# ordinary characters in this context). Strip double-quotes from the +# value so that either will work. + +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),ABGR_8888) + LOCAL_CFLAGS += -DRECOVERY_ABGR +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) + LOCAL_CFLAGS += -DRECOVERY_RGBX +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888) + LOCAL_CFLAGS += -DRECOVERY_BGRA +endif + +ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +else + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 +endif +ifeq ($(TW_SCREEN_BLANK_ON_BOOT), true) + LOCAL_CFLAGS += -DTW_SCREEN_BLANK_ON_BOOT +endif +ifeq ($(TW_FBIOPAN), true) + LOCAL_CFLAGS += -DTW_FBIOPAN +endif + +ifeq ($(BOARD_HAS_FLIPPED_SCREEN), true) +LOCAL_CFLAGS += -DBOARD_HAS_FLIPPED_SCREEN +endif + +ifeq ($(TW_IGNORE_MAJOR_AXIS_0), true) +LOCAL_CFLAGS += -DTW_IGNORE_MAJOR_AXIS_0 +endif + +ifeq ($(TW_IGNORE_MT_POSITION_0), true) +LOCAL_CFLAGS += -DTW_IGNORE_MT_POSITION_0 +endif + +ifeq ($(TW_IGNORE_ABS_MT_TRACKING_ID), true) +LOCAL_CFLAGS += -DTW_IGNORE_ABS_MT_TRACKING_ID +endif + +ifneq ($(TW_INPUT_BLACKLIST),) + LOCAL_CFLAGS += -DTW_INPUT_BLACKLIST=$(TW_INPUT_BLACKLIST) +endif + +ifneq ($(TW_WHITELIST_INPUT),) + LOCAL_CFLAGS += -DWHITELIST_INPUT=$(TW_WHITELIST_INPUT) +endif + +ifeq ($(TW_DISABLE_TTF), true) + $(warning ****************************************************************************) + $(warning * TW_DISABLE_TTF support has been deprecated in TWRP. *) + $(warning ****************************************************************************) + $(error stopping) +endif + +LOCAL_CLANG := true + +LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\" +LOCAL_SHARED_LIBRARIES += libft2 libz libc libcutils libpng libutils +ifneq ($(TW_INCLUDE_JPEG),) + LOCAL_SHARED_LIBRARIES += libjpeg +endif +LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := libminuitwrp + +include $(BUILD_SHARED_LIBRARY) diff --git a/minuitwrp/events.cpp b/minuitwrp/events.cpp new file mode 100644 index 000000000..54119ebc0 --- /dev/null +++ b/minuitwrp/events.cpp @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2007 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 <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/poll.h> +#include <limits.h> +#include <linux/input.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + + +#include "../common.h" + +#include "minui.h" + +//#define _EVENT_LOGGING + +#define MAX_DEVICES 32 + +#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable" +#define VIBRATOR_TIME_MS 50 + +#ifndef SYN_REPORT +#define SYN_REPORT 0x00 +#endif +#ifndef SYN_CONFIG +#define SYN_CONFIG 0x01 +#endif +#ifndef SYN_MT_REPORT +#define SYN_MT_REPORT 0x02 +#endif + +#define ABS_MT_POSITION 0x2a /* Group a set of X and Y */ +#define ABS_MT_AMPLITUDE 0x2b /* Group a set of Z and W */ +#define ABS_MT_SLOT 0x2f +#define ABS_MT_TOUCH_MAJOR 0x30 +#define ABS_MT_TOUCH_MINOR 0x31 +#define ABS_MT_WIDTH_MAJOR 0x32 +#define ABS_MT_WIDTH_MINOR 0x33 +#define ABS_MT_ORIENTATION 0x34 +#define ABS_MT_POSITION_X 0x35 +#define ABS_MT_POSITION_Y 0x36 +#define ABS_MT_TOOL_TYPE 0x37 +#define ABS_MT_BLOB_ID 0x38 +#define ABS_MT_TRACKING_ID 0x39 +#define ABS_MT_PRESSURE 0x3a +#define ABS_MT_DISTANCE 0x3b + +enum { + DOWN_NOT, + DOWN_SENT, + DOWN_RELEASED, +}; + +struct virtualkey { + int scancode; + int centerx, centery; + int width, height; +}; + +struct position { + int x, y; + int synced; + struct input_absinfo xi, yi; +}; + +struct ev { + struct pollfd *fd; + + struct virtualkey *vks; + int vk_count; + + char deviceName[64]; + + int ignored; + + struct position p, mt_p; + int down; +}; + +static struct pollfd ev_fds[MAX_DEVICES]; +static struct ev evs[MAX_DEVICES]; +static unsigned ev_count = 0; +static struct timeval lastInputStat; +static unsigned long lastInputMTime; +static int has_mouse = 0; + +static inline int ABS(int x) { + return x<0?-x:x; +} + +int vibrate(int timeout_ms) +{ + char str[20]; + int fd; + int ret; + + if (timeout_ms > 10000) timeout_ms = 1000; + + fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY); + if (fd < 0) + return -1; + + ret = snprintf(str, sizeof(str), "%d", timeout_ms); + ret = write(fd, str, ret); + close(fd); + + if (ret < 0) + return -1; + + return 0; +} + +/* Returns empty tokens */ +static char *vk_strtok_r(char *str, const char *delim, char **save_str) +{ + if(!str) + { + if(!*save_str) + return NULL; + + str = (*save_str) + 1; + } + *save_str = strpbrk(str, delim); + + if (*save_str) + **save_str = '\0'; + + return str; +} + +static int vk_init(struct ev *e) +{ + char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys."; + char vks[2048], *ts = NULL; + ssize_t len; + int vk_fd; + int i; + + e->vk_count = 0; + + len = strlen(vk_path); + len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(e->deviceName)), e->deviceName); + if (len <= 0) + { + printf("Unable to query event object.\n"); + return -1; + } +#ifdef _EVENT_LOGGING + printf("Event object: %s\n", e->deviceName); +#endif + +#ifdef WHITELIST_INPUT + if (strcmp(e->deviceName, EXPAND(WHITELIST_INPUT)) != 0) + { + e->ignored = 1; + } +#else +#ifndef TW_INPUT_BLACKLIST + // Blacklist these "input" devices, use TW_INPUT_BLACKLIST := "accelerometer\x0atest1\x0atest2" using the \x0a as a separator between input devices + if (strcmp(e->deviceName, "bma250") == 0 || strcmp(e->deviceName, "bma150") == 0) + { + printf("blacklisting %s input device\n", e->deviceName); + e->ignored = 1; + } +#else + char* bl = strdup(EXPAND(TW_INPUT_BLACKLIST)); + char* blacklist = strtok(bl, "\n"); + + while (blacklist != NULL) { + if (strcmp(e->deviceName, blacklist) == 0) { + printf("blacklisting %s input device\n", blacklist); + e->ignored = 1; + } + blacklist = strtok(NULL, "\n"); + } + free(bl); +#endif +#endif + + strcat(vk_path, e->deviceName); + + // Some devices split the keys from the touchscreen + e->vk_count = 0; + vk_fd = open(vk_path, O_RDONLY); + if (vk_fd >= 0) + { + len = read(vk_fd, vks, sizeof(vks)-1); + close(vk_fd); + if (len <= 0) + return -1; + + vks[len] = '\0'; + + /* Parse a line like: + keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:... + */ + for (ts = vks, e->vk_count = 1; *ts; ++ts) { + if (*ts == ':') + ++e->vk_count; + } + + if (e->vk_count % 6) { + printf("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6); + } + e->vk_count /= 6; + if (e->vk_count <= 0) + return -1; + + e->down = DOWN_NOT; + } + + ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi); + e->p.synced = 0; +#ifdef _EVENT_LOGGING + printf("EV: ST minX: %d maxX: %d minY: %d maxY: %d\n", e->p.xi.minimum, e->p.xi.maximum, e->p.yi.minimum, e->p.yi.maximum); +#endif + + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi); + e->mt_p.synced = 0; +#ifdef _EVENT_LOGGING + printf("EV: MT minX: %d maxX: %d minY: %d maxY: %d\n", e->mt_p.xi.minimum, e->mt_p.xi.maximum, e->mt_p.yi.minimum, e->mt_p.yi.maximum); +#endif + + e->vks = (virtualkey *)malloc(sizeof(*e->vks) * e->vk_count); + + for (i = 0; i < e->vk_count; ++i) { + char *token[6]; + int j; + + for (j = 0; j < 6; ++j) { + token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts); + } + + if (strcmp(token[0], "0x01") != 0) { + /* Java does string compare, so we do too. */ + printf("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]); + continue; + } + + e->vks[i].scancode = strtol(token[1], NULL, 0); + e->vks[i].centerx = strtol(token[2], NULL, 0); + e->vks[i].centery = strtol(token[3], NULL, 0); + e->vks[i].width = strtol(token[4], NULL, 0); + e->vks[i].height = strtol(token[5], NULL, 0); + } + + return 0; +} + +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + +// Check for EV_REL (REL_X and REL_Y) and, because touchscreens can have those too, +// check also for EV_KEY (BTN_LEFT and BTN_RIGHT) +static void check_mouse(int fd, const char* deviceName) +{ + if(has_mouse) + return; + + unsigned long bit[EV_MAX][NBITS(KEY_MAX)]; + memset(bit, 0, sizeof(bit)); + ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]); + + if(!test_bit(EV_REL, bit[0]) || !test_bit(EV_KEY, bit[0])) + return; + + ioctl(fd, EVIOCGBIT(EV_REL, KEY_MAX), bit[EV_REL]); + if(!test_bit(REL_X, bit[EV_REL]) || !test_bit(REL_Y, bit[EV_REL])) + return; + + ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), bit[EV_KEY]); + if(!test_bit(BTN_LEFT, bit[EV_KEY]) || !test_bit(BTN_RIGHT, bit[EV_KEY])) + return; + + printf("Found mouse '%s'\n", deviceName); + has_mouse = 1; +} + +int ev_has_mouse(void) +{ + return has_mouse; +} + +int ev_init(void) +{ + DIR *dir; + struct dirent *de; + int fd; + + has_mouse = 0; + + dir = opendir("/dev/input"); + if(dir != 0) { + while((de = readdir(dir))) { +// fprintf(stderr,"/dev/input/%s\n", de->d_name); + if(strncmp(de->d_name,"event",5)) continue; + fd = openat(dirfd(dir), de->d_name, O_RDONLY); + if(fd < 0) continue; + + ev_fds[ev_count].fd = fd; + ev_fds[ev_count].events = POLLIN; + evs[ev_count].fd = &ev_fds[ev_count]; + + /* Load virtualkeys if there are any */ + vk_init(&evs[ev_count]); + + if (!evs[ev_count].ignored) + check_mouse(fd, evs[ev_count].deviceName); + + ev_count++; + if(ev_count == MAX_DEVICES) break; + } + closedir(dir); + } + + struct stat st; + if(stat("/dev/input", &st) >= 0) + lastInputMTime = st.st_mtime; + gettimeofday(&lastInputStat, NULL); + + return 0; +} + +void ev_exit(void) +{ + while (ev_count-- > 0) { + if (evs[ev_count].vk_count) { + free(evs[ev_count].vks); + evs[ev_count].vk_count = 0; + } + close(ev_fds[ev_count].fd); + } + ev_count = 0; +} + +static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size) +{ + int screen_pos; + + if (info->minimum == info->maximum) + return 0; + + screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum); + return (screen_pos >= 0 && screen_pos < screen_size); +} + +static int vk_tp_to_screen(struct position *p, int *x, int *y) +{ + if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum) + { + // In this case, we assume the screen dimensions are the same. + *x = p->x; + *y = p->y; + return 0; + } + +#ifdef _EVENT_LOGGING + printf("EV: p->x=%d x-range=%d,%d fb-width=%d\n", p->x, p->xi.minimum, p->xi.maximum, gr_fb_width()); +#endif + +#ifndef RECOVERY_TOUCHSCREEN_SWAP_XY + int fb_width = gr_fb_width(); + int fb_height = gr_fb_height(); +#else + // We need to swap the scaling sizes, too + int fb_width = gr_fb_height(); + int fb_height = gr_fb_width(); +#endif + + *x = (p->x - p->xi.minimum) * (fb_width - 1) / (p->xi.maximum - p->xi.minimum); + *y = (p->y - p->yi.minimum) * (fb_height - 1) / (p->yi.maximum - p->yi.minimum); + + if (*x >= 0 && *x < fb_width && + *y >= 0 && *y < fb_height) + { + return 0; + } + + return 1; +} + +/* Translate a virtual key in to a real key event, if needed */ +/* Returns non-zero when the event should be consumed */ +static int vk_modify(struct ev *e, struct input_event *ev) +{ + static int downX = -1, downY = -1; + static int discard = 0; + static int last_virt_key = 0; + static int lastWasSynReport = 0; + static int touchReleaseOnNextSynReport = 0; + static int use_tracking_id_negative_as_touch_release = 0; // On some devices, type: 3 code: 39 value: -1, aka EV_ABS ABS_MT_TRACKING_ID -1 indicates a true touch release + int i; + int x, y; + + // This is used to ditch useless event handlers, like an accelerometer + if (e->ignored) return 1; + + if (ev->type == EV_REL && ev->code == REL_Z) + { + // This appears to be an accelerometer or another strange input device. It's not the touchscreen. +#ifdef _EVENT_LOGGING + printf("EV: Device disabled due to non-touchscreen messages.\n"); +#endif + e->ignored = 1; + return 1; + } + +#ifdef _EVENT_LOGGING + printf("EV: %s => type: %x code: %x value: %d\n", e->deviceName, ev->type, ev->code, ev->value); +#endif + + // Handle keyboard events, value of 1 indicates key down, 0 indicates key up + if (ev->type == EV_KEY) { + return 0; + } + + if (ev->type == EV_ABS) { + switch (ev->code) { + + case ABS_X: //00 + e->p.synced |= 0x01; + e->p.x = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_X %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_Y: //01 + e->p.synced |= 0x02; + e->p.y = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_Y %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION: //2a + e->mt_p.synced = 0x03; + if (ev->value == (1 << 31)) + { +#ifndef TW_IGNORE_MT_POSITION_0 + e->mt_p.x = 0; + e->mt_p.y = 0; + lastWasSynReport = 1; +#endif +#ifdef _EVENT_LOGGING +#ifndef TW_IGNORE_MT_POSITION_0 + printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x and y to 0 and lastWasSynReport to 1\n", e->deviceName, ev->value); +#else + printf("Ignoring ABS_MT_POSITION 0\n", e->deviceName, ev->value); +#endif +#endif + } + else + { + lastWasSynReport = 0; + e->mt_p.x = (ev->value & 0x7FFF0000) >> 16; + e->mt_p.y = (ev->value & 0xFFFF); +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x: %d and y: %d and lastWasSynReport to 0\n", e->deviceName, ev->value, (ev->value & 0x7FFF0000) >> 16, (ev->value & 0xFFFF)); +#endif + } + break; + + case ABS_MT_TOUCH_MAJOR: //30 + if (ev->value == 0) + { +#ifndef TW_IGNORE_MAJOR_AXIS_0 + // We're in a touch release, although some devices will still send positions as well + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 1; +#endif + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TOUCH_MAJOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_PRESSURE: //3a + if (ev->value == 0) + { + // We're in a touch release, although some devices will still send positions as well + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 1; + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_PRESSURE %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION_X: //35 + e->mt_p.synced |= 0x01; + e->mt_p.x = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION_X %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION_Y: //36 + e->mt_p.synced |= 0x02; + e->mt_p.y = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION_Y %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_TOUCH_MINOR: //31 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TOUCH_MINOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_WIDTH_MAJOR: //32 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_WIDTH_MAJOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_WIDTH_MINOR: //33 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_WIDTH_MINOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_TRACKING_ID: //39 +#ifdef TW_IGNORE_ABS_MT_TRACKING_ID +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d ignored\n", e->deviceName, ev->value); +#endif + return 1; +#endif + if (ev->value < 0) { + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 2; + use_tracking_id_negative_as_touch_release = 1; +#ifdef _EVENT_LOGGING + if (use_tracking_id_negative_as_touch_release) + printf("using ABS_MT_TRACKING_ID value -1 to indicate touch releases\n"); +#endif + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d\n", e->deviceName, ev->value); +#endif + break; + +#ifdef _EVENT_LOGGING + // These are for touch logging purposes only + case ABS_MT_ORIENTATION: //34 + printf("EV: %s => EV_ABS ABS_MT_ORIENTATION %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_TOOL_TYPE: //37 + LOGI("EV: %s => EV_ABS ABS_MT_TOOL_TYPE %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_BLOB_ID: //38 + printf("EV: %s => EV_ABS ABS_MT_BLOB_ID %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_DISTANCE: //3b + printf("EV: %s => EV_ABS ABS_MT_DISTANCE %d\n", e->deviceName, ev->value); + return 1; + break; + case ABS_MT_SLOT: + printf("EV: %s => ABS_MT_SLOT %d\n", e->deviceName, ev->value); + return 1; + break; +#endif + + default: + // This is an unhandled message, just skip it + return 1; + } + + if (ev->code != ABS_MT_POSITION) + { + lastWasSynReport = 0; + return 1; + } + } + + // Check if we should ignore the message + if (ev->code != ABS_MT_POSITION && (ev->type != EV_SYN || (ev->code != SYN_REPORT && ev->code != SYN_MT_REPORT))) + { + lastWasSynReport = 0; + return 0; + } + +#ifdef _EVENT_LOGGING + if (ev->type == EV_SYN && ev->code == SYN_REPORT) printf("EV: %s => EV_SYN SYN_REPORT\n", e->deviceName); + if (ev->type == EV_SYN && ev->code == SYN_MT_REPORT) printf("EV: %s => EV_SYN SYN_MT_REPORT\n", e->deviceName); +#endif + + // Discard the MT versions + if (ev->code == SYN_MT_REPORT) return 0; + + if (((lastWasSynReport == 1 || touchReleaseOnNextSynReport == 1) && !use_tracking_id_negative_as_touch_release) || (use_tracking_id_negative_as_touch_release && touchReleaseOnNextSynReport == 2)) + { + // Reset the value + touchReleaseOnNextSynReport = 0; + + // We are a finger-up state + if (!discard) + { + // Report the key up + ev->type = EV_ABS; + ev->code = 0; + ev->value = (downX << 16) | downY; + } + downX = -1; + downY = -1; + if (discard) + { + discard = 0; + + // Send the keyUp event + ev->type = EV_KEY; + ev->code = last_virt_key; + ev->value = 0; + } + return 0; + } + lastWasSynReport = 1; + + // Retrieve where the x,y position is + if (e->p.synced & 0x03) + { + vk_tp_to_screen(&e->p, &x, &y); + } + else if (e->mt_p.synced & 0x03) + { + vk_tp_to_screen(&e->mt_p, &x, &y); + } + else + { + // We don't have useful information to convey + return 1; + } + +#ifdef RECOVERY_TOUCHSCREEN_SWAP_XY + x ^= y; + y ^= x; + x ^= y; +#endif +#ifdef RECOVERY_TOUCHSCREEN_FLIP_X + x = gr_fb_width() - x; +#endif +#ifdef RECOVERY_TOUCHSCREEN_FLIP_Y + y = gr_fb_height() - y; +#endif + +#ifdef _EVENT_LOGGING + printf("EV: x: %d y: %d\n", x, y); +#endif + + // Clear the current sync states + e->p.synced = e->mt_p.synced = 0; + + // If we have nothing useful to report, skip it + if (x == -1 || y == -1) return 1; + + // Special case, we'll ignore touches on 0,0 because it usually means + // that we received extra data after our last sync and x and y were + // reset to 0. We should not be using 0,0 anyway. + if (x == 0 && y == 0) + return 1; + + // On first touch, see if we're at a virtual key + if (downX == -1) + { + // Attempt mapping to virtual key + for (i = 0; i < e->vk_count; ++i) + { + int xd = ABS(e->vks[i].centerx - x); + int yd = ABS(e->vks[i].centery - y); + + if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) + { + ev->type = EV_KEY; + ev->code = e->vks[i].scancode; + ev->value = 1; + + last_virt_key = e->vks[i].scancode; + + vibrate(VIBRATOR_TIME_MS); + + // Mark that all further movement until lift is discard, + // and make sure we don't come back into this area + discard = 1; + downX = 0; + return 0; + } + } + } + + // If we were originally a button press, discard this event + if (discard) + { + return 1; + } + + // Record where we started the touch for deciding if this is a key or a scroll + downX = x; + downY = y; + + ev->type = EV_ABS; + ev->code = 1; + ev->value = (x << 16) | y; + return 0; +} + +int ev_get(struct input_event *ev, int timeout_ms) +{ + int r; + unsigned n; + struct timeval curr; + + gettimeofday(&curr, NULL); + if(curr.tv_sec - lastInputStat.tv_sec >= 2) + { + struct stat st; + stat("/dev/input", &st); + if (st.st_mtime > lastInputMTime) + { + printf("Reloading input devices\n"); + ev_exit(); + ev_init(); + lastInputMTime = st.st_mtime; + } + lastInputStat = curr; + } + + r = poll(ev_fds, ev_count, timeout_ms); + + if(r > 0) { + for(n = 0; n < ev_count; n++) { + if(ev_fds[n].revents & POLLIN) { + r = read(ev_fds[n].fd, ev, sizeof(*ev)); + if(r == sizeof(*ev)) { + if (!vk_modify(&evs[n], ev)) + return 0; + } + } + } + return -1; + } + + return -2; +} + +int ev_wait(int timeout) +{ + return -1; +} + +void ev_dispatch(void) +{ + return; +} + +int ev_get_input(int fd, short revents, struct input_event *ev) +{ + return -1; +} diff --git a/minuitwrp/graphics.cpp b/minuitwrp/graphics.cpp new file mode 100644 index 000000000..c8ea5cb73 --- /dev/null +++ b/minuitwrp/graphics.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2007 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 <string.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 <time.h> + +#include <pixelflinger/pixelflinger.h> +#include "../gui/placement.h" +#include "minui.h" +#include "graphics.h" + +struct GRFont { + GRSurface* texture; + int cwidth; + int cheight; +}; + +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 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 unsigned char rgb_555[2]; +static unsigned char gr_current_r5 = 31; +static unsigned char gr_current_g5 = 63; +static unsigned char gr_current_b5 = 31; + +GRSurface* gr_draw = NULL; + +static GGLContext *gr_context = 0; +GGLSurface gr_mem_surface; +static int gr_is_curr_clr_opaque = 0; + +static bool outside(int x, int y) +{ + return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +} + +int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale) +{ + GGLContext *gl = gr_context; + void* vfont = pFont; + GRFont *font = (GRFont*) pFont; + unsigned off; + unsigned cwidth; + int y_scale = 0, measured_width, measured_height, ret, new_height; + + if (!s || strlen(s) == 0 || !font) + return 0; + + measured_height = gr_ttf_getMaxFontHeight(font); + + if (scale) { + measured_width = gr_ttf_measureEx(s, vfont); + if (measured_width > max_width) { + // Adjust font size down until the text fits + void *new_font = gr_ttf_scaleFont(vfont, max_width, measured_width); + if (!new_font) { + printf("gr_textEx_scaleW new_font is NULL\n"); + return 0; + } + measured_width = gr_ttf_measureEx(s, new_font); + // These next 2 lines adjust the y point based on the new font's height + new_height = gr_ttf_getMaxFontHeight(new_font); + y_scale = (measured_height - new_height) / 2; + vfont = new_font; + } + } else + measured_width = gr_ttf_measureEx(s, vfont); + + int x_adj = measured_width; + if (measured_width > max_width) + x_adj = max_width; + + if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) { + if (placement == CENTER || placement == CENTER_X_ONLY) + x -= (x_adj / 2); + else + x -= x_adj; + } + + if (placement != TOP_LEFT && placement != TOP_RIGHT) { + if (placement == CENTER || placement == TEXT_ONLY_RIGHT) + y -= (measured_height / 2); + else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT) + y -= measured_height; + } + return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1); +} + +void gr_clip(int x, int y, int w, int h) +{ + GGLContext *gl = gr_context; + gl->scissor(gl, x, y, w, h); + gl->enable(gl, GGL_SCISSOR_TEST); +} + +void gr_noclip() +{ + GGLContext *gl = gr_context; + gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height()); + gl->disable(gl, GGL_SCISSOR_TEST); +} + +void gr_line(int x0, int y0, int x1, int y1, int width) +{ + GGLContext *gl = gr_context; + + if(gr_is_curr_clr_opaque) + gl->disable(gl, GGL_BLEND); + + const int coords0[2] = { x0 << 4, y0 << 4 }; + const int coords1[2] = { x1 << 4, y1 << 4 }; + gl->linex(gl, coords0, coords1, width << 4); + + if(gr_is_curr_clr_opaque) + gl->enable(gl, GGL_BLEND); +} + +gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int rx, ry; + GGLSurface *surface; + const int diameter = radius*2 + 1; + const int radius_check = radius*radius + radius*0.8; + const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r; + uint32_t *data; + + surface = (GGLSurface *)malloc(sizeof(GGLSurface)); + memset(surface, 0, sizeof(GGLSurface)); + + data = (uint32_t *)malloc(diameter * diameter * 4); + memset(data, 0, diameter * diameter * 4); + + surface->version = sizeof(surface); + surface->width = diameter; + surface->height = diameter; + surface->stride = diameter; + surface->data = (GGLubyte*)data; + surface->format = GGL_PIXEL_FORMAT_RGBA_8888; + + for(ry = -radius; ry <= radius; ++ry) + for(rx = -radius; rx <= radius; ++rx) + if(rx*rx+ry*ry <= radius_check) + *(data + diameter*(radius + ry) + (radius+rx)) = px; + + return (gr_surface)surface; +} + +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + GGLContext *gl = gr_context; + GGLint color[4]; +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + color[0] = ((b << 8) | r) + 1; + color[1] = ((g << 8) | g) + 1; + color[2] = ((r << 8) | b) + 1; + color[3] = ((a << 8) | a) + 1; +#else + color[0] = ((r << 8) | r) + 1; + color[1] = ((g << 8) | g) + 1; + color[2] = ((b << 8) | b) + 1; + color[3] = ((a << 8) | a) + 1; +#endif + gl->color4xv(gl, color); + + gr_is_curr_clr_opaque = (a == 255); +} + +void gr_clear() +{ + if (gr_draw->pixel_bytes == 2) { + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + return; + } + + // This code only works on 32bpp devices + if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) { + memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); + } else { + unsigned char* px = gr_draw->data; + for (int y = 0; y < gr_draw->height; ++y) { + for (int x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); + } + } +} + +void gr_fill(int x, int y, int w, int h) +{ + GGLContext *gl = gr_context; + + if(gr_is_curr_clr_opaque) + gl->disable(gl, GGL_BLEND); + + gl->recti(gl, x, y, x + w, y + h); + + if(gr_is_curr_clr_opaque) + gl->enable(gl, GGL_BLEND); +} + +void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { + if (gr_context == NULL) { + return; + } + + GGLContext *gl = gr_context; + GGLSurface *surface = (GGLSurface*)source; + + if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888) + gl->disable(gl, GGL_BLEND); + + gl->bindTexture(gl, surface); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + gl->texCoord2i(gl, sx - dx, sy - dy); + gl->recti(gl, dx, dy, dx + w, dy + h); + gl->disable(gl, GGL_TEXTURE_2D); + + if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888) + gl->enable(gl, GGL_BLEND); +} + +unsigned int gr_get_width(gr_surface surface) { + if (surface == NULL) { + return 0; + } + return ((GGLSurface*) surface)->width; +} + +unsigned int gr_get_height(gr_surface surface) { + if (surface == NULL) { + return 0; + } + return ((GGLSurface*) surface)->height; +} + +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); + // On double buffered back ends, when we flip, we need to tell + // pixel flinger to draw to the other buffer + gr_mem_surface.data = (GGLubyte*)gr_draw->data; + gr_context->colorBuffer(gr_context, &gr_mem_surface); +} + +static void get_memory_surface(GGLSurface* ms) { + ms->version = sizeof(*ms); + ms->width = gr_draw->width; + ms->height = gr_draw->height; + ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes; + ms->data = (GGLubyte*)gr_draw->data; + ms->format = gr_draw->format; +} + +int gr_init(void) +{ + gr_draw = NULL; + + gr_backend = open_overlay(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } else + printf("Using overlay graphics.\n"); + } + +#ifdef HAS_ADF + if (!gr_draw) { + gr_backend = open_adf(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } else + printf("Using adf graphics.\n"); + } + } +#else +#ifdef MSM_BSP + printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n"); +#else + printf("Skipping adf graphics -- not present in build tree\n"); +#endif +#endif + +#ifdef HAS_DRM + if (!gr_draw) { + gr_backend = open_drm(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw) + printf("Using drm graphics.\n"); + } +#else + printf("Skipping drm graphics -- not present in build tree\n"); +#endif + + if (!gr_draw) { + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { + return -1; + } else + printf("Using fbdev graphics.\n"); + } + + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; + + // Set up pixelflinger + get_memory_surface(&gr_mem_surface); + gglInit(&gr_context); + GGLContext *gl = gr_context; + gl->colorBuffer(gl, &gr_mem_surface); + + gl->activeTexture(gl, 0); + gl->enable(gl, GGL_BLEND); + gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); + + gr_flip(); + gr_flip(); + +#ifdef TW_SCREEN_BLANK_ON_BOOT + printf("TW_SCREEN_BLANK_ON_BOOT := true\n"); + gr_fb_blank(true); + gr_fb_blank(false); +#endif + + return 0; +} + +void gr_exit(void) +{ + gr_backend->exit(gr_backend); +} + +int gr_fb_width(void) +{ + return gr_draw->width - 2*overscan_offset_x; +} + +int gr_fb_height(void) +{ + return gr_draw->height - 2*overscan_offset_y; +} + +void gr_fb_blank(bool blank) +{ + gr_backend->blank(gr_backend, blank); +} + +int gr_get_surface(gr_surface* surface) +{ + GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface)); + if (!ms) return -1; + + // Allocate the data + get_memory_surface(ms); + ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes); + + // Now, copy the data + memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8); + + *surface = (gr_surface*) ms; + return 0; +} + +int gr_free_surface(gr_surface surface) +{ + if (!surface) + return -1; + + GGLSurface* ms = (GGLSurface*) surface; + free(ms->data); + free(ms); + return 0; +} + +void gr_write_frame_to_file(int fd) +{ + write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8); +} diff --git a/minuitwrp/graphics.h b/minuitwrp/graphics.h new file mode 100644 index 000000000..a4115fd50 --- /dev/null +++ b/minuitwrp/graphics.h @@ -0,0 +1,44 @@ +/* + * 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_ + +#include "minui.h" + +// TODO: lose the function pointers. +struct minui_backend { + // Initializes the backend and returns a GRSurface* to draw into. + GRSurface* (*init)(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. + GRSurface* (*flip)(minui_backend*); + + // Blank (or unblank) the screen. + void (*blank)(minui_backend*, bool); + + // Device cleanup when drawing is done. + void (*exit)(minui_backend*); +}; + +minui_backend* open_fbdev(); +minui_backend* open_adf(); +minui_backend* open_drm(); +minui_backend* open_overlay(); + +#endif diff --git a/minuitwrp/graphics_adf.cpp b/minuitwrp/graphics_adf.cpp new file mode 100644 index 000000000..b71bed2a8 --- /dev/null +++ b/minuitwrp/graphics_adf.cpp @@ -0,0 +1,285 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/cdefs.h> +#include <sys/mman.h> + +#include <adf/adf.h> + +#include "graphics.h" +#include <pixelflinger/pixelflinger.h> + +struct adf_surface_pdata { + GRSurface base; + int fd; + __u32 offset; + __u32 pitch; + unsigned char* adf_data; +}; + +struct adf_pdata { + minui_backend base; + int intf_fd; + adf_id_t eng_id; + __u32 format; + + unsigned int current_surface; + unsigned int n_surfaces; + adf_surface_pdata surfaces[2]; +}; + +static GRSurface* adf_flip(minui_backend *backend); +static void adf_blank(minui_backend *backend, bool blank); + +static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) { + memset(surf, 0, sizeof(*surf)); + + surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay, + mode->vdisplay, pdata->format, &surf->offset, &surf->pitch); + if (surf->fd < 0) + return surf->fd; + + surf->base.width = mode->hdisplay; + surf->base.height = mode->vdisplay; + surf->base.row_bytes = surf->pitch; + surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4; + if (pdata->format == DRM_FORMAT_ABGR8888) + surf->base.format = GGL_PIXEL_FORMAT_BGRA_8888; + else if (pdata->format == DRM_FORMAT_BGRA8888) + surf->base.format = GGL_PIXEL_FORMAT_BGRA_8888; + else if (pdata->format == DRM_FORMAT_RGBA8888) + surf->base.format = GGL_PIXEL_FORMAT_RGBA_8888; + else if (pdata->format == DRM_FORMAT_RGBX8888) + surf->base.format = GGL_PIXEL_FORMAT_RGBX_8888; + else + surf->base.format = GGL_PIXEL_FORMAT_RGB_565; + + surf->adf_data = reinterpret_cast<uint8_t*>(mmap(NULL, + surf->pitch * surf->base.height, PROT_WRITE, + MAP_SHARED, surf->fd, surf->offset)); + if (surf->adf_data == MAP_FAILED) { + close(surf->fd); + return -errno; + } + + return 0; +} + +static int adf_interface_init(adf_pdata *pdata) +{ + adf_interface_data intf_data; + int ret = 0; + int err; + unsigned char* framebuffer_data = NULL; + + err = adf_get_interface_data(pdata->intf_fd, &intf_data); + if (err < 0) + return err; + + err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]); + if (err < 0) { + fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); + ret = err; + goto done; + } + + /* Allocate a single buffer for drawing. graphics.cpp will draw to + * this buffer which will later be copied to the mmapped adf buffer. + * Using a regular memory buffer improves performance by about 10x. */ + framebuffer_data = (unsigned char*)calloc(pdata->surfaces[0].pitch * pdata->surfaces[0].base.height, 1); + if (framebuffer_data == NULL) { + printf("failed to calloc surface data\n"); + close(pdata->surfaces[0].fd); + munmap(pdata->surfaces[0].adf_data, pdata->surfaces[0].pitch * pdata->surfaces[0].base.height); + ret = -1; + goto done; + } + pdata->surfaces[0].base.data = framebuffer_data; + + err = adf_surface_init(pdata, &intf_data.current_mode, + &pdata->surfaces[1]); + if (err < 0) { + fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); + memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1])); + pdata->n_surfaces = 1; + } else { + pdata->n_surfaces = 2; + pdata->surfaces[1].base.data = framebuffer_data; + } + +done: + adf_free_interface_data(&intf_data); + return ret; +} + +static int adf_device_init(adf_pdata *pdata, adf_device *dev) +{ + adf_id_t intf_id; + int intf_fd; + int err; + + err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, + &pdata->eng_id); + if (err < 0) + return err; + + err = adf_device_attach(dev, pdata->eng_id, intf_id); + if (err < 0 && err != -EALREADY) + return err; + + pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); + if (pdata->intf_fd < 0) + return pdata->intf_fd; + + err = adf_interface_init(pdata); + if (err < 0) { + close(pdata->intf_fd); + pdata->intf_fd = -1; + } + + return err; +} + +static GRSurface* adf_init(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_id_t *dev_ids = NULL; + ssize_t n_dev_ids, i; + GRSurface* ret; + +#if defined(RECOVERY_ABGR) + pdata->format = DRM_FORMAT_ABGR8888; + printf("setting DRM_FORMAT_ABGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n"); +#elif defined(RECOVERY_BGRA) + pdata->format = DRM_FORMAT_BGRA8888; + printf("setting DRM_FORMAT_BGRA8888 and GGL_PIXEL_FORMAT_BGRA_8888\n"); +#elif defined(RECOVERY_RGBA) + pdata->format = DRM_FORMAT_RGBA8888; + printf("setting DRM_FORMAT_RGBA8888 and GGL_PIXEL_FORMAT_RGBA_8888\n"); +#elif defined(RECOVERY_RGBX) + pdata->format = DRM_FORMAT_RGBX8888; + printf("setting DRM_FORMAT_RGBX8888 and GGL_PIXEL_FORMAT_RGBX_8888\n"); +#else + pdata->format = DRM_FORMAT_RGB565; + printf("setting DRM_FORMAT_RGB565 and GGL_PIXEL_FORMAT_RGB_565\n"); +#endif + + n_dev_ids = adf_devices(&dev_ids); + if (n_dev_ids == 0) { + return NULL; + } else if (n_dev_ids < 0) { + fprintf(stderr, "enumerating adf devices failed: %s\n", + strerror(-n_dev_ids)); + return NULL; + } + + pdata->intf_fd = -1; + + for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) { + adf_device dev; + + int err = adf_device_open(dev_ids[i], O_RDWR, &dev); + if (err < 0) { + fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], + strerror(-err)); + continue; + } + + err = adf_device_init(pdata, &dev); + if (err < 0) + fprintf(stderr, "initializing adf device %u failed: %s\n", + dev_ids[i], strerror(-err)); + + adf_device_close(&dev); + } + + free(dev_ids); + + if (pdata->intf_fd < 0) + return NULL; + + ret = adf_flip(backend); + + adf_blank(backend, true); + adf_blank(backend, false); + + return ret; +} + +static GRSurface* adf_flip(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface]; + + memcpy(surf->adf_data, surf->base.data, surf->pitch * surf->base.height); + int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id, + surf->base.width, surf->base.height, pdata->format, surf->fd, + surf->offset, surf->pitch, -1); + if (fence_fd >= 0) + close(fence_fd); + + pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces; + return &pdata->surfaces[pdata->current_surface].base; +} + +static void adf_blank(minui_backend *backend, bool blank) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_interface_blank(pdata->intf_fd, + blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); +} + +static void adf_surface_destroy(adf_surface_pdata *surf) +{ + munmap(surf->adf_data, surf->pitch * surf->base.height); + close(surf->fd); +} + +static void adf_exit(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + unsigned int i; + + free(pdata->surfaces[0].base.data); + for (i = 0; i < pdata->n_surfaces; i++) + adf_surface_destroy(&pdata->surfaces[i]); + if (pdata->intf_fd >= 0) + close(pdata->intf_fd); + free(pdata); +} + +minui_backend *open_adf() +{ + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata))); + if (!pdata) { + perror("allocating adf backend failed"); + return NULL; + } + + pdata->base.init = adf_init; + pdata->base.flip = adf_flip; + pdata->base.blank = adf_blank; + pdata->base.exit = adf_exit; + return &pdata->base; +} diff --git a/minuitwrp/graphics_drm.cpp b/minuitwrp/graphics_drm.cpp new file mode 100644 index 000000000..9bbf7e98f --- /dev/null +++ b/minuitwrp/graphics_drm.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2015 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 <drm_fourcc.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/cdefs.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "minui.h" +#include "graphics.h" +#include <pixelflinger/pixelflinger.h> + +#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A))) + +struct drm_surface { + GRSurface base; + uint32_t fb_id; + uint32_t handle; +}; + +static drm_surface *drm_surfaces[2]; +static int current_buffer; + +static drmModeCrtc *main_monitor_crtc; +static drmModeConnector *main_monitor_connector; + +static int drm_fd = -1; + +static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) { + if (crtc) { + drmModeSetCrtc(drm_fd, crtc->crtc_id, + 0, // fb_id + 0, 0, // x,y + NULL, // connectors + 0, // connector_count + NULL); // mode + } +} + +static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc, + struct drm_surface *surface) { + int32_t ret; + + 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); +} + +static void drm_blank(minui_backend* backend __unused, bool blank) { + if (blank) + drm_disable_crtc(drm_fd, main_monitor_crtc); + else + drm_enable_crtc(drm_fd, main_monitor_crtc, + drm_surfaces[current_buffer]); +} + +static void drm_destroy_surface(struct drm_surface *surface) { + struct drm_gem_close gem_close; + int ret; + + if(!surface) + return; + + if (surface->base.data) + munmap(surface->base.data, + surface->base.row_bytes * surface->base.height); + + if (surface->fb_id) { + ret = drmModeRmFB(drm_fd, surface->fb_id); + if (ret) + printf("drmModeRmFB failed ret=%d\n", ret); + } + + if (surface->handle) { + memset(&gem_close, 0, sizeof(gem_close)); + gem_close.handle = surface->handle; + + ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); + if (ret) + printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); + } + + free(surface); +} + +static int drm_format_to_bpp(uint32_t format) { + switch(format) { + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + return 32; + case DRM_FORMAT_RGB565: + return 16; + default: + printf("Unknown format %d\n", format); + return 32; + } +} + +static drm_surface *drm_create_surface(int width, int height) { + struct drm_surface *surface; + struct drm_mode_create_dumb create_dumb; + uint32_t format; + __u32 base_format; + int ret; + + surface = (struct drm_surface*)calloc(1, sizeof(*surface)); + if (!surface) { + printf("Can't allocate memory\n"); + return NULL; + } + +#if defined(RECOVERY_ABGR) + format = DRM_FORMAT_RGBA8888; + base_format = GGL_PIXEL_FORMAT_RGBA_8888; + printf("setting DRM_FORMAT_RGBA8888 and GGL_PIXEL_FORMAT_RGBA_8888\n"); +#elif defined(RECOVERY_BGRA) + format = DRM_FORMAT_ARGB8888; + base_format = GGL_PIXEL_FORMAT_BGRA_8888; + printf("setting DRM_FORMAT_ARGB8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n"); +#elif defined(RECOVERY_RGBA) + format = DRM_FORMAT_ABGR8888; + base_format = GGL_PIXEL_FORMAT_BGRA_8888; + printf("setting DRM_FORMAT_ABGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n"); +#elif defined(RECOVERY_RGBX) + format = DRM_FORMAT_XBGR8888; + base_format = GGL_PIXEL_FORMAT_BGRA_8888; + printf("setting DRM_FORMAT_XBGR8888 and GGL_PIXEL_FORMAT_BGRA_8888, GGL_PIXEL_FORMAT may not match!\n"); +#else + format = DRM_FORMAT_RGB565; + base_format = GGL_PIXEL_FORMAT_BGRA_8888; + printf("setting DRM_FORMAT_RGB565 and GGL_PIXEL_FORMAT_RGB_565\n"); +#endif + + memset(&create_dumb, 0, sizeof(create_dumb)); + create_dumb.height = height; + create_dumb.width = width; + create_dumb.bpp = drm_format_to_bpp(format); + create_dumb.flags = 0; + + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); + if (ret) { + printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret); + drm_destroy_surface(surface); + return NULL; + } + surface->handle = 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); + drm_destroy_surface(surface); + return NULL; + } + + struct drm_mode_map_dumb map_dumb; + memset(&map_dumb, 0, sizeof(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); + drm_destroy_surface(surface); + return NULL;; + } + + surface->base.height = height; + surface->base.width = width; + surface->base.row_bytes = create_dumb.pitch; + surface->base.pixel_bytes = create_dumb.bpp / 8; + surface->base.format = base_format; + surface->base.data = (unsigned char*) + mmap(NULL, + surface->base.height * surface->base.row_bytes, + PROT_READ | PROT_WRITE, MAP_SHARED, + drm_fd, map_dumb.offset); + if (surface->base.data == MAP_FAILED) { + perror("mmap() failed"); + drm_destroy_surface(surface); + return NULL; + } + + return surface; +} + +static drmModeCrtc *find_crtc_for_connector(int fd, + drmModeRes *resources, + drmModeConnector *connector) { + int i, j; + drmModeEncoder *encoder; + int32_t crtc; + + /* + * Find the encoder. If we already have one, just use it. + */ + if (connector->encoder_id) + encoder = drmModeGetEncoder(fd, connector->encoder_id); + else + encoder = NULL; + + if (encoder && encoder->crtc_id) { + crtc = encoder->crtc_id; + drmModeFreeEncoder(encoder); + return drmModeGetCrtc(fd, crtc); + } + + /* + * Didn't find anything, try to find a crtc and encoder combo. + */ + crtc = -1; + for (i = 0; i < connector->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, connector->encoders[i]); + + if (encoder) { + for (j = 0; j < resources->count_crtcs; j++) { + if (!(encoder->possible_crtcs & (1 << j))) + continue; + crtc = resources->crtcs[j]; + break; + } + if (crtc >= 0) { + drmModeFreeEncoder(encoder); + return drmModeGetCrtc(fd, crtc); + } + } + } + + return NULL; +} + +static drmModeConnector *find_used_connector_by_type(int fd, + drmModeRes *resources, + unsigned type) { + int i; + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector) { + if ((connector->connector_type == type) && + (connector->connection == DRM_MODE_CONNECTED) && + (connector->count_modes > 0)) + return connector; + + drmModeFreeConnector(connector); + } + } + return NULL; +} + +static drmModeConnector *find_first_connected_connector(int fd, + drmModeRes *resources) { + int i; + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector) { + if ((connector->count_modes > 0) && + (connector->connection == DRM_MODE_CONNECTED)) + return connector; + + drmModeFreeConnector(connector); + } + } + return NULL; +} + +static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources, + uint32_t *mode_index) { + unsigned i = 0; + int modes; + /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */ + unsigned kConnectorPriority[] = { + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_eDP, + DRM_MODE_CONNECTOR_DSI, + }; + + drmModeConnector *main_monitor_connector = NULL; + do { + main_monitor_connector = find_used_connector_by_type(fd, + resources, + kConnectorPriority[i]); + i++; + } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority)); + + /* If we didn't find a connector, grab the first one that is connected. */ + if (!main_monitor_connector) + main_monitor_connector = + find_first_connected_connector(fd, resources); + + /* If we still didn't find a connector, give up and return. */ + if (!main_monitor_connector) + return NULL; + + *mode_index = 0; + for (modes = 0; modes < main_monitor_connector->count_modes; modes++) { + if (main_monitor_connector->modes[modes].type & + DRM_MODE_TYPE_PREFERRED) { + *mode_index = modes; + break; + } + } + + return main_monitor_connector; +} + +static void disable_non_main_crtcs(int fd, + drmModeRes *resources, + drmModeCrtc* main_crtc) { + int i; + drmModeCrtc* crtc; + + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + crtc = find_crtc_for_connector(fd, resources, connector); + if (crtc->crtc_id != main_crtc->crtc_id) + drm_disable_crtc(fd, crtc); + drmModeFreeCrtc(crtc); + } +} + +static GRSurface* drm_init(minui_backend* backend __unused) { + drmModeRes *res = NULL; + uint32_t selected_mode; + char *dev_name; + int width, height; + int ret, i; + + /* Consider DRM devices in order. */ + for (i = 0; i < DRM_MAX_MINOR; i++) { + uint64_t cap = 0; + + ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret < 0) + continue; + + drm_fd = open(dev_name, O_RDWR, 0); + free(dev_name); + if (drm_fd < 0) + continue; + + /* We need dumb buffers. */ + ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap); + if (ret || cap == 0) { + close(drm_fd); + continue; + } + + res = drmModeGetResources(drm_fd); + 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; + + drmModeFreeResources(res); + close(drm_fd); + res = NULL; + } + + if (drm_fd < 0 || res == NULL) { + perror("cannot find/open a drm device"); + return NULL; + } + + main_monitor_connector = find_main_monitor(drm_fd, + res, &selected_mode); + + if (!main_monitor_connector) { + printf("main_monitor_connector not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + main_monitor_crtc = find_crtc_for_connector(drm_fd, res, + main_monitor_connector); + + if (!main_monitor_crtc) { + printf("main_monitor_crtc not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + disable_non_main_crtcs(drm_fd, + res, main_monitor_crtc); + + main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; + + width = main_monitor_crtc->mode.hdisplay; + height = main_monitor_crtc->mode.vdisplay; + + drmModeFreeResources(res); + + drm_surfaces[0] = drm_create_surface(width, height); + drm_surfaces[1] = drm_create_surface(width, height); + if (!drm_surfaces[0] || !drm_surfaces[1]) { + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + current_buffer = 0; + + drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); + + return &(drm_surfaces[0]->base); +} + +static GRSurface* drm_flip(minui_backend* backend __unused) { + int ret; + + ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, + drm_surfaces[current_buffer]->fb_id, 0, NULL); + if (ret < 0) { + printf("drmModePageFlip failed ret=%d\n", ret); + return NULL; + } + current_buffer = 1 - current_buffer; + return &(drm_surfaces[current_buffer]->base); +} + +static void drm_exit(minui_backend* backend __unused) { + drm_disable_crtc(drm_fd, main_monitor_crtc); + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeCrtc(main_monitor_crtc); + drmModeFreeConnector(main_monitor_connector); + close(drm_fd); + drm_fd = -1; +} + +static minui_backend drm_backend = { + .init = drm_init, + .flip = drm_flip, + .blank = drm_blank, + .exit = drm_exit, +}; + +minui_backend* open_drm() { + return &drm_backend; +} diff --git a/minuitwrp/graphics_fbdev.cpp b/minuitwrp/graphics_fbdev.cpp new file mode 100644 index 000000000..a1c42d05c --- /dev/null +++ b/minuitwrp/graphics_fbdev.cpp @@ -0,0 +1,348 @@ +/* + * 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 <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/cdefs.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" +#include <pixelflinger/pixelflinger.h> + +static GRSurface* fbdev_init(minui_backend*); +static GRSurface* 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 fb_var_screeninfo vi; +static int fb_fd = -1; +static __u32 smem_len; + +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 __unused, bool blank) +{ +#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS) + int fd; + char brightness[4]; + snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2); + + fd = open(TW_BRIGHTNESS_PATH, O_RDWR); + if (fd < 0) { + perror("cannot open LCD backlight"); + return; + } + write(fd, blank ? "000" : brightness, 3); + close(fd); + +#ifdef TW_SECONDARY_BRIGHTNESS_PATH + fd = open(TW_SECONDARY_BRIGHTNESS_PATH, O_RDWR); + if (fd < 0) { + perror("cannot open LCD backlight 2"); + return; + } + write(fd, blank ? "000" : brightness, 3); + close(fd); +#endif +#else +#ifndef TW_NO_SCREEN_BLANK + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +#endif +#endif +} + +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"); +#ifdef TW_FBIOPAN + } else { + if (ioctl(fb_fd, FBIOPAN_DISPLAY, &vi) < 0) { + perror("pan failed"); + } +#endif + } + displayed_buffer = n; +} + +static GRSurface* fbdev_init(minui_backend* backend) { + int retry = 20; + int fd = -1; + while (fd == -1) { + fd = open("/dev/graphics/fb0", O_RDWR); + if (fd == -1) { + if (--retry) { + // wait for init to create the device node + perror("cannot open fb0 (retrying)"); + usleep(100000); + } else { + perror("cannot open fb0 (giving up)"); + return NULL; + } + } + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info (FBIOGET_VSCREENINFO)"); + close(fd); + return NULL; + } + +#ifdef RECOVERY_FORCE_RGB_565 + // Changing fb_var_screeninfo can affect fb_fix_screeninfo, + // so this needs done before querying for fi. + printf("Forcing pixel format: RGB_565\n"); + vi.blue.offset = 0; + vi.green.offset = 5; + vi.red.offset = 11; + vi.blue.length = 5; + vi.green.length = 6; + vi.red.length = 5; + vi.blue.msb_right = 0; + vi.green.msb_right = 0; + vi.red.msb_right = 0; + vi.transp.offset = 0; + vi.transp.length = 0; + vi.bits_per_pixel = 16; + + if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("failed to put force_rgb_565 fb0 info"); + close(fd); + return NULL; + } +#endif + + fb_fix_screeninfo fi; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info (FBIOGET_FSCREENINFO)"); + 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); + + 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 NULL; + } + + 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; +#ifdef RECOVERY_GRAPHICS_FORCE_USE_LINELENGTH + printf("Forcing line length\n"); + vi.xres_virtual = fi.line_length / gr_framebuffer[0].pixel_bytes; +#endif + gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits); + if (vi.bits_per_pixel == 16) { + printf("setting GGL_PIXEL_FORMAT_RGB_565\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_RGB_565; + } else if (vi.red.offset == 8 || vi.red.offset == 16) { + printf("setting GGL_PIXEL_FORMAT_BGRA_8888\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_BGRA_8888; + } else if (vi.red.offset == 0) { + printf("setting GGL_PIXEL_FORMAT_RGBA_8888\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_RGBA_8888; + } else if (vi.red.offset == 24) { + printf("setting GGL_PIXEL_FORMAT_RGBX_8888\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_RGBX_8888; + } else { + if (vi.red.length == 8) { + printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGBX_8888\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_RGBX_8888; + } else { + printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGB_565\n"); + gr_framebuffer[0].format = GGL_PIXEL_FORMAT_RGB_565; + } + } + + // Drawing directly to the framebuffer takes about 5 times longer. + // Instead, we will allocate some memory and draw to that, then + // memcpy the data into the framebuffer later. + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + if (!gr_draw) { + perror("failed to allocate gr_draw"); + close(fd); + munmap(bits, fi.smem_len); + return NULL; + } + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) calloc(gr_draw->height * gr_draw->row_bytes, 1); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + close(fd); + free(gr_draw); + munmap(bits, fi.smem_len); + return NULL; + } + + /* check if we can use double buffering */ +#ifndef RECOVERY_GRAPHICS_FORCE_SINGLE_BUFFER + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + printf("double buffered\n"); + + 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; + + } else { +#else + { + printf("RECOVERY_GRAPHICS_FORCE_SINGLE_BUFFER := true\n"); +#endif + double_buffered = false; + printf("single buffered\n"); + } +#if defined(RECOVERY_BGRA) + printf("RECOVERY_BGRA\n"); +#endif + 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); + + smem_len = fi.smem_len; + + return gr_draw; +} + +static GRSurface* fbdev_flip(minui_backend* backend __unused) { +#if defined(RECOVERY_BGRA) + // In case of BGRA, do some byte swapping + unsigned int idx; + unsigned char tmp; + unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; + for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); + idx += 4) { + tmp = ucfb_vaddr[idx]; + ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; + ucfb_vaddr[idx + 2] = tmp; + } +#endif +#ifndef BOARD_HAS_FLIPPED_SCREEN + if (double_buffered) { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[1-displayed_buffer].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + 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); + } +#else + int gr_active_fb = 0; + if (double_buffered) + gr_active_fb = 1-displayed_buffer; + + /* flip buffer 180 degrees for devices with physically inverted screens */ + unsigned int row_pixels = gr_draw->row_bytes / gr_framebuffer[0].pixel_bytes; + if (gr_framebuffer[0].pixel_bytes == 4) { + for (unsigned int y = 0; y < gr_draw->height; ++y) { + uint32_t* dst = reinterpret_cast<uint32_t*>(gr_framebuffer[gr_active_fb].data) + y * row_pixels; + uint32_t* src = reinterpret_cast<uint32_t*>(gr_draw->data) + (gr_draw->height - y - 1) * row_pixels + gr_draw->width; + for (unsigned int x = 0; x < gr_draw->width; ++x) + *(dst++) = *(--src); + } + } else { + for (unsigned int y = 0; y < gr_draw->height; ++y) { + uint16_t* dst = reinterpret_cast<uint16_t*>(gr_framebuffer[gr_active_fb].data) + y * row_pixels; + uint16_t* src = reinterpret_cast<uint16_t*>(gr_draw->data) + (gr_draw->height - y - 1) * row_pixels + gr_draw->width; + for (unsigned int x = 0; x < gr_draw->width; ++x) + *(dst++) = *(--src); + } + } + + if (double_buffered) + set_displayed_framebuffer(1-displayed_buffer); +#endif + return gr_draw; +} + +static void fbdev_exit(minui_backend* backend __unused) { + close(fb_fd); + fb_fd = -1; + + if (gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; + munmap(gr_framebuffer[0].data, smem_len); +} diff --git a/minuitwrp/graphics_overlay.cpp b/minuitwrp/graphics_overlay.cpp new file mode 100644 index 000000000..84ea6e0ea --- /dev/null +++ b/minuitwrp/graphics_overlay.cpp @@ -0,0 +1,673 @@ +/* + * 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 <string.h> +#include <unistd.h> +#include <errno.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/cdefs.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#ifdef MSM_BSP +#include <linux/msm_mdp.h> +#include <linux/msm_ion.h> +#endif + +#include "minui.h" +#include "graphics.h" +#include <pixelflinger/pixelflinger.h> + +#define MDP_V4_0 400 +#define MAX_DISPLAY_DIM 2048 + +static GRSurface* overlay_init(minui_backend*); +static GRSurface* overlay_flip(minui_backend*); +static void overlay_blank(minui_backend*, bool); +static void overlay_exit(minui_backend*); + +static GRSurface gr_framebuffer; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static fb_var_screeninfo vi; +static int fb_fd = -1; +static bool isMDP5 = false; +static int leftSplit = 0; +static int rightSplit = 0; +#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) + +static size_t frame_size = 0; + +#ifdef MSM_BSP +typedef struct { + unsigned char *mem_buf; + int size; + int ion_fd; + int mem_fd; + struct ion_handle_data handle_data; +} memInfo; + +//Left and right overlay id +static int overlayL_id = MSMFB_NEW_REQUEST; +static int overlayR_id = MSMFB_NEW_REQUEST; + +static memInfo mem_info; + +static int map_mdp_pixel_format() +{ + if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGB_565) + return MDP_RGB_565; + else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_BGRA_8888) + return MDP_BGRA_8888; + else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGBA_8888) + return MDP_RGBA_8888; + else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGBX_8888) + return MDP_RGBA_8888; + printf("No known pixel format for map_mdp_pixel_format, defaulting to MDP_RGB_565.\n"); + return MDP_RGB_565; +} +#endif // MSM_BSP + +static minui_backend my_backend = { + .init = overlay_init, + .flip = overlay_flip, + .blank = overlay_blank, + .exit = overlay_exit, +}; + +bool target_has_overlay(char *version) +{ + int ret; + int mdp_version; + bool overlay_supported = false; + + if (strlen(version) >= 8) { + if(!strncmp(version, "msmfb", strlen("msmfb"))) { + char str_ver[4]; + memcpy(str_ver, version + strlen("msmfb"), 3); + str_ver[3] = '\0'; + mdp_version = atoi(str_ver); + if (mdp_version >= MDP_V4_0) { + overlay_supported = true; + } + } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) { + overlay_supported = true; + isMDP5 = true; + } + } + + return overlay_supported; +} + +minui_backend* open_overlay() { + fb_fix_screeninfo fi; + int fd; + + fd = open("/dev/graphics/fb0", O_RDWR); + if (fd < 0) { + perror("open_overlay cannot open fb0"); + return NULL; + } + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + if (target_has_overlay(fi.id)) { +#ifdef MSM_BSP + close(fd); + return &my_backend; +#else + printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id); +#endif + } + close(fd); + return NULL; +} + +static void overlay_blank(minui_backend* backend __unused, bool blank) +{ +#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS) + int fd; + char brightness[4]; + snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2); + + fd = open(TW_BRIGHTNESS_PATH, O_RDWR); + if (fd < 0) { + perror("cannot open LCD backlight"); + return; + } + write(fd, blank ? "000" : brightness, 3); + close(fd); + +#ifdef TW_SECONDARY_BRIGHTNESS_PATH + fd = open(TW_SECONDARY_BRIGHTNESS_PATH, O_RDWR); + if (fd < 0) { + perror("cannot open LCD backlight 2"); + return; + } + write(fd, blank ? "000" : brightness, 3); + close(fd); +#endif +#else + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +#endif +} + +#ifdef MSM_BSP +void setDisplaySplit(void) { + char split[64] = {0}; + if (!isMDP5) + return; + FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r"); + if (fp) { + //Format "left right" space as delimiter + if(fread(split, sizeof(char), 64, fp)) { + leftSplit = atoi(split); + printf("Left Split=%d\n",leftSplit); + char *rght = strpbrk(split, " "); + if (rght) + rightSplit = atoi(rght + 1); + printf("Right Split=%d\n", rightSplit); + } + } else { + printf("Failed to open mdss_fb_split node\n"); + } + if (fp) + fclose(fp); +} + +int getLeftSplit(void) { + //Default even split for all displays with high res + int lSplit = vi.xres / 2; + + //Override if split published by driver + if (leftSplit) + lSplit = leftSplit; + + return lSplit; +} + +int getRightSplit(void) { + return rightSplit; +} + +int free_ion_mem(void) { + int ret = 0; + + if (mem_info.mem_buf) + munmap(mem_info.mem_buf, mem_info.size); + + if (mem_info.ion_fd >= 0) { + ret = ioctl(mem_info.ion_fd, ION_IOC_FREE, &mem_info.handle_data); + if (ret < 0) + perror("free_mem failed "); + } + + if (mem_info.mem_fd >= 0) + close(mem_info.mem_fd); + if (mem_info.ion_fd >= 0) + close(mem_info.ion_fd); + + memset(&mem_info, 0, sizeof(mem_info)); + mem_info.mem_fd = -1; + mem_info.ion_fd = -1; + return 0; +} + +int alloc_ion_mem(unsigned int size) +{ + int result; + struct ion_fd_data fd_data; + struct ion_allocation_data ionAllocData; + + mem_info.ion_fd = open("/dev/ion", O_RDWR|O_DSYNC); + if (mem_info.ion_fd < 0) { + perror("ERROR: Can't open ion "); + return -errno; + } + + ionAllocData.flags = 0; + ionAllocData.len = size; + ionAllocData.align = sysconf(_SC_PAGESIZE); +#ifdef NEW_ION_HEAP + ionAllocData.heap_id_mask = +#else + ionAllocData.heap_mask = +#endif + ION_HEAP(ION_IOMMU_HEAP_ID) | + ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID); + + result = ioctl(mem_info.ion_fd, ION_IOC_ALLOC, &ionAllocData); + if(result){ + perror("ION_IOC_ALLOC Failed "); + close(mem_info.ion_fd); + return result; + } + + fd_data.handle = ionAllocData.handle; + mem_info.handle_data.handle = ionAllocData.handle; + result = ioctl(mem_info.ion_fd, ION_IOC_MAP, &fd_data); + if (result) { + perror("ION_IOC_MAP Failed "); + free_ion_mem(); + return result; + } + mem_info.mem_buf = (unsigned char *)mmap(NULL, size, PROT_READ | + PROT_WRITE, MAP_SHARED, fd_data.fd, 0); + mem_info.mem_fd = fd_data.fd; + + if (!mem_info.mem_buf) { + perror("ERROR: mem_buf MAP_FAILED "); + free_ion_mem(); + return -ENOMEM; + } + + return 0; +} + +bool isDisplaySplit(void) { + if (vi.xres > MAX_DISPLAY_DIM) + return true; + //check if right split is set by driver + if (getRightSplit()) + return true; + + return false; +} + +int allocate_overlay(int fd, GRSurface gr_fb) +{ + int ret = 0; + + if (!isDisplaySplit()) { + // Check if overlay is already allocated + if (MSMFB_NEW_REQUEST == overlayL_id) { + struct mdp_overlay overlayL; + + memset(&overlayL, 0 , sizeof (struct mdp_overlay)); + + /* Fill Overlay Data */ + overlayL.src.width = ALIGN(gr_fb.width, 32); + overlayL.src.height = gr_fb.height; + overlayL.src.format = map_mdp_pixel_format(); + overlayL.src_rect.w = gr_fb.width; + overlayL.src_rect.h = gr_fb.height; + overlayL.dst_rect.w = gr_fb.width; + overlayL.dst_rect.h = gr_fb.height; + overlayL.alpha = 0xFF; +#ifdef BOARD_HAS_FLIPPED_SCREEN + overlayL.flags = MDP_ROT_180; +#endif + overlayL.transp_mask = MDP_TRANSP_NOP; + overlayL.id = MSMFB_NEW_REQUEST; + ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL); + if (ret < 0) { + perror("Overlay Set Failed"); + return ret; + } + overlayL_id = overlayL.id; + } + } else { + float xres = vi.xres; + int lSplit = getLeftSplit(); + float lSplitRatio = lSplit / xres; + float lCropWidth = gr_fb.width * lSplitRatio; + int lWidth = lSplit; + int rWidth = gr_fb.width - lSplit; + int height = gr_fb.height; + + if (MSMFB_NEW_REQUEST == overlayL_id) { + + struct mdp_overlay overlayL; + + memset(&overlayL, 0 , sizeof (struct mdp_overlay)); + + /* Fill OverlayL Data */ + overlayL.src.width = ALIGN(gr_fb.width, 32); + overlayL.src.height = gr_fb.height; + overlayL.src.format = map_mdp_pixel_format(); + overlayL.src_rect.x = 0; + overlayL.src_rect.y = 0; + overlayL.src_rect.w = lCropWidth; + overlayL.src_rect.h = gr_fb.height; + overlayL.dst_rect.x = 0; + overlayL.dst_rect.y = 0; + overlayL.dst_rect.w = lWidth; + overlayL.dst_rect.h = height; + overlayL.alpha = 0xFF; +#ifdef BOARD_HAS_FLIPPED_SCREEN + overlayL.flags = MDP_ROT_180; +#endif + overlayL.transp_mask = MDP_TRANSP_NOP; + overlayL.id = MSMFB_NEW_REQUEST; + ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL); + if (ret < 0) { + perror("OverlayL Set Failed"); + return ret; + } + overlayL_id = overlayL.id; + } + if (MSMFB_NEW_REQUEST == overlayR_id) { + struct mdp_overlay overlayR; + + memset(&overlayR, 0 , sizeof (struct mdp_overlay)); + + /* Fill OverlayR Data */ + overlayR.src.width = ALIGN(gr_fb.width, 32); + overlayR.src.height = gr_fb.height; + overlayR.src.format = map_mdp_pixel_format(); + overlayR.src_rect.x = lCropWidth; + overlayR.src_rect.y = 0; + overlayR.src_rect.w = gr_fb.width - lCropWidth; + overlayR.src_rect.h = gr_fb.height; + overlayR.dst_rect.x = 0; + overlayR.dst_rect.y = 0; + overlayR.dst_rect.w = rWidth; + overlayR.dst_rect.h = height; + overlayR.alpha = 0xFF; +#ifdef BOARD_HAS_FLIPPED_SCREEN + overlayR.flags = MDSS_MDP_RIGHT_MIXER | MDP_ROT_180; +#else + overlayR.flags = MDSS_MDP_RIGHT_MIXER; +#endif + overlayR.transp_mask = MDP_TRANSP_NOP; + overlayR.id = MSMFB_NEW_REQUEST; + ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayR); + if (ret < 0) { + perror("OverlayR Set Failed"); + return ret; + } + overlayR_id = overlayR.id; + } + + } + return 0; +} + +int overlay_display_frame(int fd, void* data, size_t size) +{ + int ret = 0; + struct msmfb_overlay_data ovdataL, ovdataR; + struct mdp_display_commit ext_commit; + + if (!isDisplaySplit()) { + if (overlayL_id == MSMFB_NEW_REQUEST) { + perror("display_frame failed, no overlay\n"); + return -EINVAL; + } + + memcpy(mem_info.mem_buf, data, size); + + memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data)); + + ovdataL.id = overlayL_id; + ovdataL.data.flags = 0; + ovdataL.data.offset = 0; + ovdataL.data.memory_id = mem_info.mem_fd; + ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL); + if (ret < 0) { + perror("overlay_display_frame failed, overlay play Failed\n"); + printf("%i, %i, %i, %i\n", ret, fb_fd, fd, errno); + return ret; + } + } else { + + if (overlayL_id == MSMFB_NEW_REQUEST) { + perror("display_frame failed, no overlayL \n"); + return -EINVAL; + } + + memcpy(mem_info.mem_buf, data, size); + + memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data)); + + ovdataL.id = overlayL_id; + ovdataL.data.flags = 0; + ovdataL.data.offset = 0; + ovdataL.data.memory_id = mem_info.mem_fd; + ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL); + if (ret < 0) { + perror("overlay_display_frame failed, overlayL play Failed\n"); + return ret; + } + + if (overlayR_id == MSMFB_NEW_REQUEST) { + perror("display_frame failed, no overlayR \n"); + return -EINVAL; + } + memset(&ovdataR, 0, sizeof(struct msmfb_overlay_data)); + + ovdataR.id = overlayR_id; + ovdataR.data.flags = 0; + ovdataR.data.offset = 0; + ovdataR.data.memory_id = mem_info.mem_fd; + ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataR); + if (ret < 0) { + perror("overlay_display_frame failed, overlayR play Failed\n"); + return ret; + } + } + memset(&ext_commit, 0, sizeof(struct mdp_display_commit)); + ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY; + ext_commit.wait_for_finish = 1; + ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit); + if (ret < 0) { + perror("overlay_display_frame failed, overlay commit Failed\n!"); + } + + return ret; +} + +static GRSurface* overlay_flip(minui_backend* backend __unused) { +#if defined(RECOVERY_BGRA) + // In case of BGRA, do some byte swapping + unsigned int idx; + unsigned char tmp; + unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; + for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); + idx += 4) { + tmp = ucfb_vaddr[idx]; + ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; + ucfb_vaddr[idx + 2] = tmp; + } +#endif + // Copy from the in-memory surface to the framebuffer. + overlay_display_frame(fb_fd, gr_draw->data, frame_size); + return gr_draw; +} + +int free_overlay(int fd) +{ + int ret = 0; + struct mdp_display_commit ext_commit; + + if (!isDisplaySplit()) { + if (overlayL_id != MSMFB_NEW_REQUEST) { + ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id); + if (ret) { + perror("Overlay Unset Failed"); + overlayL_id = MSMFB_NEW_REQUEST; + return ret; + } + } + } else { + + if (overlayL_id != MSMFB_NEW_REQUEST) { + ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id); + if (ret) { + perror("OverlayL Unset Failed"); + } + } + + if (overlayR_id != MSMFB_NEW_REQUEST) { + ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayR_id); + if (ret) { + perror("OverlayR Unset Failed"); + overlayR_id = MSMFB_NEW_REQUEST; + return ret; + } + } + } + memset(&ext_commit, 0, sizeof(struct mdp_display_commit)); + ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY; + ext_commit.wait_for_finish = 1; + ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit); + if (ret < 0) { + perror("ERROR: Clear MSMFB_DISPLAY_COMMIT failed!"); + overlayL_id = MSMFB_NEW_REQUEST; + overlayR_id = MSMFB_NEW_REQUEST; + return ret; + } + overlayL_id = MSMFB_NEW_REQUEST; + overlayR_id = MSMFB_NEW_REQUEST; + + return 0; +} + +static GRSurface* overlay_init(minui_backend* backend) { + int fd = open("/dev/graphics/fb0", O_RDWR); + if (fd == -1) { + perror("cannot open fb0"); + return NULL; + } + + fb_fix_screeninfo fi; + 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); + + gr_framebuffer.width = vi.xres; + gr_framebuffer.height = vi.yres; + gr_framebuffer.row_bytes = fi.line_length; + gr_framebuffer.pixel_bytes = vi.bits_per_pixel / 8; + //gr_framebuffer.data = reinterpret_cast<uint8_t*>(bits); + if (vi.bits_per_pixel == 16) { + printf("setting GGL_PIXEL_FORMAT_RGB_565\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_RGB_565; + } else if (vi.red.offset == 8 || vi.red.offset == 16) { + printf("setting GGL_PIXEL_FORMAT_BGRA_8888\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_BGRA_8888; + } else if (vi.red.offset == 0) { + printf("setting GGL_PIXEL_FORMAT_RGBA_8888\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBA_8888; + } else if (vi.red.offset == 24) { + printf("setting GGL_PIXEL_FORMAT_RGBX_8888\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBX_8888; + } else { + if (vi.red.length == 8) { + printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGBX_8888\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBX_8888; + } else { + printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGB_565\n"); + gr_framebuffer.format = GGL_PIXEL_FORMAT_RGB_565; + } + } + + frame_size = fi.line_length * vi.yres; + + gr_framebuffer.data = reinterpret_cast<uint8_t*>(calloc(frame_size, 1)); + if (gr_framebuffer.data == NULL) { + perror("failed to calloc framebuffer"); + close(fd); + return NULL; + } + + gr_draw = &gr_framebuffer; + fb_fd = fd; + + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + + overlay_blank(backend, true); + overlay_blank(backend, false); + + if (!alloc_ion_mem(frame_size)) + allocate_overlay(fb_fd, gr_framebuffer); + + return gr_draw; +} + +static void overlay_exit(minui_backend* backend __unused) { + free_overlay(fb_fd); + free_ion_mem(); + + close(fb_fd); + fb_fd = -1; + + if (gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; +} +#else // MSM_BSP +static GRSurface* overlay_flip(minui_backend* backend __unused) { + return NULL; +} + +static GRSurface* overlay_init(minui_backend* backend __unused) { + return NULL; +} + +static void overlay_exit(minui_backend* backend __unused) { + return; +} +#endif // MSM_BSP diff --git a/minuitwrp/graphics_utils.cpp b/minuitwrp/graphics_utils.cpp new file mode 100644 index 000000000..6ce570484 --- /dev/null +++ b/minuitwrp/graphics_utils.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007 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 <stdio.h> +#include <stdlib.h> +#include <png.h> +#include <pixelflinger/pixelflinger.h> +#include <linux/fb.h> + +#include "minui.h" + +struct fb_var_screeninfo vi; +extern GGLSurface gr_mem_surface; +extern GRSurface* gr_draw; + +int gr_save_screenshot(const char *dest) +{ + uint32_t y, stride_bytes; + volatile int res = -1; + GGLContext *gl = NULL; + GGLSurface surface; + uint8_t * volatile img_data = NULL; + uint8_t *ptr; + FILE * volatile fp = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + fp = fopen(dest, "wb"); + if(!fp) + goto exit; + + img_data = (uint8_t *)malloc(gr_mem_surface.stride * gr_mem_surface.height * gr_draw->pixel_bytes); + if (!img_data) { + printf("gr_save_screenshot failed to malloc img_data\n"); + goto exit; + } + surface.version = sizeof(surface); + surface.width = gr_mem_surface.width; + surface.height = gr_mem_surface.height; + surface.stride = gr_mem_surface.stride; + surface.data = img_data; + surface.format = GGL_PIXEL_FORMAT_RGBA_8888; + + gglInit(&gl); + gl->colorBuffer(gl, &surface); + gl->activeTexture(gl, 0); + + if(gr_mem_surface.format == GGL_PIXEL_FORMAT_RGBX_8888) + gl->disable(gl, GGL_BLEND); + + gl->bindTexture(gl, &gr_mem_surface); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + gl->texCoord2i(gl, 0, 0); + gl->recti(gl, 0, 0, gr_mem_surface.width, gr_mem_surface.height); + + gglUninit(gl); + gl = NULL; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto exit; + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + goto exit; + + if (setjmp(png_jmpbuf(png_ptr))) + goto exit; + + png_init_io(png_ptr, fp); + png_set_IHDR(png_ptr, info_ptr, surface.width, surface.height, + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_write_info(png_ptr, info_ptr); + + // To remove the alpha channel for PNG_COLOR_TYPE_RGB format, + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + + ptr = img_data; + stride_bytes = surface.stride*4; + for(y = 0; y < surface.height; ++y) + { + png_write_row(png_ptr, ptr); + ptr += stride_bytes; + } + + png_write_end(png_ptr, NULL); + + res = 0; +exit: + if(info_ptr) + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + if(png_ptr) + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + if(gl) + gglUninit(gl); + if(img_data) + free(img_data); + if(fp) + fclose(fp); + return res; +} diff --git a/minuitwrp/include/linux/msm_ion.h b/minuitwrp/include/linux/msm_ion.h new file mode 100644 index 000000000..121a5a7a7 --- /dev/null +++ b/minuitwrp/include/linux/msm_ion.h @@ -0,0 +1,157 @@ +#ifndef _LINUX_MSM_ION_H +#define _LINUX_MSM_ION_H + +#include <linux/ion.h> + +/*enum msm_ion_heap_types { + ION_HEAP_TYPE_MSM_START = ION_HEAP_TYPE_CUSTOM + 1, + ION_HEAP_TYPE_IOMMU = ION_HEAP_TYPE_MSM_START, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CP, + ION_HEAP_TYPE_SECURE_DMA, + ION_HEAP_TYPE_REMOVED, +};*/ + +/** + * These are the only ids that should be used for Ion heap ids. + * The ids listed are the order in which allocation will be attempted + * if specified. Don't swap the order of heap ids unless you know what + * you are doing! + * Id's are spaced by purpose to allow new Id's to be inserted in-between (for + * possible fallbacks) + */ + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_CP_MFC_HEAP_ID = 12, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SYSTEM_CONTIG_HEAP_ID = 21, + ION_ADSP_HEAP_ID = 22, + ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */ + ION_SF_HEAP_ID = 24, + ION_IOMMU_HEAP_ID = 25, + ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */ + ION_QSECOM_HEAP_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_SYSTEM_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */ +}; + +/*enum ion_fixed_position { + NOT_FIXED, + FIXED_LOW, + FIXED_MIDDLE, + FIXED_HIGH, +};*/ + +/*enum cp_mem_usage { + VIDEO_BITSTREAM = 0x1, + VIDEO_PIXEL = 0x2, + VIDEO_NONPIXEL = 0x3, + MAX_USAGE = 0x4, + UNKNOWN = 0x7FFFFFFF, +};*/ + +#define ION_HEAP_CP_MASK (1 << ION_HEAP_TYPE_CP) +#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA) + +/** + * Flag to use when allocating to indicate that a heap is secure. + */ +#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED) + +/** + * Flag for clients to force contiguous memort allocation + * + * Use of this flag is carefully monitored! + */ +#define ION_FLAG_FORCE_CONTIGUOUS (1 << 30) + +/* + * Used in conjunction with heap which pool memory to force an allocation + * to come from the page allocator directly instead of from the pool allocation + */ +#define ION_FLAG_POOL_FORCE_ALLOC (1 << 16) + +/** +* Deprecated! Please use the corresponding ION_FLAG_* +*/ +#define ION_SECURE ION_FLAG_SECURE +#define ION_FORCE_CONTIGUOUS ION_FLAG_FORCE_CONTIGUOUS + +/** + * Macro should be used with ion_heap_ids defined above. + */ +#define ION_HEAP(bit) (1 << (bit)) + +#define ION_ADSP_HEAP_NAME "adsp" +#define ION_VMALLOC_HEAP_NAME "vmalloc" +#define ION_KMALLOC_HEAP_NAME "kmalloc" +#define ION_AUDIO_HEAP_NAME "audio" +#define ION_SF_HEAP_NAME "sf" +#define ION_MM_HEAP_NAME "mm" +#define ION_CAMERA_HEAP_NAME "camera_preview" +#define ION_IOMMU_HEAP_NAME "iommu" +#define ION_MFC_HEAP_NAME "mfc" +#define ION_WB_HEAP_NAME "wb" +#define ION_MM_FIRMWARE_HEAP_NAME "mm_fw" +#define ION_PIL1_HEAP_NAME "pil_1" +#define ION_PIL2_HEAP_NAME "pil_2" +#define ION_QSECOM_HEAP_NAME "qsecom" + +#define ION_SET_CACHED(__cache) (__cache | ION_FLAG_CACHED) +#define ION_SET_UNCACHED(__cache) (__cache & ~ION_FLAG_CACHED) + +#define ION_IS_CACHED(__flags) ((__flags) & ION_FLAG_CACHED) + + +/* struct ion_flush_data - data passed to ion for flushing caches + * + * @handle: handle with data to flush + * @fd: fd to flush + * @vaddr: userspace virtual address mapped with mmap + * @offset: offset into the handle to flush + * @length: length of handle to flush + * + * Performs cache operations on the handle. If p is the start address + * of the handle, p + offset through p + offset + length will have + * the cache operations performed + */ +struct ion_flush_data { + struct ion_handle *handle; + int fd; + void *vaddr; + unsigned int offset; + unsigned int length; +}; + +#define ION_IOC_MSM_MAGIC 'M' + +/** + * DOC: ION_IOC_CLEAN_CACHES - clean the caches + * + * Clean the caches of the handle specified. + */ +#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MSM_MAGIC, 0, \ + struct ion_flush_data) +/** + * DOC: ION_IOC_INV_CACHES - invalidate the caches + * + * Invalidate the caches of the handle specified. + */ +#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 1, \ + struct ion_flush_data) +/** + * DOC: ION_IOC_CLEAN_INV_CACHES - clean and invalidate the caches + * + * Clean and invalidate the caches of the handle specified. + */ +#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 2, \ + struct ion_flush_data) + +#endif diff --git a/minuitwrp/include/linux/msm_mdp.h b/minuitwrp/include/linux/msm_mdp.h new file mode 100644 index 000000000..abdcd29e9 --- /dev/null +++ b/minuitwrp/include/linux/msm_mdp.h @@ -0,0 +1,992 @@ +/* include/linux/msm_mdp.h + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_MDP_H_ +#define _MSM_MDP_H_ + +#include <linux/types.h> +#include <linux/fb.h> + +#define MSMFB_IOCTL_MAGIC 'm' +#define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) +#define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) +#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int) +#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int) +#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor) +#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap) +#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram_data) +/* new ioctls's for set/get ccs matrix */ +#define MSMFB_GET_CCS_MATRIX _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs) +#define MSMFB_SET_CCS_MATRIX _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs) +#define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) + +#define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ + struct msmfb_overlay_data) +#define MSMFB_OVERLAY_QUEUE MSMFB_OVERLAY_PLAY + +#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \ + struct mdp_page_protection) +#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \ + struct mdp_page_protection) +#define MSMFB_OVERLAY_GET _IOR(MSMFB_IOCTL_MAGIC, 140, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_PLAY_ENABLE _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int) +#define MSMFB_OVERLAY_BLT _IOWR(MSMFB_IOCTL_MAGIC, 142, \ + struct msmfb_overlay_blt) +#define MSMFB_OVERLAY_BLT_OFFSET _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int) +#define MSMFB_HISTOGRAM_START _IOR(MSMFB_IOCTL_MAGIC, 144, \ + struct mdp_histogram_start_req) +#define MSMFB_HISTOGRAM_STOP _IOR(MSMFB_IOCTL_MAGIC, 145, unsigned int) +#define MSMFB_NOTIFY_UPDATE _IOWR(MSMFB_IOCTL_MAGIC, 146, unsigned int) + +#define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \ + struct msmfb_overlay_3d) + +#define MSMFB_MIXER_INFO _IOWR(MSMFB_IOCTL_MAGIC, 148, \ + struct msmfb_mixer_info_req) +#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \ + struct msmfb_overlay_data) +#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150) +#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151) +#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152) +#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) +#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) +#define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int) +#define MSMFB_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int) +#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync) +#define MSMFB_OVERLAY_COMMIT _IO(MSMFB_IOCTL_MAGIC, 163) +#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 164, \ + struct mdp_display_commit) +#define MSMFB_METADATA_SET _IOW(MSMFB_IOCTL_MAGIC, 165, struct msmfb_metadata) +#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata) +#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \ + unsigned int) +#define MSMFB_ASYNC_BLIT _IOW(MSMFB_IOCTL_MAGIC, 168, unsigned int) + +#define FB_TYPE_3D_PANEL 0x10101010 +#define MDP_IMGTYPE2_START 0x10000 +#define MSMFB_DRIVER_VERSION 0xF9E8D701 + +enum { + NOTIFY_UPDATE_START, + NOTIFY_UPDATE_STOP, + NOTIFY_UPDATE_POWER_OFF, +}; + +enum { + NOTIFY_TYPE_NO_UPDATE, + NOTIFY_TYPE_SUSPEND, + NOTIFY_TYPE_UPDATE, +}; + +enum { + MDP_RGB_565, /* RGB 565 planer */ + MDP_XRGB_8888, /* RGB 888 padded */ + MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planer w/ Cb is in MSB */ + MDP_Y_CBCR_H2V2_ADRENO, + MDP_ARGB_8888, /* ARGB 888 */ + MDP_RGB_888, /* RGB 888 planer */ + MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planer w/ Cr is in MSB */ + MDP_YCRYCB_H2V1, /* YCrYCb interleave */ + MDP_CBYCRY_H2V1, /* CbYCrY interleave */ + MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CRCB_H1V2, + MDP_Y_CBCR_H1V2, + MDP_RGBA_8888, /* ARGB 888 */ + MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ + MDP_Y_CRCB_H2V2_TILE, /* Y and CrCb, pseudo planer tile */ + MDP_Y_CBCR_H2V2_TILE, /* Y and CbCr, pseudo planer tile */ + MDP_Y_CR_CB_H2V2, /* Y, Cr and Cb, planar */ + MDP_Y_CR_CB_GH2V2, /* Y, Cr and Cb, planar aligned to Android YV12 */ + MDP_Y_CB_CR_H2V2, /* Y, Cb and Cr, planar */ + MDP_Y_CRCB_H1V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ + MDP_YCRCB_H1V1, /* YCrCb interleave */ + MDP_YCBCR_H1V1, /* YCbCr interleave */ + MDP_BGR_565, /* BGR 565 planer */ + MDP_BGR_888, /* BGR 888 */ + MDP_Y_CBCR_H2V2_VENUS, + MDP_BGRX_8888, /* BGRX 8888 */ + MDP_RGBA_8888_TILE, /* RGBA 8888 in tile format */ + MDP_ARGB_8888_TILE, /* ARGB 8888 in tile format */ + MDP_ABGR_8888_TILE, /* ABGR 8888 in tile format */ + MDP_BGRA_8888_TILE, /* BGRA 8888 in tile format */ + MDP_RGBX_8888_TILE, /* RGBX 8888 in tile format */ + MDP_XRGB_8888_TILE, /* XRGB 8888 in tile format */ + MDP_XBGR_8888_TILE, /* XBGR 8888 in tile format */ + MDP_BGRX_8888_TILE, /* BGRX 8888 in tile format */ + MDP_YCBYCR_H2V1, /* YCbYCr interleave */ + MDP_IMGTYPE_LIMIT, + MDP_RGB_BORDERFILL, /* border fill pipe */ + MDP_FB_FORMAT = MDP_IMGTYPE2_START, /* framebuffer format */ + MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ +}; + +enum { + PMEM_IMG, + FB_IMG, +}; + +enum { + HSIC_HUE = 0, + HSIC_SAT, + HSIC_INT, + HSIC_CON, + NUM_HSIC_PARAM, +}; + +#define MDSS_MDP_ROT_ONLY 0x80 +#define MDSS_MDP_RIGHT_MIXER 0x100 +#define MDSS_MDP_DUAL_PIPE 0x200 + +/* mdp_blit_req flag values */ +#define MDP_ROT_NOP 0 +#define MDP_FLIP_LR 0x1 +#define MDP_FLIP_UD 0x2 +#define MDP_ROT_90 0x4 +#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_DITHER 0x8 +#define MDP_BLUR 0x10 +#define MDP_BLEND_FG_PREMULT 0x20000 +#define MDP_IS_FG 0x40000 +#define MDP_SOLID_FILL 0x0000100 +#define MDP_DEINTERLACE 0x80000000 +#define MDP_SHARPENING 0x40000000 +#define MDP_NO_DMA_BARRIER_START 0x20000000 +#define MDP_NO_DMA_BARRIER_END 0x10000000 +#define MDP_NO_BLIT 0x08000000 +#define MDP_BLIT_WITH_DMA_BARRIERS 0x000 +#define MDP_BLIT_WITH_NO_DMA_BARRIERS \ + (MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END) +#define MDP_BLIT_SRC_GEM 0x04000000 +#define MDP_BLIT_DST_GEM 0x02000000 +#define MDP_BLIT_NON_CACHED 0x01000000 +#define MDP_OV_PIPE_SHARE 0x00800000 +#define MDP_DEINTERLACE_ODD 0x00400000 +#define MDP_OV_PLAY_NOWAIT 0x00200000 +#define MDP_SOURCE_ROTATED_90 0x00100000 +#define MDP_OVERLAY_PP_CFG_EN 0x00080000 +#define MDP_BACKEND_COMPOSITION 0x00040000 +#define MDP_BORDERFILL_SUPPORTED 0x00010000 +#define MDP_SECURE_OVERLAY_SESSION 0x00008000 +#define MDP_SECURE_DISPLAY_OVERLAY_SESSION 0x00002000 +#define MDP_OV_PIPE_FORCE_DMA 0x00004000 +#define MDP_MEMORY_ID_TYPE_FB 0x00001000 +#define MDP_BWC_EN 0x00000400 +#define MDP_DECIMATION_EN 0x00000800 +#define MDP_TRANSP_NOP 0xffffffff +#define MDP_ALPHA_NOP 0xff + +#define MDP_FB_PAGE_PROTECTION_NONCACHED (0) +#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE (1) +#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE (3) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE (4) +/* Sentinel: Don't use! */ +#define MDP_FB_PAGE_PROTECTION_INVALID (5) +/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */ +#define MDP_NUM_FB_PAGE_PROTECTION_VALUES (5) + +struct mdp_rect { + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; +}; + +struct mdp_img { + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t offset; + int memory_id; /* the file descriptor */ + uint32_t priv; +}; + +/* + * {3x3} + {3} ccs matrix + */ + +#define MDP_CCS_RGB2YUV 0 +#define MDP_CCS_YUV2RGB 1 + +#define MDP_CCS_SIZE 9 +#define MDP_BV_SIZE 3 + +struct mdp_ccs { + int direction; /* MDP_CCS_RGB2YUV or YUV2RGB */ + uint16_t ccs[MDP_CCS_SIZE]; /* 3x3 color coefficients */ + uint16_t bv[MDP_BV_SIZE]; /* 1x3 bias vector */ +}; + +struct mdp_csc { + int id; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +/* The version of the mdp_blit_req structure so that + * user applications can selectively decide which functionality + * to include + */ + +#define MDP_BLIT_REQ_VERSION 2 + +struct color { + uint32_t r; + uint32_t g; + uint32_t b; + uint32_t alpha; +}; + +struct mdp_blit_req { + struct mdp_img src; + struct mdp_img dst; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + struct color const_color; + uint32_t alpha; + uint32_t transp_mask; + uint32_t flags; + int sharpening_strength; /* -127 <--> 127, default 64 */ +}; + +struct mdp_blit_req_list { + uint32_t count; + struct mdp_blit_req req[]; +}; + +#define MSMFB_DATA_VERSION 2 + +struct msmfb_data { + uint32_t offset; + int memory_id; + int id; + uint32_t flags; + uint32_t priv; + uint32_t iova; +}; + +#define MSMFB_NEW_REQUEST -1 + +struct msmfb_overlay_data { + uint32_t id; + struct msmfb_data data; + uint32_t version_key; + struct msmfb_data plane1_data; + struct msmfb_data plane2_data; + struct msmfb_data dst_data; +}; + +struct msmfb_img { + uint32_t width; + uint32_t height; + uint32_t format; +}; + +#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1 +struct msmfb_writeback_data { + struct msmfb_data buf_info; + struct msmfb_img img; +}; + +#define MDP_PP_OPS_ENABLE 0x1 +#define MDP_PP_OPS_READ 0x2 +#define MDP_PP_OPS_WRITE 0x4 +#define MDP_PP_OPS_DISABLE 0x8 +#define MDP_PP_IGC_FLAG_ROM0 0x10 +#define MDP_PP_IGC_FLAG_ROM1 0x20 + +#define MDSS_PP_DSPP_CFG 0x000 +#define MDSS_PP_SSPP_CFG 0x100 +#define MDSS_PP_LM_CFG 0x200 +#define MDSS_PP_WB_CFG 0x300 + +#define MDSS_PP_ARG_MASK 0x3C00 +#define MDSS_PP_ARG_NUM 4 +#define MDSS_PP_ARG_SHIFT 10 +#define MDSS_PP_LOCATION_MASK 0x0300 +#define MDSS_PP_LOGICAL_MASK 0x00FF + +#define MDSS_PP_ADD_ARG(var, arg) ((var) | (0x1 << (MDSS_PP_ARG_SHIFT + (arg)))) +#define PP_ARG(x, var) ((var) & (0x1 << (MDSS_PP_ARG_SHIFT + (x)))) +#define PP_LOCAT(var) ((var) & MDSS_PP_LOCATION_MASK) +#define PP_BLOCK(var) ((var) & MDSS_PP_LOGICAL_MASK) + + +struct mdp_qseed_cfg { + uint32_t table_num; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + +struct mdp_sharp_cfg { + uint32_t flags; + uint32_t strength; + uint32_t edge_thr; + uint32_t smooth_thr; + uint32_t noise_thr; +}; + +struct mdp_qseed_cfg_data { + uint32_t block; + struct mdp_qseed_cfg qseed_data; +}; + +#define MDP_OVERLAY_PP_CSC_CFG 0x1 +#define MDP_OVERLAY_PP_QSEED_CFG 0x2 +#define MDP_OVERLAY_PP_PA_CFG 0x4 +#define MDP_OVERLAY_PP_IGC_CFG 0x8 +#define MDP_OVERLAY_PP_SHARP_CFG 0x10 +#define MDP_OVERLAY_PP_HIST_CFG 0x20 +#define MDP_OVERLAY_PP_HIST_LUT_CFG 0x40 + +#define MDP_CSC_FLAG_ENABLE 0x1 +#define MDP_CSC_FLAG_YUV_IN 0x2 +#define MDP_CSC_FLAG_YUV_OUT 0x4 + +struct mdp_csc_cfg { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data { + uint32_t block; + struct mdp_csc_cfg csc_data; +}; + +struct mdp_pa_cfg { + uint32_t flags; + uint32_t hue_adj; + uint32_t sat_adj; + uint32_t val_adj; + uint32_t cont_adj; +}; + +struct mdp_igc_lut_data { + uint32_t block; + uint32_t len, ops; + uint32_t *c0_c1_data; + uint32_t *c2_data; +}; + +struct mdp_histogram_cfg { + uint32_t ops; + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint16_t num_bins; +}; + +struct mdp_hist_lut_data { + uint32_t block; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + +struct mdp_overlay_pp_params { + uint32_t config_ops; + struct mdp_csc_cfg csc_cfg; + struct mdp_qseed_cfg qseed_cfg[2]; + struct mdp_pa_cfg pa_cfg; + struct mdp_igc_lut_data igc_cfg; + struct mdp_sharp_cfg sharp_cfg; + struct mdp_histogram_cfg hist_cfg; + struct mdp_hist_lut_data hist_lut_cfg; +}; + +/** + * enum mdss_mdp_blend_op - Different blend operations set by userspace + * + * @BLEND_OP_NOT_DEFINED: No blend operation defined for the layer. + * @BLEND_OP_OPAQUE: Apply a constant blend operation. The layer + * would appear opaque in case fg plane alpha is + * 0xff. + * @BLEND_OP_PREMULTIPLIED: Apply source over blend rule. Layer already has + * alpha pre-multiplication done. If fg plane alpha + * is less than 0xff, apply modulation as well. This + * operation is intended on layers having alpha + * channel. + * @BLEND_OP_COVERAGE: Apply source over blend rule. Layer is not alpha + * pre-multiplied. Apply pre-multiplication. If fg + * plane alpha is less than 0xff, apply modulation as + * well. + * @BLEND_OP_MAX: Used to track maximum blend operation possible by + * mdp. + */ +enum mdss_mdp_blend_op { + BLEND_OP_NOT_DEFINED = 0, + BLEND_OP_OPAQUE, + BLEND_OP_PREMULTIPLIED, + BLEND_OP_COVERAGE, + BLEND_OP_MAX, +}; + +#define MAX_PLANES 4 +struct mdp_scale_data { + uint8_t enable_pxl_ext; + + int init_phase_x[MAX_PLANES]; + int phase_step_x[MAX_PLANES]; + int init_phase_y[MAX_PLANES]; + int phase_step_y[MAX_PLANES]; + + int num_ext_pxls_left[MAX_PLANES]; + int num_ext_pxls_right[MAX_PLANES]; + int num_ext_pxls_top[MAX_PLANES]; + int num_ext_pxls_btm[MAX_PLANES]; + + int left_ftch[MAX_PLANES]; + int left_rpt[MAX_PLANES]; + int right_ftch[MAX_PLANES]; + int right_rpt[MAX_PLANES]; + + int top_rpt[MAX_PLANES]; + int btm_rpt[MAX_PLANES]; + int top_ftch[MAX_PLANES]; + int btm_ftch[MAX_PLANES]; + + uint32_t roi_w[MAX_PLANES]; +}; + +/** + * struct mdp_overlay - overlay surface structure + * @src: Source image information (width, height, format). + * @src_rect: Source crop rectangle, portion of image that will be fetched. + * This should always be within boundaries of source image. + * @dst_rect: Destination rectangle, the position and size of image on screen. + * This should always be within panel boundaries. + * @z_order: Blending stage to occupy in display, if multiple layers are + * present, highest z_order usually means the top most visible + * layer. The range acceptable is from 0-3 to support blending + * up to 4 layers. + * @is_fg: This flag is used to disable blending of any layers with z_order + * less than this overlay. It means that any layers with z_order + * less than this layer will not be blended and will be replaced + * by the background border color. + * @alpha: Used to set plane opacity. The range can be from 0-255, where + * 0 means completely transparent and 255 means fully opaque. + * @transp_mask: Color used as color key for transparency. Any pixel in fetched + * image matching this color will be transparent when blending. + * The color should be in same format as the source image format. + * @flags: This is used to customize operation of overlay. See MDP flags + * for more information. + * @user_data: DEPRECATED* Used to store user application specific information. + * @bg_color: Solid color used to fill the overlay surface when no source + * buffer is provided. + * @horz_deci: Horizontal decimation value, this indicates the amount of pixels + * dropped for each pixel that is fetched from a line. The value + * given should be power of two of decimation amount. + * 0: no decimation + * 1: decimate by 2 (drop 1 pixel for each pixel fetched) + * 2: decimate by 4 (drop 3 pixels for each pixel fetched) + * 3: decimate by 8 (drop 7 pixels for each pixel fetched) + * 4: decimate by 16 (drop 15 pixels for each pixel fetched) + * @vert_deci: Vertical decimation value, this indicates the amount of lines + * dropped for each line that is fetched from overlay. The value + * given should be power of two of decimation amount. + * 0: no decimation + * 1: decimation by 2 (drop 1 line for each line fetched) + * 2: decimation by 4 (drop 3 lines for each line fetched) + * 3: decimation by 8 (drop 7 lines for each line fetched) + * 4: decimation by 16 (drop 15 lines for each line fetched) + * @overlay_pp_cfg: Overlay post processing configuration, for more information + * see struct mdp_overlay_pp_params. + */ +struct mdp_overlay { + struct msmfb_img src; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + uint32_t z_order; /* stage number */ + uint32_t is_fg; /* control alpha & transp */ + uint32_t alpha; + uint32_t blend_op; + uint32_t transp_mask; + uint32_t flags; + uint32_t id; + uint32_t user_data[6]; + uint32_t bg_color; + uint8_t horz_deci; + uint8_t vert_deci; + struct mdp_overlay_pp_params overlay_pp_cfg; + struct mdp_scale_data scale; +}; + +struct msmfb_overlay_3d { + uint32_t is_3d; + uint32_t width; + uint32_t height; +}; + + +struct msmfb_overlay_blt { + uint32_t enable; + uint32_t offset; + uint32_t width; + uint32_t height; + uint32_t bpp; +}; + +struct mdp_histogram { + uint32_t frame_cnt; + uint32_t bin_cnt; + uint32_t *r; + uint32_t *g; + uint32_t *b; +}; + +enum { + DISPLAY_MISR_EDP, + DISPLAY_MISR_DSI0, + DISPLAY_MISR_DSI1, + DISPLAY_MISR_HDMI, + DISPLAY_MISR_LCDC, + DISPLAY_MISR_ATV, + DISPLAY_MISR_DSI_CMD, + DISPLAY_MISR_MAX +}; + +enum { + MISR_OP_NONE, + MISR_OP_SFM, + MISR_OP_MFM, + MISR_OP_BM, + MISR_OP_MAX +}; + +struct mdp_misr { + uint32_t block_id; + uint32_t frame_count; + uint32_t crc_op_mode; + uint32_t crc_value[32]; +}; + +/* + + mdp_block_type defines the identifiers for pipes in MDP 4.3 and up + + MDP_BLOCK_RESERVED is provided for backward compatibility and is + deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used + instead. + + MDP_LOGICAL_BLOCK_DISP_0 identifies the display pipe which fb0 uses, + same for others. + +*/ + +enum { + MDP_BLOCK_RESERVED = 0, + MDP_BLOCK_OVERLAY_0, + MDP_BLOCK_OVERLAY_1, + MDP_BLOCK_VG_1, + MDP_BLOCK_VG_2, + MDP_BLOCK_RGB_1, + MDP_BLOCK_RGB_2, + MDP_BLOCK_DMA_P, + MDP_BLOCK_DMA_S, + MDP_BLOCK_DMA_E, + MDP_BLOCK_OVERLAY_2, + MDP_LOGICAL_BLOCK_DISP_0 = 0x10, + MDP_LOGICAL_BLOCK_DISP_1, + MDP_LOGICAL_BLOCK_DISP_2, + MDP_BLOCK_MAX, +}; + +/* + * mdp_histogram_start_req is used to provide the parameters for + * histogram start request + */ + +struct mdp_histogram_start_req { + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint16_t num_bins; +}; + +/* + * mdp_histogram_data is used to return the histogram data, once + * the histogram is done/stopped/cance + */ + +struct mdp_histogram_data { + uint32_t block; + uint32_t bin_cnt; + uint32_t *c0; + uint32_t *c1; + uint32_t *c2; + uint32_t *extra_info; +}; + +struct mdp_pcc_coeff { + uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1; +}; + +struct mdp_pcc_cfg_data { + uint32_t block; + uint32_t ops; + struct mdp_pcc_coeff r, g, b; +}; + +#define MDP_GAMUT_TABLE_NUM 8 + +enum { + mdp_lut_igc, + mdp_lut_pgc, + mdp_lut_hist, + mdp_lut_max, +}; + +struct mdp_ar_gc_lut_data { + uint32_t x_start; + uint32_t slope; + uint32_t offset; +}; + +struct mdp_pgc_lut_data { + uint32_t block; + uint32_t flags; + uint8_t num_r_stages; + uint8_t num_g_stages; + uint8_t num_b_stages; + struct mdp_ar_gc_lut_data *r_data; + struct mdp_ar_gc_lut_data *g_data; + struct mdp_ar_gc_lut_data *b_data; +}; + + +struct mdp_lut_cfg_data { + uint32_t lut_type; + union { + struct mdp_igc_lut_data igc_lut_data; + struct mdp_pgc_lut_data pgc_lut_data; + struct mdp_hist_lut_data hist_lut_data; + } data; +}; + +struct mdp_bl_scale_data { + uint32_t min_lvl; + uint32_t scale; +}; + +struct mdp_pa_cfg_data { + uint32_t block; + struct mdp_pa_cfg pa_data; +}; + +struct mdp_dither_cfg_data { + uint32_t block; + uint32_t flags; + uint32_t g_y_depth; + uint32_t r_cr_depth; + uint32_t b_cb_depth; +}; + +struct mdp_gamut_cfg_data { + uint32_t block; + uint32_t flags; + uint32_t gamut_first; + uint32_t tbl_size[MDP_GAMUT_TABLE_NUM]; + uint16_t *r_tbl[MDP_GAMUT_TABLE_NUM]; + uint16_t *g_tbl[MDP_GAMUT_TABLE_NUM]; + uint16_t *b_tbl[MDP_GAMUT_TABLE_NUM]; +}; + +struct mdp_calib_config_data { + uint32_t ops; + uint32_t addr; + uint32_t data; +}; + +struct mdp_calib_config_buffer { + uint32_t ops; + uint32_t size; + uint32_t *buffer; +}; + +struct mdp_calib_dcm_state { + uint32_t ops; + uint32_t dcm_state; +}; + +enum { + DCM_UNINIT, + DCM_UNBLANK, + DCM_ENTER, + DCM_EXIT, + DCM_BLANK, +}; + +#define MDSS_MAX_BL_BRIGHTNESS 255 +#define AD_BL_LIN_LEN (MDSS_MAX_BL_BRIGHTNESS + 1) + +#define MDSS_AD_MODE_AUTO_BL 0x0 +#define MDSS_AD_MODE_AUTO_STR 0x1 +#define MDSS_AD_MODE_TARG_STR 0x3 +#define MDSS_AD_MODE_MAN_STR 0x7 +#define MDSS_AD_MODE_CALIB 0xF + +#define MDP_PP_AD_INIT 0x10 +#define MDP_PP_AD_CFG 0x20 + +struct mdss_ad_init { + uint32_t asym_lut[33]; + uint32_t color_corr_lut[33]; + uint8_t i_control[2]; + uint16_t black_lvl; + uint16_t white_lvl; + uint8_t var; + uint8_t limit_ampl; + uint8_t i_dither; + uint8_t slope_max; + uint8_t slope_min; + uint8_t dither_ctl; + uint8_t format; + uint8_t auto_size; + uint16_t frame_w; + uint16_t frame_h; + uint8_t logo_v; + uint8_t logo_h; + uint32_t bl_lin_len; + uint32_t *bl_lin; + uint32_t *bl_lin_inv; +}; + +#define MDSS_AD_BL_CTRL_MODE_EN 1 +#define MDSS_AD_BL_CTRL_MODE_DIS 0 +struct mdss_ad_cfg { + uint32_t mode; + uint32_t al_calib_lut[33]; + uint16_t backlight_min; + uint16_t backlight_max; + uint16_t backlight_scale; + uint16_t amb_light_min; + uint16_t filter[2]; + uint16_t calib[4]; + uint8_t strength_limit; + uint8_t t_filter_recursion; + uint16_t stab_itr; + uint32_t bl_ctrl_mode; +}; + +/* ops uses standard MDP_PP_* flags */ +struct mdss_ad_init_cfg { + uint32_t ops; + union { + struct mdss_ad_init init; + struct mdss_ad_cfg cfg; + } params; +}; + +/* mode uses MDSS_AD_MODE_* flags */ +struct mdss_ad_input { + uint32_t mode; + union { + uint32_t amb_light; + uint32_t strength; + uint32_t calib_bl; + } in; + uint32_t output; +}; + +#define MDSS_CALIB_MODE_BL 0x1 +struct mdss_calib_cfg { + uint32_t ops; + uint32_t calib_mask; +}; + +enum { + mdp_op_pcc_cfg, + mdp_op_csc_cfg, + mdp_op_lut_cfg, + mdp_op_qseed_cfg, + mdp_bl_scale_cfg, + mdp_op_pa_cfg, + mdp_op_dither_cfg, + mdp_op_gamut_cfg, + mdp_op_calib_cfg, + mdp_op_ad_cfg, + mdp_op_ad_input, + mdp_op_calib_mode, + mdp_op_calib_buffer, + mdp_op_calib_dcm_state, + mdp_op_max, +}; + +enum { + WB_FORMAT_NV12, + WB_FORMAT_RGB_565, + WB_FORMAT_RGB_888, + WB_FORMAT_xRGB_8888, + WB_FORMAT_ARGB_8888, + WB_FORMAT_BGRA_8888, + WB_FORMAT_BGRX_8888, + WB_FORMAT_ARGB_8888_INPUT_ALPHA /* Need to support */ +}; + +struct msmfb_mdp_pp { + uint32_t op; + union { + struct mdp_pcc_cfg_data pcc_cfg_data; + struct mdp_csc_cfg_data csc_cfg_data; + struct mdp_lut_cfg_data lut_cfg_data; + struct mdp_qseed_cfg_data qseed_cfg_data; + struct mdp_bl_scale_data bl_scale_data; + struct mdp_pa_cfg_data pa_cfg_data; + struct mdp_dither_cfg_data dither_cfg_data; + struct mdp_gamut_cfg_data gamut_cfg_data; + struct mdp_calib_config_data calib_cfg; + struct mdss_ad_init_cfg ad_init_cfg; + struct mdss_calib_cfg mdss_calib_cfg; + struct mdss_ad_input ad_input; + struct mdp_calib_config_buffer calib_buffer; + struct mdp_calib_dcm_state calib_dcm; + } data; +}; + +#define FB_METADATA_VIDEO_INFO_CODE_SUPPORT 1 +enum { + metadata_op_none, + metadata_op_base_blend, + metadata_op_frame_rate, + metadata_op_vic, + metadata_op_wb_format, + metadata_op_get_caps, + metadata_op_crc, + metadata_op_max +}; + +struct mdp_blend_cfg { + uint32_t is_premultiplied; +}; + +struct mdp_mixer_cfg { + uint32_t writeback_format; + uint32_t alpha; +}; + +struct mdss_hw_caps { + uint32_t mdp_rev; + uint8_t rgb_pipes; + uint8_t vig_pipes; + uint8_t dma_pipes; + uint32_t features; +}; + +struct msmfb_metadata { + uint32_t op; + uint32_t flags; + union { + struct mdp_misr misr_request; + struct mdp_blend_cfg blend_cfg; + struct mdp_mixer_cfg mixer_cfg; + uint32_t panel_frame_rate; + uint32_t video_info_code; + struct mdss_hw_caps caps; + } data; +}; + +#define MDP_MAX_FENCE_FD 32 +#define MDP_BUF_SYNC_FLAG_WAIT 1 + +struct mdp_buf_sync { + uint32_t flags; + uint32_t acq_fen_fd_cnt; + uint32_t session_id; + int *acq_fen_fd; + int *rel_fen_fd; +}; + +struct mdp_async_blit_req_list { + struct mdp_buf_sync sync; + uint32_t count; + struct mdp_blit_req req[]; +}; + +#define MDP_DISPLAY_COMMIT_OVERLAY 1 +struct mdp_buf_fence { + uint32_t flags; + uint32_t acq_fen_fd_cnt; + int acq_fen_fd[MDP_MAX_FENCE_FD]; + int rel_fen_fd[MDP_MAX_FENCE_FD]; +}; + + +struct mdp_display_commit { + uint32_t flags; + uint32_t wait_for_finish; + struct fb_var_screeninfo var; + struct mdp_buf_fence buf_fence; + struct mdp_rect roi; +}; + +struct mdp_page_protection { + uint32_t page_protection; +}; + + +struct mdp_mixer_info { + int pndx; + int pnum; + int ptype; + int mixer_num; + int z_order; +}; + +#define MAX_PIPE_PER_MIXER 4 + +struct msmfb_mixer_info_req { + int mixer_num; + int cnt; + struct mdp_mixer_info info[MAX_PIPE_PER_MIXER]; +}; + +enum { + DISPLAY_SUBSYSTEM_ID, + ROTATOR_SUBSYSTEM_ID, +}; + +enum { + MDP_IOMMU_DOMAIN_CP, + MDP_IOMMU_DOMAIN_NS, +}; + +enum { + MDP_WRITEBACK_MIRROR_OFF, + MDP_WRITEBACK_MIRROR_ON, + MDP_WRITEBACK_MIRROR_PAUSE, + MDP_WRITEBACK_MIRROR_RESUME, +}; + + +#endif /*_MSM_MDP_H_*/ diff --git a/minuitwrp/minui.h b/minuitwrp/minui.h new file mode 100644 index 000000000..018f3274f --- /dev/null +++ b/minuitwrp/minui.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2007 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 _MINUI_H_ +#define _MINUI_H_ + +#include "../gui/placement.h" +#include <stdbool.h> + +struct GRSurface { + int width; + int height; + int row_bytes; + int pixel_bytes; + unsigned char* data; + __u32 format; +}; + +typedef void* gr_surface; +typedef unsigned short gr_pixel; + +#define FONT_TYPE_TWRP 0 +#define FONT_TYPE_TTF 1 + +int gr_init(void); +void gr_exit(void); + +int gr_fb_width(void); +int gr_fb_height(void); +gr_pixel *gr_fb_data(void); +void gr_flip(void); +void gr_fb_blank(bool blank); + +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void gr_clip(int x, int y, int w, int h); +void gr_noclip(); +void gr_fill(int x, int y, int w, int h); +void gr_line(int x0, int y0, int x1, int y1, int width); +gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale); + +int gr_getMaxFontHeight(void *font); + +void *gr_ttf_loadFont(const char *filename, int size, int dpi); +void *gr_ttf_scaleFont(void *font, int max_width, int measured_width); +void gr_ttf_freeFont(void *font); +int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height); +int gr_ttf_measureEx(const char *s, void *font); +int gr_ttf_maxExW(const char *s, void *font, int max_width); +int gr_ttf_getMaxFontHeight(void *font); +void gr_ttf_dump_stats(void); + +void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy); +unsigned int gr_get_width(gr_surface surface); +unsigned int gr_get_height(gr_surface surface); +int gr_get_surface(gr_surface* surface); +int gr_free_surface(gr_surface surface); + +// Functions in graphics_utils.c +int gr_save_screenshot(const char *dest); + +// input event structure, include <linux/input.h> for the definition. +// see http://www.mjmwired.net/kernel/Documentation/input/ for info. +struct input_event; + +int ev_init(void); +void ev_exit(void); +int ev_get(struct input_event *ev, int timeout_ms); +int ev_has_mouse(void); + +// Resources + +// Returns 0 if no error, else negative. +int res_create_surface(const char* name, gr_surface* pSurface); +void res_free_surface(gr_surface surface); +int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h); + +int vibrate(int timeout_ms); + +#endif diff --git a/minuitwrp/resources.cpp b/minuitwrp/resources.cpp new file mode 100644 index 000000000..c4325e412 --- /dev/null +++ b/minuitwrp/resources.cpp @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2007 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 <stdlib.h> +#include <string.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 <png.h> + +#include <pixelflinger/pixelflinger.h> +#ifdef TW_INCLUDE_JPEG +extern "C" { +#include "jpeglib.h" +} +#endif +#include "minui.h" + +#define SURFACE_DATA_ALIGNMENT 8 + +static GGLSurface* malloc_surface(size_t data_size) { + size_t size = sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT; + unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size)); + if (temp == NULL) return NULL; + GGLSurface* surface = reinterpret_cast<GGLSurface*>(temp); + surface->data = temp + sizeof(GGLSurface) + + (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT)); + return surface; +} + +static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, + png_uint_32* width, png_uint_32* height, png_byte* channels, FILE** fpp) { + char resPath[256]; + unsigned char header[8]; + int result = 0; + int color_type, bit_depth; + size_t bytesRead; + + snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + fp = fopen(name, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + } + + bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!*png_ptr) { + result = -4; + goto exit; + } + + *info_ptr = png_create_info_struct(*png_ptr); + if (!*info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(*png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(*png_ptr, fp); + png_set_sig_bytes(*png_ptr, sizeof(header)); + png_read_info(*png_ptr, *info_ptr); + + png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth, + &color_type, NULL, NULL, NULL); + + *channels = png_get_channels(*png_ptr, *info_ptr); + + if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) { + // 8-bit RGB images: great, nothing to do. + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) { + // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. + png_set_expand_gray_1_2_4_to_8(*png_ptr); + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { + // paletted images: expand to 8-bit RGB. Note that we DON'T + // currently expand the tRNS chunk (if any) to an alpha + // channel, because minui doesn't support alpha channels in + // general. + png_set_palette_to_rgb(*png_ptr); + *channels = 3; + } else if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(*png_ptr); + } + + *fpp = fp; + return result; + + exit: + if (result < 0) { + png_destroy_read_struct(png_ptr, info_ptr, NULL); + } + if (fp != NULL) { + fclose(fp); + } + + return result; +} + +// "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 GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) { + GGLSurface* surface = malloc_surface(width * height * 4); + if (surface == NULL) return NULL; + + surface->version = sizeof(GGLSurface); + surface->width = width; + surface->height = height; + surface->stride = width; + + return surface; +} + +// Copy '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; + } +} + +int res_create_surface_png(const char* name, gr_surface* pSurface) { + GGLSurface* surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + FILE* fp; + unsigned char* p_row; + unsigned int y; + + *pSurface = NULL; + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels, &fp); + if (result < 0) return result; + + surface = init_display_surface(width, height); + if (surface == NULL) { + result = -8; + goto exit; + } + +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + png_set_bgr(png_ptr); +#endif + + p_row = reinterpret_cast<unsigned char*>(malloc(width * 4)); + if (p_row == NULL) { + result = -9; + goto exit; + } + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, p_row, NULL); + transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width); + } + free(p_row); + + if (channels == 3) + surface->format = GGL_PIXEL_FORMAT_RGBX_8888; + else + surface->format = GGL_PIXEL_FORMAT_RGBA_8888; + + *pSurface = (gr_surface) surface; + + exit: + fclose(fp); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} + +#ifdef TW_INCLUDE_JPEG +int res_create_surface_jpg(const char* name, gr_surface* pSurface) { + GGLSurface* surface = NULL; + int result = 0, y; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char* pData; + size_t width, height, stride, pixelSize; + + FILE* fp = fopen(name, "rb"); + if (fp == NULL) { + char resPath[256]; + + snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s", name); + resPath[sizeof(resPath)-1] = '\0'; + fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + /* Specify data source for decompression */ + jpeg_stdio_src(&cinfo, fp); + + /* Read file header, set default decompression parameters */ + if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) + goto exit; + + /* Start decompressor */ + (void) jpeg_start_decompress(&cinfo); + + width = cinfo.image_width; + height = cinfo.image_height; + stride = 4 * width; + pixelSize = stride * height; + + surface = reinterpret_cast<GGLSurface*>(malloc(sizeof(GGLSurface) + pixelSize)); + //p_row = reinterpret_cast<unsigned char*>(malloc(width * 4)); + if (surface == NULL) { + result = -8; + goto exit; + } + + pData = (unsigned char*) (surface + 1); + surface->version = sizeof(GGLSurface); + surface->width = width; + surface->height = height; + surface->stride = width; /* Yes, pixels, not bytes */ + surface->data = pData; + surface->format = GGL_PIXEL_FORMAT_RGBX_8888; + + for (y = 0; y < (int) height; ++y) { + unsigned char* pRow = pData + y * stride; + jpeg_read_scanlines(&cinfo, &pRow, 1); + + int x; + for(x = width - 1; x >= 0; x--) { + int sx = x * 3; + int dx = x * 4; + unsigned char r = pRow[sx]; + unsigned char g = pRow[sx + 1]; + unsigned char b = pRow[sx + 2]; + unsigned char a = 0xff; +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + pRow[dx ] = b; // r + pRow[dx + 1] = g; // g + pRow[dx + 2] = r; // b + pRow[dx + 3] = a; +#else + pRow[dx ] = r; // r + pRow[dx + 1] = g; // g + pRow[dx + 2] = b; // b + pRow[dx + 3] = a; +#endif + } + } + *pSurface = (gr_surface) surface; + +exit: + if (fp != NULL) + { + if (surface) + { + (void) jpeg_finish_decompress(&cinfo); + if (result < 0) + { + free(surface); + } + } + jpeg_destroy_decompress(&cinfo); + fclose(fp); + } + return result; +} +#endif + +int res_create_surface(const char* name, gr_surface* pSurface) { + int ret; + + if (!name) return -1; + +#ifdef TW_INCLUDE_JPEG + if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0) + return res_create_surface_jpg(name,pSurface); +#endif + + ret = res_create_surface_png(name,pSurface); +#ifdef TW_INCLUDE_JPEG + if (ret < 0) + ret = res_create_surface_jpg(name,pSurface); +#endif + + return ret; +} + +void res_free_surface(gr_surface surface) { + GGLSurface* pSurface = (GGLSurface*) surface; + if (pSurface) { + free(pSurface); + } +} + +// Scale image function +int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h) { + GGLContext *gl = NULL; + GGLSurface* sc_mem_surface = NULL; + *destination = NULL; + GGLSurface *surface = (GGLSurface*)source; + int w = gr_get_width(source), h = gr_get_height(source); + int sx = 0, sy = 0, dx = 0, dy = 0; + float dw = (float)w * scale_w; + float dh = (float)h * scale_h; + + // Create a new surface that is the appropriate size + sc_mem_surface = init_display_surface((int)dw, (int)dh); + if (!sc_mem_surface) { + printf("gr_scale_surface failed to init_display_surface\n"); + return -1; + } + sc_mem_surface->format = surface->format; + + // Initialize the context + gglInit(&gl); + gl->colorBuffer(gl, sc_mem_surface); + gl->activeTexture(gl, 0); + + // Enable or disable blending based on source surface format + if (surface->format == GGL_PIXEL_FORMAT_RGBX_8888) { + gl->disable(gl, GGL_BLEND); + } else { + gl->enable(gl, GGL_BLEND); + gl->blendFunc(gl, GGL_ONE, GGL_ZERO); + } + + // Bind our source surface to the context + gl->bindTexture(gl, surface); + + // Deal with the scaling + gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MIN_FILTER, GGL_LINEAR); + gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MAG_FILTER, GGL_LINEAR); + gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); + gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + gl->enable(gl, GGL_TEXTURE_2D); + + int32_t grad[8]; + memset(grad, 0, sizeof(grad)); + // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale <- this is wrong! + // This api uses block floating-point for S and T texture coordinates. + // All values are given in 16.16, scaled by 'scale'. In other words, + // set scale to 0, for 16.16 values. + + // s, dsdx, dsdy, t, dtdx, dtdy, sscale, tscale + float dsdx = (float)w / dw; + float dtdy = (float)h / dh; + grad[0] = ((float)sx - (dsdx * dx)) * 65536; + grad[1] = dsdx * 65536; + grad[3] = ((float)sy - (dtdy * dy)) * 65536; + grad[5] = dtdy * 65536; +// printf("blit: w=%d h=%d dx=%d dy=%d dw=%f dh=%f dsdx=%f dtdy=%f s0=%x dsdx=%x t0=%x dtdy=%x\n", +// w, h, dx, dy, dw, dh, dsdx, dtdy, grad[0], grad[1], grad[3], grad[5]); + gl->texCoordGradScale8xv(gl, 0 /*tmu*/, grad); + + // draw / scale the source surface to our target context + gl->recti(gl, dx, dy, dx + dw, dy + dh); + gglUninit(gl); + gl = NULL; + // put the scaled surface in our destination + *destination = (gr_surface*) sc_mem_surface; + // free memory used in the source + res_free_surface(source); + source = NULL; + return 0; +} diff --git a/minuitwrp/truetype.cpp b/minuitwrp/truetype.cpp new file mode 100644 index 000000000..18635a886 --- /dev/null +++ b/minuitwrp/truetype.cpp @@ -0,0 +1,817 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <stdio.h> + +#include "minui.h" + +#include <cutils/hashmap.h> +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include <pixelflinger/pixelflinger.h> +#include <pthread.h> + +#define STRING_CACHE_MAX_ENTRIES 400 +#define STRING_CACHE_TRUNCATE_ENTRIES 150 + +typedef struct +{ + int size; + int dpi; + char *path; +} TrueTypeFontKey; + +typedef struct +{ + int type; + int refcount; + int size; + int dpi; + int max_height; + int base; + FT_Face face; + Hashmap *glyph_cache; + Hashmap *string_cache; + struct StringCacheEntry *string_cache_head; + struct StringCacheEntry *string_cache_tail; + pthread_mutex_t mutex; + TrueTypeFontKey *key; +} TrueTypeFont; + +typedef struct +{ + FT_BBox bbox; + FT_BitmapGlyph glyph; +} TrueTypeCacheEntry; + +typedef struct +{ + char *text; + int max_width; +} StringCacheKey; + +struct StringCacheEntry +{ + GGLSurface surface; + int rendered_bytes; // number of bytes from C string rendered, not number of UTF8 characters! + StringCacheKey *key; + struct StringCacheEntry *prev; + struct StringCacheEntry *next; +}; + +typedef struct StringCacheEntry StringCacheEntry; + +typedef struct +{ + FT_Library ft_library; + Hashmap *fonts; + pthread_mutex_t mutex; +} FontData; + +static FontData font_data = { + .ft_library = NULL, + .fonts = NULL, + .mutex = PTHREAD_MUTEX_INITIALIZER, +}; + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +// 32bit FNV-1a hash algorithm +// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a +static const uint32_t FNV_prime = 16777619U; +static const uint32_t offset_basis = 2166136261U; + +static uint32_t fnv_hash(void *data, uint32_t len) +{ + uint8_t *d8 = (uint8_t *)data; + uint32_t *d32 = (uint32_t *)data; + uint32_t i, max; + uint32_t hash = offset_basis; + + max = len/4; + + // 32 bit data + for(i = 0; i < max; ++i) + { + hash ^= *d32++; + hash *= FNV_prime; + } + + // last bits + for(i *= 4; i < len; ++i) + { + hash ^= (uint32_t) d8[i]; + hash *= FNV_prime; + } + return hash; +} + +static inline uint32_t fnv_hash_add(uint32_t cur_hash, uint32_t word) +{ + cur_hash ^= word; + cur_hash *= FNV_prime; + return cur_hash; +} + +int utf8_to_unicode(const char* pIn, unsigned int *pOut) +{ + int utf_bytes = 1; + unsigned int unicode = 0; + unsigned char tmp; + tmp = (unsigned char)*pIn++; + if (tmp < 0x80) + { + *pOut = tmp; + } + else + { + unsigned int high_bit_mask = 0x3F; + unsigned int high_bit_shift = 0; + int total_bits = 0; + while((tmp & 0xC0) == 0xC0) + { + utf_bytes ++; + if(utf_bytes > 6) + { + *pOut = tmp; + return 1; + } + tmp = 0xFF & (tmp << 1); + total_bits += 6; + high_bit_mask >>= 1; + high_bit_shift++; + unicode <<= 6; + unicode |= (*pIn++) & 0x3F; + } + unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits; + *pOut = unicode; + } + + return utf_bytes; +} + +static bool gr_ttf_string_cache_equals(void *keyA, void *keyB) +{ + StringCacheKey *a = (StringCacheKey *)keyA; + StringCacheKey *b = (StringCacheKey *)keyB; + return a->max_width == b->max_width && strcmp(a->text, b->text) == 0; +} + +static int gr_ttf_string_cache_hash(void *key) +{ + StringCacheKey *k = (StringCacheKey *)key; + return fnv_hash(k->text, strlen(k->text)); +} + +static bool gr_ttf_font_cache_equals(void *keyA, void *keyB) +{ + TrueTypeFontKey *a = (TrueTypeFontKey *)keyA; + TrueTypeFontKey *b = (TrueTypeFontKey *)keyB; + return (a->size == b->size) && (a->dpi == b->dpi) && !strcmp(a->path, b->path); +} + +static int gr_ttf_font_cache_hash(void *key) +{ + TrueTypeFontKey *k = (TrueTypeFontKey *)key; + + uint32_t hash = fnv_hash(k->path, strlen(k->path)); + hash = fnv_hash_add(hash, k->size); + hash = fnv_hash_add(hash, k->dpi); + return hash; +} + +void *gr_ttf_loadFont(const char *filename, int size, int dpi) +{ + int error; + TrueTypeFont *res = NULL; + TrueTypeFontKey *key = NULL; + + pthread_mutex_lock(&font_data.mutex); + + if(font_data.fonts) + { + TrueTypeFontKey k = { + .size = size, + .dpi = dpi, + .path = (char*)filename + }; + + res = (TrueTypeFont *)hashmapGet(font_data.fonts, &k); + if(res) + { + ++res->refcount; + goto exit; + } + } + + if(!font_data.ft_library) + { + error = FT_Init_FreeType(&font_data.ft_library); + if(error) + { + fprintf(stderr, "Failed to init libfreetype! %d\n", error); + goto exit; + } + } + + FT_Face face; + error = FT_New_Face(font_data.ft_library, filename, 0, &face); + if(error) + { + fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error); + goto exit; + } + + error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi); + if(error) + { + fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error); + FT_Done_Face(face); + goto exit; + } + + res = (TrueTypeFont *)malloc(sizeof(TrueTypeFont)); + memset(res, 0, sizeof(TrueTypeFont)); + res->type = FONT_TYPE_TTF; + res->size = size; + res->dpi = dpi; + res->face = face; + res->max_height = -1; + res->base = -1; + res->refcount = 1; + res->glyph_cache = hashmapCreate(32, hashmapIntHash, hashmapIntEquals); + res->string_cache = hashmapCreate(128, gr_ttf_string_cache_hash, gr_ttf_string_cache_equals); + pthread_mutex_init(&res->mutex, 0); + + if(!font_data.fonts) + font_data.fonts = hashmapCreate(4, gr_ttf_font_cache_hash, gr_ttf_font_cache_equals); + + key = (TrueTypeFontKey *)malloc(sizeof(TrueTypeFontKey)); + memset(key, 0, sizeof(TrueTypeFontKey)); + key->path = strdup(filename); + key->size = size; + key->dpi = dpi; + + res->key = key; + + hashmapPut(font_data.fonts, key, res); + +exit: + pthread_mutex_unlock(&font_data.mutex); + return res; +} + +void *gr_ttf_scaleFont(void *font, int max_width, int measured_width) +{ + if (!font) + return NULL; + + TrueTypeFont *f = (TrueTypeFont *)font; + float scale_value = (float)(max_width) / (float)(measured_width); + int new_size = ((int)((float)f->size * scale_value)) - 1; + if (new_size < 1) + new_size = 1; + const char* file = f->key->path; + int dpi = f->dpi; + return gr_ttf_loadFont(file, new_size, dpi); +} + +static bool gr_ttf_freeFontCache(void *key, void *value, void *context __unused) +{ + TrueTypeCacheEntry *e = (TrueTypeCacheEntry *)value; + FT_Done_Glyph((FT_Glyph)e->glyph); + free(e); + free(key); + return true; +} + +static bool gr_ttf_freeStringCache(void *key, void *value, void *context __unused) +{ + StringCacheKey *k = (StringCacheKey *)key; + free(k->text); + free(k); + + StringCacheEntry *e = (StringCacheEntry *)value; + free(e->surface.data); + free(e); + return true; +} + +void gr_ttf_freeFont(void *font) +{ + pthread_mutex_lock(&font_data.mutex); + + TrueTypeFont *d = (TrueTypeFont *)font; + + if(--d->refcount == 0) + { + hashmapRemove(font_data.fonts, d->key); + + if(hashmapSize(font_data.fonts) == 0) + { + hashmapFree(font_data.fonts); + font_data.fonts = NULL; + } + + free(d->key->path); + free(d->key); + + FT_Done_Face(d->face); + hashmapForEach(d->string_cache, gr_ttf_freeStringCache, NULL); + hashmapFree(d->string_cache); + hashmapForEach(d->glyph_cache, gr_ttf_freeFontCache, NULL); + hashmapFree(d->glyph_cache); + pthread_mutex_destroy(&d->mutex); + free(d); + } + + pthread_mutex_unlock(&font_data.mutex); +} + +static TrueTypeCacheEntry *gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index) +{ + return (TrueTypeCacheEntry *)hashmapGet(font->glyph_cache, &char_index); +} + +static TrueTypeCacheEntry *gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index) +{ + TrueTypeCacheEntry *res = (TrueTypeCacheEntry *)hashmapGet(font->glyph_cache, &char_index); + if(!res) + { + int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER); + if(error) + { + fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error); + return NULL; + } + + FT_BitmapGlyph glyph; + error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph); + if(error) + { + fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error); + return NULL; + } + + res = (TrueTypeCacheEntry *)malloc(sizeof(TrueTypeCacheEntry)); + memset(res, 0, sizeof(TrueTypeCacheEntry)); + res->glyph = glyph; + FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox); + + int *key = (int *)malloc(sizeof(int)); + *key = char_index; + + hashmapPut(font->glyph_cache, key, res); + } + + return res; +} + +static int gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base) +{ + unsigned y; + uint8_t *src_itr = glyph->bitmap.buffer; + uint8_t *dest_itr = dest->data; + + if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) + { + fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode); + return -1; + } + + dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left); + + // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular), + // the result might end up being before the buffer - I'm not sure how to properly handle this. + if(dest_itr < dest->data) + dest_itr = dest->data; + + for(y = 0; y < glyph->bitmap.rows; ++y) + { + memcpy(dest_itr, src_itr, glyph->bitmap.width); + src_itr += glyph->bitmap.pitch; + dest_itr += dest->stride; + } + return 0; +} + +static void gr_ttf_calcMaxFontHeight(TrueTypeFont *f) +{ + char c; + int char_idx; + int error; + FT_Glyph glyph; + FT_BBox bbox; + FT_BBox bbox_glyph; + TrueTypeCacheEntry *ent; + + bbox.yMin = bbox_glyph.yMin = LONG_MAX; + bbox.yMax = bbox_glyph.yMax = LONG_MIN; + + for(c = '!'; c <= '~'; ++c) + { + char_idx = FT_Get_Char_Index(f->face, c); + ent = gr_ttf_glyph_cache_peek(f, char_idx); + if(ent) + { + bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin); + bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax); + } + else + { + error = FT_Load_Glyph(f->face, char_idx, 0); + if(error) + continue; + + error = FT_Get_Glyph(f->face->glyph, &glyph); + if(error) + continue; + + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph); + bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin); + bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax); + + FT_Done_Glyph(glyph); + } + } + + if(bbox.yMin > bbox.yMax) + bbox.yMin = bbox.yMax = 0; + + f->max_height = bbox.yMax - bbox.yMin; + f->base = bbox.yMax; + + // FIXME: twrp fonts have some padding on top, I'll add it here + // Should be fixed in the themes + f->max_height += f->size / 4; + f->base += f->size / 4; +} + +// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters! +static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width) +{ + TrueTypeFont *f = font; + TrueTypeCacheEntry *ent; + int bytes_rendered = 0, total_w = 0; + int utf_bytes = 0; + unsigned int unicode = 0; + int i, x, diff, char_idx, prev_idx = 0; + int height, base; + FT_Vector delta; + uint8_t *data = NULL; + const char *text_itr = text; + int *char_idxs; + int char_idxs_len = 0; + + char_idxs = (int *)malloc(strlen(text) * sizeof(int)); + + while(*text_itr) + { + utf_bytes = utf8_to_unicode(text_itr, &unicode); + text_itr += utf_bytes; + bytes_rendered += utf_bytes; + + char_idx = FT_Get_Char_Index(f->face, unicode); + char_idxs[char_idxs_len] = char_idx; + + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(ent) + { + diff = ent->glyph->root.advance.x >> 16; + + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + diff += delta.x >> 6; + } + + if(max_width != -1 && total_w + diff > max_width) + break; + + total_w += diff; + } + prev_idx = char_idx; + ++char_idxs_len; + } + + if(font->max_height == -1) + gr_ttf_calcMaxFontHeight(font); + + if(font->max_height == -1) + { + free(char_idxs); + return -1; + } + + height = font->max_height; + + data = (uint8_t *)malloc(total_w*height); + memset(data, 0, total_w*height); + x = 0; + prev_idx = 0; + + surface->version = sizeof(*surface); + surface->width = total_w; + surface->height = height; + surface->stride = total_w; + surface->data = (GGLubyte*)data; + surface->format = GGL_PIXEL_FORMAT_A_8; + + for(i = 0; i < char_idxs_len; ++i) + { + char_idx = char_idxs[i]; + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + x += delta.x >> 6; + } + + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(ent) + { + gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base); + x += ent->glyph->root.advance.x >> 16; + } + + prev_idx = char_idx; + } + + free(char_idxs); + return bytes_rendered; +} + +static StringCacheEntry *gr_ttf_string_cache_peek(TrueTypeFont *font, const char *text, int max_width) +{ + StringCacheEntry *res; + StringCacheKey k = { + .text = (char*)text, + .max_width = max_width + }; + + return (StringCacheEntry *)hashmapGet(font->string_cache, &k); +} + +static StringCacheEntry *gr_ttf_string_cache_get(TrueTypeFont *font, const char *text, int max_width) +{ + StringCacheEntry *res; + StringCacheKey k = { + .text = (char*)text, + .max_width = max_width + }; + + res = (StringCacheEntry *)hashmapGet(font->string_cache, &k); + if(!res) + { + res = (StringCacheEntry *)malloc(sizeof(StringCacheEntry)); + memset(res, 0, sizeof(StringCacheEntry)); + res->rendered_bytes = gr_ttf_render_text(font, &res->surface, text, max_width); + if(res->rendered_bytes < 0) + { + free(res); + return NULL; + } + + StringCacheKey *new_key = (StringCacheKey *)malloc(sizeof(StringCacheKey)); + memset(new_key, 0, sizeof(StringCacheKey)); + new_key->max_width = max_width; + new_key->text = strdup(text); + + res->key = new_key; + + if(font->string_cache_tail) + { + res->prev = font->string_cache_tail; + res->prev->next = res; + } + else + font->string_cache_head = res; + font->string_cache_tail = res; + + hashmapPut(font->string_cache, new_key, res); + } + else if(res->next) + { + // move this entry to the tail of the linked list + // if it isn't already there + if(res->prev) + res->prev->next = res->next; + + res->next->prev = res->prev; + + if(!res->prev) + font->string_cache_head = res->next; + + res->next = NULL; + res->prev = font->string_cache_tail; + res->prev->next = res; + font->string_cache_tail = res; + + // truncate old entries + if(hashmapSize(font->string_cache) >= STRING_CACHE_MAX_ENTRIES) + { + printf("Truncating string cache entries.\n"); + int i; + StringCacheEntry *ent; + for(i = 0; i < STRING_CACHE_TRUNCATE_ENTRIES; ++i) + { + ent = font->string_cache_head; + font->string_cache_head = ent->next; + font->string_cache_head->prev = NULL; + + hashmapRemove(font->string_cache, ent->key); + + gr_ttf_freeStringCache(ent->key, ent, NULL); + } + } + } + return res; +} + +int gr_ttf_measureEx(const char *s, void *font) +{ + TrueTypeFont *f = (TrueTypeFont *)font; + int res = -1; + + pthread_mutex_lock(&f->mutex); + StringCacheEntry *e = gr_ttf_string_cache_get(f, s, -1); + if(e) + res = e->surface.width; + pthread_mutex_unlock(&f->mutex); + + return res; +} + +int gr_ttf_maxExW(const char *s, void *font, int max_width) +{ + TrueTypeFont *f = (TrueTypeFont *)font; + TrueTypeCacheEntry *ent; + int max_bytes = 0, total_w = 0; + int utf_bytes, prev_utf_bytes = 0; + unsigned int unicode = 0; + int char_idx, prev_idx = 0; + FT_Vector delta; + StringCacheEntry *e; + + pthread_mutex_lock(&f->mutex); + + e = gr_ttf_string_cache_peek(f, s, max_width); + if(e) + { + max_bytes = e->rendered_bytes; + pthread_mutex_unlock(&f->mutex); + return max_bytes; + } + + while(*s) + { + utf_bytes = utf8_to_unicode(s, &unicode); + s += utf_bytes; + + char_idx = FT_Get_Char_Index(f->face, unicode); + if(FT_HAS_KERNING(f->face) && prev_idx && char_idx) + { + FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta); + total_w += delta.x >> 6; + } + prev_idx = char_idx; + + if(total_w > max_width) + { + max_bytes -= prev_utf_bytes; + break; + } + prev_utf_bytes = utf_bytes; + + ent = gr_ttf_glyph_cache_get(f, char_idx); + if(!ent) + continue; + + total_w += ent->glyph->root.advance.x >> 16; + max_bytes += utf_bytes; + } + pthread_mutex_unlock(&f->mutex); + return max_bytes; +} + +int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height) +{ + GGLContext *gl = (GGLContext *)context; + TrueTypeFont *font = (TrueTypeFont *)pFont; + + // not actualy max width, but max_width + x + if(max_width != -1) + { + max_width -= x; + if(max_width <= 0) + return 0; + } + + pthread_mutex_lock(&font->mutex); + + StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width); + if(!e) + { + pthread_mutex_unlock(&font->mutex); + return -1; + } + + int y_bottom = y + e->surface.height; + int res = e->rendered_bytes; + + if(max_height != -1 && max_height < y_bottom) + { + y_bottom = max_height; + if(y_bottom <= y) + { + pthread_mutex_unlock(&font->mutex); + return 0; + } + } + + gl->bindTexture(gl, &e->surface); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + + gl->enable(gl, GGL_TEXTURE_2D); + gl->texCoord2i(gl, -x, -y); + gl->recti(gl, x, y, x + e->surface.width, y_bottom); + gl->disable(gl, GGL_TEXTURE_2D); + + pthread_mutex_unlock(&font->mutex); + return res; +} + +int gr_ttf_getMaxFontHeight(void *font) +{ + int res; + TrueTypeFont *f = (TrueTypeFont *)font; + + pthread_mutex_lock(&f->mutex); + + if(f->max_height == -1) + gr_ttf_calcMaxFontHeight(f); + res = f->max_height; + + pthread_mutex_unlock(&f->mutex); + return res; +} + +static bool gr_ttf_dump_stats_count_string_cache(void *key __unused, void *value, void *context) +{ + int *string_cache_size = (int *)context; + StringCacheEntry *e = (StringCacheEntry *)value; + *string_cache_size += e->surface.height*e->surface.width + sizeof(StringCacheEntry); + return true; +} + +static bool gr_ttf_dump_stats_font(void *key, void *value, void *context) +{ + TrueTypeFontKey *k = (TrueTypeFontKey *)key; + TrueTypeFont *f = (TrueTypeFont *)value; + int *total_string_cache_size = (int *)context; + int string_cache_size = 0; + + pthread_mutex_lock(&f->mutex); + + hashmapForEach(f->string_cache, gr_ttf_dump_stats_count_string_cache, &string_cache_size); + + printf(" Font %s (size %d, dpi %d):\n" + " refcount: %d\n" + " max_height: %d\n" + " base: %d\n" + " glyph_cache: %d entries\n" + " string_cache: %d entries (%.2f kB)\n", + k->path, k->size, k->dpi, + f->refcount, f->max_height, f->base, + hashmapSize(f->glyph_cache), + hashmapSize(f->string_cache), ((double)string_cache_size)/1024); + + pthread_mutex_unlock(&f->mutex); + + *total_string_cache_size += string_cache_size; + return true; +} + +void gr_ttf_dump_stats(void) +{ + pthread_mutex_lock(&font_data.mutex); + + printf("TrueType fonts system stats: "); + if(!font_data.fonts) + printf("no truetype fonts loaded.\n"); + else + { + int total_string_cache_size = 0; + printf("%d fonts loaded.\n", hashmapSize(font_data.fonts)); + hashmapForEach(font_data.fonts, gr_ttf_dump_stats_font, &total_string_cache_size); + printf(" Total string cache size: %.2f kB\n", ((double)total_string_cache_size)/1024); + } + + pthread_mutex_unlock(&font_data.mutex); +} |