From fbb4353a247157d32208f8f133cd1ee42f4fbc49 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Mon, 28 Dec 2015 21:54:50 +0100 Subject: Update minuitwrp graphics in line with latest minui Note: events.cpp is still old code renamed to cpp to make it easier to call functions like gr_fb_width(). I had to modify AOSP fbdev code to provide a separate memory surface for drawing to as drawing directly to the framebuffer resulted in rendering taking about 5 times longer. I also modified AOSP adf code to provide a separate memory surface for drawing for the same performance reasons. The Nexus 9 supports adf graphics. Overlay graphics work on at least one device. Overlay provides a separate memory buffer already so performance is good. I do not have a drm device yet that I know of. I made some attempt to update the drm code to determine the correct pixel format based on the drm graphics format, but what is available in pixel flinger and what is available in drm do not line up all that well. Reports are that the Pixel C is using drm graphics, but performance is slow, likely due to the use of a mmap instead of a memory buffyer. Change-Id: Ibd45bccca6ac2cb826037aa9b2aa5065cf683eed --- data.cpp | 2 +- gui/action.cpp | 3 +- gui/animation.cpp | 2 +- gui/blanktimer.cpp | 10 +- gui/button.cpp | 2 +- gui/checkbox.cpp | 2 +- gui/console.cpp | 2 +- gui/fileselector.cpp | 2 +- gui/fill.cpp | 2 +- gui/gui.cpp | 2 +- gui/image.cpp | 2 +- gui/input.cpp | 28 +- gui/keyboard.cpp | 6 +- gui/listbox.cpp | 2 +- gui/mousecursor.cpp | 2 +- gui/pages.cpp | 2 +- gui/partitionlist.cpp | 2 +- gui/patternpassword.cpp | 2 +- gui/progressbar.cpp | 2 +- gui/resources.cpp | 2 +- gui/resources.hpp | 2 +- gui/scrolllist.cpp | 2 +- gui/slider.cpp | 2 +- gui/slidervalue.cpp | 4 +- gui/terminal.cpp | 8 +- gui/text.cpp | 10 +- gui/textbox.cpp | 4 +- minuitwrp/Android.mk | 44 ++- minuitwrp/events.c | 792 --------------------------------------- minuitwrp/events.cpp | 792 +++++++++++++++++++++++++++++++++++++++ minuitwrp/graphics.c | 754 ------------------------------------- minuitwrp/graphics.cpp | 416 +++++++++++++++++++++ minuitwrp/graphics.h | 44 +++ minuitwrp/graphics_adf.cpp | 285 ++++++++++++++ minuitwrp/graphics_drm.cpp | 492 +++++++++++++++++++++++++ minuitwrp/graphics_fbdev.cpp | 281 ++++++++++++++ minuitwrp/graphics_overlay.c | 463 ----------------------- minuitwrp/graphics_overlay.cpp | 653 ++++++++++++++++++++++++++++++++ minuitwrp/graphics_utils.c | 113 ------ minuitwrp/graphics_utils.cpp | 118 ++++++ minuitwrp/minui.h | 24 +- minuitwrp/resources.c | 443 ---------------------- minuitwrp/resources.cpp | 447 ++++++++++++++++++++++ minuitwrp/truetype.c | 816 ---------------------------------------- minuitwrp/truetype.cpp | 817 +++++++++++++++++++++++++++++++++++++++++ 45 files changed, 4450 insertions(+), 3455 deletions(-) delete mode 100644 minuitwrp/events.c create mode 100644 minuitwrp/events.cpp delete mode 100644 minuitwrp/graphics.c create mode 100644 minuitwrp/graphics.cpp create mode 100644 minuitwrp/graphics.h create mode 100644 minuitwrp/graphics_adf.cpp create mode 100644 minuitwrp/graphics_drm.cpp create mode 100644 minuitwrp/graphics_fbdev.cpp delete mode 100644 minuitwrp/graphics_overlay.c create mode 100644 minuitwrp/graphics_overlay.cpp delete mode 100644 minuitwrp/graphics_utils.c create mode 100644 minuitwrp/graphics_utils.cpp delete mode 100644 minuitwrp/resources.c create mode 100644 minuitwrp/resources.cpp delete mode 100644 minuitwrp/truetype.c create mode 100644 minuitwrp/truetype.cpp diff --git a/data.cpp b/data.cpp index 77e8f8de4..07f9e61de 100644 --- a/data.cpp +++ b/data.cpp @@ -59,9 +59,9 @@ extern "C" { #include "twcommon.h" #include "gui/pages.h" - #include "minuitwrp/minui.h" void gui_notifyVarChange(const char *name, const char* value); } +#include "minuitwrp/minui.h" #define FILE_VERSION 0x00010010 diff --git a/gui/action.cpp b/gui/action.cpp index 8895e4013..240db8f24 100644 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -43,15 +43,16 @@ #include "../adb_install.h" #include "../fuse_sideload.h" #include "blanktimer.hpp" + extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" #include "../variables.h" #include "../twinstall.h" #include "cutils/properties.h" #include "../adb_install.h" #include "../set_metadata.h" }; +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/animation.cpp b/gui/animation.cpp index 888b4ab08..d45373d81 100644 --- a/gui/animation.cpp +++ b/gui/animation.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/blanktimer.cpp b/gui/blanktimer.cpp index 06208e273..4662b1fab 100644 --- a/gui/blanktimer.cpp +++ b/gui/blanktimer.cpp @@ -26,9 +26,9 @@ using namespace std; #include "blanktimer.hpp" #include "../data.hpp" extern "C" { -#include "../minuitwrp/minui.h" #include "../twcommon.h" } +#include "../minuitwrp/minui.h" #include "../twrp-functions.hpp" #include "../variables.h" @@ -71,7 +71,8 @@ void blanktimer::checkForTimeout() { PageManager::ChangeOverlay("lock"); } #ifndef TW_NO_SCREEN_BLANK - if (state == kOff && gr_fb_blank(1) >= 0) { + if (state == kOff) { + gr_fb_blank(true); state = kBlanked; } #endif @@ -97,10 +98,7 @@ void blanktimer::resetTimerAndUnblank(void) { switch (state) { case kBlanked: #ifndef TW_NO_SCREEN_BLANK - if (gr_fb_blank(0) < 0) { - LOGINFO("blanktimer::resetTimerAndUnblank failed to gr_fb_blank(0)\n"); - break; - } + gr_fb_blank(false); #endif // TODO: this is asymmetric with postscreenblank.sh - shouldn't it be under the next case label? TWFunc::check_and_run_script("/sbin/postscreenunblank.sh", "unblank"); diff --git a/gui/button.cpp b/gui/button.cpp index a4c1b52bd..45614e461 100644 --- a/gui/button.cpp +++ b/gui/button.cpp @@ -36,8 +36,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/checkbox.cpp b/gui/checkbox.cpp index 1760bac25..e79f0cdcd 100644 --- a/gui/checkbox.cpp +++ b/gui/checkbox.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/console.cpp b/gui/console.cpp index b5204fb7c..6f375ff28 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -37,8 +37,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp index a97ff34b1..23f235c75 100644 --- a/gui/fileselector.cpp +++ b/gui/fileselector.cpp @@ -23,8 +23,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/fill.cpp b/gui/fill.cpp index b315cd08a..d0a1cfda7 100644 --- a/gui/fill.cpp +++ b/gui/fill.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/gui.cpp b/gui/gui.cpp index 7cf21b0a0..f8a6a73e2 100644 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -38,9 +38,9 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" #include } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/image.cpp b/gui/image.cpp index 8b43aaa34..2107d5506 100644 --- a/gui/image.cpp +++ b/gui/image.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/input.cpp b/gui/input.cpp index 68bd163b9..c12ecc2b2 100644 --- a/gui/input.cpp +++ b/gui/input.cpp @@ -39,8 +39,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -196,7 +196,7 @@ int GUIInput::HandleTextLocation(int x) maskedValue += mMask; displayValue = maskedValue; } - textWidth = gr_measureEx(displayValue.c_str(), fontResource); + textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); if (textWidth <= mRenderW) { lastX = x; scrollingX = 0; @@ -209,7 +209,7 @@ int GUIInput::HandleTextLocation(int x) if (skipChars && skipChars < displayValue.size()) displayValue.erase(0, skipChars); - textWidth = gr_measureEx(displayValue.c_str(), fontResource); + textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); mRendered = false; int deltaX, deltaText, newWidth; @@ -222,11 +222,11 @@ int GUIInput::HandleTextLocation(int x) if (mCursorLocation == -1) { displayValue = originalValue; skipChars = 0; - textWidth = gr_measureEx(displayValue.c_str(), fontResource); + textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); while (textWidth > mRenderW) { displayValue.erase(0, 1); skipChars++; - textWidth = gr_measureEx(displayValue.c_str(), fontResource); + textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); } scrollingX = mRenderW - textWidth; mInputText->SkipCharCount(skipChars); @@ -237,12 +237,12 @@ int GUIInput::HandleTextLocation(int x) cursorLocate = displayValue; cursorLocate.resize(mCursorLocation); - textWidth = gr_measureEx(cursorLocate.c_str(), fontResource); + textWidth = gr_ttf_measureEx(cursorLocate.c_str(), fontResource); while (textWidth > mRenderW) { skipChars++; mCursorLocation--; cursorLocate.erase(0, 1); - textWidth = gr_measureEx(cursorLocate.c_str(), fontResource); + textWidth = gr_ttf_measureEx(cursorLocate.c_str(), fontResource); adjust_scrollingX = -1; } if (adjust_scrollingX) { @@ -261,7 +261,7 @@ int GUIInput::HandleTextLocation(int x) } insertChar = originalValue.substr(skipChars - 1, 1); displayValue.insert(0, insertChar); - newWidth = gr_measureEx(displayValue.c_str(), fontResource); + newWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); deltaText = newWidth - textWidth; if (newWidth > mRenderW) { scrollingX = mRenderW - textWidth; @@ -289,7 +289,7 @@ int GUIInput::HandleTextLocation(int x) } insertChar = originalValue.substr(skipChars - 1, 1); displayValue.insert(0, insertChar); - newWidth = gr_measureEx(displayValue.c_str(), fontResource); + newWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); deltaText = newWidth - textWidth; if (deltaText < deltaX) { lastX += deltaText; @@ -324,7 +324,7 @@ int GUIInput::HandleTextLocation(int x) deltaX = lastX - x; displayValue.erase(0, 1); skipChars++; - newWidth = gr_measureEx(displayValue.c_str(), fontResource); + newWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); deltaText = textWidth - newWidth; if (newWidth <= mRenderW) { scrollingX = mRenderW - newWidth; @@ -407,15 +407,15 @@ int GUIInput::Render(void) cursorDisplay = displayValue; cursorDisplay.resize(mCursorLocation); - cursorX = gr_measureEx(cursorDisplay.c_str(), fontResource) + mRenderX; + cursorX = gr_ttf_measureEx(cursorDisplay.c_str(), fontResource) + mRenderX; } else { // Cursor location is after the end of the text - reset to -1 mCursorLocation = -1; - cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX; + cursorX = gr_ttf_measureEx(displayValue.c_str(), fontResource) + mRenderX; } } else { // Cursor is at the end (-1) - cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX; + cursorX = gr_ttf_measureEx(displayValue.c_str(), fontResource) + mRenderX; } } cursorX += scrollingX; @@ -534,7 +534,7 @@ int GUIInput::NotifyTouch(TOUCH_STATE state, int x, int y) for(index=0; index x) { if (index > 0) mCursorLocation = index - 1; diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp index 3b8fdc9ae..4a78ad798 100644 --- a/gui/keyboard.cpp +++ b/gui/keyboard.cpp @@ -25,9 +25,9 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" #include "gui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -333,7 +333,7 @@ void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH) else if (!labelText.empty()) { void* fontResource = labelFont->GetResource(); - int textW = gr_measureEx(labelText.c_str(), fontResource); + int textW = gr_ttf_measureEx(labelText.c_str(), fontResource); int textH = labelFont->GetHeight(); int textX = keyX + (keyW - textW) / 2; int textY = keyY + (keyH - textH) / 2; @@ -347,7 +347,7 @@ void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH) gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha); string text(1, keychar); int textH = mLongpressFont->GetHeight(); - int textW = gr_measureEx(text.c_str(), fontResource); + int textW = gr_ttf_measureEx(text.c_str(), fontResource); int textX = keyX + keyW - longpressOffsetX - textW; int textY = keyY + longpressOffsetY; gr_textEx_scaleW(textX, textY, text.c_str(), fontResource, keyW, TOP_LEFT, 0); diff --git a/gui/listbox.cpp b/gui/listbox.cpp index b0d2731fe..e10fee72a 100644 --- a/gui/listbox.cpp +++ b/gui/listbox.cpp @@ -20,8 +20,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/mousecursor.cpp b/gui/mousecursor.cpp index 84e6322a4..bd730df6b 100644 --- a/gui/mousecursor.cpp +++ b/gui/mousecursor.cpp @@ -17,8 +17,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/pages.cpp b/gui/pages.cpp index c45566232..4a4c52361 100644 --- a/gui/pages.cpp +++ b/gui/pages.cpp @@ -39,11 +39,11 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" #include "../minzip/SysUtil.h" #include "../minzip/Zip.h" #include "gui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp index 102802d7d..ff207fa9a 100644 --- a/gui/partitionlist.cpp +++ b/gui/partitionlist.cpp @@ -22,8 +22,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/patternpassword.cpp b/gui/patternpassword.cpp index a6ab83128..96054c15f 100644 --- a/gui/patternpassword.cpp +++ b/gui/patternpassword.cpp @@ -13,8 +13,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/progressbar.cpp b/gui/progressbar.cpp index a478a40d0..fc2a4976c 100644 --- a/gui/progressbar.cpp +++ b/gui/progressbar.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/resources.cpp b/gui/resources.cpp index a8f41991b..e7611dd9f 100644 --- a/gui/resources.cpp +++ b/gui/resources.cpp @@ -14,9 +14,9 @@ #include "../minzip/Zip.h" extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" #include "gui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/resources.hpp b/gui/resources.hpp index ce06d9679..4548c10b4 100644 --- a/gui/resources.hpp +++ b/gui/resources.hpp @@ -49,7 +49,7 @@ public: public: void* GetResource() { return this ? mFont : NULL; } - int GetHeight() { return gr_getMaxFontHeight(this ? mFont : NULL); } + int GetHeight() { return gr_ttf_getMaxFontHeight(this ? mFont : NULL); } void Override(xml_node<>* node, ZipArchive* pZip); protected: diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp index 6e974f205..291b3820b 100644 --- a/gui/scrolllist.cpp +++ b/gui/scrolllist.cpp @@ -20,8 +20,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/slider.cpp b/gui/slider.cpp index 6ee2024d5..59b295bd0 100644 --- a/gui/slider.cpp +++ b/gui/slider.cpp @@ -21,8 +21,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" diff --git a/gui/slidervalue.cpp b/gui/slidervalue.cpp index 8a44a3cb7..088620a21 100644 --- a/gui/slidervalue.cpp +++ b/gui/slidervalue.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -249,7 +249,7 @@ int GUISliderValue::measureText(const std::string& str) void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); - return gr_measureEx(str.c_str(), fontResource); + return gr_ttf_measureEx(str.c_str(), fontResource); } int GUISliderValue::Render(void) diff --git a/gui/terminal.cpp b/gui/terminal.cpp index 044782e4e..29b0e8469 100644 --- a/gui/terminal.cpp +++ b/gui/terminal.cpp @@ -32,8 +32,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -869,10 +869,10 @@ void GUITerminal::RenderItem(size_t itemindex, int yPos, bool selected) // render cursor int cursorX = engine->getCursorX(); std::string leftOfCursor = line.substr(0, cursorX); - int x = gr_measureEx(leftOfCursor.c_str(), mFont->GetResource()); + int x = gr_ttf_measureEx(leftOfCursor.c_str(), mFont->GetResource()); // note that this single character can be a UTF-8 sequence std::string atCursor = (size_t)cursorX < line.length() ? line.substr(cursorX, 1) : " "; - int w = gr_measureEx(atCursor.c_str(), mFont->GetResource()); + int w = gr_ttf_measureEx(atCursor.c_str(), mFont->GetResource()); gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); gr_fill(mRenderX + x, yPos, w, actualItemHeight); gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha); @@ -890,7 +890,7 @@ void GUITerminal::InitAndResize() // make sure the shell is started engine->initPty(); // send window resize - int charWidth = gr_measureEx("N", mFont->GetResource()); + int charWidth = gr_ttf_measureEx("N", mFont->GetResource()); engine->setSize(mRenderW / charWidth, GetDisplayItemCount(), mRenderW, mRenderH); } diff --git a/gui/text.cpp b/gui/text.cpp index 9898e2838..6498af62c 100644 --- a/gui/text.cpp +++ b/gui/text.cpp @@ -19,8 +19,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -47,6 +47,8 @@ GUIText::GUIText(xml_node<>* node) // Load the font, and possibly override the color mFont = LoadAttrFont(FindNode(node, "font"), "resource"); + if (!mFont) + return; mColor = LoadAttrColor(FindNode(node, "font"), "color", mColor); mHighlightColor = LoadAttrColor(FindNode(node, "font"), "highlightcolor", mColor); @@ -88,6 +90,8 @@ int GUIText::Render(void) void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); + else + return -1; mLastValue = gui_parse_text(mText); string displayValue = mLastValue; @@ -98,7 +102,7 @@ int GUIText::Render(void) mVarChanged = 0; int x = mRenderX, y = mRenderY; - int width = gr_measureEx(displayValue.c_str(), fontResource); + int width = gr_ttf_measureEx(displayValue.c_str(), fontResource); if (isHighlighted) gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha); @@ -145,7 +149,7 @@ int GUIText::GetCurrentBounds(int& w, int& h) h = mFontHeight; mLastValue = gui_parse_text(mText); - w = gr_measureEx(mLastValue.c_str(), fontResource); + w = gr_ttf_measureEx(mLastValue.c_str(), fontResource); return 0; } diff --git a/gui/textbox.cpp b/gui/textbox.cpp index 277297fec..2e2512c50 100644 --- a/gui/textbox.cpp +++ b/gui/textbox.cpp @@ -22,8 +22,8 @@ extern "C" { #include "../twcommon.h" -#include "../minuitwrp/minui.h" } +#include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" @@ -86,7 +86,7 @@ void GUITextBox::RenderItem(size_t itemindex, int yPos, bool selected __unused) // render text const char* text = rText[itemindex].c_str(); - gr_textEx(mRenderX, yPos, text, mFont->GetResource()); + gr_textEx_scaleW(mRenderX, yPos, text, mFont->GetResource(), mRenderW, TOP_LEFT, 0); } void GUITextBox::NotifySelect(size_t item_selected __unused) diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk index 01225c32e..39c61c40e 100644 --- a/minuitwrp/Android.mk +++ b/minuitwrp/Android.mk @@ -2,12 +2,20 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := events.c resources.c graphics_overlay.c graphics_utils.c truetype.c +LOCAL_SRC_FILES := \ + graphics.cpp \ + graphics_fbdev.cpp \ + resources.cpp \ + graphics_overlay.cpp \ + truetype.cpp \ + graphics_utils.cpp \ + events.cpp ifneq ($(TW_BOARD_CUSTOM_GRAPHICS),) - LOCAL_SRC_FILES += $(TW_BOARD_CUSTOM_GRAPHICS) -else - LOCAL_SRC_FILES += graphics.c + $(warning ****************************************************************************) + $(warning * TW_BOARD_CUSTOM_GRAPHICS support has been deprecated in TWRP. *) + $(warning ****************************************************************************) + $(error stopping) endif ifeq ($(TW_TARGET_USES_QCOM_BSP), true) @@ -17,25 +25,39 @@ ifeq ($(TW_TARGET_USES_QCOM_BSP), true) 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 + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include else LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS) endif endif else - LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/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/jpeg \ - external/freetype/include + external/freetype/include \ + external/libcxx/include ifeq ($(RECOVERY_TOUCHSCREEN_SWAP_XY), true) LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_SWAP_XY @@ -76,6 +98,12 @@ ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGB_565) LOCAL_CFLAGS += -DRECOVERY_RGB_565 endif +ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +else + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 +endif + ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGBX_8888") LOCAL_CFLAGS += -DRECOVERY_RGBX endif @@ -120,6 +148,8 @@ ifeq ($(TW_DISABLE_TTF), true) $(error stopping) endif +LOCAL_CLANG := true + LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\" LOCAL_SHARED_LIBRARIES += libft2 libz libc libcutils libjpeg libpng libutils LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp diff --git a/minuitwrp/events.c b/minuitwrp/events.c deleted file mode 100644 index 4e10e72fd..000000000 --- a/minuitwrp/events.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "../common.h" - -#include "minui.h" - -//#define _EVENT_LOGGING - -#define MAX_DEVICES 32 - -#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable" -#define VIBRATOR_TIME_MS 50 - -#ifndef SYN_REPORT -#define SYN_REPORT 0x00 -#endif -#ifndef SYN_CONFIG -#define SYN_CONFIG 0x01 -#endif -#ifndef SYN_MT_REPORT -#define SYN_MT_REPORT 0x02 -#endif - -#define ABS_MT_POSITION 0x2a /* Group a set of X and Y */ -#define ABS_MT_AMPLITUDE 0x2b /* Group a set of Z and W */ -#define ABS_MT_SLOT 0x2f -#define ABS_MT_TOUCH_MAJOR 0x30 -#define ABS_MT_TOUCH_MINOR 0x31 -#define ABS_MT_WIDTH_MAJOR 0x32 -#define ABS_MT_WIDTH_MINOR 0x33 -#define ABS_MT_ORIENTATION 0x34 -#define ABS_MT_POSITION_X 0x35 -#define ABS_MT_POSITION_Y 0x36 -#define ABS_MT_TOOL_TYPE 0x37 -#define ABS_MT_BLOB_ID 0x38 -#define ABS_MT_TRACKING_ID 0x39 -#define ABS_MT_PRESSURE 0x3a -#define ABS_MT_DISTANCE 0x3b - -enum { - DOWN_NOT, - DOWN_SENT, - DOWN_RELEASED, -}; - -struct virtualkey { - int scancode; - int centerx, centery; - int width, height; -}; - -struct position { - int x, y; - int synced; - struct input_absinfo xi, yi; -}; - -struct ev { - struct pollfd *fd; - - struct virtualkey *vks; - int vk_count; - - char deviceName[64]; - - int ignored; - - struct position p, mt_p; - int down; -}; - -static struct pollfd ev_fds[MAX_DEVICES]; -static struct ev evs[MAX_DEVICES]; -static unsigned ev_count = 0; -static struct timeval lastInputStat; -static unsigned long lastInputMTime; -static int has_mouse = 0; - -static inline int ABS(int x) { - return x<0?-x:x; -} - -int vibrate(int timeout_ms) -{ - char str[20]; - int fd; - int ret; - - if (timeout_ms > 10000) timeout_ms = 1000; - - fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY); - if (fd < 0) - return -1; - - ret = snprintf(str, sizeof(str), "%d", timeout_ms); - ret = write(fd, str, ret); - close(fd); - - if (ret < 0) - return -1; - - return 0; -} - -/* Returns empty tokens */ -static char *vk_strtok_r(char *str, const char *delim, char **save_str) -{ - if(!str) - { - if(!*save_str) - return NULL; - - str = (*save_str) + 1; - } - *save_str = strpbrk(str, delim); - - if (*save_str) - **save_str = '\0'; - - return str; -} - -static int vk_init(struct ev *e) -{ - char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys."; - char vks[2048], *ts = NULL; - ssize_t len; - int vk_fd; - int i; - - e->vk_count = 0; - - len = strlen(vk_path); - len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(e->deviceName)), e->deviceName); - if (len <= 0) - { - printf("Unable to query event object.\n"); - return -1; - } -#ifdef _EVENT_LOGGING - printf("Event object: %s\n", e->deviceName); -#endif - -#ifdef WHITELIST_INPUT - if (strcmp(e->deviceName, EXPAND(WHITELIST_INPUT)) != 0) - { - e->ignored = 1; - } -#else -#ifndef TW_INPUT_BLACKLIST - // Blacklist these "input" devices - if (strcmp(e->deviceName, "bma250") == 0 || strcmp(e->deviceName, "bma150") == 0) - { - printf("blacklisting %s input device\n", e->deviceName); - e->ignored = 1; - } -#else - char* bl = strdup(EXPAND(TW_INPUT_BLACKLIST)); - char* blacklist = strtok(bl, "\n"); - - while (blacklist != NULL) { - if (strcmp(e->deviceName, blacklist) == 0) { - printf("blacklisting %s input device\n", blacklist); - e->ignored = 1; - } - blacklist = strtok(NULL, "\n"); - } - free(bl); -#endif -#endif - - strcat(vk_path, e->deviceName); - - // Some devices split the keys from the touchscreen - e->vk_count = 0; - vk_fd = open(vk_path, O_RDONLY); - if (vk_fd >= 0) - { - len = read(vk_fd, vks, sizeof(vks)-1); - close(vk_fd); - if (len <= 0) - return -1; - - vks[len] = '\0'; - - /* Parse a line like: - keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:... - */ - for (ts = vks, e->vk_count = 1; *ts; ++ts) { - if (*ts == ':') - ++e->vk_count; - } - - if (e->vk_count % 6) { - printf("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6); - } - e->vk_count /= 6; - if (e->vk_count <= 0) - return -1; - - e->down = DOWN_NOT; - } - - ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi); - ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi); - e->p.synced = 0; -#ifdef _EVENT_LOGGING - printf("EV: ST minX: %d maxX: %d minY: %d maxY: %d\n", e->p.xi.minimum, e->p.xi.maximum, e->p.yi.minimum, e->p.yi.maximum); -#endif - - ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi); - ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi); - e->mt_p.synced = 0; -#ifdef _EVENT_LOGGING - printf("EV: MT minX: %d maxX: %d minY: %d maxY: %d\n", e->mt_p.xi.minimum, e->mt_p.xi.maximum, e->mt_p.yi.minimum, e->mt_p.yi.maximum); -#endif - - e->vks = malloc(sizeof(*e->vks) * e->vk_count); - - for (i = 0; i < e->vk_count; ++i) { - char *token[6]; - int j; - - for (j = 0; j < 6; ++j) { - token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts); - } - - if (strcmp(token[0], "0x01") != 0) { - /* Java does string compare, so we do too. */ - printf("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]); - continue; - } - - e->vks[i].scancode = strtol(token[1], NULL, 0); - e->vks[i].centerx = strtol(token[2], NULL, 0); - e->vks[i].centery = strtol(token[3], NULL, 0); - e->vks[i].width = strtol(token[4], NULL, 0); - e->vks[i].height = strtol(token[5], NULL, 0); - } - - return 0; -} - -#define BITS_PER_LONG (sizeof(long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define LONG(x) ((x)/BITS_PER_LONG) -#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) - -// Check for EV_REL (REL_X and REL_Y) and, because touchscreens can have those too, -// check also for EV_KEY (BTN_LEFT and BTN_RIGHT) -static void check_mouse(int fd) -{ - 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; - - has_mouse = 1; -} - -int ev_has_mouse(void) -{ - return has_mouse; -} - -int ev_init(void) -{ - DIR *dir; - struct dirent *de; - int fd; - - has_mouse = 0; - - dir = opendir("/dev/input"); - if(dir != 0) { - while((de = readdir(dir))) { -// fprintf(stderr,"/dev/input/%s\n", de->d_name); - if(strncmp(de->d_name,"event",5)) continue; - fd = openat(dirfd(dir), de->d_name, O_RDONLY); - if(fd < 0) continue; - - ev_fds[ev_count].fd = fd; - ev_fds[ev_count].events = POLLIN; - evs[ev_count].fd = &ev_fds[ev_count]; - - /* Load virtualkeys if there are any */ - vk_init(&evs[ev_count]); - - check_mouse(fd); - - ev_count++; - if(ev_count == MAX_DEVICES) break; - } - closedir(dir); - } - - struct stat st; - if(stat("/dev/input", &st) >= 0) - lastInputMTime = st.st_mtime; - gettimeofday(&lastInputStat, NULL); - - return 0; -} - -void ev_exit(void) -{ - while (ev_count-- > 0) { - if (evs[ev_count].vk_count) { - free(evs[ev_count].vks); - evs[ev_count].vk_count = 0; - } - close(ev_fds[ev_count].fd); - } - ev_count = 0; -} - -static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size) -{ - int screen_pos; - - if (info->minimum == info->maximum) - return 0; - - screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum); - return (screen_pos >= 0 && screen_pos < screen_size); -} - -static int vk_tp_to_screen(struct position *p, int *x, int *y) -{ - if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum) - { - // In this case, we assume the screen dimensions are the same. - *x = p->x; - *y = p->y; - return 0; - } - -#ifdef _EVENT_LOGGING - printf("EV: p->x=%d x-range=%d,%d fb-width=%d\n", p->x, p->xi.minimum, p->xi.maximum, gr_fb_width()); -#endif - -#ifndef RECOVERY_TOUCHSCREEN_SWAP_XY - int fb_width = gr_fb_width(); - int fb_height = gr_fb_height(); -#else - // We need to swap the scaling sizes, too - int fb_width = gr_fb_height(); - int fb_height = gr_fb_width(); -#endif - - *x = (p->x - p->xi.minimum) * (fb_width - 1) / (p->xi.maximum - p->xi.minimum); - *y = (p->y - p->yi.minimum) * (fb_height - 1) / (p->yi.maximum - p->yi.minimum); - - if (*x >= 0 && *x < fb_width && - *y >= 0 && *y < fb_height) - { - return 0; - } - - return 1; -} - -/* Translate a virtual key in to a real key event, if needed */ -/* Returns non-zero when the event should be consumed */ -static int vk_modify(struct ev *e, struct input_event *ev) -{ - static int downX = -1, downY = -1; - static int discard = 0; - static int last_virt_key = 0; - static int lastWasSynReport = 0; - static int touchReleaseOnNextSynReport = 0; - static int use_tracking_id_negative_as_touch_release = 0; // On some devices, type: 3 code: 39 value: -1, aka EV_ABS ABS_MT_TRACKING_ID -1 indicates a true touch release - int i; - int x, y; - - // This is used to ditch useless event handlers, like an accelerometer - if (e->ignored) return 1; - - if (ev->type == EV_REL && ev->code == REL_Z) - { - // This appears to be an accelerometer or another strange input device. It's not the touchscreen. -#ifdef _EVENT_LOGGING - printf("EV: Device disabled due to non-touchscreen messages.\n"); -#endif - e->ignored = 1; - return 1; - } - -#ifdef _EVENT_LOGGING - printf("EV: %s => type: %x code: %x value: %d\n", e->deviceName, ev->type, ev->code, ev->value); -#endif - - // Handle keyboard events, value of 1 indicates key down, 0 indicates key up - if (ev->type == EV_KEY) { - return 0; - } - - if (ev->type == EV_ABS) { - switch (ev->code) { - - case ABS_X: //00 - e->p.synced |= 0x01; - e->p.x = ev->value; -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_X %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_Y: //01 - e->p.synced |= 0x02; - e->p.y = ev->value; -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_Y %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_POSITION: //2a - e->mt_p.synced = 0x03; - if (ev->value == (1 << 31)) - { -#ifndef TW_IGNORE_MT_POSITION_0 - e->mt_p.x = 0; - e->mt_p.y = 0; - lastWasSynReport = 1; -#endif -#ifdef _EVENT_LOGGING -#ifndef TW_IGNORE_MT_POSITION_0 - printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x and y to 0 and lastWasSynReport to 1\n", e->deviceName, ev->value); -#else - printf("Ignoring ABS_MT_POSITION 0\n", e->deviceName, ev->value); -#endif -#endif - } - else - { - lastWasSynReport = 0; - e->mt_p.x = (ev->value & 0x7FFF0000) >> 16; - e->mt_p.y = (ev->value & 0xFFFF); -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x: %d and y: %d and lastWasSynReport to 0\n", e->deviceName, ev->value, (ev->value & 0x7FFF0000) >> 16, (ev->value & 0xFFFF)); -#endif - } - break; - - case ABS_MT_TOUCH_MAJOR: //30 - if (ev->value == 0) - { -#ifndef TW_IGNORE_MAJOR_AXIS_0 - // We're in a touch release, although some devices will still send positions as well - e->mt_p.x = 0; - e->mt_p.y = 0; - touchReleaseOnNextSynReport = 1; -#endif - } -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_TOUCH_MAJOR %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_PRESSURE: //3a - if (ev->value == 0) - { - // We're in a touch release, although some devices will still send positions as well - e->mt_p.x = 0; - e->mt_p.y = 0; - touchReleaseOnNextSynReport = 1; - } -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_PRESSURE %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_POSITION_X: //35 - e->mt_p.synced |= 0x01; - e->mt_p.x = ev->value; -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_POSITION_X %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_POSITION_Y: //36 - e->mt_p.synced |= 0x02; - e->mt_p.y = ev->value; -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_POSITION_Y %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_TOUCH_MINOR: //31 -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_TOUCH_MINOR %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_WIDTH_MAJOR: //32 -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_WIDTH_MAJOR %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_WIDTH_MINOR: //33 -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_WIDTH_MINOR %d\n", e->deviceName, ev->value); -#endif - break; - - case ABS_MT_TRACKING_ID: //39 -#ifdef TW_IGNORE_ABS_MT_TRACKING_ID -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d ignored\n", e->deviceName, ev->value); -#endif - return 1; -#endif - if (ev->value < 0) { - e->mt_p.x = 0; - e->mt_p.y = 0; - touchReleaseOnNextSynReport = 2; - use_tracking_id_negative_as_touch_release = 1; -#ifdef _EVENT_LOGGING - if (use_tracking_id_negative_as_touch_release) - printf("using ABS_MT_TRACKING_ID value -1 to indicate touch releases\n"); -#endif - } -#ifdef _EVENT_LOGGING - printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d\n", e->deviceName, ev->value); -#endif - break; - -#ifdef _EVENT_LOGGING - // These are for touch logging purposes only - case ABS_MT_ORIENTATION: //34 - printf("EV: %s => EV_ABS ABS_MT_ORIENTATION %d\n", e->deviceName, ev->value); - return 1; - break; - - case ABS_MT_TOOL_TYPE: //37 - LOGI("EV: %s => EV_ABS ABS_MT_TOOL_TYPE %d\n", e->deviceName, ev->value); - return 1; - break; - - case ABS_MT_BLOB_ID: //38 - printf("EV: %s => EV_ABS ABS_MT_BLOB_ID %d\n", e->deviceName, ev->value); - return 1; - break; - - case ABS_MT_DISTANCE: //3b - printf("EV: %s => EV_ABS ABS_MT_DISTANCE %d\n", e->deviceName, ev->value); - return 1; - break; - case ABS_MT_SLOT: - printf("EV: %s => ABS_MT_SLOT %d\n", e->deviceName, ev->value); - return 1; - break; -#endif - - default: - // This is an unhandled message, just skip it - return 1; - } - - if (ev->code != ABS_MT_POSITION) - { - lastWasSynReport = 0; - return 1; - } - } - - // Check if we should ignore the message - if (ev->code != ABS_MT_POSITION && (ev->type != EV_SYN || (ev->code != SYN_REPORT && ev->code != SYN_MT_REPORT))) - { - lastWasSynReport = 0; - return 0; - } - -#ifdef _EVENT_LOGGING - if (ev->type == EV_SYN && ev->code == SYN_REPORT) printf("EV: %s => EV_SYN SYN_REPORT\n", e->deviceName); - if (ev->type == EV_SYN && ev->code == SYN_MT_REPORT) printf("EV: %s => EV_SYN SYN_MT_REPORT\n", e->deviceName); -#endif - - // Discard the MT versions - if (ev->code == SYN_MT_REPORT) return 0; - - if (((lastWasSynReport == 1 || touchReleaseOnNextSynReport == 1) && !use_tracking_id_negative_as_touch_release) || (use_tracking_id_negative_as_touch_release && touchReleaseOnNextSynReport == 2)) - { - // Reset the value - touchReleaseOnNextSynReport = 0; - - // We are a finger-up state - if (!discard) - { - // Report the key up - ev->type = EV_ABS; - ev->code = 0; - ev->value = (downX << 16) | downY; - } - downX = -1; - downY = -1; - if (discard) - { - discard = 0; - - // Send the keyUp event - ev->type = EV_KEY; - ev->code = last_virt_key; - ev->value = 0; - } - return 0; - } - lastWasSynReport = 1; - - // Retrieve where the x,y position is - if (e->p.synced & 0x03) - { - vk_tp_to_screen(&e->p, &x, &y); - } - else if (e->mt_p.synced & 0x03) - { - vk_tp_to_screen(&e->mt_p, &x, &y); - } - else - { - // We don't have useful information to convey - return 1; - } - -#ifdef RECOVERY_TOUCHSCREEN_SWAP_XY - x ^= y; - y ^= x; - x ^= y; -#endif -#ifdef RECOVERY_TOUCHSCREEN_FLIP_X - x = gr_fb_width() - x; -#endif -#ifdef RECOVERY_TOUCHSCREEN_FLIP_Y - y = gr_fb_height() - y; -#endif - -#ifdef _EVENT_LOGGING - printf("EV: x: %d y: %d\n", x, y); -#endif - - // Clear the current sync states - e->p.synced = e->mt_p.synced = 0; - - // If we have nothing useful to report, skip it - if (x == -1 || y == -1) return 1; - - // Special case, we'll ignore touches on 0,0 because it usually means - // that we received extra data after our last sync and x and y were - // reset to 0. We should not be using 0,0 anyway. - if (x == 0 && y == 0) - return 1; - - // On first touch, see if we're at a virtual key - if (downX == -1) - { - // Attempt mapping to virtual key - for (i = 0; i < e->vk_count; ++i) - { - int xd = ABS(e->vks[i].centerx - x); - int yd = ABS(e->vks[i].centery - y); - - if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) - { - ev->type = EV_KEY; - ev->code = e->vks[i].scancode; - ev->value = 1; - - last_virt_key = e->vks[i].scancode; - - vibrate(VIBRATOR_TIME_MS); - - // Mark that all further movement until lift is discard, - // and make sure we don't come back into this area - discard = 1; - downX = 0; - return 0; - } - } - } - - // If we were originally a button press, discard this event - if (discard) - { - return 1; - } - - // Record where we started the touch for deciding if this is a key or a scroll - downX = x; - downY = y; - - ev->type = EV_ABS; - ev->code = 1; - ev->value = (x << 16) | y; - return 0; -} - -int ev_get(struct input_event *ev, int timeout_ms) -{ - int r; - unsigned n; - struct timeval curr; - - gettimeofday(&curr, NULL); - if(curr.tv_sec - lastInputStat.tv_sec >= 2) - { - struct stat st; - stat("/dev/input", &st); - if (st.st_mtime > lastInputMTime) - { - printf("Reloading input devices\n"); - ev_exit(); - ev_init(); - lastInputMTime = st.st_mtime; - } - lastInputStat = curr; - } - - r = poll(ev_fds, ev_count, timeout_ms); - - if(r > 0) { - for(n = 0; n < ev_count; n++) { - if(ev_fds[n].revents & POLLIN) { - r = read(ev_fds[n].fd, ev, sizeof(*ev)); - if(r == sizeof(*ev)) { - if (!vk_modify(&evs[n], ev)) - return 0; - } - } - } - return -1; - } - - return -2; -} - -int ev_wait(int timeout) -{ - return -1; -} - -void ev_dispatch(void) -{ - return; -} - -int ev_get_input(int fd, short revents, struct input_event *ev) -{ - return -1; -} diff --git a/minuitwrp/events.cpp b/minuitwrp/events.cpp new file mode 100644 index 000000000..d4ca0abbc --- /dev/null +++ b/minuitwrp/events.cpp @@ -0,0 +1,792 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "../common.h" + +#include "minui.h" + +//#define _EVENT_LOGGING + +#define MAX_DEVICES 32 + +#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable" +#define VIBRATOR_TIME_MS 50 + +#ifndef SYN_REPORT +#define SYN_REPORT 0x00 +#endif +#ifndef SYN_CONFIG +#define SYN_CONFIG 0x01 +#endif +#ifndef SYN_MT_REPORT +#define SYN_MT_REPORT 0x02 +#endif + +#define ABS_MT_POSITION 0x2a /* Group a set of X and Y */ +#define ABS_MT_AMPLITUDE 0x2b /* Group a set of Z and W */ +#define ABS_MT_SLOT 0x2f +#define ABS_MT_TOUCH_MAJOR 0x30 +#define ABS_MT_TOUCH_MINOR 0x31 +#define ABS_MT_WIDTH_MAJOR 0x32 +#define ABS_MT_WIDTH_MINOR 0x33 +#define ABS_MT_ORIENTATION 0x34 +#define ABS_MT_POSITION_X 0x35 +#define ABS_MT_POSITION_Y 0x36 +#define ABS_MT_TOOL_TYPE 0x37 +#define ABS_MT_BLOB_ID 0x38 +#define ABS_MT_TRACKING_ID 0x39 +#define ABS_MT_PRESSURE 0x3a +#define ABS_MT_DISTANCE 0x3b + +enum { + DOWN_NOT, + DOWN_SENT, + DOWN_RELEASED, +}; + +struct virtualkey { + int scancode; + int centerx, centery; + int width, height; +}; + +struct position { + int x, y; + int synced; + struct input_absinfo xi, yi; +}; + +struct ev { + struct pollfd *fd; + + struct virtualkey *vks; + int vk_count; + + char deviceName[64]; + + int ignored; + + struct position p, mt_p; + int down; +}; + +static struct pollfd ev_fds[MAX_DEVICES]; +static struct ev evs[MAX_DEVICES]; +static unsigned ev_count = 0; +static struct timeval lastInputStat; +static unsigned long lastInputMTime; +static int has_mouse = 0; + +static inline int ABS(int x) { + return x<0?-x:x; +} + +int vibrate(int timeout_ms) +{ + char str[20]; + int fd; + int ret; + + if (timeout_ms > 10000) timeout_ms = 1000; + + fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY); + if (fd < 0) + return -1; + + ret = snprintf(str, sizeof(str), "%d", timeout_ms); + ret = write(fd, str, ret); + close(fd); + + if (ret < 0) + return -1; + + return 0; +} + +/* Returns empty tokens */ +static char *vk_strtok_r(char *str, const char *delim, char **save_str) +{ + if(!str) + { + if(!*save_str) + return NULL; + + str = (*save_str) + 1; + } + *save_str = strpbrk(str, delim); + + if (*save_str) + **save_str = '\0'; + + return str; +} + +static int vk_init(struct ev *e) +{ + char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys."; + char vks[2048], *ts = NULL; + ssize_t len; + int vk_fd; + int i; + + e->vk_count = 0; + + len = strlen(vk_path); + len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(e->deviceName)), e->deviceName); + if (len <= 0) + { + printf("Unable to query event object.\n"); + return -1; + } +#ifdef _EVENT_LOGGING + printf("Event object: %s\n", e->deviceName); +#endif + +#ifdef WHITELIST_INPUT + if (strcmp(e->deviceName, EXPAND(WHITELIST_INPUT)) != 0) + { + e->ignored = 1; + } +#else +#ifndef TW_INPUT_BLACKLIST + // Blacklist these "input" devices + if (strcmp(e->deviceName, "bma250") == 0 || strcmp(e->deviceName, "bma150") == 0) + { + printf("blacklisting %s input device\n", e->deviceName); + e->ignored = 1; + } +#else + char* bl = strdup(EXPAND(TW_INPUT_BLACKLIST)); + char* blacklist = strtok(bl, "\n"); + + while (blacklist != NULL) { + if (strcmp(e->deviceName, blacklist) == 0) { + printf("blacklisting %s input device\n", blacklist); + e->ignored = 1; + } + blacklist = strtok(NULL, "\n"); + } + free(bl); +#endif +#endif + + strcat(vk_path, e->deviceName); + + // Some devices split the keys from the touchscreen + e->vk_count = 0; + vk_fd = open(vk_path, O_RDONLY); + if (vk_fd >= 0) + { + len = read(vk_fd, vks, sizeof(vks)-1); + close(vk_fd); + if (len <= 0) + return -1; + + vks[len] = '\0'; + + /* Parse a line like: + keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:... + */ + for (ts = vks, e->vk_count = 1; *ts; ++ts) { + if (*ts == ':') + ++e->vk_count; + } + + if (e->vk_count % 6) { + printf("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6); + } + e->vk_count /= 6; + if (e->vk_count <= 0) + return -1; + + e->down = DOWN_NOT; + } + + ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi); + e->p.synced = 0; +#ifdef _EVENT_LOGGING + printf("EV: ST minX: %d maxX: %d minY: %d maxY: %d\n", e->p.xi.minimum, e->p.xi.maximum, e->p.yi.minimum, e->p.yi.maximum); +#endif + + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi); + e->mt_p.synced = 0; +#ifdef _EVENT_LOGGING + printf("EV: MT minX: %d maxX: %d minY: %d maxY: %d\n", e->mt_p.xi.minimum, e->mt_p.xi.maximum, e->mt_p.yi.minimum, e->mt_p.yi.maximum); +#endif + + e->vks = (virtualkey *)malloc(sizeof(*e->vks) * e->vk_count); + + for (i = 0; i < e->vk_count; ++i) { + char *token[6]; + int j; + + for (j = 0; j < 6; ++j) { + token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts); + } + + if (strcmp(token[0], "0x01") != 0) { + /* Java does string compare, so we do too. */ + printf("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]); + continue; + } + + e->vks[i].scancode = strtol(token[1], NULL, 0); + e->vks[i].centerx = strtol(token[2], NULL, 0); + e->vks[i].centery = strtol(token[3], NULL, 0); + e->vks[i].width = strtol(token[4], NULL, 0); + e->vks[i].height = strtol(token[5], NULL, 0); + } + + return 0; +} + +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + +// Check for EV_REL (REL_X and REL_Y) and, because touchscreens can have those too, +// check also for EV_KEY (BTN_LEFT and BTN_RIGHT) +static void check_mouse(int fd) +{ + 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; + + has_mouse = 1; +} + +int ev_has_mouse(void) +{ + return has_mouse; +} + +int ev_init(void) +{ + DIR *dir; + struct dirent *de; + int fd; + + has_mouse = 0; + + dir = opendir("/dev/input"); + if(dir != 0) { + while((de = readdir(dir))) { +// fprintf(stderr,"/dev/input/%s\n", de->d_name); + if(strncmp(de->d_name,"event",5)) continue; + fd = openat(dirfd(dir), de->d_name, O_RDONLY); + if(fd < 0) continue; + + ev_fds[ev_count].fd = fd; + ev_fds[ev_count].events = POLLIN; + evs[ev_count].fd = &ev_fds[ev_count]; + + /* Load virtualkeys if there are any */ + vk_init(&evs[ev_count]); + + check_mouse(fd); + + ev_count++; + if(ev_count == MAX_DEVICES) break; + } + closedir(dir); + } + + struct stat st; + if(stat("/dev/input", &st) >= 0) + lastInputMTime = st.st_mtime; + gettimeofday(&lastInputStat, NULL); + + return 0; +} + +void ev_exit(void) +{ + while (ev_count-- > 0) { + if (evs[ev_count].vk_count) { + free(evs[ev_count].vks); + evs[ev_count].vk_count = 0; + } + close(ev_fds[ev_count].fd); + } + ev_count = 0; +} + +static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size) +{ + int screen_pos; + + if (info->minimum == info->maximum) + return 0; + + screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum); + return (screen_pos >= 0 && screen_pos < screen_size); +} + +static int vk_tp_to_screen(struct position *p, int *x, int *y) +{ + if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum) + { + // In this case, we assume the screen dimensions are the same. + *x = p->x; + *y = p->y; + return 0; + } + +#ifdef _EVENT_LOGGING + printf("EV: p->x=%d x-range=%d,%d fb-width=%d\n", p->x, p->xi.minimum, p->xi.maximum, gr_fb_width()); +#endif + +#ifndef RECOVERY_TOUCHSCREEN_SWAP_XY + int fb_width = gr_fb_width(); + int fb_height = gr_fb_height(); +#else + // We need to swap the scaling sizes, too + int fb_width = gr_fb_height(); + int fb_height = gr_fb_width(); +#endif + + *x = (p->x - p->xi.minimum) * (fb_width - 1) / (p->xi.maximum - p->xi.minimum); + *y = (p->y - p->yi.minimum) * (fb_height - 1) / (p->yi.maximum - p->yi.minimum); + + if (*x >= 0 && *x < fb_width && + *y >= 0 && *y < fb_height) + { + return 0; + } + + return 1; +} + +/* Translate a virtual key in to a real key event, if needed */ +/* Returns non-zero when the event should be consumed */ +static int vk_modify(struct ev *e, struct input_event *ev) +{ + static int downX = -1, downY = -1; + static int discard = 0; + static int last_virt_key = 0; + static int lastWasSynReport = 0; + static int touchReleaseOnNextSynReport = 0; + static int use_tracking_id_negative_as_touch_release = 0; // On some devices, type: 3 code: 39 value: -1, aka EV_ABS ABS_MT_TRACKING_ID -1 indicates a true touch release + int i; + int x, y; + + // This is used to ditch useless event handlers, like an accelerometer + if (e->ignored) return 1; + + if (ev->type == EV_REL && ev->code == REL_Z) + { + // This appears to be an accelerometer or another strange input device. It's not the touchscreen. +#ifdef _EVENT_LOGGING + printf("EV: Device disabled due to non-touchscreen messages.\n"); +#endif + e->ignored = 1; + return 1; + } + +#ifdef _EVENT_LOGGING + printf("EV: %s => type: %x code: %x value: %d\n", e->deviceName, ev->type, ev->code, ev->value); +#endif + + // Handle keyboard events, value of 1 indicates key down, 0 indicates key up + if (ev->type == EV_KEY) { + return 0; + } + + if (ev->type == EV_ABS) { + switch (ev->code) { + + case ABS_X: //00 + e->p.synced |= 0x01; + e->p.x = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_X %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_Y: //01 + e->p.synced |= 0x02; + e->p.y = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_Y %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION: //2a + e->mt_p.synced = 0x03; + if (ev->value == (1 << 31)) + { +#ifndef TW_IGNORE_MT_POSITION_0 + e->mt_p.x = 0; + e->mt_p.y = 0; + lastWasSynReport = 1; +#endif +#ifdef _EVENT_LOGGING +#ifndef TW_IGNORE_MT_POSITION_0 + printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x and y to 0 and lastWasSynReport to 1\n", e->deviceName, ev->value); +#else + printf("Ignoring ABS_MT_POSITION 0\n", e->deviceName, ev->value); +#endif +#endif + } + else + { + lastWasSynReport = 0; + e->mt_p.x = (ev->value & 0x7FFF0000) >> 16; + e->mt_p.y = (ev->value & 0xFFFF); +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION %d, set x: %d and y: %d and lastWasSynReport to 0\n", e->deviceName, ev->value, (ev->value & 0x7FFF0000) >> 16, (ev->value & 0xFFFF)); +#endif + } + break; + + case ABS_MT_TOUCH_MAJOR: //30 + if (ev->value == 0) + { +#ifndef TW_IGNORE_MAJOR_AXIS_0 + // We're in a touch release, although some devices will still send positions as well + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 1; +#endif + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TOUCH_MAJOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_PRESSURE: //3a + if (ev->value == 0) + { + // We're in a touch release, although some devices will still send positions as well + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 1; + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_PRESSURE %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION_X: //35 + e->mt_p.synced |= 0x01; + e->mt_p.x = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION_X %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_POSITION_Y: //36 + e->mt_p.synced |= 0x02; + e->mt_p.y = ev->value; +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_POSITION_Y %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_TOUCH_MINOR: //31 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TOUCH_MINOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_WIDTH_MAJOR: //32 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_WIDTH_MAJOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_WIDTH_MINOR: //33 +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_WIDTH_MINOR %d\n", e->deviceName, ev->value); +#endif + break; + + case ABS_MT_TRACKING_ID: //39 +#ifdef TW_IGNORE_ABS_MT_TRACKING_ID +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d ignored\n", e->deviceName, ev->value); +#endif + return 1; +#endif + if (ev->value < 0) { + e->mt_p.x = 0; + e->mt_p.y = 0; + touchReleaseOnNextSynReport = 2; + use_tracking_id_negative_as_touch_release = 1; +#ifdef _EVENT_LOGGING + if (use_tracking_id_negative_as_touch_release) + printf("using ABS_MT_TRACKING_ID value -1 to indicate touch releases\n"); +#endif + } +#ifdef _EVENT_LOGGING + printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d\n", e->deviceName, ev->value); +#endif + break; + +#ifdef _EVENT_LOGGING + // These are for touch logging purposes only + case ABS_MT_ORIENTATION: //34 + printf("EV: %s => EV_ABS ABS_MT_ORIENTATION %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_TOOL_TYPE: //37 + LOGI("EV: %s => EV_ABS ABS_MT_TOOL_TYPE %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_BLOB_ID: //38 + printf("EV: %s => EV_ABS ABS_MT_BLOB_ID %d\n", e->deviceName, ev->value); + return 1; + break; + + case ABS_MT_DISTANCE: //3b + printf("EV: %s => EV_ABS ABS_MT_DISTANCE %d\n", e->deviceName, ev->value); + return 1; + break; + case ABS_MT_SLOT: + printf("EV: %s => ABS_MT_SLOT %d\n", e->deviceName, ev->value); + return 1; + break; +#endif + + default: + // This is an unhandled message, just skip it + return 1; + } + + if (ev->code != ABS_MT_POSITION) + { + lastWasSynReport = 0; + return 1; + } + } + + // Check if we should ignore the message + if (ev->code != ABS_MT_POSITION && (ev->type != EV_SYN || (ev->code != SYN_REPORT && ev->code != SYN_MT_REPORT))) + { + lastWasSynReport = 0; + return 0; + } + +#ifdef _EVENT_LOGGING + if (ev->type == EV_SYN && ev->code == SYN_REPORT) printf("EV: %s => EV_SYN SYN_REPORT\n", e->deviceName); + if (ev->type == EV_SYN && ev->code == SYN_MT_REPORT) printf("EV: %s => EV_SYN SYN_MT_REPORT\n", e->deviceName); +#endif + + // Discard the MT versions + if (ev->code == SYN_MT_REPORT) return 0; + + if (((lastWasSynReport == 1 || touchReleaseOnNextSynReport == 1) && !use_tracking_id_negative_as_touch_release) || (use_tracking_id_negative_as_touch_release && touchReleaseOnNextSynReport == 2)) + { + // Reset the value + touchReleaseOnNextSynReport = 0; + + // We are a finger-up state + if (!discard) + { + // Report the key up + ev->type = EV_ABS; + ev->code = 0; + ev->value = (downX << 16) | downY; + } + downX = -1; + downY = -1; + if (discard) + { + discard = 0; + + // Send the keyUp event + ev->type = EV_KEY; + ev->code = last_virt_key; + ev->value = 0; + } + return 0; + } + lastWasSynReport = 1; + + // Retrieve where the x,y position is + if (e->p.synced & 0x03) + { + vk_tp_to_screen(&e->p, &x, &y); + } + else if (e->mt_p.synced & 0x03) + { + vk_tp_to_screen(&e->mt_p, &x, &y); + } + else + { + // We don't have useful information to convey + return 1; + } + +#ifdef RECOVERY_TOUCHSCREEN_SWAP_XY + x ^= y; + y ^= x; + x ^= y; +#endif +#ifdef RECOVERY_TOUCHSCREEN_FLIP_X + x = gr_fb_width() - x; +#endif +#ifdef RECOVERY_TOUCHSCREEN_FLIP_Y + y = gr_fb_height() - y; +#endif + +#ifdef _EVENT_LOGGING + printf("EV: x: %d y: %d\n", x, y); +#endif + + // Clear the current sync states + e->p.synced = e->mt_p.synced = 0; + + // If we have nothing useful to report, skip it + if (x == -1 || y == -1) return 1; + + // Special case, we'll ignore touches on 0,0 because it usually means + // that we received extra data after our last sync and x and y were + // reset to 0. We should not be using 0,0 anyway. + if (x == 0 && y == 0) + return 1; + + // On first touch, see if we're at a virtual key + if (downX == -1) + { + // Attempt mapping to virtual key + for (i = 0; i < e->vk_count; ++i) + { + int xd = ABS(e->vks[i].centerx - x); + int yd = ABS(e->vks[i].centery - y); + + if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) + { + ev->type = EV_KEY; + ev->code = e->vks[i].scancode; + ev->value = 1; + + last_virt_key = e->vks[i].scancode; + + vibrate(VIBRATOR_TIME_MS); + + // Mark that all further movement until lift is discard, + // and make sure we don't come back into this area + discard = 1; + downX = 0; + return 0; + } + } + } + + // If we were originally a button press, discard this event + if (discard) + { + return 1; + } + + // Record where we started the touch for deciding if this is a key or a scroll + downX = x; + downY = y; + + ev->type = EV_ABS; + ev->code = 1; + ev->value = (x << 16) | y; + return 0; +} + +int ev_get(struct input_event *ev, int timeout_ms) +{ + int r; + unsigned n; + struct timeval curr; + + gettimeofday(&curr, NULL); + if(curr.tv_sec - lastInputStat.tv_sec >= 2) + { + struct stat st; + stat("/dev/input", &st); + if (st.st_mtime > lastInputMTime) + { + printf("Reloading input devices\n"); + ev_exit(); + ev_init(); + lastInputMTime = st.st_mtime; + } + lastInputStat = curr; + } + + r = poll(ev_fds, ev_count, timeout_ms); + + if(r > 0) { + for(n = 0; n < ev_count; n++) { + if(ev_fds[n].revents & POLLIN) { + r = read(ev_fds[n].fd, ev, sizeof(*ev)); + if(r == sizeof(*ev)) { + if (!vk_modify(&evs[n], ev)) + return 0; + } + } + } + return -1; + } + + return -2; +} + +int ev_wait(int timeout) +{ + return -1; +} + +void ev_dispatch(void) +{ + return; +} + +int ev_get_input(int fd, short revents, struct input_event *ev) +{ + return -1; +} diff --git a/minuitwrp/graphics.c b/minuitwrp/graphics.c deleted file mode 100644 index fb86c1450..000000000 --- a/minuitwrp/graphics.c +++ /dev/null @@ -1,754 +0,0 @@ -/* - * 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 -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#include "minui.h" -#include "../gui/placement.h" - -#ifdef RECOVERY_BGRA -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888 -#define PIXEL_SIZE 4 -#endif -#ifdef RECOVERY_RGBA -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBA_8888 -#define PIXEL_SIZE 4 -#endif -#ifdef RECOVERY_RGBX -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888 -#define PIXEL_SIZE 4 -#endif -#ifndef PIXEL_FORMAT -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565 -#define PIXEL_SIZE 2 -#endif - -#define NUM_BUFFERS 2 -#define MAX_DISPLAY_DIM 2048 - -// #define PRINT_SCREENINFO 1 // Enables printing of screen info to log - -typedef struct { - int type; - GGLSurface texture; - unsigned offset[97]; - unsigned cheight; - unsigned ascent; -} GRFont; - -static GRFont *gr_font = 0; -static GGLContext *gr_context = 0; -static GGLSurface gr_font_texture; -static GGLSurface gr_framebuffer[NUM_BUFFERS]; -GGLSurface gr_mem_surface; -static unsigned gr_active_fb = 0; -static unsigned double_buffering = 0; -static int gr_is_curr_clr_opaque = 0; - -static int gr_fb_fd = -1; -static int gr_vt_fd = -1; - -struct fb_var_screeninfo vi; -static struct fb_fix_screeninfo fi; - -static bool has_overlay = false; -static int leftSplit = 0; -static int rightSplit = 0; - -bool target_has_overlay(char *version); -int free_ion_mem(void); -int alloc_ion_mem(unsigned int size); -int allocate_overlay(int fd, GGLSurface gr_fb[]); -int free_overlay(int fd); -int overlay_display_frame(int fd, GGLubyte* data, size_t size); - -#ifdef PRINT_SCREENINFO -static void print_fb_var_screeninfo() -{ - printf("vi.xres: %d\n", vi.xres); - printf("vi.yres: %d\n", vi.yres); - printf("vi.xres_virtual: %d\n", vi.xres_virtual); - printf("vi.yres_virtual: %d\n", vi.yres_virtual); - printf("vi.xoffset: %d\n", vi.xoffset); - printf("vi.yoffset: %d\n", vi.yoffset); - printf("vi.bits_per_pixel: %d\n", vi.bits_per_pixel); - printf("vi.grayscale: %d\n", vi.grayscale); -} -#endif - -#ifdef MSM_BSP -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; -} - - -void setDisplaySplit(void) { - char split[64] = {0}; - 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); -} - -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 getFbXres(void) { - return vi.xres; -} - -int getFbYres (void) { - return vi.yres; -} -#endif // MSM_BSP - -static int get_framebuffer(GGLSurface *fb) -{ - int fd, index = 0; - void *bits; - - fd = open("/dev/graphics/fb0", O_RDWR); - - while (fd < 0 && index < 30) { - usleep(1000); - fd = open("/dev/graphics/fb0", O_RDWR); - index++; - } - if (fd < 0) { - perror("cannot open fb0\n"); - return -1; - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - fprintf(stderr, "Pixel format: %dx%d @ %dbpp\n", vi.xres, vi.yres, vi.bits_per_pixel); - - vi.bits_per_pixel = PIXEL_SIZE * 8; - if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) { - fprintf(stderr, "Pixel format: BGRA_8888\n"); - if (PIXEL_SIZE != 4) fprintf(stderr, "E: Pixel Size mismatch!\n"); - vi.red.offset = 8; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 24; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBA_8888) { - fprintf(stderr, "Pixel format: RGBA_8888\n"); - if (PIXEL_SIZE != 4) fprintf(stderr, "E: Pixel Size mismatch!\n"); - vi.red.offset = 0; - vi.red.length = 8; - vi.green.offset = 8; - vi.green.length = 8; - vi.blue.offset = 16; - vi.blue.length = 8; - vi.transp.offset = 24; - vi.transp.length = 8; - } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) { - fprintf(stderr, "Pixel format: RGBX_8888\n"); - if (PIXEL_SIZE != 4) fprintf(stderr, "E: Pixel Size mismatch!\n"); - vi.red.offset = 24; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 8; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGB_565) { -#ifdef RECOVERY_RGB_565 - fprintf(stderr, "Pixel format: RGB_565\n"); - vi.blue.offset = 0; - vi.green.offset = 5; - vi.red.offset = 11; -#else - fprintf(stderr, "Pixel format: BGR_565\n"); - vi.blue.offset = 11; - vi.green.offset = 5; - vi.red.offset = 0; -#endif - if (PIXEL_SIZE != 2) fprintf(stderr, "E: Pixel Size mismatch!\n"); - 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; - } - else - { - perror("unknown pixel format"); - close(fd); - return -1; - } - - vi.vmode = FB_VMODE_NONINTERLACED; - vi.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; - - if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("failed to put fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - -#ifdef MSM_BSP - has_overlay = target_has_overlay(fi.id); - - if (isTargetMdp5()) - setDisplaySplit(); -#else - has_overlay = false; -#endif - - if (!has_overlay) { - printf("Not using qualcomm overlay, '%s'\n", fi.id); - bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bits == MAP_FAILED) { - perror("failed to mmap framebuffer"); - close(fd); - return -1; - } - } else { - printf("Using qualcomm overlay\n"); - } - -#ifdef RECOVERY_GRAPHICS_USE_LINELENGTH - vi.xres_virtual = fi.line_length / PIXEL_SIZE; -#endif - - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; -#ifdef BOARD_HAS_JANKY_BACKBUFFER - printf("setting JANKY BACKBUFFER\n"); - fb->stride = fi.line_length/2; -#else - fb->stride = vi.xres_virtual; -#endif - fb->format = PIXEL_FORMAT; - if (!has_overlay) { - fb->data = bits; - memset(fb->data, 0, vi.yres * fb->stride * PIXEL_SIZE); - } - - fb++; - -#ifndef TW_DISABLE_DOUBLE_BUFFERING - /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 > fi.smem_len) -#else - printf("TW_DISABLE_DOUBLE_BUFFERING := true\n"); -#endif - return fd; - - double_buffering = 1; - - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; -#ifdef BOARD_HAS_JANKY_BACKBUFFER - fb->stride = fi.line_length/2; - fb->data = (GGLubyte*) (((unsigned long) bits) + vi.yres * fi.line_length); -#else - fb->stride = vi.xres_virtual; - fb->data = (GGLubyte*) (((unsigned long) bits) + vi.yres * fb->stride * PIXEL_SIZE); -#endif - fb->format = PIXEL_FORMAT; - if (!has_overlay) { - memset(fb->data, 0, vi.yres * fb->stride * PIXEL_SIZE); - } - -#ifdef PRINT_SCREENINFO - print_fb_var_screeninfo(); -#endif - - return fd; -} - -static void get_memory_surface(GGLSurface* ms) { - ms->version = sizeof(*ms); - ms->width = vi.xres; - ms->height = vi.yres; - ms->stride = vi.xres_virtual; - ms->data = malloc(vi.xres_virtual * vi.yres * PIXEL_SIZE); - ms->format = PIXEL_FORMAT; -} - -static void set_active_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffering) return; - vi.yres_virtual = vi.yres * NUM_BUFFERS; - vi.yoffset = n * vi.yres; -// vi.bits_per_pixel = PIXEL_SIZE * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); - } -} - -void gr_flip(void) -{ - if (-EINVAL == overlay_display_frame(gr_fb_fd, gr_mem_surface.data, - (fi.line_length * vi.yres))) { - GGLContext *gl = gr_context; - - /* swap front and back buffers */ - if (double_buffering) - gr_active_fb = (gr_active_fb + 1) & 1; - -#ifdef BOARD_HAS_FLIPPED_SCREEN - /* flip buffer 180 degrees for devices with physicaly inverted screens */ - unsigned int i; - unsigned int j; - for (i = 0; i < vi.yres; i++) { - for (j = 0; j < vi.xres; j++) { - memcpy(gr_framebuffer[gr_active_fb].data + (i * vi.xres_virtual + j) * PIXEL_SIZE, - gr_mem_surface.data + ((vi.yres - i - 1) * vi.xres_virtual + vi.xres - j - 1) * PIXEL_SIZE, PIXEL_SIZE); - } - } -#else - /* copy data from the in-memory surface to the buffer we're about - * to make active. */ - memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data, - vi.xres_virtual * vi.yres * PIXEL_SIZE); -#endif - - /* inform the display driver */ - set_active_framebuffer(gr_active_fb); - } -} - -void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - GGLContext *gl = gr_context; - GGLint color[4]; - color[0] = ((r << 8) | r) + 1; - color[1] = ((g << 8) | g) + 1; - color[2] = ((b << 8) | b) + 1; - color[3] = ((a << 8) | a) + 1; - gl->color4xv(gl, color); - - gr_is_curr_clr_opaque = (a == 255); -} - -int gr_measureEx(const char *s, void* font) -{ - GRFont* fnt = (GRFont*) font; - - if (!fnt) - return 0; - - return gr_ttf_measureEx(s, font); -} - -int gr_maxExW(const char *s, void* font, int max_width) -{ - GRFont* fnt = (GRFont*) font; - - if (!fnt) - return 0; - - return gr_ttf_maxExW(s, font, max_width); -} - -int gr_textEx(int x, int y, const char *s, void* pFont) -{ - GGLContext *gl = gr_context; - GRFont *font = (GRFont*) pFont; - - if (!font) - return 0; - - return gr_ttf_textExWH(gl, x, y, s, pFont, -1, -1); -} - -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); -} - -int gr_textExW(int x, int y, const char *s, void* pFont, int max_width) -{ - GGLContext *gl = gr_context; - GRFont *font = (GRFont*) pFont; - - if (!font) - return 0; - - return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, -1); -} - -int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max_height) -{ - GGLContext *gl = gr_context; - GRFont *font = (GRFont*) pFont; - - if (!font) - return 0; - - return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, max_height); -} - -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_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_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 = malloc(sizeof(GGLSurface)); - memset(surface, 0, sizeof(GGLSurface)); - - data = 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 surface; -} - -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; -} - -int gr_getMaxFontHeight(void *font) -{ - GRFont *fnt = (GRFont*) font; - - if (!fnt) - return -1; - - return gr_ttf_getMaxFontHeight(font); -} - -int gr_init(void) -{ - gglInit(&gr_context); - GGLContext *gl = gr_context; - - gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC); - if (gr_vt_fd < 0) { - // This is non-fatal; post-Cupcake kernels don't have tty0. - } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) { - // However, if we do open tty0, we expect the ioctl to work. - perror("failed KDSETMODE to KD_GRAPHICS on tty0"); - gr_exit(); - return -1; - } - - gr_fb_fd = get_framebuffer(gr_framebuffer); - if (gr_fb_fd < 0) { - perror("Unable to get framebuffer.\n"); - gr_exit(); - return -1; - } - - get_memory_surface(&gr_mem_surface); - - fprintf(stderr, "framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); - - /* start with 0 as front (displayed) and 1 as back (drawing) */ - gr_active_fb = 0; - if (!has_overlay) - set_active_framebuffer(0); - 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); - -#ifdef TW_SCREEN_BLANK_ON_BOOT - printf("TW_SCREEN_BLANK_ON_BOOT := true\n"); - gr_fb_blank(true); - gr_fb_blank(false); -#endif - - if (!alloc_ion_mem(fi.line_length * vi.yres)) - allocate_overlay(gr_fb_fd, gr_framebuffer); - - return 0; -} - -void gr_exit(void) -{ - free_overlay(gr_fb_fd); - free_ion_mem(); - - close(gr_fb_fd); - gr_fb_fd = -1; - - free(gr_mem_surface.data); - - ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); - close(gr_vt_fd); - gr_vt_fd = -1; -} - -int gr_fb_width(void) -{ - return gr_framebuffer[0].width; -} - -int gr_fb_height(void) -{ - return gr_framebuffer[0].height; -} - -gr_pixel *gr_fb_data(void) -{ - return (unsigned short *) gr_mem_surface.data; -} - -int gr_fb_blank(int blank) -{ - int ret; - //if (blank) - //free_overlay(gr_fb_fd); - - ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - if (ret < 0) - perror("ioctl(): blank"); - - //if (!blank) - //allocate_overlay(gr_fb_fd, gr_framebuffer); - return ret; -} - -int gr_get_surface(gr_surface* surface) -{ - GGLSurface* ms = malloc(sizeof(GGLSurface)); - if (!ms) return -1; - - // Allocate the data - get_memory_surface(ms); - - // Now, copy the data - memcpy(ms->data, gr_mem_surface.data, vi.xres * vi.yres * vi.bits_per_pixel / 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, vi.xres * vi.yres * vi.bits_per_pixel / 8); -} diff --git a/minuitwrp/graphics.cpp b/minuitwrp/graphics.cpp new file mode 100644 index 000000000..47325d01e --- /dev/null +++ b/minuitwrp/graphics.cpp @@ -0,0 +1,416 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include "../gui/placement.h" +#include "minui.h" +#include "graphics.h" + +struct GRFont { + GRSurface* texture; + int cwidth; + int cheight; +}; + +static GRFont* gr_font = NULL; +static minui_backend* gr_backend = NULL; + +static int overscan_percent = OVERSCAN_PERCENT; +static int overscan_offset_x = 0; +static int overscan_offset_y = 0; + +static unsigned char gr_current_r = 255; +static unsigned char gr_current_g = 255; +static unsigned char gr_current_b = 255; +static unsigned char gr_current_a = 255; +static unsigned char rgb_555[2]; +static unsigned char gr_current_r5 = 31; +static unsigned char gr_current_g5 = 63; +static unsigned char gr_current_b5 = 31; + +GRSurface* gr_draw = NULL; + +static GGLContext *gr_context = 0; +GGLSurface gr_mem_surface; +static int gr_is_curr_clr_opaque = 0; + +static bool outside(int x, int y) +{ + return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +} + +int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale) +{ + GGLContext *gl = gr_context; + void* vfont = pFont; + GRFont *font = (GRFont*) pFont; + unsigned off; + unsigned cwidth; + int y_scale = 0, measured_width, measured_height, ret, new_height; + + if (!s || strlen(s) == 0 || !font) + return 0; + + measured_height = gr_ttf_getMaxFontHeight(font); + + if (scale) { + measured_width = gr_ttf_measureEx(s, vfont); + if (measured_width > max_width) { + // Adjust font size down until the text fits + void *new_font = gr_ttf_scaleFont(vfont, max_width, measured_width); + if (!new_font) { + printf("gr_textEx_scaleW new_font is NULL\n"); + return 0; + } + measured_width = gr_ttf_measureEx(s, new_font); + // These next 2 lines adjust the y point based on the new font's height + new_height = gr_ttf_getMaxFontHeight(new_font); + y_scale = (measured_height - new_height) / 2; + vfont = new_font; + } + } else + measured_width = gr_ttf_measureEx(s, vfont); + + int x_adj = measured_width; + if (measured_width > max_width) + x_adj = max_width; + + if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) { + if (placement == CENTER || placement == CENTER_X_ONLY) + x -= (x_adj / 2); + else + x -= x_adj; + } + + if (placement != TOP_LEFT && placement != TOP_RIGHT) { + if (placement == CENTER || placement == TEXT_ONLY_RIGHT) + y -= (measured_height / 2); + else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT) + y -= measured_height; + } + return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1); +} + +void gr_clip(int x, int y, int w, int h) +{ + GGLContext *gl = gr_context; + gl->scissor(gl, x, y, w, h); + gl->enable(gl, GGL_SCISSOR_TEST); +} + +void gr_noclip() +{ + GGLContext *gl = gr_context; + gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height()); + gl->disable(gl, GGL_SCISSOR_TEST); +} + +void gr_line(int x0, int y0, int x1, int y1, int width) +{ + GGLContext *gl = gr_context; + + if(gr_is_curr_clr_opaque) + gl->disable(gl, GGL_BLEND); + + const int coords0[2] = { x0 << 4, y0 << 4 }; + const int coords1[2] = { x1 << 4, y1 << 4 }; + gl->linex(gl, coords0, coords1, width << 4); + + if(gr_is_curr_clr_opaque) + gl->enable(gl, GGL_BLEND); +} + +gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int rx, ry; + GGLSurface *surface; + const int diameter = radius*2 + 1; + const int radius_check = radius*radius + radius*0.8; + const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r; + uint32_t *data; + + surface = (GGLSurface *)malloc(sizeof(GGLSurface)); + memset(surface, 0, sizeof(GGLSurface)); + + data = (uint32_t *)malloc(diameter * diameter * 4); + memset(data, 0, diameter * diameter * 4); + + surface->version = sizeof(surface); + surface->width = diameter; + surface->height = diameter; + surface->stride = diameter; + surface->data = (GGLubyte*)data; + surface->format = GGL_PIXEL_FORMAT_RGBA_8888; + + for(ry = -radius; ry <= radius; ++ry) + for(rx = -radius; rx <= radius; ++rx) + if(rx*rx+ry*ry <= radius_check) + *(data + diameter*(radius + ry) + (radius+rx)) = px; + + return (gr_surface)surface; +} + +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + GGLContext *gl = gr_context; + GGLint color[4]; +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + color[0] = ((b << 8) | r) + 1; + color[1] = ((g << 8) | g) + 1; + color[2] = ((r << 8) | b) + 1; + color[3] = ((a << 8) | a) + 1; +#else + color[0] = ((r << 8) | r) + 1; + color[1] = ((g << 8) | g) + 1; + color[2] = ((b << 8) | b) + 1; + color[3] = ((a << 8) | a) + 1; +#endif + gl->color4xv(gl, color); + + gr_is_curr_clr_opaque = (a == 255); +} + +void gr_clear() +{ + if (gr_draw->pixel_bytes == 2) { + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + return; + } + + // This code only works on 32bpp devices + if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) { + memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); + } else { + unsigned char* px = gr_draw->data; + for (int y = 0; y < gr_draw->height; ++y) { + for (int x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); + } + } +} + +void gr_fill(int x, int y, int w, int h) +{ + GGLContext *gl = gr_context; + + if(gr_is_curr_clr_opaque) + gl->disable(gl, GGL_BLEND); + + gl->recti(gl, x, y, x + w, y + h); + + if(gr_is_curr_clr_opaque) + gl->enable(gl, GGL_BLEND); +} + +void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { + if (gr_context == NULL) { + return; + } + + GGLContext *gl = gr_context; + GGLSurface *surface = (GGLSurface*)source; + + if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888) + gl->disable(gl, GGL_BLEND); + + gl->bindTexture(gl, surface); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + gl->texCoord2i(gl, sx - dx, sy - dy); + gl->recti(gl, dx, dy, dx + w, dy + h); + gl->disable(gl, GGL_TEXTURE_2D); + + if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888) + gl->enable(gl, GGL_BLEND); +} + +unsigned int gr_get_width(gr_surface surface) { + if (surface == NULL) { + return 0; + } + return ((GGLSurface*) surface)->width; +} + +unsigned int gr_get_height(gr_surface surface) { + if (surface == NULL) { + return 0; + } + return ((GGLSurface*) surface)->height; +} + +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); + // On double buffered back ends, when we flip, we need to tell + // pixel flinger to draw to the other buffer + gr_mem_surface.data = (GGLubyte*)gr_draw->data; + gr_context->colorBuffer(gr_context, &gr_mem_surface); +} + +static void get_memory_surface(GGLSurface* ms) { + ms->version = sizeof(*ms); + ms->width = gr_draw->width; + ms->height = gr_draw->height; + ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes; + ms->data = (GGLubyte*)gr_draw->data; + ms->format = gr_draw->format; +} + +int gr_init(void) +{ + gr_draw = NULL; + + gr_backend = open_overlay(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } else + printf("Using overlay graphics.\n"); + } + +#ifdef HAS_ADF + if (!gr_draw) { + gr_backend = open_adf(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } else + printf("Using adf graphics.\n"); + } + } +#else +#ifdef MSM_BSP + printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n"); +#else + printf("Skipping adf graphics -- not present in build tree\n"); +#endif +#endif + +#ifdef HAS_DRM + if (!gr_draw) { + gr_backend = open_drm(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw) + printf("Using drm graphics.\n"); + } +#else + printf("Skipping drm graphics -- not present in build tree\n"); +#endif + + if (!gr_draw) { + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { + return -1; + } else + printf("Using fbdev graphics.\n"); + } + + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; + + // Set up pixelflinger + get_memory_surface(&gr_mem_surface); + gglInit(&gr_context); + GGLContext *gl = gr_context; + gl->colorBuffer(gl, &gr_mem_surface); + + gl->activeTexture(gl, 0); + gl->enable(gl, GGL_BLEND); + gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); + + gr_flip(); + gr_flip(); + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "graphics.h" +#include + +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(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(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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minui.h" +#include "graphics.h" +#include + +#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..10ef4fec2 --- /dev/null +++ b/minuitwrp/graphics_fbdev.cpp @@ -0,0 +1,281 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "minui.h" +#include "graphics.h" +#include + +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); +#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"); + } + displayed_buffer = n; +} + +static GRSurface* fbdev_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); + + 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); + +#ifdef RECOVERY_RGB_565 + 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; +#endif + + 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_USE_LINELENGTH + vi.xres_virtual = fi.line_length / gr_framebuffer[0].pixel_bytes; +#endif + gr_framebuffer[0].data = reinterpret_cast(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) { + 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 */ + 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 { + 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 (double_buffered) { +#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. + 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); + } + 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.c b/minuitwrp/graphics_overlay.c deleted file mode 100644 index 723ffa2d4..000000000 --- a/minuitwrp/graphics_overlay.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of The Linux Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#ifdef MSM_BSP -#include -#include -#endif - -#include - -#include "minui.h" - -#define MDP_V4_0 400 - -#ifdef MSM_BSP -#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) - -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() -{ - int format = MDP_RGB_565; -#if defined(RECOVERY_BGRA) - format = MDP_BGRA_8888; -#elif defined(RECOVERY_RGBA) - format = MDP_RGBA_8888; -#elif defined(RECOVERY_RGBX) - format = MDP_RGBA_8888; -#endif - return format; -} - -static bool overlay_supported = false; -static bool isMDP5 = false; - -bool target_has_overlay(char *version) -{ - int mdp_version; - - 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; - } - } - if (overlay_supported) printf("Using qcomm overlay\n"); - return overlay_supported; -} - -bool isTargetMdp5() -{ - if (isMDP5) - return true; - - return false; -} - -int free_ion_mem(void) { - if (!overlay_supported) - return -EINVAL; - - 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) -{ - if (!overlay_supported) - return -EINVAL; - 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; -} - -int allocate_overlay(int fd, GGLSurface gr_fb[]) -{ - int ret = 0; - - if (!overlay_supported) - return -EINVAL; - - 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[0].width, 32); - overlayL.src.height = gr_fb[0].height; - overlayL.src.format = map_mdp_pixel_format(); - overlayL.src_rect.w = gr_fb[0].width; - overlayL.src_rect.h = gr_fb[0].height; - overlayL.dst_rect.w = gr_fb[0].width; - overlayL.dst_rect.h = gr_fb[0].height; - overlayL.alpha = 0xFF; - 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 = getFbXres(); - int lSplit = getLeftSplit(); - float lSplitRatio = lSplit / xres; - float lCropWidth = gr_fb[0].width * lSplitRatio; - int lWidth = lSplit; - int rWidth = gr_fb[0].width - lSplit; - int height = gr_fb[0].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[0].width, 32); - overlayL.src.height = gr_fb[0].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[0].height; - overlayL.dst_rect.x = 0; - overlayL.dst_rect.y = 0; - overlayL.dst_rect.w = lWidth; - overlayL.dst_rect.h = height; - overlayL.alpha = 0xFF; - 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[0].width, 32); - overlayR.src.height = gr_fb[0].height; - overlayR.src.format = map_mdp_pixel_format(); - overlayR.src_rect.x = lCropWidth; - overlayR.src_rect.y = 0; - overlayR.src_rect.w = gr_fb[0].width - lCropWidth; - overlayR.src_rect.h = gr_fb[0].height; - overlayR.dst_rect.x = 0; - overlayR.dst_rect.y = 0; - overlayR.dst_rect.w = rWidth; - overlayR.dst_rect.h = height; - overlayR.alpha = 0xFF; - overlayR.flags = MDSS_MDP_RIGHT_MIXER; - 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 free_overlay(int fd) -{ - if (!overlay_supported) - return -EINVAL; - - 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"); - overlayL_id = MSMFB_NEW_REQUEST; - return ret; - } - } - - 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; -} - -int overlay_display_frame(int fd, GGLubyte* data, size_t size) -{ - if (!overlay_supported) - return -EINVAL; - - 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"); - 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; -} - -#else - -bool target_has_overlay(char *version) { - return false; -} - -bool isTargetMdp5() { - return false; -} - -int free_ion_mem(void) { - return -EINVAL; -} - -int alloc_ion_mem(unsigned int size) -{ - return -EINVAL; -} - -int allocate_overlay(int fd, GGLSurface gr_fb[]) -{ - return -EINVAL; -} - -int free_overlay(int fd) -{ - return -EINVAL; -} - -int overlay_display_frame(int fd, GGLubyte* data, size_t size) -{ - return -EINVAL; -} - -#endif //#ifdef MSM_BSP diff --git a/minuitwrp/graphics_overlay.cpp b/minuitwrp/graphics_overlay.cpp new file mode 100644 index 000000000..5445b6f62 --- /dev/null +++ b/minuitwrp/graphics_overlay.cpp @@ -0,0 +1,653 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef MSM_BSP +#include +#include +#endif + +#include "minui.h" +#include "graphics.h" +#include + +#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); +#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; + 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; + 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; + overlayR.flags = MDSS_MDP_RIGHT_MIXER; + 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(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) { + 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(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.c b/minuitwrp/graphics_utils.c deleted file mode 100644 index ea1182dd9..000000000 --- a/minuitwrp/graphics_utils.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include "minui.h" - -struct fb_var_screeninfo vi; -GGLSurface gr_mem_surface; - -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 = malloc(gr_mem_surface.stride * vi.yres * 4); - 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/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 +#include +#include +#include +#include + +#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/minui.h b/minuitwrp/minui.h index 25a785a5b..018f3274f 100644 --- a/minuitwrp/minui.h +++ b/minuitwrp/minui.h @@ -18,6 +18,16 @@ #define _MINUI_H_ #include "../gui/placement.h" +#include + +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; @@ -32,7 +42,7 @@ int gr_fb_width(void); int gr_fb_height(void); gr_pixel *gr_fb_data(void); void gr_flip(void); -int gr_fb_blank(int blank); +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); @@ -41,14 +51,7 @@ 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(int x, int y, const char *s, void* font); int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale); -int gr_textExW(int x, int y, const char *s, void* font, int max_width); -int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max_height); -static inline int gr_text(int x, int y, const char *s) { return gr_textEx(x, y, s, NULL); } -int gr_measureEx(const char *s, void* font); -static inline int gr_measure(const char *s) { return gr_measureEx(s, NULL); } -int gr_maxExW(const char *s, void* font, int max_width); int gr_getMaxFontHeight(void *font); @@ -86,11 +89,6 @@ 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); -// Needed for AOSP: -int ev_wait(int timeout); -void ev_dispatch(void); -int ev_get_input(int fd, short revents, struct input_event *ev); - int vibrate(int timeout_ms); #endif diff --git a/minuitwrp/resources.c b/minuitwrp/resources.c deleted file mode 100644 index 0e124608e..000000000 --- a/minuitwrp/resources.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - * 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 -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include "jpeglib.h" - -#include "minui.h" - -// libpng gives "undefined reference to 'pow'" errors, and I have no -// idea how to convince the build system to link with -lm. We don't -// need this functionality (it's used for gamma adjustment) so provide -// a dummy implementation to satisfy the linker. -double pow(double x, double y) { - return x; -} - -#define SURFACE_DATA_ALIGNMENT 8 - -static GGLSurface* malloc_surface(size_t data_size) { - unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT); - if (temp == NULL) return NULL; - GGLSurface* surface = (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; - - snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s.png", name); - printf("open_png %s\n", resPath); - resPath[sizeof(resPath)-1] = '\0'; - FILE* fp = fopen(resPath, "rb"); - if (fp == NULL) { - fp = fopen(name, "rb"); - if (fp == NULL) { - result = -1; - goto exit; - } - } - - size_t 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); - - int color_type, bit_depth; - 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 { - fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", - bit_depth, *channels, color_type); - result = -7; - goto exit; - }*/ - 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 gr_surface 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; - - surface = (GGLSurface*) 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; - - *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; - } - - unsigned char* p_row = malloc(width * 4); - unsigned int y; - 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; -} - -int res_create_surface_jpg(const char* name, gr_surface* pSurface) { - GGLSurface* surface = NULL; - int result = 0; - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - - 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); - - size_t width = cinfo.image_width; - size_t height = cinfo.image_height; - size_t stride = 4 * width; - size_t pixelSize = stride * height; - - surface = malloc(sizeof(GGLSurface) + pixelSize); - if (surface == NULL) { - result = -8; - goto exit; - } - - unsigned char* 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; - - int y; - 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; - pRow[dx ] = r; // r - pRow[dx + 1] = g; // g - pRow[dx + 2] = b; // b - pRow[dx + 3] = a; - } - } - *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; -} - -int res_create_surface(const char* name, gr_surface* pSurface) { - int ret; - - if (!name) return -1; - - if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0) - return res_create_surface_jpg(name,pSurface); - - ret = res_create_surface_png(name,pSurface); - if (ret < 0) - ret = res_create_surface_jpg(name,pSurface); - - 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/resources.cpp b/minuitwrp/resources.cpp new file mode 100644 index 000000000..781df2b06 --- /dev/null +++ b/minuitwrp/resources.cpp @@ -0,0 +1,447 @@ +/* + * 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +extern "C" { +#include "jpeglib.h" +} +#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(malloc(size)); + if (temp == NULL) return NULL; + GGLSurface* surface = reinterpret_cast(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(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; +} + +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(malloc(sizeof(GGLSurface) + pixelSize)); + //p_row = reinterpret_cast(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; +} + +int res_create_surface(const char* name, gr_surface* pSurface) { + int ret; + + if (!name) return -1; + + if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0) + return res_create_surface_jpg(name,pSurface); + + ret = res_create_surface_png(name,pSurface); + if (ret < 0) + ret = res_create_surface_jpg(name,pSurface); + + 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.c b/minuitwrp/truetype.c deleted file mode 100644 index d9ed0198b..000000000 --- a/minuitwrp/truetype.c +++ /dev/null @@ -1,816 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "minui.h" - -#include -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H - -#include -#include - -#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 = data; - uint32_t *d32 = 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(unsigned char* pIn, unsigned int *pOut) -{ - int utf_bytes = 1; - unsigned int unicode = 0; - unsigned char tmp; - tmp = *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 = keyA; - StringCacheKey *b = 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 = key; - return fnv_hash(k->text, strlen(k->text)); -} - -static bool gr_ttf_font_cache_equals(void *keyA, void *keyB) -{ - TrueTypeFontKey *a = keyA; - TrueTypeFontKey *b = 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 = 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; - - pthread_mutex_lock(&font_data.mutex); - - if(font_data.fonts) - { - TrueTypeFontKey k = { - .size = size, - .dpi = dpi, - .path = (char*)filename - }; - - res = 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 = 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); - - TrueTypeFontKey *key = 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 = 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) -{ - TrueTypeCacheEntry *e = 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) -{ - StringCacheKey *k = key; - free(k->text); - free(k); - - StringCacheEntry *e = value; - free(e->surface.data); - free(e); - return true; -} - -void gr_ttf_freeFont(void *font) -{ - pthread_mutex_lock(&font_data.mutex); - - TrueTypeFont *d = 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 hashmapGet(font->glyph_cache, &char_index); -} - -static TrueTypeCacheEntry *gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index) -{ - TrueTypeCacheEntry *res = 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 = 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 = 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) -{ - int 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 = 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 = 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 = (void*)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 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 = hashmapGet(font->string_cache, &k); - if(!res) - { - res = 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 = 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 = font; - int res = -1; - - pthread_mutex_lock(&f->mutex); - StringCacheEntry *e = gr_ttf_string_cache_get(font, 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 = 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(font, 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 = context; - TrueTypeFont *font = 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 = 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, void *value, void *context) -{ - int *string_cache_size = context; - StringCacheEntry *e = 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 = key; - TrueTypeFont *f = value; - int *total_string_cache_size = 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); -} 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 +#include +#include + +#include +#include + +#include "minui.h" + +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include +#include + +#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); +} -- cgit v1.2.3