summaryrefslogtreecommitdiffstats
path: root/minuitwrp
diff options
context:
space:
mode:
Diffstat (limited to 'minuitwrp')
-rw-r--r--minuitwrp/Android.mk189
-rw-r--r--minuitwrp/events.cpp798
-rw-r--r--minuitwrp/graphics.cpp424
-rw-r--r--minuitwrp/graphics.h44
-rw-r--r--minuitwrp/graphics_adf.cpp285
-rw-r--r--minuitwrp/graphics_drm.cpp492
-rw-r--r--minuitwrp/graphics_fbdev.cpp348
-rw-r--r--minuitwrp/graphics_overlay.cpp673
-rw-r--r--minuitwrp/graphics_utils.cpp118
-rw-r--r--minuitwrp/include/linux/msm_ion.h157
-rw-r--r--minuitwrp/include/linux/msm_mdp.h992
-rw-r--r--minuitwrp/minui.h94
-rw-r--r--minuitwrp/resources.cpp455
-rw-r--r--minuitwrp/truetype.cpp817
14 files changed, 5886 insertions, 0 deletions
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
new file mode 100644
index 000000000..3f83c9754
--- /dev/null
+++ b/minuitwrp/Android.mk
@@ -0,0 +1,189 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ graphics.cpp \
+ graphics_fbdev.cpp \
+ resources.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
+ LOCAL_SRC_FILES += graphics_overlay.cpp
+ 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
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+LOCAL_SHARED_LIBRARIES += libcutils liblog libutils
+endif
+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..04545b64f
--- /dev/null
+++ b/minuitwrp/events.cpp
@@ -0,0 +1,798 @@
+/*
+ * 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)
+ {
+ LOGE("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)
+ {
+ LOGI("Blacklisting input device: %s\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) {
+ LOGI("Blacklisting input device: %s\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) {
+ LOGI("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. */
+ LOGI("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;
+
+ LOGI("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))) {
+#ifdef _EVENT_LOGGING
+ fprintf(stderr,"/dev/input/%s\n", de->d_name);
+#endif
+ 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)
+ {
+ LOGI("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..3dc88d9db
--- /dev/null
+++ b/minuitwrp/graphics.cpp
@@ -0,0 +1,424 @@
+/*
+ * 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;
+
+#ifdef MSM_BSP
+ 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");
+ }
+#endif
+
+#ifdef HAS_ADF
+ if (!gr_backend || !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_backend || !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_backend || !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);
+}