Merge pull request #941 from UnknownShadow200/LBackend45893

Redesign launcher backend to defer drawing and move dirty region tracking into default backend
This commit is contained in:
UnknownShadow200 2022-04-25 09:02:06 +10:00 committed by GitHub
commit f7c81da3c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 448 additions and 435 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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. */ \

View File

@ -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);

View File

@ -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. */

View File

@ -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

View File

@ -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. */

View File

@ -11,6 +11,7 @@
#include "LWidgets.h"
#include "LScreens.h"
#include "LWeb.h"
#include "Funcs.h"
#include <mach-o/dyld.h>
#include <sys/stat.h>
#include <UIKit/UIPasteboard.h>
@ -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<UIView*>* 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);
}
/*########################################################################################################################*