From f0583c32ca79f8900596f139fdc7f2a7b48266e2 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 23 Apr 2022 19:35:39 +1000 Subject: [PATCH 1/6] WIP moving more drawing/dirty tracking into LBackend --- misc/buildbot.sh | 5 +++ src/LBackend.c | 92 +++++++++++++++++++++++++++++++++++++++++------- src/LBackend.h | 7 ++-- src/LScreens.c | 6 ++-- src/Launcher.c | 63 +++++---------------------------- src/Launcher.h | 5 --- 6 files changed, 101 insertions(+), 77 deletions(-) diff --git a/misc/buildbot.sh b/misc/buildbot.sh index 42fbcdb5c..3a9263974 100644 --- a/misc/buildbot.sh +++ b/misc/buildbot.sh @@ -201,6 +201,11 @@ IOS_FLAGS="-s -O1 -fvisibility=hidden -rdynamic -funwind-tables" build_ios() { echo "Building ios.." $IOS_CC *.c interop_ios.m $IOS_FLAGS $IOS_LIBS -o cc-ios + mkdir -p Payload/ClassiCube.app + cp cc-ios Payload/ClassiCube.app/ClassiCube + # https://askubuntu.com/questions/681949/plutil-equivalent-in-ubuntu + plistutil -i $ROOT_DIR/ios/Info.plist -o Payload/ClassiCube.app/Info.plist -f bin + zip -r cc.ipa Payload } # ----------------------------- driver diff --git a/src/LBackend.c b/src/LBackend.c index bc9c1fca6..9ad32f35b 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -23,7 +23,15 @@ #include "LScreens.h" #include "Input.h" #include "Utils.h" +#include "Event.h" + struct FontDesc titleFont, textFont, hintFont; +static struct LScreen* activeScreen; +static cc_uint8 pendingRedraw; +extern Rect2D dirty_rect; + +#define REDRAW_ALL 0x02 +#define REDRAW_SOME 0x01 static int xBorder, xBorder2, xBorder3, xBorder4; static int yBorder, yBorder2, yBorder3, yBorder4; @@ -34,6 +42,7 @@ static int hdrYOffset, hdrYPadding, rowYOffset, rowYPadding; static int cellXOffset, cellXPadding, cellMinWidth; static int flagXOffset, flagYOffset; +static void HookEvents(void); void LBackend_Init(void) { xBorder = Display_ScaleX(1); xBorder2 = xBorder * 2; xBorder3 = xBorder * 3; xBorder4 = xBorder * 4; yBorder = Display_ScaleY(1); yBorder2 = yBorder * 2; yBorder3 = yBorder * 3; yBorder4 = yBorder * 4; @@ -65,6 +74,7 @@ void LBackend_Init(void) { Drawer2D_MakeFont(&titleFont, 16, FONT_FLAGS_BOLD); Drawer2D_MakeFont(&textFont, 14, FONT_FLAGS_NONE); Drawer2D_MakeFont(&hintFont, 12, FONT_FLAGS_NONE); + HookEvents(); } void LBackend_Free(void) { @@ -73,33 +83,91 @@ void LBackend_Free(void) { Font_Free(&hintFont); } -static void DrawBoxBounds(BitmapCol col, int x, int y, int width, int height) { - Drawer2D_Clear(&Launcher_Framebuffer, col, +void LBackend_SetScreen(struct LScreen* s) { activeScreen = s; } +void LBackend_CloseScreen(struct LScreen* s) { activeScreen = NULL; } + +void LBackend_WidgetRepositioned(struct LWidget* w) { +} + +/* Marks the entire window as needing to be redrawn. */ +static CC_NOINLINE void MarkAllDirty(void) { + dirty_rect.X = 0; dirty_rect.Width = Launcher_Framebuffer.width; + dirty_rect.Y = 0; dirty_rect.Height = Launcher_Framebuffer.height; +} + + +/*########################################################################################################################* +*------------------------------------------------------Base drawing-------------------------------------------------------* +*#########################################################################################################################*/ +static void DrawBoxBounds(BitmapCol color, int x, int y, int width, int height) { + Drawer2D_Clear(&Launcher_Framebuffer, color, x, y, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&Launcher_Framebuffer, color, x, y + height - yBorder, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&Launcher_Framebuffer, color, x, y, xBorder, height); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&Launcher_Framebuffer, color, x + width - xBorder, y, xBorder, height); } -void LBackend_WidgetRepositioned(struct LWidget* w) { } -void LBackend_SetScreen(struct LScreen* s) { } -void LBackend_CloseScreen(struct LScreen* s) { } - -void LBackend_RedrawScreen(struct LScreen* s) { +static CC_NOINLINE void RedrawAll(void) { + struct LScreen* s = activeScreen; int i; s->DrawBackground(s, &Launcher_Framebuffer); for (i = 0; i < s->numWidgets; i++) { LWidget_Draw(s->widgets[i]); } - Launcher_MarkAllDirty(); + MarkAllDirty(); +} + +void LBackend_Redraw(void) { + pendingRedraw = REDRAW_ALL; + MarkAllDirty(); +} + +void LBackend_Tick(void) { + activeScreen->Tick(activeScreen); + if (!dirty_rect.Width) return; + + if (pendingRedraw & REDRAW_ALL) { + RedrawAll(); + pendingRedraw = 0; + } + + Window_DrawFramebuffer(dirty_rect); + dirty_rect.X = 0; dirty_rect.Width = 0; + dirty_rect.Y = 0; dirty_rect.Height = 0; +} + + +/*########################################################################################################################* +*-----------------------------------------------------Event handling------------------------------------------------------* +*#########################################################################################################################*/ +static void ReqeustRedraw(void* obj) { LBackend_Redraw(); } + +static void OnPointerDown(void* obj, int idx) { + activeScreen->MouseDown(activeScreen, idx); +} + +static void OnPointerUp(void* obj, int idx) { + activeScreen->MouseUp(activeScreen, idx); +} + +static void OnPointerMove(void* obj, int idx) { + if (!activeScreen) return; + activeScreen->MouseMove(activeScreen, idx); +} + +static void HookEvents(void) { + Event_Register_(&WindowEvents.Redraw, NULL, ReqeustRedraw); + Event_Register_(&PointerEvents.Down, NULL, OnPointerDown); + Event_Register_(&PointerEvents.Up, NULL, OnPointerUp); + Event_Register_(&PointerEvents.Moved, NULL, OnPointerMove); } @@ -753,7 +821,7 @@ void LBackend_TableDraw(struct LTable* w) { LTable_DrawHeaders(w); LTable_DrawRows(w); LTable_DrawScrollbar(w); - Launcher_MarkAllDirty(); + MarkAllDirty(); } diff --git a/src/LBackend.h b/src/LBackend.h index 41e396249..9c446d533 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -17,10 +17,13 @@ struct LTable; void LBackend_Init(void); void LBackend_Free(void); -void LBackend_WidgetRepositioned(struct LWidget* w); void LBackend_SetScreen(struct LScreen* s); void LBackend_CloseScreen(struct LScreen* s); -void LBackend_RedrawScreen(struct LScreen* s); + +/* Resets pixels to default, then draws widgets of current screen over it */ +void LBackend_Redraw(void); +void LBackend_Tick(void); +void LBackend_WidgetRepositioned(struct LWidget* w); void LBackend_ButtonInit(struct LButton* w, int width, int height); void LBackend_ButtonUpdate(struct LButton* w); diff --git a/src/LScreens.c b/src/LScreens.c index 22641e020..31c3ffda8 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -395,7 +395,7 @@ static void ColoursScreen_TextChanged(struct LInput* w) { *color = BitmapCol_Make(r, g, b, 255); Launcher_SaveTheme(); - Launcher_Redraw(); + LBackend_Redraw(); } static void ColoursScreen_AdjustSelected(struct LScreen* s, int delta) { @@ -439,7 +439,7 @@ static void ColoursScreen_KeyDown(struct LScreen* s, int key, cc_bool was) { static void ColoursScreen_ToggleBG(struct LCheckbox* w) { Launcher_Theme.ClassicBackground = w->value; Launcher_SaveTheme(); - Launcher_Redraw(); + LBackend_Redraw(); } static void ColoursScreen_Init(struct LScreen* s_) { @@ -1604,7 +1604,7 @@ static struct LWidget* themes_widgets[] = { static void ThemesScreen_Set(const struct LauncherTheme* theme) { Launcher_Theme = *theme; Launcher_SaveTheme(); - Launcher_Redraw(); + LBackend_Redraw(); } static void ThemesScreen_Modern(void* w) { diff --git a/src/Launcher.c b/src/Launcher.c index 279fd8aa1..e00401cbc 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -23,12 +23,11 @@ /* The area/region of the window that needs to be redrawn and presented to the screen. */ /* If width is 0, means no area needs to be redrawn. */ -static Rect2D dirty_rect; +Rect2D dirty_rect; static struct LScreen* activeScreen; struct Bitmap Launcher_Framebuffer; struct FontDesc Launcher_LogoFont; -static cc_bool pendingRedraw; cc_bool Launcher_ShouldExit, Launcher_ShouldUpdate; static char hashBuffer[STRING_SIZE], userBuffer[STRING_SIZE]; @@ -62,7 +61,7 @@ void Launcher_SetScreen(struct LScreen* screen) { } LBackend_SetScreen(screen); - Launcher_Redraw(); + LBackend_Redraw(); } void Launcher_DisplayHttpError(cc_result res, int status, const char* action, cc_string* dst) { @@ -176,19 +175,12 @@ cc_bool Launcher_ConnectToServer(const cc_string* hash) { /*########################################################################################################################* *---------------------------------------------------------Event handler---------------------------------------------------* *#########################################################################################################################*/ -static void ReqeustRedraw(void* obj) { - /* We may get multiple Redraw events in short timespan */ - /* So we just request a redraw at next launcher tick */ - pendingRedraw = true; - Launcher_MarkAllDirty(); -} - static void OnResize(void* obj) { Window_FreeFramebuffer(&Launcher_Framebuffer); InitFramebuffer(); if (activeScreen) activeScreen->Layout(activeScreen); - Launcher_Redraw(); + LBackend_Redraw(); } static cc_bool IsShutdown(int key) { @@ -219,47 +211,18 @@ static void OnMouseWheel(void* obj, float delta) { activeScreen->MouseWheel(activeScreen, delta); } -static void OnPointerDown(void* obj, int idx) { - activeScreen->MouseDown(activeScreen, idx); -} - -static void OnPointerUp(void* obj, int idx) { - activeScreen->MouseUp(activeScreen, idx); -} - -static void OnPointerMove(void* obj, int idx) { - if (!activeScreen) return; - activeScreen->MouseMove(activeScreen, idx); -} - /*########################################################################################################################* *-----------------------------------------------------------Main body-----------------------------------------------------* *#########################################################################################################################*/ -static void Launcher_Display(void) { - if (pendingRedraw) { - Launcher_Redraw(); - pendingRedraw = false; - } - - Window_DrawFramebuffer(dirty_rect); - dirty_rect.X = 0; dirty_rect.Width = 0; - dirty_rect.Y = 0; dirty_rect.Height = 0; -} - static void Launcher_Init(void) { Event_Register_(&WindowEvents.Resized, NULL, OnResize); Event_Register_(&WindowEvents.StateChanged, NULL, OnResize); - Event_Register_(&WindowEvents.Redraw, NULL, ReqeustRedraw); - Event_Register_(&InputEvents.Down, NULL, OnInputDown); - Event_Register_(&InputEvents.Press, NULL, OnKeyPress); - Event_Register_(&InputEvents.Wheel, NULL, OnMouseWheel); - Event_Register_(&InputEvents.TextChanged, NULL, OnTextChanged); - - Event_Register_(&PointerEvents.Down, NULL, OnPointerDown); - Event_Register_(&PointerEvents.Up, NULL, OnPointerUp); - Event_Register_(&PointerEvents.Moved, NULL, OnPointerMove); + Event_Register_(&InputEvents.Down, NULL, OnInputDown); + Event_Register_(&InputEvents.Press, NULL, OnKeyPress); + Event_Register_(&InputEvents.Wheel, NULL, OnMouseWheel); + Event_Register_(&InputEvents.TextChanged, NULL, OnTextChanged); Utils_EnsureDirectory("texpacks"); Utils_EnsureDirectory("audio"); @@ -323,8 +286,7 @@ void Launcher_Run(void) { Window_ProcessEvents(); if (!WindowInfo.Exists || Launcher_ShouldExit) break; - activeScreen->Tick(activeScreen); - if (dirty_rect.Width) Launcher_Display(); + LBackend_Tick(); Thread_Sleep(10); } @@ -561,10 +523,6 @@ void Launcher_ResetArea(int x, int y, int width, int height) { Launcher_MarkDirty(x, y, width, height); } -void Launcher_Redraw(void) { - LBackend_RedrawScreen(activeScreen); -} - void Launcher_MarkDirty(int x, int y, int width, int height) { int x1, y1, x2, y2; if (!Drawer2D_Clamp(&Launcher_Framebuffer, &x, &y, &width, &height)) return; @@ -584,9 +542,4 @@ void Launcher_MarkDirty(int x, int y, int width, int height) { dirty_rect.X = x; dirty_rect.Width = width; dirty_rect.Y = y; dirty_rect.Height = height; } - -void Launcher_MarkAllDirty(void) { - dirty_rect.X = 0; dirty_rect.Width = Launcher_Framebuffer.width; - dirty_rect.Y = 0; dirty_rect.Height = Launcher_Framebuffer.height; -} #endif diff --git a/src/Launcher.h b/src/Launcher.h index 10342b1d3..7564e7cfb 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -65,13 +65,8 @@ void Launcher_DrawBackgroundAll(struct Bitmap* bmp); /* Redraws the specified region with the background pixels. */ /* Also marks that area as neeing to be redrawn. */ void Launcher_ResetArea(int x, int y, int width, int height); -/* Resets pixels to default, then draws widgets of current screen over it. */ -/* Marks the entire window as needing to be redrawn. */ -void Launcher_Redraw(void); /* Marks the given area/region as needing to be redrawn. */ CC_NOINLINE void Launcher_MarkDirty(int x, int y, int width, int height); -/* Marks the entire window as needing to be redrawn. */ -CC_NOINLINE void Launcher_MarkAllDirty(void); /* Sets currently active screen/menu, freeing old one. */ void Launcher_SetScreen(struct LScreen* screen); From ccfb87e54fa8e45ade43344928223178712ff8a8 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 23 Apr 2022 23:11:23 +1000 Subject: [PATCH 2/6] Completely redesign default backend widget drawing to use deferred drawing instead Now instead of always having to remember to call LXYZ_Draw after LXYZ_Set, LXYZ_Set simply marks the widget as 'needing to be redrawn' - and the default backend will subsequently redraw it on next tick The framebuffer was only redrawn on ticks anyways, so doesn't make any significant difference latency wise to defer drawing This is still a WIP and causes the status widget in CheckResources menu to be redrawn with wrong background. However, this does fix some obscure issues such as e.g. typing many words into input widget causing it to get extended horizontally, then backspacing multiple characters, causing ghost parts of the input widget to get left behind still --- src/LBackend.c | 84 ++++++++++++++++++++++++++++++++++---------------- src/LBackend.h | 1 + src/LScreens.c | 50 +++++++++--------------------- src/LWidgets.c | 43 ++++++++++---------------- src/LWidgets.h | 4 +-- 5 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/LBackend.c b/src/LBackend.c index 9ad32f35b..1fa906d4d 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -89,6 +89,12 @@ void LBackend_CloseScreen(struct LScreen* s) { activeScreen = NULL; } void LBackend_WidgetRepositioned(struct LWidget* w) { } +void LBackend_MarkDirty(void* widget) { + struct LWidget* w = (struct LWidget*)widget; + pendingRedraw |= REDRAW_SOME; + w->dirty = true; +} + /* Marks the entire window as needing to be redrawn. */ static CC_NOINLINE void MarkAllDirty(void) { dirty_rect.X = 0; dirty_rect.Width = Launcher_Framebuffer.width; @@ -114,13 +120,41 @@ static void DrawBoxBounds(BitmapCol color, int x, int y, int width, int height) xBorder, height); } +static CC_NOINLINE void DrawWidget(struct LWidget* w) { + w->last.X = w->x; w->last.Width = w->width; + w->last.Y = w->y; w->last.Height = w->height; + + w->dirty = false; + w->VTABLE->Draw(w); + Launcher_MarkDirty(w->x, w->y, w->width, w->height); +} + static CC_NOINLINE void RedrawAll(void) { struct LScreen* s = activeScreen; int i; s->DrawBackground(s, &Launcher_Framebuffer); for (i = 0; i < s->numWidgets; i++) { - LWidget_Draw(s->widgets[i]); + DrawWidget(s->widgets[i]); + } + MarkAllDirty(); +} + +static CC_NOINLINE void RedrawDirty(void) { + struct LScreen* s = activeScreen; + struct LWidget* w; + int i; + + for (i = 0; i < s->numWidgets; i++) { + w = s->widgets[i]; + if (!w->dirty) continue; + + /* check if widget might need redrawing of background behind */ + if (!w->opaque || w->last.Width > w->width || w->last.Height > w->height) { + Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); + Launcher_MarkDirty(w->last.X, w->last.Y, w->last.Width, w->last.Height); + } + DrawWidget(w); } MarkAllDirty(); } @@ -132,13 +166,16 @@ void LBackend_Redraw(void) { void LBackend_Tick(void) { activeScreen->Tick(activeScreen); - if (!dirty_rect.Width) return; if (pendingRedraw & REDRAW_ALL) { RedrawAll(); pendingRedraw = 0; + } else if (pendingRedraw & REDRAW_SOME) { + RedrawDirty(); + pendingRedraw = 0; } + if (!dirty_rect.Width) return; Window_DrawFramebuffer(dirty_rect); dirty_rect.X = 0; dirty_rect.Width = 0; dirty_rect.Y = 0; dirty_rect.Height = 0; @@ -182,6 +219,7 @@ void LBackend_ButtonInit(struct LButton* w, int width, int height) { void LBackend_ButtonUpdate(struct LButton* w) { struct DrawTextArgs args; DrawTextArgs_Make(&args, &w->text, &titleFont, true); + LBackend_MarkDirty(w); w->_textWidth = Drawer2D_TextWidth(&args); w->_textHeight = Drawer2D_TextHeight(&args); @@ -190,7 +228,6 @@ void LBackend_ButtonUpdate(struct LButton* w) { void LBackend_ButtonDraw(struct LButton* w) { struct DrawTextArgs args; int xOffset, yOffset; - Launcher_MarkDirty(w->x, w->y, w->width, w->height); LButton_DrawBackground(w, &Launcher_Framebuffer, w->x, w->y); xOffset = w->width - w->_textWidth; @@ -213,9 +250,9 @@ void LBackend_ButtonDraw(struct LButton* w) { static void LCheckbox_OnClick(void* w) { struct LCheckbox* cb = (struct LCheckbox*)w; - cb->value = !cb->value; + LBackend_MarkDirty(cb); - LWidget_Redraw(cb); + cb->value = !cb->value; if (cb->ValueChanged) cb->ValueChanged(cb); } @@ -285,7 +322,6 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { BitmapCol boxBottom = BitmapCol_Make(240, 240, 240, 255); struct DrawTextArgs args; int x, y, width, height; - Launcher_MarkDirty(w->x, w->y, w->width, w->height); width = Display_ScaleX(CB_SIZE); height = Display_ScaleY(CB_SIZE); @@ -387,24 +423,21 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { } } -static void LInput_UpdateDimensions(struct LInput* w, const cc_string* text) { +void LBackend_InputUpdate(struct LInput* w) { + cc_string text; char textBuffer[STRING_SIZE]; struct DrawTextArgs args; int textWidth; - DrawTextArgs_Make(&args, text, &textFont, false); + String_InitArray(text, textBuffer); + LInput_UNSAFE_GetText(w, &text); + LBackend_MarkDirty(w); + + DrawTextArgs_Make(&args, &text, &textFont, false); textWidth = Drawer2D_TextWidth(&args); w->width = max(w->minWidth, textWidth + inputExpand); w->_textHeight = Drawer2D_TextHeight(&args); } -void LBackend_InputUpdate(struct LInput* w) { - cc_string text; char textBuffer[STRING_SIZE]; - String_InitArray(text, textBuffer); - - LInput_UNSAFE_GetText(w, &text); - LInput_UpdateDimensions(w, &text); -} - void LBackend_InputDraw(struct LInput* w) { cc_string text; char textBuffer[STRING_SIZE]; struct DrawTextArgs args; @@ -413,8 +446,6 @@ void LBackend_InputDraw(struct LInput* w) { LInput_UNSAFE_GetText(w, &text); DrawTextArgs_Make(&args, &text, &textFont, false); - /* TODO shouldn't be recalcing size in draw.... */ - LInput_UpdateDimensions(w, &text); LInput_DrawOuterBorder(w); LInput_DrawInnerBorder(w); Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_WHITE, @@ -523,18 +554,16 @@ void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { struct OpenKeyboardArgs args; caretStart = DateTime_CurrentUTC_MS(); LInput_MoveCaretToCursor(w, idx); - /* TODO: Only draw outer border */ if (wasSelected) return; - LWidget_Draw(w); + LBackend_MarkDirty(w); OpenKeyboardArgs_Init(&args, &w->text, w->inputType); Window_OpenKeyboard(&args); } void LBackend_InputUnselect(struct LInput* w) { caretStart = 0; - /* TODO: Only draw outer border */ - LWidget_Draw(w); + LBackend_MarkDirty(w); Window_CloseKeyboard(); } @@ -548,6 +577,7 @@ void LBackend_LabelInit(struct LLabel* w) { } void LBackend_LabelUpdate(struct LLabel* w) { struct DrawTextArgs args; DrawTextArgs_Make(&args, &w->text, LLabel_GetFont(w), true); + LBackend_MarkDirty(w); w->width = Drawer2D_TextWidth(&args); w->height = Drawer2D_TextHeight(&args); @@ -583,7 +613,7 @@ void LBackend_SliderInit(struct LSlider* w, int width, int height) { } void LBackend_SliderUpdate(struct LSlider* w) { - LWidget_Draw(w); + LBackend_MarkDirty(w); } static void LSlider_DrawBoxBounds(struct LSlider* w) { @@ -653,7 +683,7 @@ void LBackend_TableReposition(struct LTable* w) { void LBackend_TableFlagAdded(struct LTable* w) { /* TODO: Only redraw flags */ - LWidget_Draw(w); + LBackend_MarkDirty(w); } /* Draws background behind column headers */ @@ -887,7 +917,7 @@ void LBackend_TableMouseDown(struct LTable* w, int idx) { } else { LTable_RowsClick(w, idx); } - LWidget_Draw(w); + LBackend_MarkDirty(w); } void LBackend_TableMouseMove(struct LTable* w, int idx) { @@ -902,7 +932,7 @@ void LBackend_TableMouseMove(struct LTable* w, int idx) { w->topRow = row; LTable_ClampTopRow(w); - LWidget_Draw(w); + LBackend_MarkDirty(w); } else if (w->draggingColumn >= 0) { col = w->draggingColumn; width = x - w->dragXStart; @@ -914,7 +944,7 @@ void LBackend_TableMouseMove(struct LTable* w, int idx) { Math_Clamp(width, cellMinWidth, maxW - cellMinWidth); if (width == w->columns[col].width) return; w->columns[col].width = width; - LWidget_Draw(w); + LBackend_MarkDirty(w); } } diff --git a/src/LBackend.h b/src/LBackend.h index 9c446d533..c76910180 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -24,6 +24,7 @@ void LBackend_CloseScreen(struct LScreen* s); void LBackend_Redraw(void); void LBackend_Tick(void); void LBackend_WidgetRepositioned(struct LWidget* w); +void LBackend_MarkDirty(void* widget); void LBackend_ButtonInit(struct LButton* w, int width, int height); void LBackend_ButtonUpdate(struct LButton* w); diff --git a/src/LScreens.c b/src/LScreens.c index 31c3ffda8..840bef657 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -545,12 +545,6 @@ static void DirectConnectScreen_UrlFilter(cc_string* str) { str->length = 0; } -CC_NOINLINE static void DirectConnectScreen_SetStatus(const char* text) { - struct LLabel* w = &DirectConnectScreen.lblStatus; - LLabel_SetConst(w, text); - LWidget_Redraw(w); -} - static void DirectConnectScreen_Load(struct DirectConnectScreen* s) { cc_string addr; char addrBuffer[STRING_SIZE]; cc_string mppass; char mppassBuffer[STRING_SIZE]; @@ -580,13 +574,14 @@ static void DirectConnectScreen_StartClient(void* w) { const cc_string* user = &DirectConnectScreen.iptUsername.text; const cc_string* addr = &DirectConnectScreen.iptAddress.text; const cc_string* mppass = &DirectConnectScreen.iptMppass.text; + struct LLabel* status = &DirectConnectScreen.lblStatus; cc_string ip, port; cc_uint16 raw_port; int index = String_LastIndexOf(addr, ':'); if (index == 0 || index == addr->length - 1) { - DirectConnectScreen_SetStatus("&eInvalid address"); return; + LLabel_SetConst(status, "&eInvalid address"); return; } /* support either "[IP]" or "[IP]:[PORT]" */ @@ -599,13 +594,13 @@ static void DirectConnectScreen_StartClient(void* w) { } if (!user->length) { - DirectConnectScreen_SetStatus("&eUsername required"); return; + LLabel_SetConst(status, "&eUsername required"); return; } if (!Socket_ValidAddress(&ip)) { - DirectConnectScreen_SetStatus("&eInvalid ip"); return; + LLabel_SetConst(status, "&eInvalid ip"); return; } if (!Convert_ParseUInt16(&port, &raw_port)) { - DirectConnectScreen_SetStatus("&eInvalid port"); return; + LLabel_SetConst(status, "&eInvalid port"); return; } if (!mppass->length) mppass = &defMppass; @@ -614,7 +609,7 @@ static void DirectConnectScreen_StartClient(void* w) { Options_Set("launcher-dc-port", &port); Options_SetSecure("launcher-dc-mppass", mppass); - DirectConnectScreen_SetStatus(""); + LLabel_SetConst(status, ""); Launcher_StartGame(user, mppass, &ip, &port, &String_Empty); } @@ -787,7 +782,6 @@ CC_NOINLINE static void MainScreen_Error(struct LWebTask* task, const char* acti LWebTask_DisplayError(task, action, &str); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); s->signingIn = false; } @@ -797,12 +791,10 @@ static void MainScreen_DoLogin(void) { cc_string* pass = &s->iptPassword.text; if (!user->length) { - LLabel_SetConst(&s->lblStatus, "&eUsername required"); - LWidget_Redraw(&s->lblStatus); return; + LLabel_SetConst(&s->lblStatus, "&eUsername required"); return; } if (!pass->length) { - LLabel_SetConst(&s->lblStatus, "&ePassword required"); - LWidget_Redraw(&s->lblStatus); return; + LLabel_SetConst(&s->lblStatus, "&ePassword required"); return; } if (GetTokenTask.Base.working) return; @@ -811,7 +803,6 @@ static void MainScreen_DoLogin(void) { GetTokenTask_Run(); LLabel_SetConst(&s->lblStatus, "&eSigning in.."); - LWidget_Redraw(&s->lblStatus); s->signingIn = true; } static void MainScreen_Login(void* w) { MainScreen_DoLogin(); } @@ -855,9 +846,7 @@ static void MainScreen_ResumeHover(void* w) { } else { String_Format3(&str, "&eResume as %s to %s:%s", &info.user, &info.ip, &info.port); } - LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_ResumeUnhover(void* w) { @@ -865,7 +854,6 @@ static void MainScreen_ResumeUnhover(void* w) { if (s->signingIn) return; LLabel_SetConst(&s->lblStatus, ""); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_Init(struct LScreen* s_) { @@ -964,20 +952,17 @@ static void MainScreen_TickCheckUpdates(struct MainScreen* s) { } else { LLabel_SetConst(&s->lblUpdate, "&cCheck failed"); } - LWidget_Redraw(&s->lblUpdate); } static void MainScreen_LoginPhase2(struct MainScreen* s, const cc_string* user) { /* website returns case correct username */ if (!String_Equals(&s->iptUsername.text, user)) { LInput_SetText(&s->iptUsername, user); - LWidget_Redraw(&s->iptUsername); } String_Copy(&Launcher_Username, user); FetchServersTask_Run(); LLabel_SetConst(&s->lblStatus, "&eRetrieving servers list.."); - LWidget_Redraw(&s->lblStatus); } static void MainScreen_TickGetToken(struct MainScreen* s) { @@ -1008,7 +993,6 @@ static void MainScreen_TickSignIn(struct MainScreen* s) { if (SignInTask.error) { LLabel_SetConst(&s->lblStatus, SignInTask.error); - LWidget_Redraw(&s->lblStatus); } else if (SignInTask.Base.success) { MainScreen_LoginPhase2(s, &SignInTask.username); } else { @@ -1191,10 +1175,10 @@ static void FetchResourcesScreen_Layout(struct LScreen* s_) { static void FetchResourcesScreen_SetStatus(struct FetchResourcesScreen* s, const cc_string* str) { struct LLabel* w = &s->lblStatus; + /* TODO FIXY FIXY FIXY */ w->opaque = true; /* need to rethink ResetArea for Launcher */ CheckResourcesScreen_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); LLabel_SetText(w, str); - LWidget_Draw(w); } static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, int reqID) { @@ -1293,7 +1277,6 @@ static void ServersScreen_Refresh(void* w) { FetchServersTask_Run(); btn = &ServersScreen.btnRefresh; LButton_SetConst(btn, "&eWorking.."); - LWidget_Redraw(btn); } static void ServersScreen_HashFilter(cc_string* str) { @@ -1313,19 +1296,19 @@ static void ServersScreen_HashFilter(cc_string* str) { static void ServersScreen_SearchChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ApplyFilter(&s->table); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } static void ServersScreen_HashChanged(struct LInput* w) { struct ServersScreen* s = &ServersScreen; LTable_ShowSelected(&s->table); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } static void ServersScreen_OnSelectedChanged(void) { struct ServersScreen* s = &ServersScreen; - LWidget_Redraw(&s->iptHash); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->iptHash); + LBackend_MarkDirty(&s->table); } static void ServersScreen_ReloadServers(struct ServersScreen* s) { @@ -1394,12 +1377,11 @@ static void ServersScreen_Tick(struct LScreen* s_) { if (FetchServersTask.Base.success) { ServersScreen_ReloadServers(s); - LWidget_Draw(&s->table); + LBackend_MarkDirty(&s->table); } LButton_SetConst(&s->btnRefresh, FetchServersTask.Base.success ? "Refresh" : "&cFailed"); - LWidget_Redraw(&s->btnRefresh); } static void ServersScreen_Free(struct LScreen* s_) { @@ -1718,7 +1700,6 @@ static void UpdatesScreen_Format(struct LLabel* lbl, const char* prefix, cc_uint UpdatesScreen_FormatTime(&str, delta); } LLabel_SetText(lbl, &str); - LWidget_Redraw(lbl); } static void UpdatesScreen_FormatBoth(struct UpdatesScreen* s) { @@ -1749,7 +1730,6 @@ static void UpdatesScreen_DoFetch(struct UpdatesScreen* s) { UpdatesScreen_UpdateHeader(s, &str); String_AppendConst(&str, ".."); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void UpdatesScreen_Get(cc_bool release, int buildIndex) { @@ -1785,7 +1765,6 @@ static void UpdatesScreen_UpdateProgress(struct UpdatesScreen* s, struct LWebTas UpdatesScreen_UpdateHeader(s, &str); String_Format1(&str, " &a%i%%", &s->buildProgress); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) { @@ -1800,7 +1779,6 @@ static void UpdatesScreen_FetchTick(struct UpdatesScreen* s) { String_InitArray(str, strBuffer); LWebTask_DisplayError(&FetchUpdateTask.Base, "fetching update", &str); LLabel_SetText(&s->lblStatus, &str); - LWidget_Redraw(&s->lblStatus); } else { /* FetchUpdateTask handles saving the updated file for us */ Launcher_ShouldExit = true; diff --git a/src/LWidgets.c b/src/LWidgets.c index d705c6e76..8c2d9fd64 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -40,21 +40,6 @@ void LWidget_CalcPosition(void* widget) { LBackend_WidgetRepositioned(w); } -void LWidget_Draw(void* widget) { - struct LWidget* w = (struct LWidget*)widget; - w->last.X = w->x; w->last.Width = w->width; - w->last.Y = w->y; w->last.Height = w->height; - - w->VTABLE->Draw(w); - Launcher_MarkDirty(w->x, w->y, w->width, w->height); -} - -void LWidget_Redraw(void* widget) { - struct LWidget* w = (struct LWidget*)widget; - Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); - LWidget_Draw(w); -} - /*########################################################################################################################* *------------------------------------------------------ButtonWidget-------------------------------------------------------* @@ -132,14 +117,18 @@ static void LButton_Draw(void* widget) { static void LButton_Hover(void* w, int idx, cc_bool wasOver) { /* only need to redraw when changing from unhovered to hovered */ - if (!wasOver) LWidget_Draw(w); + if (!wasOver) LBackend_MarkDirty(w); +} + +static void LButton_Unhover(void* w) { + LBackend_MarkDirty(w); } static const struct LWidgetVTABLE lbutton_VTABLE = { LButton_Draw, NULL, - NULL, NULL, /* Key */ - LButton_Hover, LWidget_Draw, /* Hover */ - NULL, NULL /* Select */ + NULL, NULL, /* Key */ + LButton_Hover, LButton_Unhover, /* Hover */ + NULL, NULL /* Select */ }; void LButton_Init(struct LButton* w, int width, int height, const char* text) { w->VTABLE = &lbutton_VTABLE; @@ -218,7 +207,7 @@ static void LInput_AdvanceCaretPos(struct LInput* w, cc_bool forwards) { w->caretPos += (forwards ? 1 : -1); if (w->caretPos < 0 || w->caretPos >= w->text.length) w->caretPos = -1; - LWidget_Redraw(w); + LBackend_InputUpdate(w); } static void LInput_CopyFromClipboard(struct LInput* w) { @@ -252,7 +241,7 @@ static void LInput_Backspace(struct LInput* w) { if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); - LWidget_Redraw(w); + LBackend_InputUpdate(w); } /* Removes the character at the caret in the currently entered text */ @@ -264,7 +253,7 @@ static void LInput_Delete(struct LInput* w) { if (w->TextChanged) w->TextChanged(w); LInput_ClampCaret(w); - LWidget_Redraw(w); + LBackend_InputUpdate(w); } static void LInput_KeyDown(void* widget, int key, cc_bool was) { @@ -315,7 +304,7 @@ static void LInput_KeyChar(void* widget, char c) { cc_bool appended = LInput_Append(w, c); if (appended && w->TextChanged) w->TextChanged(w); - if (appended) LWidget_Redraw(w); + if (appended) LBackend_InputUpdate(w); } static void LInput_TextChanged(void* widget, const cc_string* str) { @@ -335,6 +324,7 @@ static const struct LWidgetVTABLE linput_VTABLE = { void LInput_Init(struct LInput* w, int width, const char* hintText) { w->VTABLE = &linput_VTABLE; w->tabSelectable = true; + w->opaque = true; String_InitArray(w->text, w->_textBuffer); w->hintText = hintText; @@ -362,13 +352,12 @@ void LInput_AppendString(struct LInput* w, const cc_string* str) { } if (appended && w->TextChanged) w->TextChanged(w); - if (appended) LWidget_Redraw(w); + if (appended) LBackend_InputUpdate(w); } void LInput_SetString(struct LInput* w, const cc_string* str) { LInput_SetText(w, str); if (w->TextChanged) w->TextChanged(w); - LWidget_Redraw(w); } @@ -448,6 +437,7 @@ static const struct LWidgetVTABLE lslider_VTABLE = { void LSlider_Init(struct LSlider* w, int width, int height, BitmapCol color) { w->VTABLE = &lslider_VTABLE; w->color = color; + w->opaque = true; LBackend_SliderInit(w, width, height); } @@ -613,7 +603,7 @@ static void LTable_MouseWheel(void* widget, float delta) { struct LTable* w = (struct LTable*)widget; w->topRow -= Utils_AccumulateWheelDelta(&w->_wheelAcc, delta); LTable_ClampTopRow(w); - LWidget_Draw(w); + LBackend_MarkDirty(w); w->_lastRow = -1; } @@ -641,6 +631,7 @@ void LTable_Init(struct LTable* w, struct FontDesc* rowFont) { w->numColumns = Array_Elems(tableColumns); w->rowFont = rowFont; w->sortingCol = -1; + w->opaque = true; for (i = 0; i < w->numColumns; i++) { w->columns[i].width = Display_ScaleX(w->columns[i].width); diff --git a/src/LWidgets.h b/src/LWidgets.h index 064d8d43d..51dddf4aa 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -39,6 +39,8 @@ struct LWidgetVTABLE { cc_bool selected; /* Whether this widget is last widget to be clicked on */ \ cc_bool tabSelectable; /* Whether this widget gets selected when pressing tab */ \ cc_uint8 horAnchor, verAnchor; /* Specifies the reference point for when this widget is resized */ \ + cc_bool dirty; /* Whether this widget needs to be redrawn */ \ + cc_bool opaque; /* Whethe this widget completely obscures background behind it */ \ int xOffset, yOffset; /* Offset from the reference point */ \ void (*OnClick)(void* widget); /* Called when widget is clicked */ \ void (*OnHover)(void* widget); /* Called when widget is hovered over */ \ @@ -50,8 +52,6 @@ struct LWidgetVTABLE { struct LWidget { LWidget_Layout }; void LWidget_SetLocation(void* widget, cc_uint8 horAnchor, cc_uint8 verAnchor, int xOffset, int yOffset); void LWidget_CalcPosition(void* widget); -void LWidget_Draw(void* widget); -void LWidget_Redraw(void* widget); void LWidget_CalcOffsets(void); struct LButton { From d9b365955194e547a3b85d823daa30549fa117d5 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 23 Apr 2022 23:38:29 +1000 Subject: [PATCH 3/6] Move dirty region tracking into LBackend, fix background area resetting to work with resources screens --- src/LBackend.c | 66 +++++++++++++++++++++++++++++++++++--------------- src/LScreens.c | 22 ++++++----------- src/LScreens.h | 1 + src/LWidgets.h | 2 +- src/Launcher.c | 29 ---------------------- src/Launcher.h | 6 ----- 6 files changed, 57 insertions(+), 69 deletions(-) diff --git a/src/LBackend.c b/src/LBackend.c index 1fa906d4d..96b4b7a55 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -27,9 +27,11 @@ struct FontDesc titleFont, textFont, hintFont; static struct LScreen* activeScreen; -static cc_uint8 pendingRedraw; -extern Rect2D dirty_rect; +/* The area/region of the window that needs to be redrawn and presented to the screen. */ +/* If width is 0, means no area needs to be redrawn. */ +static Rect2D dirty_rect; +static cc_uint8 pendingRedraw; #define REDRAW_ALL 0x02 #define REDRAW_SOME 0x01 @@ -101,6 +103,27 @@ static CC_NOINLINE void MarkAllDirty(void) { dirty_rect.Y = 0; dirty_rect.Height = Launcher_Framebuffer.height; } +/* Marks the given area/region as needing to be redrawn. */ +static CC_NOINLINE void MarkAreaDirty(int x, int y, int width, int height) { + int x1, y1, x2, y2; + if (!Drawer2D_Clamp(&Launcher_Framebuffer, &x, &y, &width, &height)) return; + + /* union with existing dirty area */ + if (dirty_rect.Width) { + x1 = min(x, dirty_rect.X); + y1 = min(y, dirty_rect.Y); + + x2 = max(x + width, dirty_rect.X + dirty_rect.Width); + y2 = max(y + height, dirty_rect.Y + dirty_rect.Height); + + x = x1; width = x2 - x1; + y = y1; height = y2 - y1; + } + + dirty_rect.X = x; dirty_rect.Width = width; + dirty_rect.Y = y; dirty_rect.Height = height; +} + /*########################################################################################################################* *------------------------------------------------------Base drawing-------------------------------------------------------* @@ -126,7 +149,7 @@ static CC_NOINLINE void DrawWidget(struct LWidget* w) { w->dirty = false; w->VTABLE->Draw(w); - Launcher_MarkDirty(w->x, w->y, w->width, w->height); + MarkAreaDirty(w->x, w->y, w->width, w->height); } static CC_NOINLINE void RedrawAll(void) { @@ -151,8 +174,9 @@ static CC_NOINLINE void RedrawDirty(void) { /* check if widget might need redrawing of background behind */ if (!w->opaque || w->last.Width > w->width || w->last.Height > w->height) { - Launcher_ResetArea(w->last.X, w->last.Y, w->last.Width, w->last.Height); - Launcher_MarkDirty(w->last.X, w->last.Y, w->last.Width, w->last.Height); + s->ResetArea(&Launcher_Framebuffer, + w->last.X, w->last.Y, w->last.Width, w->last.Height); + MarkAreaDirty(w->last.X, w->last.Y, w->last.Width, w->last.Height); } DrawWidget(w); } @@ -355,19 +379,21 @@ void LBackend_InputInit(struct LInput* w, int width) { } static void LInput_DrawOuterBorder(struct LInput* w) { - BitmapCol color = BitmapCol_Make(97, 81, 110, 255); + struct LScreen* s = activeScreen; + struct Bitmap* bmp = &Launcher_Framebuffer; + BitmapCol color = BitmapCol_Make(97, 81, 110, 255); if (w->selected) { DrawBoxBounds(color, w->x, w->y, w->width, w->height); } else { - Launcher_ResetArea(w->x, w->y, - w->width, yBorder); - Launcher_ResetArea(w->x, w->y + w->height - yBorder, - w->width, yBorder); - Launcher_ResetArea(w->x, w->y, - xBorder, w->height); - Launcher_ResetArea(w->x + w->width - xBorder, w->y, - xBorder, w->height); + s->ResetArea(bmp, w->x, w->y, + w->width, yBorder); + s->ResetArea(bmp, w->x, w->y + w->height - yBorder, + w->width, yBorder); + s->ResetArea(bmp, w->x, w->y, + xBorder, w->height); + s->ResetArea(bmp, w->x + w->width - xBorder, w->y, + xBorder, w->height); } } @@ -543,9 +569,9 @@ void LBackend_InputTick(struct LInput* w) { if (Rect2D_Equals(r, lastCaretRec)) { /* Fast path, caret is blinking in same spot */ - Launcher_MarkDirty(r.X, r.Y, r.Width, r.Height); + MarkAreaDirty(r.X, r.Y, r.Width, r.Height); } else { - Launcher_MarkDirty(w->x, w->y, w->width, w->height); + MarkAreaDirty(w->x, w->y, w->width, w->height); } lastCaretRec = r; } @@ -694,7 +720,8 @@ static void LTable_DrawHeaderBackground(struct LTable* w) { Drawer2D_Clear(&Launcher_Framebuffer, gridColor, w->x, w->y, w->width, w->hdrHeight); } else { - Launcher_ResetArea(w->x, w->y, w->width, w->hdrHeight); + Launcher_DrawBackground(&Launcher_Framebuffer, + w->x, w->y, w->width, w->hdrHeight); } } @@ -739,9 +766,10 @@ static void LTable_DrawRowsBackground(struct LTable* w) { if (color) { Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x, y, w->width, height); + w->x, y, w->width, height); } else { - Launcher_ResetArea(w->x, y, w->width, height); + Launcher_DrawBackground(&Launcher_Framebuffer, + w->x, y, w->width, height); } } } diff --git a/src/LScreens.c b/src/LScreens.c index 840bef657..e9b2f80af 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -195,6 +195,7 @@ CC_NOINLINE static void LScreen_Reset(struct LScreen* s) { s->MouseWheel = LScreen_MouseWheel; s->TextChanged = LScreen_TextChanged; s->DrawBackground = LScreen_DrawBackground; + s->ResetArea = Launcher_DrawBackground; /* reset all widgets mouse state */ for (i = 0; i < s->numWidgets; i++) { @@ -1109,9 +1110,8 @@ static void CheckResourcesScreen_Layout(struct LScreen* s_) { #define RESOURCES_BACK_COLOR BitmapCol_Make( 12, 12, 12, 255) #define RESOURCES_FORE_COLOR BitmapCol_Make(120, 85, 151, 255) -static void CheckResourcesScreen_ResetArea(int x, int y, int width, int height) { - Gradient_Noise(&Launcher_Framebuffer, RESOURCES_FORE_COLOR, 4, x, y, width, height); - Launcher_MarkDirty(x, y, width, height); +static void CheckResourcesScreen_ResetArea(struct Bitmap* bmp, int x, int y, int width, int height) { + Gradient_Noise(bmp, RESOURCES_FORE_COLOR, 4, x, y, width, height); } static void CheckResourcesScreen_DrawBackground(struct LScreen* s, struct Bitmap* bmp) { @@ -1122,7 +1122,7 @@ static void CheckResourcesScreen_DrawBackground(struct LScreen* s, struct Bitmap x = Gui_CalcPos(ANCHOR_CENTRE, 0, width, bmp->width); y = Gui_CalcPos(ANCHOR_CENTRE, 0, height, bmp->height); - Gradient_Noise(bmp, RESOURCES_FORE_COLOR, 4, x, y, width, height); + CheckResourcesScreen_ResetArea(bmp, x, y, width, height); } void CheckResourcesScreen_SetActive(void) { @@ -1132,6 +1132,7 @@ void CheckResourcesScreen_SetActive(void) { s->Show = CheckResourcesScreen_Show; s->Layout = CheckResourcesScreen_Layout; s->DrawBackground = CheckResourcesScreen_DrawBackground; + s->ResetArea = CheckResourcesScreen_ResetArea; s->onEnterWidget = (struct LWidget*)&s->btnYes; Launcher_SetScreen((struct LScreen*)s); } @@ -1173,14 +1174,6 @@ static void FetchResourcesScreen_Layout(struct LScreen* s_) { LWidget_SetLocation(&s->sdrProgress, ANCHOR_CENTRE, ANCHOR_CENTRE, 0, 15); } -static void FetchResourcesScreen_SetStatus(struct FetchResourcesScreen* s, const cc_string* str) { - struct LLabel* w = &s->lblStatus; - /* TODO FIXY FIXY FIXY */ w->opaque = true; /* need to rethink ResetArea for Launcher */ - CheckResourcesScreen_ResetArea(w->last.X, w->last.Y, - w->last.Width, w->last.Height); - LLabel_SetText(w, str); -} - static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, int reqID) { cc_string str; char strBuffer[STRING_SIZE]; const char* name; @@ -1194,7 +1187,7 @@ static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, in String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_Count); if (String_Equals(&str, &s->lblStatus.text)) return; - FetchResourcesScreen_SetStatus(s, &str); + LLabel_SetText(&s->lblStatus, &str); } static void FetchResourcesScreen_UpdateProgress(struct FetchResourcesScreen* s) { @@ -1213,7 +1206,7 @@ static void FetchResourcesScreen_Error(struct FetchResourcesScreen* s) { String_InitArray(str, buffer); Launcher_DisplayHttpError(Fetcher_Result, Fetcher_StatusCode, "downloading resources", &str); - FetchResourcesScreen_SetStatus(s, &str); + LLabel_SetText(&s->lblStatus, &str); } static void FetchResourcesScreen_Tick(struct LScreen* s_) { @@ -1238,6 +1231,7 @@ void FetchResourcesScreen_SetActive(void) { s->Tick = FetchResourcesScreen_Tick; s->Layout = FetchResourcesScreen_Layout; s->DrawBackground = CheckResourcesScreen_DrawBackground; + s->ResetArea = CheckResourcesScreen_ResetArea; Launcher_SetScreen((struct LScreen*)s); } diff --git a/src/LScreens.h b/src/LScreens.h index babe5a12b..77602b686 100644 --- a/src/LScreens.h +++ b/src/LScreens.h @@ -24,6 +24,7 @@ typedef void (*LScreen_Func)(struct LScreen* s); void (*MouseMove)(struct LScreen* s, int idx); \ void (*MouseWheel)(struct LScreen* s, float delta); \ void (*TextChanged)(struct LScreen* s, const cc_string* str); \ + void (*ResetArea)(struct Bitmap* bmp, int x, int y, int width, int height); \ struct LWidget* onEnterWidget; /* Default widget to auto-click when Enter is pressed. Can be NULL. */ \ struct LWidget* hoveredWidget; /* Widget the mouse is currently hovering over. */ \ struct LWidget* selectedWidget; /* Widget mouse last clicked on. */ \ diff --git a/src/LWidgets.h b/src/LWidgets.h index 51dddf4aa..b3c96ffdc 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -40,7 +40,7 @@ struct LWidgetVTABLE { cc_bool tabSelectable; /* Whether this widget gets selected when pressing tab */ \ cc_uint8 horAnchor, verAnchor; /* Specifies the reference point for when this widget is resized */ \ cc_bool dirty; /* Whether this widget needs to be redrawn */ \ - cc_bool opaque; /* Whethe this widget completely obscures background behind it */ \ + cc_bool opaque; /* Whether this widget completely obscures background behind it */ \ int xOffset, yOffset; /* Offset from the reference point */ \ void (*OnClick)(void* widget); /* Called when widget is clicked */ \ void (*OnHover)(void* widget); /* Called when widget is hovered over */ \ diff --git a/src/Launcher.c b/src/Launcher.c index e00401cbc..7bfa1f7d9 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -21,10 +21,6 @@ #include "LBackend.h" #include "PackedCol.h" -/* The area/region of the window that needs to be redrawn and presented to the screen. */ -/* If width is 0, means no area needs to be redrawn. */ -Rect2D dirty_rect; - static struct LScreen* activeScreen; struct Bitmap Launcher_Framebuffer; struct FontDesc Launcher_LogoFont; @@ -517,29 +513,4 @@ void Launcher_UpdateLogoFont(void) { Drawer2D_MakeFont(&Launcher_LogoFont, 32, FONT_FLAGS_NONE); Drawer2D.BitmappedText = false; } - -void Launcher_ResetArea(int x, int y, int width, int height) { - Launcher_DrawBackground(&Launcher_Framebuffer, x, y, width, height); - Launcher_MarkDirty(x, y, width, height); -} - -void Launcher_MarkDirty(int x, int y, int width, int height) { - int x1, y1, x2, y2; - if (!Drawer2D_Clamp(&Launcher_Framebuffer, &x, &y, &width, &height)) return; - - /* union with existing dirty area */ - if (dirty_rect.Width) { - x1 = min(x, dirty_rect.X); - y1 = min(y, dirty_rect.Y); - - x2 = max(x + width, dirty_rect.X + dirty_rect.Width); - y2 = max(y + height, dirty_rect.Y + dirty_rect.Height); - - x = x1; width = x2 - x1; - y = y1; height = y2 - y1; - } - - dirty_rect.X = x; dirty_rect.Width = width; - dirty_rect.Y = y; dirty_rect.Height = height; -} #endif diff --git a/src/Launcher.h b/src/Launcher.h index 7564e7cfb..c8cc2a2b4 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -62,12 +62,6 @@ void Launcher_DrawBackground(struct Bitmap* bmp, int x, int y, int width, int he /* NOTE: Also draws titlebar at top, if current screen permits it */ void Launcher_DrawBackgroundAll(struct Bitmap* bmp); -/* Redraws the specified region with the background pixels. */ -/* Also marks that area as neeing to be redrawn. */ -void Launcher_ResetArea(int x, int y, int width, int height); -/* Marks the given area/region as needing to be redrawn. */ -CC_NOINLINE void Launcher_MarkDirty(int x, int y, int width, int height); - /* Sets currently active screen/menu, freeing old one. */ void Launcher_SetScreen(struct LScreen* screen); /* Attempts to start the game by connecting to the given server. */ From 5338115bf288341eae5ce7f89e910a76552dd65e Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 23 Apr 2022 23:58:24 +1000 Subject: [PATCH 4/6] Move framebuffer to LBackend --- src/LBackend.c | 106 +++++++++++++++++++++++++++---------------------- src/LBackend.h | 3 ++ src/LWidgets.c | 12 +++--- src/LWidgets.h | 2 +- src/Launcher.c | 15 ++----- src/Launcher.h | 3 -- 6 files changed, 73 insertions(+), 68 deletions(-) diff --git a/src/LBackend.c b/src/LBackend.c index 96b4b7a55..6e30a016a 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -27,6 +27,8 @@ struct FontDesc titleFont, textFont, hintFont; static struct LScreen* activeScreen; +/* Contains the pixels that are drawn to the window */ +static struct Bitmap framebuffer; /* The area/region of the window that needs to be redrawn and presented to the screen. */ /* If width is 0, means no area needs to be redrawn. */ static Rect2D dirty_rect; @@ -99,14 +101,14 @@ void LBackend_MarkDirty(void* widget) { /* Marks the entire window as needing to be redrawn. */ static CC_NOINLINE void MarkAllDirty(void) { - dirty_rect.X = 0; dirty_rect.Width = Launcher_Framebuffer.width; - dirty_rect.Y = 0; dirty_rect.Height = Launcher_Framebuffer.height; + dirty_rect.X = 0; dirty_rect.Width = framebuffer.width; + dirty_rect.Y = 0; dirty_rect.Height = framebuffer.height; } /* Marks the given area/region as needing to be redrawn. */ static CC_NOINLINE void MarkAreaDirty(int x, int y, int width, int height) { int x1, y1, x2, y2; - if (!Drawer2D_Clamp(&Launcher_Framebuffer, &x, &y, &width, &height)) return; + if (!Drawer2D_Clamp(&framebuffer, &x, &y, &width, &height)) return; /* union with existing dirty area */ if (dirty_rect.Width) { @@ -124,21 +126,31 @@ static CC_NOINLINE void MarkAreaDirty(int x, int y, int width, int height) { dirty_rect.Y = y; dirty_rect.Height = height; } +void LBackend_InitFramebuffer(void) { + framebuffer.width = max(WindowInfo.Width, 1); + framebuffer.height = max(WindowInfo.Height, 1); + Window_AllocFramebuffer(&framebuffer); +} + +void LBackend_FreeFramebuffer(void) { + Window_FreeFramebuffer(&framebuffer); +} + /*########################################################################################################################* *------------------------------------------------------Base drawing-------------------------------------------------------* *#########################################################################################################################*/ static void DrawBoxBounds(BitmapCol color, int x, int y, int width, int height) { - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, x, y, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, x, y + height - yBorder, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, x, y, xBorder, height); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, x + width - xBorder, y, xBorder, height); } @@ -155,7 +167,7 @@ static CC_NOINLINE void DrawWidget(struct LWidget* w) { static CC_NOINLINE void RedrawAll(void) { struct LScreen* s = activeScreen; int i; - s->DrawBackground(s, &Launcher_Framebuffer); + s->DrawBackground(s, &framebuffer); for (i = 0; i < s->numWidgets; i++) { DrawWidget(s->widgets[i]); @@ -174,7 +186,7 @@ static CC_NOINLINE void RedrawDirty(void) { /* check if widget might need redrawing of background behind */ if (!w->opaque || w->last.Width > w->width || w->last.Height > w->height) { - s->ResetArea(&Launcher_Framebuffer, + s->ResetArea(&framebuffer, w->last.X, w->last.Y, w->last.Width, w->last.Height); MarkAreaDirty(w->last.X, w->last.Y, w->last.Width, w->last.Height); } @@ -253,13 +265,13 @@ void LBackend_ButtonDraw(struct LButton* w) { struct DrawTextArgs args; int xOffset, yOffset; - LButton_DrawBackground(w, &Launcher_Framebuffer, w->x, w->y); + LButton_DrawBackground(w, &framebuffer, w->x, w->y); xOffset = w->width - w->_textWidth; yOffset = w->height - w->_textHeight; DrawTextArgs_Make(&args, &w->text, &titleFont, true); if (!w->hovered) Drawer2D.Colors['f'] = Drawer2D.Colors['7']; - Drawer2D_DrawText(&Launcher_Framebuffer, &args, + Drawer2D_DrawText(&framebuffer, &args, w->x + xOffset / 2, w->y + yOffset / 2); if (!w->hovered) Drawer2D.Colors['f'] = Drawer2D.Colors['F']; @@ -350,23 +362,23 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { width = Display_ScaleX(CB_SIZE); height = Display_ScaleY(CB_SIZE); - Gradient_Vertical(&Launcher_Framebuffer, boxTop, boxBottom, + Gradient_Vertical(&framebuffer, boxTop, boxBottom, w->x, w->y, width, height / 2); - Gradient_Vertical(&Launcher_Framebuffer, boxBottom, boxTop, + Gradient_Vertical(&framebuffer, boxBottom, boxTop, w->x, w->y + height / 2, width, height / 2); if (w->value) { const int size = 12; x = w->x + width / 2 - size / 2; y = w->y + height / 2 - size / 2; - DrawIndexed(size, x, y, &Launcher_Framebuffer); + DrawIndexed(size, x, y, &framebuffer); } DrawBoxBounds(BITMAPCOL_BLACK, w->x, w->y, width, height); DrawTextArgs_Make(&args, &w->text, &textFont, true); x = w->x + Display_ScaleX(CB_SIZE + CB_OFFSET); y = w->y + (height - Drawer2D_TextHeight(&args)) / 2; - Drawer2D_DrawText(&Launcher_Framebuffer, &args, x, y); + Drawer2D_DrawText(&framebuffer, &args, x, y); } @@ -380,7 +392,7 @@ void LBackend_InputInit(struct LInput* w, int width) { static void LInput_DrawOuterBorder(struct LInput* w) { struct LScreen* s = activeScreen; - struct Bitmap* bmp = &Launcher_Framebuffer; + struct Bitmap* bmp = &framebuffer; BitmapCol color = BitmapCol_Make(97, 81, 110, 255); if (w->selected) { @@ -400,16 +412,16 @@ static void LInput_DrawOuterBorder(struct LInput* w) { static void LInput_DrawInnerBorder(struct LInput* w) { BitmapCol color = BitmapCol_Make(165, 142, 168, 255); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, w->x + xBorder, w->y + yBorder, w->width - xBorder2, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, w->x + xBorder, w->y + w->height - yBorder2, w->width - xBorder2, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, w->x + xBorder, w->y + yBorder, xBorder, w->height - yBorder2); - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, w->x + w->width - xBorder2, w->y + yBorder, xBorder, w->height - yBorder2); } @@ -417,13 +429,13 @@ static void LInput_DrawInnerBorder(struct LInput* w) { static void LInput_BlendBoxTop(struct LInput* w) { BitmapCol color = BitmapCol_Make(0, 0, 0, 255); - Gradient_Blend(&Launcher_Framebuffer, color, 75, + Gradient_Blend(&framebuffer, color, 75, w->x + xBorder, w->y + yBorder, w->width - xBorder2, yBorder); - Gradient_Blend(&Launcher_Framebuffer, color, 50, + Gradient_Blend(&framebuffer, color, 50, w->x + xBorder, w->y + yBorder2, w->width - xBorder2, yBorder); - Gradient_Blend(&Launcher_Framebuffer, color, 25, + Gradient_Blend(&framebuffer, color, 25, w->x + xBorder, w->y + yBorder3, w->width - xBorder2, yBorder); } @@ -433,7 +445,7 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { if (w->text.length || !w->hintText) { y = w->y + (w->height - w->_textHeight) / 2; - Drawer2D_DrawText(&Launcher_Framebuffer, args, + Drawer2D_DrawText(&framebuffer, args, w->x + xInputOffset, y + yInputOffset); } else { args->text = String_FromReadonly(w->hintText); @@ -443,7 +455,7 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { y = w->y + (w->height - hintHeight) / 2; Drawer2D.Colors['f'] = BitmapCol_Make(125, 125, 125, 255); - Drawer2D_DrawText(&Launcher_Framebuffer, args, + Drawer2D_DrawText(&framebuffer, args, w->x + xInputOffset, y); Drawer2D.Colors['f'] = BITMAPCOL_WHITE; } @@ -474,7 +486,7 @@ void LBackend_InputDraw(struct LInput* w) { LInput_DrawOuterBorder(w); LInput_DrawInnerBorder(w); - Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_WHITE, + Drawer2D_Clear(&framebuffer, BITMAPCOL_WHITE, w->x + xBorder2, w->y + yBorder2, w->width - xBorder4, w->height - yBorder4); LInput_BlendBoxTop(w); @@ -563,7 +575,7 @@ void LBackend_InputTick(struct LInput* w) { r = LInput_MeasureCaret(w); if (caretShow) { - Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_BLACK, + Drawer2D_Clear(&framebuffer, BITMAPCOL_BLACK, r.X, r.Y, r.Width, r.Height); } @@ -612,7 +624,7 @@ void LBackend_LabelUpdate(struct LLabel* w) { void LBackend_LabelDraw(struct LLabel* w) { struct DrawTextArgs args; DrawTextArgs_Make(&args, &w->text, LLabel_GetFont(w), true); - Drawer2D_DrawText(&Launcher_Framebuffer, &args, w->x, w->y); + Drawer2D_DrawText(&framebuffer, &args, w->x, w->y); } @@ -626,7 +638,7 @@ void LBackend_LineInit(struct LLine* w, int width) { void LBackend_LineDraw(struct LLine* w) { BitmapCol color = LLine_GetColor(); - Gradient_Blend(&Launcher_Framebuffer, color, 128, w->x, w->y, w->width, w->height); + Gradient_Blend(&framebuffer, color, 128, w->x, w->y, w->width, w->height); } @@ -647,17 +659,17 @@ static void LSlider_DrawBoxBounds(struct LSlider* w) { BitmapCol boundsBottom = BitmapCol_Make(150, 130, 165, 255); /* TODO: Check these are actually right */ - Drawer2D_Clear(&Launcher_Framebuffer, boundsTop, + Drawer2D_Clear(&framebuffer, boundsTop, w->x, w->y, w->width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, boundsBottom, + Drawer2D_Clear(&framebuffer, boundsBottom, w->x, w->y + w->height - yBorder, w->width, yBorder); - Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom, + Gradient_Vertical(&framebuffer, boundsTop, boundsBottom, w->x, w->y, xBorder, w->height); - Gradient_Vertical(&Launcher_Framebuffer, boundsTop, boundsBottom, + Gradient_Vertical(&framebuffer, boundsTop, boundsBottom, w->x + w->width - xBorder, w->y, xBorder, w->height); } @@ -667,10 +679,10 @@ static void LSlider_DrawBox(struct LSlider* w) { BitmapCol progBottom = BitmapCol_Make(207, 181, 216, 255); int halfHeight = (w->height - yBorder2) / 2; - Gradient_Vertical(&Launcher_Framebuffer, progTop, progBottom, + Gradient_Vertical(&framebuffer, progTop, progBottom, w->x + xBorder, w->y + yBorder, w->width - xBorder2, halfHeight); - Gradient_Vertical(&Launcher_Framebuffer, progBottom, progTop, + Gradient_Vertical(&framebuffer, progBottom, progTop, w->x + xBorder, w->y + yBorder + halfHeight, w->width - xBorder2, halfHeight); } @@ -682,7 +694,7 @@ void LBackend_SliderDraw(struct LSlider* w) { LSlider_DrawBox(w); curWidth = (int)((w->width - xBorder2) * w->value / LSLIDER_MAXVALUE); - Drawer2D_Clear(&Launcher_Framebuffer, w->color, + Drawer2D_Clear(&framebuffer, w->color, w->x + xBorder, w->y + yBorder, curWidth, w->height - yBorder2); } @@ -717,10 +729,10 @@ static void LTable_DrawHeaderBackground(struct LTable* w) { BitmapCol gridColor = BitmapCol_Make(20, 20, 10, 255); if (!Launcher_Theme.ClassicBackground) { - Drawer2D_Clear(&Launcher_Framebuffer, gridColor, + Drawer2D_Clear(&framebuffer, gridColor, w->x, w->y, w->width, w->hdrHeight); } else { - Launcher_DrawBackground(&Launcher_Framebuffer, + Launcher_DrawBackground(&framebuffer, w->x, w->y, w->width, w->hdrHeight); } } @@ -765,10 +777,10 @@ static void LTable_DrawRowsBackground(struct LTable* w) { if (height < 0) break; if (color) { - Drawer2D_Clear(&Launcher_Framebuffer, color, + Drawer2D_Clear(&framebuffer, color, w->x, y, w->width, height); } else { - Launcher_DrawBackground(&Launcher_Framebuffer, + Launcher_DrawBackground(&framebuffer, w->x, y, w->width, height); } } @@ -780,14 +792,14 @@ static void LTable_DrawGridlines(struct LTable* w) { if (Launcher_Theme.ClassicBackground) return; x = w->x; - Drawer2D_Clear(&Launcher_Framebuffer, Launcher_Theme.BackgroundColor, + Drawer2D_Clear(&framebuffer, Launcher_Theme.BackgroundColor, x, w->y + w->hdrHeight, w->width, gridlineHeight); for (i = 0; i < w->numColumns; i++) { x += w->columns[i].width; if (!w->columns[i].hasGridline) continue; - Drawer2D_Clear(&Launcher_Framebuffer, Launcher_Theme.BackgroundColor, + Drawer2D_Clear(&framebuffer, Launcher_Theme.BackgroundColor, x, w->y, gridlineWidth, w->height); x += gridlineWidth; } @@ -810,7 +822,7 @@ static void LTable_DrawHeaders(struct LTable* w) { for (i = 0; i < w->numColumns; i++) { args.text = String_FromReadonly(w->columns[i].name); - Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args, + Drawer2D_DrawClippedText(&framebuffer, &args, x + cellXOffset, y + hdrYOffset, w->columns[i].width - cellXPadding); @@ -843,10 +855,10 @@ static void LTable_DrawRows(struct LTable* w) { for (i = 0; i < w->numColumns; i++) { args.text = str; cell.x = x; cell.y = y; cell.width = w->columns[i].width; - w->columns[i].DrawRow(entry, &args, &cell); + w->columns[i].DrawRow(entry, &args, &cell, &framebuffer); if (args.text.length) { - Drawer2D_DrawClippedText(&Launcher_Framebuffer, &args, + Drawer2D_DrawClippedText(&framebuffer, &args, x + cellXOffset, y + rowYOffset, cell.width - cellXPadding); } @@ -868,9 +880,9 @@ static void LTable_DrawScrollbar(struct LTable* w) { x = w->x + w->width - scrollbarWidth; LTable_GetScrollbarCoords(w, &y, &height); - Drawer2D_Clear(&Launcher_Framebuffer, backCol, + Drawer2D_Clear(&framebuffer, backCol, x, w->y, scrollbarWidth, w->height); - Drawer2D_Clear(&Launcher_Framebuffer, scrollCol, + Drawer2D_Clear(&framebuffer, scrollCol, x, w->y + y, scrollbarWidth, height); } diff --git a/src/LBackend.h b/src/LBackend.h index c76910180..78c2f1de0 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -26,6 +26,9 @@ void LBackend_Tick(void); void LBackend_WidgetRepositioned(struct LWidget* w); void LBackend_MarkDirty(void* widget); +void LBackend_InitFramebuffer(void); +void LBackend_FreeFramebuffer(void); + void LBackend_ButtonInit(struct LButton* w, int width, int height); void LBackend_ButtonUpdate(struct LButton* w); void LBackend_ButtonDraw(struct LButton* w); diff --git a/src/LWidgets.c b/src/LWidgets.c index 8c2d9fd64..bb58dfb6a 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -451,34 +451,34 @@ void LSlider_SetProgress(struct LSlider* w, int progress) { /*########################################################################################################################* *------------------------------------------------------TableWidget--------------------------------------------------------* *#########################################################################################################################*/ -static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell) { +static void FlagColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp) { struct Flag* flag = Flags_Get(row); if (!flag) return; - Drawer2D_BmpCopy(&Launcher_Framebuffer, cell->x + flagXOffset, cell->y + flagYOffset, &flag->bmp); + Drawer2D_BmpCopy(bmp, cell->x + flagXOffset, cell->y + flagYOffset, &flag->bmp); } -static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell) { +static void NameColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp) { args->text = row->name; } static int NameColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return String_Compare(&b->name, &a->name); } -static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell) { +static void PlayersColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp) { String_Format2(&args->text, "%i/%i", &row->players, &row->maxPlayers); } static int PlayersColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return b->players - a->players; } -static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell) { +static void UptimeColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp) { LTable_FormatUptime(&args->text, row->uptime); } static int UptimeColumn_Sort(const struct ServerInfo* a, const struct ServerInfo* b) { return b->uptime - a->uptime; } -static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell) { +static void SoftwareColumn_Draw(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp) { /* last column, so adjust to fit size of table */ int leftover = cell->table->width - cell->x; cell->width = max(cell->width, leftover); diff --git a/src/LWidgets.h b/src/LWidgets.h index b3c96ffdc..6ac1fb31c 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -142,7 +142,7 @@ struct LTableColumn { /* Draws the value of this column for the given row. */ /* If args.Text is changed to something, that text gets drawn afterwards. */ /* Most of the time that's all you need to do. */ - void (*DrawRow)(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell); + void (*DrawRow)(struct ServerInfo* row, struct DrawTextArgs* args, struct LTableCell* cell, struct Bitmap* bmp); /* Returns sort order of two rows, based on value of this column in both rows. */ int (*SortOrder)(const struct ServerInfo* a, const struct ServerInfo* b); /* Whether a vertical gridline (and padding) appears after this. */ diff --git a/src/Launcher.c b/src/Launcher.c index 7bfa1f7d9..3df41f29e 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -22,7 +22,6 @@ #include "PackedCol.h" static struct LScreen* activeScreen; -struct Bitmap Launcher_Framebuffer; struct FontDesc Launcher_LogoFont; cc_bool Launcher_ShouldExit, Launcher_ShouldUpdate; @@ -72,12 +71,6 @@ void Launcher_DisplayHttpError(cc_result res, int status, const char* action, cc } } -static CC_NOINLINE void InitFramebuffer(void) { - Launcher_Framebuffer.width = max(WindowInfo.Width, 1); - Launcher_Framebuffer.height = max(WindowInfo.Height, 1); - Window_AllocFramebuffer(&Launcher_Framebuffer); -} - /*########################################################################################################################* *--------------------------------------------------------Starter/Updater--------------------------------------------------* @@ -172,8 +165,8 @@ cc_bool Launcher_ConnectToServer(const cc_string* hash) { *---------------------------------------------------------Event handler---------------------------------------------------* *#########################################################################################################################*/ static void OnResize(void* obj) { - Window_FreeFramebuffer(&Launcher_Framebuffer); - InitFramebuffer(); + LBackend_FreeFramebuffer(); + LBackend_InitFramebuffer(); if (activeScreen) activeScreen->Layout(activeScreen); LBackend_Redraw(); @@ -233,7 +226,7 @@ static void Launcher_Free(void) { CloseActiveScreen(); activeScreen = NULL; - Window_FreeFramebuffer(&Launcher_Framebuffer); + LBackend_FreeFramebuffer(); } void Launcher_Run(void) { @@ -258,7 +251,7 @@ void Launcher_Run(void) { Drawer2D.BlackTextShadows = true; LBackend_Init(); - InitFramebuffer(); + LBackend_InitFramebuffer(); Launcher_ShowEmptyServers = Options_GetBool(LOPT_SHOW_EMPTY, true); Options_Get(LOPT_USERNAME, &Launcher_Username, ""); diff --git a/src/Launcher.h b/src/Launcher.h index c8cc2a2b4..477c29493 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -7,11 +7,8 @@ struct LScreen; struct FontDesc; -/* Contains the pixels that are drawn to the window */ -extern struct Bitmap Launcher_Framebuffer; /* Default font for screen/menu titles */ extern struct FontDesc Launcher_LogoFont; - /* Whether at the next tick, the launcher window should proceed to stop displaying frames and subsequently exit */ extern cc_bool Launcher_ShouldExit; /* Whether game should be updated on exit */ From 3e8db412fd12c097de390704d4b4b3889a93103e Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 24 Apr 2022 10:03:42 +1000 Subject: [PATCH 5/6] ios: Free framebuffer after it has been blitted to the UIWindow's background, instead of wastefully keeping it around --- src/LBackend.c | 21 ++++++------- src/LScreens.c | 2 +- src/LWidgets.c | 3 +- src/LWidgets.h | 1 - src/Launcher.c | 23 +++++++------- src/Launcher.h | 3 ++ src/interop_ios.m | 77 +++++++++++++++++++++++------------------------ 7 files changed, 63 insertions(+), 67 deletions(-) diff --git a/src/LBackend.c b/src/LBackend.c index 6e30a016a..eedd2b930 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -26,7 +26,6 @@ #include "Event.h" struct FontDesc titleFont, textFont, hintFont; -static struct LScreen* activeScreen; /* Contains the pixels that are drawn to the window */ static struct Bitmap framebuffer; /* The area/region of the window that needs to be redrawn and presented to the screen. */ @@ -87,8 +86,8 @@ void LBackend_Free(void) { Font_Free(&hintFont); } -void LBackend_SetScreen(struct LScreen* s) { activeScreen = s; } -void LBackend_CloseScreen(struct LScreen* s) { activeScreen = NULL; } +void LBackend_SetScreen(struct LScreen* s) { } +void LBackend_CloseScreen(struct LScreen* s) { } void LBackend_WidgetRepositioned(struct LWidget* w) { } @@ -165,7 +164,7 @@ static CC_NOINLINE void DrawWidget(struct LWidget* w) { } static CC_NOINLINE void RedrawAll(void) { - struct LScreen* s = activeScreen; + struct LScreen* s = Launcher_Active; int i; s->DrawBackground(s, &framebuffer); @@ -176,7 +175,7 @@ static CC_NOINLINE void RedrawAll(void) { } static CC_NOINLINE void RedrawDirty(void) { - struct LScreen* s = activeScreen; + struct LScreen* s = Launcher_Active; struct LWidget* w; int i; @@ -201,8 +200,6 @@ void LBackend_Redraw(void) { } void LBackend_Tick(void) { - activeScreen->Tick(activeScreen); - if (pendingRedraw & REDRAW_ALL) { RedrawAll(); pendingRedraw = 0; @@ -224,16 +221,16 @@ void LBackend_Tick(void) { static void ReqeustRedraw(void* obj) { LBackend_Redraw(); } static void OnPointerDown(void* obj, int idx) { - activeScreen->MouseDown(activeScreen, idx); + Launcher_Active->MouseDown(Launcher_Active, idx); } static void OnPointerUp(void* obj, int idx) { - activeScreen->MouseUp(activeScreen, idx); + Launcher_Active->MouseUp(Launcher_Active, idx); } static void OnPointerMove(void* obj, int idx) { - if (!activeScreen) return; - activeScreen->MouseMove(activeScreen, idx); + if (!Launcher_Active) return; + Launcher_Active->MouseMove(Launcher_Active, idx); } static void HookEvents(void) { @@ -391,7 +388,7 @@ void LBackend_InputInit(struct LInput* w, int width) { } static void LInput_DrawOuterBorder(struct LInput* w) { - struct LScreen* s = activeScreen; + struct LScreen* s = Launcher_Active; struct Bitmap* bmp = &framebuffer; BitmapCol color = BitmapCol_Make(97, 81, 110, 255); diff --git a/src/LScreens.c b/src/LScreens.c index e9b2f80af..ab86b5c96 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -1469,7 +1469,7 @@ static struct LWidget* settings_classic[] = { static void SettingsScreen_LockOrientation(struct LCheckbox* w) { Options_SetBool(OPT_LANDSCAPE_MODE, w->value); Window_LockLandscapeOrientation(w->value); - Launcher_Redraw(); + LBackend_Redraw(); } #else static void SettingsScreen_AutoClose(struct LCheckbox* w) { diff --git a/src/LWidgets.c b/src/LWidgets.c index bb58dfb6a..ea815ea65 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -161,8 +161,7 @@ void LCheckbox_Init(struct LCheckbox* w, const char* text) { w->VTABLE = &lcheckbox_VTABLE; w->tabSelectable = true; - String_InitArray(w->text, w->_textBuffer); - String_AppendConst(&w->text, text); + w->text = String_FromReadonly(text); LBackend_CheckboxInit(w); } diff --git a/src/LWidgets.h b/src/LWidgets.h index 6ac1fb31c..af8263ad6 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -68,7 +68,6 @@ struct LCheckbox { LWidget_Layout cc_bool value; cc_string text; - char _textBuffer[STRING_SIZE]; void (*ValueChanged)(struct LCheckbox* w); }; CC_NOINLINE void LCheckbox_Init(struct LCheckbox* w, const char* text); diff --git a/src/Launcher.c b/src/Launcher.c index 3df41f29e..66250c69c 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -21,7 +21,7 @@ #include "LBackend.h" #include "PackedCol.h" -static struct LScreen* activeScreen; +struct LScreen* Launcher_Active; struct FontDesc Launcher_LogoFont; cc_bool Launcher_ShouldExit, Launcher_ShouldUpdate; @@ -36,16 +36,17 @@ static struct Bitmap dirtBmp, stoneBmp; static void CloseActiveScreen(void) { Window_CloseKeyboard(); - if (!activeScreen) return; + if (!Launcher_Active) return; - activeScreen->Free(activeScreen); - LBackend_CloseScreen(activeScreen); + Launcher_Active->Free(Launcher_Active); + LBackend_CloseScreen(Launcher_Active); + Launcher_Active = NULL; } void Launcher_SetScreen(struct LScreen* screen) { int i; CloseActiveScreen(); - activeScreen = screen; + Launcher_Active = screen; if (!screen->numWidgets) screen->Init(screen); screen->Show(screen); @@ -168,7 +169,7 @@ static void OnResize(void* obj) { LBackend_FreeFramebuffer(); LBackend_InitFramebuffer(); - if (activeScreen) activeScreen->Layout(activeScreen); + if (Launcher_Active) Launcher_Active->Layout(Launcher_Active); LBackend_Redraw(); } @@ -185,19 +186,19 @@ static cc_bool IsShutdown(int key) { static void OnInputDown(void* obj, int key, cc_bool was) { if (IsShutdown(key)) Launcher_ShouldExit = true; - activeScreen->KeyDown(activeScreen, key, was); + Launcher_Active->KeyDown(Launcher_Active, key, was); } static void OnKeyPress(void* obj, int c) { - activeScreen->KeyPress(activeScreen, c); + Launcher_Active->KeyPress(Launcher_Active, c); } static void OnTextChanged(void* obj, const cc_string* str) { - activeScreen->TextChanged(activeScreen, str); + Launcher_Active->TextChanged(Launcher_Active, str); } static void OnMouseWheel(void* obj, float delta) { - activeScreen->MouseWheel(activeScreen, delta); + Launcher_Active->MouseWheel(Launcher_Active, delta); } @@ -225,7 +226,6 @@ static void Launcher_Free(void) { hasBitmappedFont = false; CloseActiveScreen(); - activeScreen = NULL; LBackend_FreeFramebuffer(); } @@ -275,6 +275,7 @@ void Launcher_Run(void) { Window_ProcessEvents(); if (!WindowInfo.Exists || Launcher_ShouldExit) break; + Launcher_Active->Tick(Launcher_Active); LBackend_Tick(); Thread_Sleep(10); } diff --git a/src/Launcher.h b/src/Launcher.h index 477c29493..d7689220b 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -7,8 +7,11 @@ struct LScreen; struct FontDesc; +/* The screen/menu currently being shown */ +extern struct LScreen* Launcher_Active; /* Default font for screen/menu titles */ extern struct FontDesc Launcher_LogoFont; + /* Whether at the next tick, the launcher window should proceed to stop displaying frames and subsequently exit */ extern cc_bool Launcher_ShouldExit; /* Whether game should be updated on exit */ diff --git a/src/interop_ios.m b/src/interop_ios.m index 21a64b8e4..936cfcf24 100644 --- a/src/interop_ios.m +++ b/src/interop_ios.m @@ -11,6 +11,7 @@ #include "LWidgets.h" #include "LScreens.h" #include "LWeb.h" +#include "Funcs.h" #include #include #include @@ -304,32 +305,6 @@ void Window_Create2D(int width, int height) { controller.view = view_handle; } -static CGContextRef win_ctx; -static struct Bitmap fb_bmp; -void Window_AllocFramebuffer(struct Bitmap* bmp) { - bmp->scan0 = (BitmapCol*)Mem_Alloc(bmp->width * bmp->height, 4, "window pixels"); - fb_bmp = *bmp; - - win_ctx = CGBitmapContextCreate(bmp->scan0, bmp->width, bmp->height, 8, bmp->width * 4, - CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst); -} - -void Window_DrawFramebuffer(Rect2D r) { - CGRect rect; - rect.origin.x = r.X; - rect.origin.y = WindowInfo.Height - r.Y - r.Height; - rect.size.width = r.Width; - rect.size.height = r.Height; - view_handle.layer.contents = CFBridgingRelease(CGBitmapContextCreateImage(win_ctx)); - // TODO always redraws entire launcher which is quite terrible performance wise - //[win_handle setNeedsDisplayInRect:rect]; -} - -void Window_FreeFramebuffer(struct Bitmap* bmp) { - Mem_Free(bmp->scan0); - CGContextRelease(win_ctx); -} - /*#########################################################################################################################* *--------------------------------------------------------3D window--------------------------------------------------------* @@ -459,7 +434,8 @@ cc_result Process_StartOpen(const cc_string* args) { } cc_result Process_StartGame2(const cc_string* args, int numArgs) { - for (int i = 0; i < numArgs; i++) { + for (int i = 0; i < numArgs; i++) + { String_CopyToRawArray(gameArgs[i], &args[i]); } @@ -469,7 +445,8 @@ cc_result Process_StartGame2(const cc_string* args, int numArgs) { int Platform_GetCommandLineArgs(int argc, STRING_REF char** argv, cc_string* args) { int count = gameNumArgs; - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) + { args[i] = String_FromRawArray(gameArgs[i]); } @@ -495,7 +472,8 @@ cc_result Platform_SetDefaultCurrentDirectory(int argc, char **argv) { int len = String_CalcLen(path, NATIVE_STR_LEN); // get rid of filename at end of directory - for (int i = len - 1; i >= 0; i--, len--) { + for (int i = len - 1; i >= 0; i--, len--) + { if (path[i] == '/') break; } @@ -586,11 +564,9 @@ void LBackend_WidgetRepositioned(struct LWidget* w) { [view setFrame:rect]; } -struct LScreen* active; void LBackend_SetScreen(struct LScreen* s) { - active = s; - - for (int i = 0; i < s->numWidgets; i++) { + for (int i = 0; i < s->numWidgets; i++) + { void* obj = s->widgets[i]->meta; if (!obj) continue; @@ -604,7 +580,8 @@ void LBackend_CloseScreen(struct LScreen* s) { // remove all widgets from previous screen NSArray* elems = [view_handle subviews]; - for (UIView* view in elems) { + for (UIView* view in elems) + { [view removeFromSuperview]; } } @@ -618,11 +595,13 @@ static void AssignView(void* widget, UIView* view) { } static struct LWidget* FindWidgetForView(id obj) { - for (int i = 0; i < active->numWidgets; i++) { - void* meta = active->widgets[i]->meta; + struct LScreen* s = Launcher_Active; + for (int i = 0; i < s->numWidgets; i++) + { + void* meta = s->widgets[i]->meta; if (meta != (__bridge void*)obj) continue; - return active->widgets[i]; + return s->widgets[i]; } return NULL; } @@ -713,11 +692,29 @@ void LBackend_Init(void) { CFBridgingRetain(ui_controller); // prevent GC TODO even needed? } +void LBackend_MarkDirty(void* widget) { } +void LBackend_Tick(void) { } void LBackend_Free(void) { } -void LBackend_RedrawScreen(struct LScreen* s) { - s->DrawBackground(s, &Launcher_Framebuffer); - Launcher_MarkAllDirty(); +static CGContextRef win_ctx; +void LBackend_InitFramebuffer(void) { } +void LBackend_FreeFramebuffer(void) { } + +void LBackend_Redraw(void) { + struct Bitmap fb_bmp; + fb_bmp.width = max(WindowInfo.Width, 1); + fb_bmp.height = max(WindowInfo.Height, 1); + fb_bmp.scan0 = (BitmapCol*)Mem_Alloc(fb_bmp.width * fb_bmp.height, 4, "window pixels"); + + win_ctx = CGBitmapContextCreate(fb_bmp.scan0, fb_bmp.width, fb_bmp.height, 8, fb_bmp.width * 4, + CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst); + + struct LScreen* s = Launcher_Active; + s->DrawBackground(s, &fb_bmp); + view_handle.layer.contents = CFBridgingRelease(CGBitmapContextCreateImage(win_ctx)); + + Mem_Free(fb_bmp.scan0); + CGContextRelease(win_ctx); } /*########################################################################################################################* From 6d3c77db5da5cd82f90f31b1220bd885e4e21dc0 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 24 Apr 2022 22:12:00 +1000 Subject: [PATCH 6/6] Fix caret --- src/LBackend.c | 244 ++++++++++++++++++++++++------------------------- src/LWidgets.h | 2 + 2 files changed, 123 insertions(+), 123 deletions(-) diff --git a/src/LBackend.c b/src/LBackend.c index eedd2b930..4a7c3b8db 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -382,11 +382,128 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { /*########################################################################################################################* *------------------------------------------------------InputWidget--------------------------------------------------------* *#########################################################################################################################*/ +static TimeMS caretStart; +static cc_bool lastCaretShow; +static Rect2D caretRect, lastCaretRect; +#define Rect2D_Equals(a, b) a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height + void LBackend_InputInit(struct LInput* w, int width) { w->width = Display_ScaleX(width); w->height = Display_ScaleY(30); } +void LBackend_InputUpdate(struct LInput* w) { + cc_string text; char textBuffer[STRING_SIZE]; + struct DrawTextArgs args; + int textWidth; + + String_InitArray(text, textBuffer); + LInput_UNSAFE_GetText(w, &text); + LBackend_MarkDirty(w); + + DrawTextArgs_Make(&args, &text, &textFont, false); + textWidth = Drawer2D_TextWidth(&args); + w->width = max(w->minWidth, textWidth + inputExpand); + w->_textHeight = Drawer2D_TextHeight(&args); +} + +static Rect2D LInput_MeasureCaret(struct LInput* w, cc_string* text) { + struct DrawTextArgs args; + Rect2D r; + DrawTextArgs_Make(&args, text, &textFont, true); + + r.X = w->x + xInputOffset; + r.Y = w->y + w->height - caretOffset; r.Height = caretHeight; + + if (w->caretPos == -1) { + r.X += Drawer2D_TextWidth(&args); + r.Width = caretWidth; + } else { + args.text = String_UNSAFE_Substring(text, 0, w->caretPos); + r.X += Drawer2D_TextWidth(&args); + + args.text = String_UNSAFE_Substring(text, w->caretPos, 1); + r.Width = Drawer2D_TextWidth(&args); + } + return r; +} + +static void LInput_MoveCaretToCursor(struct LInput* w, int idx) { + cc_string text; char textBuffer[STRING_SIZE]; + int x = Pointers[idx].x, y = Pointers[idx].y; + struct DrawTextArgs args; + int i, charX, charWidth; + + /* Input widget may have been selected by pressing tab */ + /* In which case cursor is completely outside, so ignore */ + if (!Gui_Contains(w->x, w->y, w->width, w->height, x, y)) return; + + String_InitArray(text, textBuffer); + LInput_UNSAFE_GetText(w, &text); + x -= w->x; y -= w->y; + + DrawTextArgs_Make(&args, &text, &textFont, true); + if (x >= Drawer2D_TextWidth(&args)) { + w->caretPos = -1; return; + } + + for (i = 0; i < text.length; i++) { + args.text = String_UNSAFE_Substring(&text, 0, i); + charX = Drawer2D_TextWidth(&args); + + args.text = String_UNSAFE_Substring(&text, i, 1); + charWidth = Drawer2D_TextWidth(&args); + if (x >= charX && x < charX + charWidth) { + w->caretPos = i; return; + } + } +} + +void LBackend_InputTick(struct LInput* w) { + int elapsed; + cc_bool caretShow; + Rect2D r; + + if (!caretStart) return; + elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart); + + caretShow = (elapsed % 1000) < 500; + if (caretShow == w->caretShow) return; + w->caretShow = caretShow; + + LBackend_InputDraw(w); + r = caretRect; + + if (Rect2D_Equals(r, lastCaretRect)) { + /* Fast path, caret is blinking in same spot */ + MarkAreaDirty(r.X, r.Y, r.Width, r.Height); + } else { + /* Slow path (new widget, caret moved, etc) */ + MarkAreaDirty(w->x, w->y, w->width, w->height); + } + lastCaretRect = r; +} + +void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { + struct OpenKeyboardArgs args; + caretStart = DateTime_CurrentUTC_MS(); + w->caretShow = true; + LInput_MoveCaretToCursor(w, idx); + LBackend_MarkDirty(w); + + if (wasSelected) return; + OpenKeyboardArgs_Init(&args, &w->text, w->inputType); + Window_OpenKeyboard(&args); +} + +void LBackend_InputUnselect(struct LInput* w) { + caretStart = 0; + w->caretShow = false; + LBackend_MarkDirty(w); + Window_CloseKeyboard(); +} + + static void LInput_DrawOuterBorder(struct LInput* w) { struct LScreen* s = Launcher_Active; struct Bitmap* bmp = &framebuffer; @@ -458,21 +575,6 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { } } -void LBackend_InputUpdate(struct LInput* w) { - cc_string text; char textBuffer[STRING_SIZE]; - struct DrawTextArgs args; - int textWidth; - - String_InitArray(text, textBuffer); - LInput_UNSAFE_GetText(w, &text); - LBackend_MarkDirty(w); - - DrawTextArgs_Make(&args, &text, &textFont, false); - textWidth = Drawer2D_TextWidth(&args); - w->width = max(w->minWidth, textWidth + inputExpand); - w->_textHeight = Drawer2D_TextHeight(&args); -} - void LBackend_InputDraw(struct LInput* w) { cc_string text; char textBuffer[STRING_SIZE]; struct DrawTextArgs args; @@ -491,115 +593,11 @@ void LBackend_InputDraw(struct LInput* w) { Drawer2D.Colors['f'] = Drawer2D.Colors['0']; LInput_DrawText(w, &args); Drawer2D.Colors['f'] = Drawer2D.Colors['F']; -} - -static TimeMS caretStart; -static cc_bool lastCaretShow; -static Rect2D lastCaretRec; -#define Rect2D_Equals(a, b) a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height - -static Rect2D LInput_MeasureCaret(struct LInput* w) { - cc_string text; char textBuffer[STRING_SIZE]; - struct DrawTextArgs args; - Rect2D r; - - String_InitArray(text, textBuffer); - LInput_UNSAFE_GetText(w, &text); - DrawTextArgs_Make(&args, &text, &textFont, true); - - r.X = w->x + xInputOffset; - r.Y = w->y + w->height - caretOffset; r.Height = caretHeight; - - if (w->caretPos == -1) { - r.X += Drawer2D_TextWidth(&args); - r.Width = caretWidth; - } else { - args.text = String_UNSAFE_Substring(&text, 0, w->caretPos); - r.X += Drawer2D_TextWidth(&args); - - args.text = String_UNSAFE_Substring(&text, w->caretPos, 1); - r.Width = Drawer2D_TextWidth(&args); - } - return r; -} - -static void LInput_MoveCaretToCursor(struct LInput* w, int idx) { - cc_string text; char textBuffer[STRING_SIZE]; - int x = Pointers[idx].x, y = Pointers[idx].y; - struct DrawTextArgs args; - int i, charX, charWidth; - - /* Input widget may have been selected by pressing tab */ - /* In which case cursor is completely outside, so ignore */ - if (!Gui_Contains(w->x, w->y, w->width, w->height, x, y)) return; - lastCaretShow = false; - - String_InitArray(text, textBuffer); - LInput_UNSAFE_GetText(w, &text); - x -= w->x; y -= w->y; - - DrawTextArgs_Make(&args, &text, &textFont, true); - if (x >= Drawer2D_TextWidth(&args)) { - w->caretPos = -1; return; - } - - for (i = 0; i < text.length; i++) { - args.text = String_UNSAFE_Substring(&text, 0, i); - charX = Drawer2D_TextWidth(&args); - - args.text = String_UNSAFE_Substring(&text, i, 1); - charWidth = Drawer2D_TextWidth(&args); - if (x >= charX && x < charX + charWidth) { - w->caretPos = i; return; - } - } -} - -void LBackend_InputTick(struct LInput* w) { - int elapsed; - cc_bool caretShow; - Rect2D r; - - if (!caretStart) return; - elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart); - - caretShow = (elapsed % 1000) < 500; - if (caretShow == lastCaretShow) return; - lastCaretShow = caretShow; - - LBackend_InputDraw(w); - r = LInput_MeasureCaret(w); - - if (caretShow) { - Drawer2D_Clear(&framebuffer, BITMAPCOL_BLACK, - r.X, r.Y, r.Width, r.Height); - } - - if (Rect2D_Equals(r, lastCaretRec)) { - /* Fast path, caret is blinking in same spot */ - MarkAreaDirty(r.X, r.Y, r.Width, r.Height); - } else { - MarkAreaDirty(w->x, w->y, w->width, w->height); - } - lastCaretRec = r; -} - -void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { - struct OpenKeyboardArgs args; - caretStart = DateTime_CurrentUTC_MS(); - LInput_MoveCaretToCursor(w, idx); - if (wasSelected) return; - - LBackend_MarkDirty(w); - OpenKeyboardArgs_Init(&args, &w->text, w->inputType); - Window_OpenKeyboard(&args); -} - -void LBackend_InputUnselect(struct LInput* w) { - caretStart = 0; - LBackend_MarkDirty(w); - Window_CloseKeyboard(); + caretRect = LInput_MeasureCaret(w, &text); + if (!w->caretShow) return; + Drawer2D_Clear(&framebuffer, BITMAPCOL_BLACK, + caretRect.X, caretRect.Y, caretRect.Width, caretRect.Height); } diff --git a/src/LWidgets.h b/src/LWidgets.h index af8263ad6..80b9cd23d 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -81,6 +81,8 @@ struct LInput { /* The type of this input (see KEYBOARD_TYPE_ enum in Window.h) */ /* If type is KEYBOARD_TYPE_PASSWORD, all characters are drawn as *. */ cc_uint8 inputType; + /* Whether caret is currently visible */ + cc_bool caretShow; /* Filter applied to text received from the clipboard. Can be NULL. */ void (*ClipboardFilter)(cc_string* str); /* Callback invoked when the text is changed. Can be NULL. */