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..4a7c3b8db 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -23,7 +23,18 @@ #include "LScreens.h" #include "Input.h" #include "Utils.h" +#include "Event.h" + struct FontDesc titleFont, textFont, hintFont; +/* 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; + +static cc_uint8 pendingRedraw; +#define REDRAW_ALL 0x02 +#define REDRAW_SOME 0x01 static int xBorder, xBorder2, xBorder3, xBorder4; static int yBorder, yBorder2, yBorder3, yBorder4; @@ -34,6 +45,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 +77,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 +86,158 @@ 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) { } +void LBackend_CloseScreen(struct LScreen* s) { } + +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 = 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(&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; +} + +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(&framebuffer, color, x, y, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&framebuffer, color, x, y + height - yBorder, width, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&framebuffer, color, x, y, xBorder, height); - Drawer2D_Clear(&Launcher_Framebuffer, col, + Drawer2D_Clear(&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) { } +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; -void LBackend_RedrawScreen(struct LScreen* s) { + w->dirty = false; + w->VTABLE->Draw(w); + MarkAreaDirty(w->x, w->y, w->width, w->height); +} + +static CC_NOINLINE void RedrawAll(void) { + struct LScreen* s = Launcher_Active; int i; - s->DrawBackground(s, &Launcher_Framebuffer); + s->DrawBackground(s, &framebuffer); for (i = 0; i < s->numWidgets; i++) { - LWidget_Draw(s->widgets[i]); + DrawWidget(s->widgets[i]); } - Launcher_MarkAllDirty(); + MarkAllDirty(); +} + +static CC_NOINLINE void RedrawDirty(void) { + struct LScreen* s = Launcher_Active; + 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) { + 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); + } + DrawWidget(w); + } + MarkAllDirty(); +} + +void LBackend_Redraw(void) { + pendingRedraw = REDRAW_ALL; + MarkAllDirty(); +} + +void LBackend_Tick(void) { + 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; +} + + +/*########################################################################################################################* +*-----------------------------------------------------Event handling------------------------------------------------------* +*#########################################################################################################################*/ +static void ReqeustRedraw(void* obj) { LBackend_Redraw(); } + +static void OnPointerDown(void* obj, int idx) { + Launcher_Active->MouseDown(Launcher_Active, idx); +} + +static void OnPointerUp(void* obj, int idx) { + Launcher_Active->MouseUp(Launcher_Active, idx); +} + +static void OnPointerMove(void* obj, int idx) { + if (!Launcher_Active) return; + Launcher_Active->MouseMove(Launcher_Active, 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); } @@ -114,6 +252,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); @@ -122,15 +261,14 @@ 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); + 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']; @@ -145,9 +283,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); } @@ -217,162 +355,62 @@ 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); - 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); } /*########################################################################################################################* *------------------------------------------------------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); } -static void LInput_DrawOuterBorder(struct LInput* w) { - 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); - } -} - -static void LInput_DrawInnerBorder(struct LInput* w) { - BitmapCol color = BitmapCol_Make(165, 142, 168, 255); - - Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x + xBorder, w->y + yBorder, - w->width - xBorder2, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x + xBorder, w->y + w->height - yBorder2, - w->width - xBorder2, yBorder); - Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x + xBorder, w->y + yBorder, - xBorder, w->height - yBorder2); - Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x + w->width - xBorder2, w->y + yBorder, - xBorder, w->height - yBorder2); -} - -static void LInput_BlendBoxTop(struct LInput* w) { - BitmapCol color = BitmapCol_Make(0, 0, 0, 255); - - Gradient_Blend(&Launcher_Framebuffer, color, 75, - w->x + xBorder, w->y + yBorder, - w->width - xBorder2, yBorder); - Gradient_Blend(&Launcher_Framebuffer, color, 50, - w->x + xBorder, w->y + yBorder2, - w->width - xBorder2, yBorder); - Gradient_Blend(&Launcher_Framebuffer, color, 25, - w->x + xBorder, w->y + yBorder3, - w->width - xBorder2, yBorder); -} - -static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { - int y, hintHeight; - - if (w->text.length || !w->hintText) { - y = w->y + (w->height - w->_textHeight) / 2; - Drawer2D_DrawText(&Launcher_Framebuffer, args, - w->x + xInputOffset, y + yInputOffset); - } else { - args->text = String_FromReadonly(w->hintText); - args->font = &hintFont; - - hintHeight = Drawer2D_TextHeight(args); - y = w->y + (w->height - hintHeight) / 2; - - Drawer2D.Colors['f'] = BitmapCol_Make(125, 125, 125, 255); - Drawer2D_DrawText(&Launcher_Framebuffer, args, - w->x + xInputOffset, y); - Drawer2D.Colors['f'] = BITMAPCOL_WHITE; - } -} - -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; - - String_InitArray(text, textBuffer); - 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, - w->x + xBorder2, w->y + yBorder2, - w->width - xBorder4, w->height - yBorder4); - LInput_BlendBoxTop(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]; +static Rect2D LInput_MeasureCaret(struct LInput* w, cc_string* text) { struct DrawTextArgs args; Rect2D r; - - String_InitArray(text, textBuffer); - LInput_UNSAFE_GetText(w, &text); - DrawTextArgs_Make(&args, &text, &textFont, true); + DrawTextArgs_Make(&args, text, &textFont, true); r.X = w->x + xInputOffset; r.Y = w->y + w->height - caretOffset; r.Height = caretHeight; @@ -381,10 +419,10 @@ static Rect2D LInput_MeasureCaret(struct LInput* w) { r.X += Drawer2D_TextWidth(&args); r.Width = caretWidth; } else { - args.text = String_UNSAFE_Substring(&text, 0, w->caretPos); + args.text = String_UNSAFE_Substring(text, 0, w->caretPos); r.X += Drawer2D_TextWidth(&args); - args.text = String_UNSAFE_Substring(&text, w->caretPos, 1); + args.text = String_UNSAFE_Substring(text, w->caretPos, 1); r.Width = Drawer2D_TextWidth(&args); } return r; @@ -399,7 +437,6 @@ static void LInput_MoveCaretToCursor(struct LInput* w, int idx) { /* 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); @@ -431,46 +468,139 @@ void LBackend_InputTick(struct LInput* w) { elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart); caretShow = (elapsed % 1000) < 500; - if (caretShow == lastCaretShow) return; - lastCaretShow = caretShow; + if (caretShow == w->caretShow) return; + w->caretShow = caretShow; LBackend_InputDraw(w); - r = LInput_MeasureCaret(w); - - if (caretShow) { - Drawer2D_Clear(&Launcher_Framebuffer, BITMAPCOL_BLACK, - r.X, r.Y, r.Width, r.Height); - } + r = caretRect; - if (Rect2D_Equals(r, lastCaretRec)) { + if (Rect2D_Equals(r, lastCaretRect)) { /* 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); + /* Slow path (new widget, caret moved, etc) */ + MarkAreaDirty(w->x, w->y, w->width, w->height); } - lastCaretRec = r; + lastCaretRect = r; } void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { struct OpenKeyboardArgs args; - caretStart = DateTime_CurrentUTC_MS(); + caretStart = DateTime_CurrentUTC_MS(); + w->caretShow = true; LInput_MoveCaretToCursor(w, idx); - /* TODO: Only draw outer border */ - if (wasSelected) return; + LBackend_MarkDirty(w); - LWidget_Draw(w); + if (wasSelected) return; 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); + 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; + BitmapCol color = BitmapCol_Make(97, 81, 110, 255); + + if (w->selected) { + DrawBoxBounds(color, w->x, w->y, w->width, w->height); + } else { + 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); + } +} + +static void LInput_DrawInnerBorder(struct LInput* w) { + BitmapCol color = BitmapCol_Make(165, 142, 168, 255); + + Drawer2D_Clear(&framebuffer, color, + w->x + xBorder, w->y + yBorder, + w->width - xBorder2, yBorder); + Drawer2D_Clear(&framebuffer, color, + w->x + xBorder, w->y + w->height - yBorder2, + w->width - xBorder2, yBorder); + Drawer2D_Clear(&framebuffer, color, + w->x + xBorder, w->y + yBorder, + xBorder, w->height - yBorder2); + Drawer2D_Clear(&framebuffer, color, + w->x + w->width - xBorder2, w->y + yBorder, + xBorder, w->height - yBorder2); +} + +static void LInput_BlendBoxTop(struct LInput* w) { + BitmapCol color = BitmapCol_Make(0, 0, 0, 255); + + Gradient_Blend(&framebuffer, color, 75, + w->x + xBorder, w->y + yBorder, + w->width - xBorder2, yBorder); + Gradient_Blend(&framebuffer, color, 50, + w->x + xBorder, w->y + yBorder2, + w->width - xBorder2, yBorder); + Gradient_Blend(&framebuffer, color, 25, + w->x + xBorder, w->y + yBorder3, + w->width - xBorder2, yBorder); +} + +static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { + int y, hintHeight; + + if (w->text.length || !w->hintText) { + y = w->y + (w->height - w->_textHeight) / 2; + Drawer2D_DrawText(&framebuffer, args, + w->x + xInputOffset, y + yInputOffset); + } else { + args->text = String_FromReadonly(w->hintText); + args->font = &hintFont; + + hintHeight = Drawer2D_TextHeight(args); + y = w->y + (w->height - hintHeight) / 2; + + Drawer2D.Colors['f'] = BitmapCol_Make(125, 125, 125, 255); + Drawer2D_DrawText(&framebuffer, args, + w->x + xInputOffset, y); + Drawer2D.Colors['f'] = BITMAPCOL_WHITE; + } +} + +void LBackend_InputDraw(struct LInput* w) { + cc_string text; char textBuffer[STRING_SIZE]; + struct DrawTextArgs args; + + String_InitArray(text, textBuffer); + LInput_UNSAFE_GetText(w, &text); + DrawTextArgs_Make(&args, &text, &textFont, false); + + LInput_DrawOuterBorder(w); + LInput_DrawInnerBorder(w); + Drawer2D_Clear(&framebuffer, BITMAPCOL_WHITE, + w->x + xBorder2, w->y + yBorder2, + w->width - xBorder4, w->height - yBorder4); + LInput_BlendBoxTop(w); + + Drawer2D.Colors['f'] = Drawer2D.Colors['0']; + LInput_DrawText(w, &args); + Drawer2D.Colors['f'] = Drawer2D.Colors['F']; + + caretRect = LInput_MeasureCaret(w, &text); + if (!w->caretShow) return; + Drawer2D_Clear(&framebuffer, BITMAPCOL_BLACK, + caretRect.X, caretRect.Y, caretRect.Width, caretRect.Height); +} + + /*########################################################################################################################* *------------------------------------------------------LabelWidget--------------------------------------------------------* *#########################################################################################################################*/ @@ -480,6 +610,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); @@ -488,7 +619,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); } @@ -502,7 +633,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); } @@ -515,7 +646,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) { @@ -523,17 +654,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); } @@ -543,10 +674,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); } @@ -558,7 +689,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); } @@ -585,7 +716,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 */ @@ -593,10 +724,11 @@ 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_ResetArea(w->x, w->y, w->width, w->hdrHeight); + Launcher_DrawBackground(&framebuffer, + w->x, w->y, w->width, w->hdrHeight); } } @@ -640,10 +772,11 @@ static void LTable_DrawRowsBackground(struct LTable* w) { if (height < 0) break; if (color) { - Drawer2D_Clear(&Launcher_Framebuffer, color, - w->x, y, w->width, height); + Drawer2D_Clear(&framebuffer, color, + w->x, y, w->width, height); } else { - Launcher_ResetArea(w->x, y, w->width, height); + Launcher_DrawBackground(&framebuffer, + w->x, y, w->width, height); } } } @@ -654,14 +787,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; } @@ -684,7 +817,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); @@ -717,10 +850,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); } @@ -742,9 +875,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); } @@ -753,7 +886,7 @@ void LBackend_TableDraw(struct LTable* w) { LTable_DrawHeaders(w); LTable_DrawRows(w); LTable_DrawScrollbar(w); - Launcher_MarkAllDirty(); + MarkAllDirty(); } @@ -819,7 +952,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) { @@ -834,7 +967,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; @@ -846,7 +979,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 41e396249..78c2f1de0 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -17,10 +17,17 @@ 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_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); diff --git a/src/LScreens.c b/src/LScreens.c index 22641e020..ab86b5c96 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++) { @@ -395,7 +396,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 +440,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_) { @@ -545,12 +546,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 +575,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 +595,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 +610,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 +783,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 +792,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 +804,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 +847,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 +855,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 +953,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 +994,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 { @@ -1125,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) { @@ -1138,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) { @@ -1148,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); } @@ -1189,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; - 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) { cc_string str; char strBuffer[STRING_SIZE]; const char* name; @@ -1210,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) { @@ -1229,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_) { @@ -1254,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); } @@ -1293,7 +1271,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 +1290,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 +1371,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_) { @@ -1493,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) { @@ -1604,7 +1580,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) { @@ -1718,7 +1694,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 +1724,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 +1759,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 +1773,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/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.c b/src/LWidgets.c index d705c6e76..ea815ea65 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; @@ -172,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); } @@ -218,7 +206,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 +240,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 +252,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 +303,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 +323,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 +351,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 +436,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); } @@ -461,34 +450,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); @@ -613,7 +602,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 +630,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..80b9cd23d 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; /* 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 */ \ @@ -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 { @@ -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); @@ -82,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. */ @@ -142,7 +143,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 279fd8aa1..66250c69c 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -21,14 +21,8 @@ #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. */ -static Rect2D dirty_rect; - -static struct LScreen* activeScreen; -struct Bitmap Launcher_Framebuffer; +struct LScreen* Launcher_Active; struct FontDesc Launcher_LogoFont; -static cc_bool pendingRedraw; cc_bool Launcher_ShouldExit, Launcher_ShouldUpdate; static char hashBuffer[STRING_SIZE], userBuffer[STRING_SIZE]; @@ -42,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); @@ -62,7 +57,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) { @@ -77,12 +72,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--------------------------------------------------* @@ -176,19 +165,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(); + LBackend_FreeFramebuffer(); + LBackend_InitFramebuffer(); - if (activeScreen) activeScreen->Layout(activeScreen); - Launcher_Redraw(); + if (Launcher_Active) Launcher_Active->Layout(Launcher_Active); + LBackend_Redraw(); } static cc_bool IsShutdown(int key) { @@ -204,62 +186,33 @@ 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); -} - -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); + Launcher_Active->MouseWheel(Launcher_Active, delta); } /*########################################################################################################################* *-----------------------------------------------------------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"); @@ -273,8 +226,7 @@ static void Launcher_Free(void) { hasBitmappedFont = false; CloseActiveScreen(); - activeScreen = NULL; - Window_FreeFramebuffer(&Launcher_Framebuffer); + LBackend_FreeFramebuffer(); } void Launcher_Run(void) { @@ -299,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, ""); @@ -323,8 +275,8 @@ void Launcher_Run(void) { Window_ProcessEvents(); if (!WindowInfo.Exists || Launcher_ShouldExit) break; - activeScreen->Tick(activeScreen); - if (dirty_rect.Width) Launcher_Display(); + Launcher_Active->Tick(Launcher_Active); + LBackend_Tick(); Thread_Sleep(10); } @@ -555,38 +507,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_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; - - /* 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; -} - -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..d7689220b 100644 --- a/src/Launcher.h +++ b/src/Launcher.h @@ -7,8 +7,8 @@ struct LScreen; struct FontDesc; -/* Contains the pixels that are drawn to the window */ -extern struct Bitmap Launcher_Framebuffer; +/* The screen/menu currently being shown */ +extern struct LScreen* Launcher_Active; /* Default font for screen/menu titles */ extern struct FontDesc Launcher_LogoFont; @@ -62,17 +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); -/* 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); /* Attempts to start the game by connecting to the given server. */ 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); } /*########################################################################################################################*