Optimized window drawing. Added window caching.

This commit is contained in:
Scott Duensing 2022-06-15 20:02:39 -05:00
parent 7d32a1df31
commit 10196882ef
12 changed files with 585 additions and 231 deletions

View file

@ -4,8 +4,8 @@ CONFIG -= qt
CONFIG += console
CONFIG += c99
#CONFIG += BACKEND_SDL2
CONFIG += BACKEND_DJGPP
CONFIG += BACKEND_SDL2
#CONFIG += BACKEND_DJGPP
SHARED = $$PWD/../shared

View file

@ -1,7 +1,20 @@
#include "font.h"
#include "gui.h"
#include "platform/platform.h"
static FontT *_font = NULL;
static ColorT _foreground = 0;
static ColorT _background = 0;
static uint8_t _allowMods = 1;
void fontColorSet(ColorT foreground, ColorT background) {
_foreground = foreground;
_background = background;
}
FontT *fontFromRAMLoad(uint8_t *pointer) {
FontT *font = NULL;
uint32_t size = 0;
@ -67,7 +80,12 @@ FontT *fontLoad(char *filename) {
}
void fontRender(FontT *font, char *string, ColorT foreground, ColorT background, uint16_t x, uint16_t y) {
void fontModsEnabledSet(uint8_t enabled) {
_allowMods = enabled;
}
void fontRender(char *string, uint16_t x, uint16_t y) {
uint8_t cx;
uint8_t cy;
uint16_t offset;
@ -83,37 +101,54 @@ void fontRender(FontT *font, char *string, ColorT foreground, ColorT background,
for (c=0; c<strlen(string); c++) {
character = string[c];
if (_allowMods) {
// Change _foreground to GUI Color Index.
if (character == 1) {
character = string[++c];
if (character == 16) character = 0; // Because we can't pass NULL in a string, 16 is used for black.
_foreground = __guiBaseColors[character];
continue;
}
// Change _background to GUI Color Index.
if (character == 2) {
character = string[++c];
if (character == 16) character = 0; // Because we can't pass NULL in a string, 16 is used for black.
_background = __guiBaseColors[character];
continue;
}
}
// Find character position in font grid.
cx = character % font->span;
cy = character / font->span;
cx = character % _font->span;
cy = character / _font->span;
yp = y;
// Half byte or full byte font?
if (font->width == 4) {
if (_font->width == 4) {
// Half Byte.
// Which half of the byte do we want?
odd = ((cx & 1) == 1);
// Find offset byte based on font bits.
offset = cy * (font->span / 2) * font->height + cx * ((float)font->width / 8.0f);
offset = cy * (_font->span / 2) * _font->height + cx * ((float)_font->width / 8.0f);
for (yl=0; yl<font->height; yl++) {
for (yl=0; yl<_font->height; yl++) {
// We do 4 pixels unrolled hoping it's fast.
data = font->bits[offset];
offset += (font->span / 2);
data = _font->bits[offset];
offset += (_font->span / 2);
if (odd) {
surfacePixelSet(x, yp, (data & 0x08) ? foreground : background);
surfacePixelSet(x + 1, yp, (data & 0x04) ? foreground : background);
surfacePixelSet(x + 2, yp, (data & 0x02) ? foreground : background);
surfacePixelSet(x + 3, yp, (data & 0x01) ? foreground : background);
surfacePixelSet(x, yp, (data & 0x08) ? _foreground : _background);
surfacePixelSet(x + 1, yp, (data & 0x04) ? _foreground : _background);
surfacePixelSet(x + 2, yp, (data & 0x02) ? _foreground : _background);
surfacePixelSet(x + 3, yp, (data & 0x01) ? _foreground : _background);
} else {
surfacePixelSet(x, yp, (data & 0x80) ? foreground : background);
surfacePixelSet(x + 1, yp, (data & 0x40) ? foreground : background);
surfacePixelSet(x + 2, yp, (data & 0x20) ? foreground : background);
surfacePixelSet(x + 3, yp, (data & 0x10) ? foreground : background);
surfacePixelSet(x, yp, (data & 0x80) ? _foreground : _background);
surfacePixelSet(x + 1, yp, (data & 0x40) ? _foreground : _background);
surfacePixelSet(x + 2, yp, (data & 0x20) ? _foreground : _background);
surfacePixelSet(x + 3, yp, (data & 0x10) ? _foreground : _background);
}
yp++;
@ -122,38 +157,43 @@ void fontRender(FontT *font, char *string, ColorT foreground, ColorT background,
// Full Byte.
// Find offset byte based on font bits.
offset = cy * font->span * font->height + cx;
offset = cy * _font->span * _font->height + cx;
// Draw out 8 lines.
for (yl=0; yl<font->height; yl++) {
for (yl=0; yl<_font->height; yl++) {
// We do 8 pixels unrolled hoping it's fast.
data = font->bits[offset];
offset += font->span;
data = _font->bits[offset];
offset += _font->span;
surfacePixelSet(x, yp, (data & 0x80) ? foreground : background);
surfacePixelSet(x + 1, yp, (data & 0x40) ? foreground : background);
surfacePixelSet(x + 2, yp, (data & 0x20) ? foreground : background);
surfacePixelSet(x + 3, yp, (data & 0x10) ? foreground : background);
surfacePixelSet(x + 4, yp, (data & 0x08) ? foreground : background);
surfacePixelSet(x + 5, yp, (data & 0x04) ? foreground : background);
surfacePixelSet(x + 6, yp, (data & 0x02) ? foreground : background);
surfacePixelSet(x + 7, yp, (data & 0x01) ? foreground : background);
surfacePixelSet(x, yp, (data & 0x80) ? _foreground : _background);
surfacePixelSet(x + 1, yp, (data & 0x40) ? _foreground : _background);
surfacePixelSet(x + 2, yp, (data & 0x20) ? _foreground : _background);
surfacePixelSet(x + 3, yp, (data & 0x10) ? _foreground : _background);
surfacePixelSet(x + 4, yp, (data & 0x08) ? _foreground : _background);
surfacePixelSet(x + 5, yp, (data & 0x04) ? _foreground : _background);
surfacePixelSet(x + 6, yp, (data & 0x02) ? _foreground : _background);
surfacePixelSet(x + 7, yp, (data & 0x01) ? _foreground : _background);
yp++;
}
}
x += font->width;
x += _font->width;
}
}
void fontSet(FontT *font) {
_font = font;
}
void fontUnload(FontT **font) {
FontT *f = *font;
free(f->bits);
free(f);
f = NULL;
*font = NULL;
}

View file

@ -17,10 +17,13 @@ typedef struct FontS {
} FontT;
void fontColorSet(ColorT foreground, ColorT background);
FontT *fontFromRAMLoad(uint8_t *pointer);
uint16_t fontHeightGet(FontT *font);
FontT *fontLoad(char *filename);
void fontRender(FontT *font, char *string, ColorT foreground, ColorT background, uint16_t x, uint16_t y);
void fontModsEnabledSet(uint8_t enabled);
void fontRender(char *string, uint16_t x, uint16_t y);
void fontSet(FontT *font);
void fontUnload(FontT **font);
uint16_t fontWidthGet(FontT *font);

View file

@ -13,7 +13,9 @@ typedef struct WidgetCatalogS {
ColorT *__guiBaseColors = NULL;
SurfaceT *__guiBackBuffer = NULL;
FontT *__guiFontVGA8x8 = NULL;
FontT *__guiFontVGA8x14 = NULL;
FontT *__guiFontVGA8x16 = NULL;
static uint8_t _magicCount = 0;
@ -23,29 +25,27 @@ static SurfaceT *_mousePointer = NULL;
static ColorT _mouseTransparency;
void guiRun(void) {
void guiEventsDo(void) {
EventT event = { 0 };
while (_guiRunning) {
// Read mouse & keyboard.
platformEventGet(&event);
// Read mouse & keyboard.
platformEventGet(&event);
// Paint desktop.
surfaceSet(__guiBackBuffer);
surfaceClear(GUI_CYAN);
// Paint desktop.
surfaceSet(__guiBackBuffer);
surfaceClear(GUI_CYAN);
// Paint GUI.
wmPaint(&event);
// Paint GUI.
wmPaint(&event);
// Paint mouse pointer.
surfaceBlitWithTransparency(__guiBackBuffer, event.x, event.y, _mousePointer, _mouseTransparency);
// Paint mouse pointer.
surfaceBlitWithTransparency(__guiBackBuffer, event.x, event.y, _mousePointer, _mouseTransparency);
// Copy to screen.
videoBlit(0, 0, __guiBackBuffer);
// Copy to screen.
videoBlit(0, 0, __guiBackBuffer);
// Emergency Exit?
if (event.flags & EVENT_FLAG_KEYPRESS && event.key == KEY_ESC) guiStop();
}
// Emergency Exit?
if (event.flags & EVENT_FLAG_KEYPRESS && event.key == KEY_ESC) guiStop();
}
@ -56,9 +56,20 @@ void guiRegister(WidgetRegisterT widgetRegister) {
}
void guiRun(void) {
while (_guiRunning) {
// Process all GUI events.
guiEventsDo();
}
}
void guiShutdown(void) {
free(__guiBaseColors);
DEL(__guiBaseColors);
while (hmlen(_widgetCatalog) > 0) {
if (_widgetCatalog[0].value->unregister) _widgetCatalog[0].value->unregister(NULL);
@ -68,10 +79,12 @@ void guiShutdown(void) {
wmShutdown();
fontUnload(&__guiFontVGA8x16);
fontUnload(&__guiFontVGA8x14);
fontUnload(&__guiFontVGA8x8);
surfaceDestroy(_mousePointer);
surfaceDestroy(__guiBackBuffer);
surfaceDestroy(&_mousePointer);
surfaceDestroy(&__guiBackBuffer);
platformShutdown();
}
@ -111,10 +124,15 @@ uint8_t guiStartup(int16_t width, int16_t height, int16_t depth) {
_mousePointer = imageLoad("mouse.png");
_mouseTransparency = surfacePixelGet(_mousePointer, surfaceWidthGet(_mousePointer) - 2, 0); // Find our transparency color.
__guiFontVGA8x8 = fontLoad("vga8x8.dat");
__guiFontVGA8x14 = fontLoad("vga8x14.dat");
__guiFontVGA8x16 = fontLoad("vga8x16.dat");
fontSet(__guiFontVGA8x14);
fontColorSet(GUI_WHITE, GUI_BLACK);
wmStartup();
// Register all known widgets with the GUI.
guiRegister(windowRegister);
return SUCCESS;
@ -135,4 +153,16 @@ void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, ui
widget->r.h = h;
widget->reg = hmget(_widgetCatalog, magic);
widget->dirty = 1; // Everything starts dirty to force a paint.
}
uint8_t guiWidgetDirtyGet(WidgetT *widget) {
return widget->dirty;
}
void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty) {
widget->dirty = dirty;
}

View file

@ -31,16 +31,17 @@ typedef struct RectS {
} RectT;
typedef struct RegisterS {
char *widgetName;
WidgetEventT paint;
WidgetEventT destroy;
GuiCallbackT unregister;
char *widgetName; // Text name of widget.
WidgetEventT paint; // Paint routine.
WidgetEventT destroy; // Destroy routine.
GuiCallbackT unregister; // Unregister routine.
} RegisterT;
typedef struct WidgetS {
uint8_t magic;
RectT r;
RegisterT *reg;
uint8_t magic; // Magic ID of widget.
RectT r; // Outer bounds, except for windows.
RegisterT *reg; // Registration information.
uint8_t dirty; // Is the widget dirty?
} WidgetT;
typedef RegisterT *(*WidgetRegisterT)(uint8_t);
@ -49,7 +50,9 @@ typedef RegisterT *(*WidgetRegisterT)(uint8_t);
extern SurfaceT *__guiBackBuffer;
extern SurfaceT *__guiScreenBuffer;
extern ColorT *__guiBaseColors;
extern FontT *__guiFontVGA8x8;
extern FontT *__guiFontVGA8x14;
extern FontT *__guiFontVGA8x16;
#define GUI_BLACK __guiBaseColors[0]
@ -70,13 +73,19 @@ extern FontT *__guiFontVGA8x14;
#define GUI_WHITE __guiBaseColors[15]
void guiModesShow(void);
#define W (WidgetT *) // Cast any widget to base WidgetT type.
#define C(n) ((n) << 3) // Multiply by 8 to get "cells".
void guiEventsDo(void);
void guiRegister(WidgetRegisterT widgetRegister);
void guiRun(void);
void guiShutdown(void);
uint8_t guiStartup(int16_t width, int16_t height, int16_t depth);
void guiStop(void);
void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
uint8_t guiWidgetDirtyGet(WidgetT *widget);
void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty);
#endif // GUI_H

View file

@ -111,8 +111,6 @@ SurfaceT *surfaceCreate(int16_t width, int16_t height) {
return NULL;
}
memset(surface->buffer.bits8, 0, surface->bytes);
return surface;
}
@ -160,9 +158,19 @@ void surfaceBoxFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c)
}
void surfaceDestroy(SurfaceT *surface) {
free(surface->buffer.bits8);
free(surface);
void surfaceBoxHighlight(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT highlight, ColorT shadow) {
surfaceLineH(x1, x2, y1, highlight);
surfaceLineV(x1, y1, y2, highlight);
surfaceLineH(x1, x2, y2, shadow);
surfaceLineV(x2, y1, y2, shadow);
}
void surfaceDestroy(SurfaceT **surface) {
SurfaceT *s = *surface;
DEL(s->buffer.bits8);
DEL(s);
*surface = NULL;
}
@ -176,6 +184,66 @@ int16_t surfaceHeightGet(SurfaceT *surface) {
}
void surfaceLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
int16_t x;
int16_t y;
int16_t dx;
int16_t dy;
int16_t incX;
int16_t incY;
int16_t balance;
if (x2 >= x1) {
dx = x2 - x1;
incX = 1;
} else {
dx = x1 - x2;
incX = -1;
}
if (y2 >= y1) {
dy = y2 - y1;
incY = 1;
} else {
dy = y1 - y2;
incY = -1;
}
x = x1;
y = y1;
if (dx >= dy) {
dy <<= 1;
balance = dy - dx;
dx <<= 1;
while (x != x2) {
surfacePixelSet(x, y, color);
if (balance >= 0) {
y += incY;
balance -= dx;
}
balance += dy;
x += incX;
}
surfacePixelSet(x, y, color);
} else {
dx <<= 1;
balance = dx - dy;
dy <<= 1;
while (y != y2) {
surfacePixelSet(x, y, color);
if (balance >= 0) {
x += incX;
balance -= dy;
}
balance += dx;
y += incY;
}
surfacePixelSet(x, y, color);
}
}
void surfaceLineH(int16_t x1, int16_t x2, int16_t y, ColorT c) {
int16_t i;
int16_t t;

View file

@ -52,9 +52,11 @@ ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b);
SurfaceT *surfaceCreate(int16_t width, int16_t height);
void surfaceBox(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c);
void surfaceBoxFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT c);
void surfaceDestroy(SurfaceT *surface);
void surfaceBoxHighlight(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT highlight, ColorT shadow);
void surfaceDestroy(SurfaceT **surface);
SurfaceT *surfaceGet(void);
int16_t surfaceHeightGet(SurfaceT *surface);
void surfaceLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color);
void surfaceLineH(int16_t x1, int16_t x2, int16_t y, ColorT c);
void surfaceLineV(int16_t x, int16_t y1, int16_t y2, ColorT c);
void surfaceSet(SurfaceT *surface);

View file

@ -3,9 +3,13 @@
#include "font.h"
#define USE_CACHING
uint8_t __MAGIC_WINDOW = 0;
static WindowT **_windowList = NULL;
static WindowT *_windowTop = NULL;
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...) {
@ -22,8 +26,12 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl
win->maximize.x = win->maximize.y = win->maximize.x2 = win->maximize.y2 = 0;
win->bounds.x = win->bounds.y = win->bounds.x2 = win->bounds.y2 = 0;
win->cached = NULL;
arrput(_windowList, win);
windowFocusSet(win);
return win;
}
@ -32,169 +40,288 @@ void windowDestroy(struct WidgetS *widget, ...) {
uint16_t i;
WindowT *window = (WindowT *)widget;
// Find the window to delete.
for (i=0; i<arrlen(_windowList); i++) {
if (window == _windowList[i]) {
// Was it the focused window?
if (_windowList[i] == _windowTop) {
// Find new topmost window on next call to focus.
_windowTop = NULL;
}
// Free the title.
if (_windowList[i]->title) DEL(_windowList[i]->title);
// Free cached surface.
if (_windowList[i]->cached) surfaceDestroy(&_windowList[i]->cached);
// Delete the window.
DEL(_windowList[i]);
// Remove it from window list.
arrdel(_windowList, i);
// Fixup focus.
windowFocusSet(NULL);
break;
}
}
}
void windowPaint(struct WidgetS *widget, ...) {
WindowT *w = (WindowT *)widget;
uint16_t x1 = w->base.r.x;
uint16_t y1 = w->base.r.y;
uint16_t x2 = w->base.r.x + w->base.r.w - 1;
uint16_t y2 = w->base.r.y + w->base.r.h - 1;
uint16_t tx1;
uint16_t ty1;
uint16_t tx2;
uint16_t ty2;
uint16_t minimizeOffset = 0;
ColorT titleBackgroundColor = GUI_DARKGRAY;
void windowFocusSet(WindowT *win) {
int16_t i;
// Fake Window contents.
surfaceBoxFilled(x1, y1, x2, y2, GUI_BLACK);
// If we need a titlebar, it's 18px.
if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {
// Draw title bar background.
y1 -= 18;
surfaceBoxFilled(x1, y1, x2, y1 + 17, titleBackgroundColor);
// Close box?
if (w->flags & WIN_CLOSE) {
// 26px wide, 18 tall including highlight and shadow.
w->close.x = x1 + 1;
w->close.y = y1 + 1;
w->close.x2 = w->close.x + 24;
w->close.y2 = w->close.y + 15;
surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, GUI_LIGHTGRAY);
surfaceLineH(w->close.x, w->close.x2, w->close.y, GUI_WHITE);
surfaceLineV(w->close.x, w->close.y, w->close.y2, GUI_WHITE);
// Button is 8px down, 3px tall, and 4px in on both sides.
tx1 = w->close.x + 4;
ty1 = w->close.y + 7;
tx2 = w->close.x2 - 4;
ty2 = w->close.y + 9;
surfaceLineH(tx1, tx2, ty1, GUI_WHITE);
surfacePixelSet(tx1, ty1 + 1, GUI_WHITE);
surfaceLineH(tx1, tx2, ty2, GUI_BLACK);
surfaceLineV(tx2, ty1, ty2, GUI_BLACK);
// Set titlebar area.
w->titlebar.x = w->close.x2 + 2;
// Do we have a focused window at the moment?
if (!_windowTop || !win) {
// If there's a list of windows, use the topmost.
if (arrlen(_windowList) > 0) {
_windowTop = _windowList[arrlen(_windowList) - 1];
guiWidgetDirtySet(W(_windowTop), 1);
} else {
// No close box - set titlebar area.
w->titlebar.x = x1;
// If no list, set it to NULL.
_windowTop = NULL;
}
return;
}
// Did they even pass in a window? If not, we're just intended to fixup _windowTop.
if (!win) return;
// Are we already the topmost window?
if (win == _windowTop) return;
// Mark old focus and new focus dirty to repaint title bar.
guiWidgetDirtySet(W(win), 1);
guiWidgetDirtySet(W(_windowTop), 1);
// Change who has focus.
_windowTop = win;
// Reorder window list.
i = arrlen(_windowList) - 2;
if (i >= 0) {
for (; i>=0; i--) {
if (_windowList[i] == win) {
arrdel(_windowList, i);
arrput(_windowList, win);
break;
}
}
}
}
void windowMoveTo(WindowT *w, uint16_t x, uint16_t y) {
int16_t dx = x - w->base.r.x;
int16_t dy = y - w->base.r.y;
// This is all because we draw the window at 0,0 in order to cache it.
// To keep the coordinates correct, we have to adjust them all when
// the window is moved.
if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {
if (w->flags & WIN_CLOSE) {
w->close.x += dx;
w->close.y += dy;
w->close.x2 += dx;
w->close.y2 += dy;
}
w->titlebar.y = y1;
w->titlebar.x2 = x2;
w->titlebar.y2 = y1 + 17;
// Maximize box?
if (w->flags & WIN_MAXIMIZE) {
// 26px wide, 18 tall including highlight and shadow.
w->maximize.y = y1 + 1;
w->maximize.x2 = x2 - 1;
w->maximize.x = w->maximize.x2 - 24;
w->maximize.y2 = w->maximize.y + 15;
surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, GUI_LIGHTGRAY);
surfaceLineH(w->maximize.x, w->maximize.x2, w->maximize.y, GUI_WHITE);
surfaceLineV(w->maximize.x, w->maximize.y, w->maximize.y2, GUI_WHITE);
// Button is 3px down, and 4px in on both sides.
tx1 = w->maximize.x + 4;
ty1 = w->maximize.y + 4;
tx2 = w->maximize.x2 - 3;
ty2 = w->maximize.y + 12;
surfaceLineH(tx1, tx2, ty1, GUI_WHITE);
surfaceLineV(tx1, ty1, ty2, GUI_WHITE);
surfaceLineH(tx1, tx2, ty2, GUI_BLACK);
surfaceLineV(tx2, ty1, ty2, GUI_BLACK);
// Move minimize button over.
minimizeOffset = 26;
// Set titlebar area.
w->titlebar.x2 -= 26;
w->maximize.x += dx;
w->maximize.y += dy;
w->maximize.x2 += dx;
w->maximize.y2 += dy;
}
// Minimize box?
if (w->flags & WIN_MINIMIZE) {
// 26px wide, 18 tall including highlight and shadow.
w->minimize.y = y1 + 1;
w->minimize.x2 = x2 - 1 - minimizeOffset;
w->minimize.x = w->minimize.x2 - 24;
w->minimize.y2 = w->minimize.y + 15;
surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, GUI_LIGHTGRAY);
surfaceLineH(w->minimize.x, w->minimize.x2, w->minimize.y, GUI_WHITE);
surfaceLineV(w->minimize.x, w->minimize.y, w->minimize.y2, GUI_WHITE);
tx1 = w->minimize.x + 10;
ty1 = w->minimize.y + 6;
tx2 = w->minimize.x2 - 8;
ty2 = w->minimize.y + 9;
surfaceLineH(tx1, tx2, ty1, GUI_WHITE);
surfaceLineV(tx1, ty1, ty2, GUI_WHITE);
surfaceLineH(tx1, tx2, ty2, GUI_BLACK);
surfaceLineV(tx2, ty1, ty2, GUI_BLACK);
// Set titlebar area.
w->titlebar.x2 -= 26;
w->minimize.x += dx;
w->minimize.y += dy;
w->minimize.x2 += dx;
w->minimize.y2 += dy;
}
// Title font area is 12px high.
surfaceLineH(w->titlebar.x, w->titlebar.x2 - 1, w->titlebar.y, GUI_WHITE);
surfaceLineV(w->titlebar.x, w->titlebar.y, w->titlebar.y2 - 1, GUI_WHITE);
if (w->title) {
ty1 = w->titlebar.y + 2;
tx1 = w->titlebar.x + 2 + (w->titlebar.x2 - w->titlebar.x - 4) * 0.5 - ((strlen(w->title) * 8) * 0.5);
fontRender(__guiFontVGA8x14, w->title, GUI_WHITE, titleBackgroundColor, tx1, ty1);
w->titlebar.x += dx;
w->titlebar.y += dy;
w->titlebar.x2 += dx;
w->titlebar.y2 += dy;
}
w->bounds.x += dx;
w->bounds.y += dy;
w->bounds.x2 += dx;
w->bounds.y2 += dy;
w->base.r.x = x;
w->base.r.y = y;
}
void windowPaint(struct WidgetS *widget, ...) {
int16_t i;
int16_t x1;
int16_t y1;
int16_t x2;
int16_t y2;
int16_t tx1;
int16_t tx2;
int16_t ty2;
int16_t originalX;
int16_t originalY;
WindowT *w;
SurfaceT *target;
ColorT titleBackgroundColor;
ColorT widgetColor;
target = surfaceGet();
w = (WindowT *)widget;
#ifdef USE_CACHING
// Do we need redrawn?
if (guiWidgetDirtyGet(widget)) {
guiWidgetDirtySet(widget, 0);
// Move the window to 0,0 to cache to it's own surface.
originalX = w->base.r.x;
originalY = w->base.r.y;
w->base.r.x = 0;
w->base.r.y = 0;
// Do we have a cached surface already?
if (w->cached) {
// Did the size change?
if ((surfaceWidthGet(w->cached) != w->base.r.w) || (surfaceHeightGet(w->cached) != w->base.r.h)) {
// Yeah. We will recreate it.
surfaceDestroy(&w->cached);
}
}
// Do we need to create a surface?
if (!w->cached) {
w->cached = surfaceCreate(w->base.r.w, w->base.r.h);
}
// Draw into cache.
surfaceSet(w->cached);
#endif
// Determine some colors.
titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY;
// Get ready!
x1 = w->base.r.x;
y1 = w->base.r.y;
x2 = w->base.r.x + w->base.r.w - 1;
y2 = w->base.r.y + w->base.r.h - 1;
// Draw border.
surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK);
x1++; y1++; x2--; y2--;
for (i=0; i<3; i++) {
surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY);
x1++; y1++; x2--; y2--;
}
surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE);
x1++; y1++; x2--; y2--;
// Do we need a titlebar?
if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) {
tx1 = x1;
tx2 = x2;
ty2 = y1 + 20;
// Close box?
if (w->flags & WIN_CLOSE) {
tx1 += 20;
w->close.x = x1;
w->close.y = y1;
w->close.x2 = tx1 - 1;
w->close.y2 = ty2 - 1;
surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
}
// Maximize box?
if (w->flags & WIN_MAXIMIZE) {
tx2 -= 20;
w->maximize.x = tx2 + 1;
w->maximize.y = y1;
w->maximize.x2 = tx2 + 20;
w->maximize.y2 = ty2 - 1;
surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE);
surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor);
surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor);
surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
}
// Minimize box?
if (w->flags & WIN_MINIMIZE) {
tx2 -= 20;
w->minimize.x = tx2 + 1;
w->minimize.y = y1;
w->minimize.x2 = tx2 + 20;
w->minimize.y2 = ty2 - 1;
surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor);
surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor);
surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK);
} else {
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
}
// Draw titlebar background.
w->titlebar.x = tx1;
w->titlebar.y = y1;
w->titlebar.x2 = tx2;
w->titlebar.y2 = ty2 - 1;
surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor);
if (w->title) {
fontSet(__guiFontVGA8x16);
fontColorSet(GUI_WHITE, titleBackgroundColor);
fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2);
}
surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK);
} else {
w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0;
w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0;
w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0;
w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0;
}
// Find content area.
y1 += 20;
w->bounds.x = x1;
w->bounds.y = y1;
w->bounds.x2 = x2;
w->bounds.y2 = y2;
// Resize handle.
if (w->flags & WIN_RESIZE) {
x1 = w->base.r.x + w->base.r.w - 2;
x2 = x1 - 3;
y1 = w->base.r.y + w->base.r.h - 21;
surfaceLineH(x1, x2, y1, GUI_BLACK);
surfaceLineH(x1, x2, y1 + 1, GUI_WHITE);
x1 = w->base.r.x + w->base.r.w - 21;
y1 = w->base.r.y + w->base.r.h - 2;
y2 = y1 - 3;
surfaceLineV(x1, y1, y2, GUI_BLACK);
surfaceLineV(x1 + 1, y1, y2, GUI_WHITE);
}
// Fake Window contents.
surfaceBoxFilled(w->bounds.x, w->bounds.y, w->bounds.x2, w->bounds.y2, GUI_BLACK);
#ifdef USE_CACHING
// Fixup all the widget coordinates.
windowMoveTo(w, originalX, originalY);
}
// Innermost shadow frame. 1px wide.
x1--;
y1--;
x2++;
y2++;
surfaceLineH(x1, x2, y2, GUI_WHITE);
surfaceLineV(x2, y1, y2, GUI_WHITE);
surfaceLineH(x1, x2, y1, GUI_DARKGRAY);
surfaceLineV(x1, y1, y2, GUI_DARKGRAY);
// Frame Border. 4px wide.
x1 -= 4;
y1 -= 4;
x2 += 4;
y2 += 4;
surfaceBoxFilled(x1, y1, x1 + 3, y2, GUI_LIGHTGRAY);
surfaceBoxFilled(x2, y1, x2 - 3, y2, GUI_LIGHTGRAY);
surfaceBoxFilled(x1, y1, x2, y1 + 3, GUI_LIGHTGRAY);
surfaceBoxFilled(x1, y2, x2, y2 - 3, GUI_LIGHTGRAY);
// Resize handle.
if (w->flags & WIN_RESIZE) {
ty1 = y2 - 15 - 3;
tx1 = x2 - 15 - 3;
surfaceLineH(x2, x2 - 3, ty1, GUI_DARKGRAY);
surfaceLineH(x2, x2 - 3, ty1 + 1, GUI_WHITE);
surfaceLineV(tx1, y2, y2 - 3, GUI_DARKGRAY);
surfaceLineV(tx1 + 1, y2, y2 - 3, GUI_WHITE);
}
// Outermost shadow frame. 1px wide.
x1--;
y1--;
x2++;
y2++;
surfaceLineH(x1, x2, y1, GUI_WHITE);
surfaceLineV(x1, y1, y2, GUI_WHITE);
surfaceLineH(x1, x2, y2, GUI_DARKGRAY);
surfaceLineV(x2, y1, y2, GUI_DARKGRAY);
w->bounds.x = x1;
w->bounds.x2 = x2;
w->bounds.y = y1;
w->bounds.y2 = y2;
// By now we have a valid cached window. Blit it.
surfaceBlit(target, w->base.r.x, w->base.r.y, w->cached);
#endif
}
@ -215,6 +342,8 @@ RegisterT *windowRegister(uint8_t magic) {
void wmPaint(EventT *event) {
int16_t i;
int16_t x2;
int16_t y2;
WidgetT *widget;
WindowT *win;
static uint8_t dragging = 0;
@ -233,25 +362,29 @@ void wmPaint(EventT *event) {
// Get top window.
win = _windowList[arrlen(_windowList) - 1];
// Get right/bottom of window.
x2 = win->base.r.x + win->base.r.w - 1;
y2 = win->base.r.y + win->base.r.h - 1;
// Wrap left button processing with a 'for' so we can 'break' out of it.
for (;;) {
// Is the left mouse button down?
if (event->buttons & BUTTON_LEFT) {
// DEBUG - draw active regions. ***TODO*** No resize grabber here.
surfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_YELLOW);
surfaceBox(win->base.r.x, win->base.r.y, win->base.r.x + win->base.r.w - 1, win->base.r.y + win->base.r.h - 1, GUI_YELLOW);
surfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_RED);
surfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_RED);
surfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_RED);
surfaceSet(__guiBackBuffer);
surfaceBox(win->base.r.x, win->base.r.y, x2, y2, GUI_YELLOW);
surfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_CYAN);
surfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_LIGHTBLUE);
surfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_LIGHTCYAN);
surfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_LIGHTGREEN);
surfaceBox(win->maximize.x, win->maximize.y, win->maximize.x2, win->maximize.y2, GUI_RED);
// Are we currently dragging?
if (dragging) {
// Move window to new mouse location.
win->base.r.x = event->x - dragOffset.x;
win->base.r.y = event->y - dragOffset.y;
windowMoveTo(win, event->x - dragOffset.x, event->y - dragOffset.y);
break;
} else { // Dragging.
@ -260,11 +393,10 @@ void wmPaint(EventT *event) {
if (event->flags & EVENT_FLAG_LEFT_DOWN) {
// Are we on the topmost window?
if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
if (event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) {
//***TODO*** Are we inside the window content? Most likely, check first.
if (event->x <= (win->base.r.x + win->base.r.w - 1) && event->x >= win->base.r.x &&
event->y <= (win->base.r.y + win->base.r.h - 1) && event->y >= win->base.r.y) {
if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
//***TODO*** Send to window for processing.
}
@ -288,10 +420,13 @@ void wmPaint(EventT *event) {
if (i >= 0) {
for (; i>=0; i--) {
win = _windowList[i];
if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
// Get right/bottom of window.
x2 = win->base.r.x + win->base.r.w - 1;
y2 = win->base.r.y + win->base.r.h - 1;
// Inside this window?
if (event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) {
// Bring this window forward.
arrdel(_windowList, i);
arrput(_windowList, win);
windowFocusSet(win);
// If we happened to be in the title bar, go ahead and start dragging.
if (event->x <= win->titlebar.x2 && event->x >= win->titlebar.x && event->y <= win->titlebar.y2 && event->y >= win->titlebar.y) {
dragging = 1;

View file

@ -13,22 +13,25 @@
typedef struct WindowS {
WidgetT base;
char *title;
uint8_t flags;
RectT close;
RectT titlebar;
RectT minimize;
RectT maximize;
RectT bounds;
WidgetT base; // Required by all widgets.
char *title; // Title of window.
uint8_t flags; // Window flags (see defines above).
RectT close; // Coordinates of close box, if any.
RectT titlebar; // Coordinates of title bar, if any.
RectT minimize; // Coordinates of minimize box, if any.
RectT maximize; // Coordinates of maximize box, if any.
RectT bounds; // Inside edge of window frame.
SurfaceT *cached; // Once rendered, keep a cached copy for faster redrawing.
} WindowT;
extern uint8_t __MAGIC_WINDOW;
extern uint8_t __MAGIC_WINDOW; // Magic ID assigned to us from the GUI.
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...);
void windowDestroy(struct WidgetS *widget, ...);
void windowFocusSet(WindowT *win);
void windowMoveTo(WindowT *win, uint16_t x, uint16_t y);
void windowPaint(struct WidgetS *widget, ...);
RegisterT *windowRegister(uint8_t magic);

View file

@ -25,3 +25,61 @@ int main(int argc, char *argv[]) {
return 0;
}
/*
int tui(int argc, char *argv[]) {
EventT event = { 0 };
uint16_t x = 5;
uint16_t y = 5;
uint16_t tx;
uint16_t ty;
FontT *vga8x16 = NULL;
FontT *vga8x8 = NULL;
(void)argc;
memoryStartup(argv[0]);
logOpenByHandle(memoryLogHandleGet());
if (guiStartup(800, 600, 32) == SUCCESS) {
vga8x8 = fontLoad("vga8x8.dat");
vga8x16 = fontLoad("vga8x16.dat");
// Run until keypress.
while (1) {
// Paint desktop.
surfaceSet(__guiBackBuffer);
surfaceClear(GUI_CYAN);
// Paint GUI.
ty = y;
fontSet(vga8x16);
fontRender("\x1\xf\x2\x8 Title Bar _ \xfe X ", C(x), C(ty++)); ty++;
fontSet(vga8x8);
fontRender("\x2\x7\xda\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xbf\x1e", C(x), C(ty++));
for (tx=0; tx<10; tx++) fontRender("\xb3 \xb3\xb0", C(x), C(ty++));
fontRender("\xc0\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xc4\xd9\x1f", C(x), C(ty++));
fontRender("\x11\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\x10\xfe", C(x), C(ty++));
// Copy to screen.
videoBlit(0, 0, __guiBackBuffer);
// Check for exit.
platformEventGet(&event);
if (event.flags & EVENT_FLAG_KEYPRESS && event.key == KEY_ESC) break;
}
fontUnload(&vga8x16);
fontUnload(&vga8x8);
guiShutdown();
}
logClose();
memoryShutdown();
return 0;
}
*/

View file

@ -187,18 +187,22 @@ void platformEventGet(EventT *event) {
int16_t meta = bioskey(KEYBOARD_META_EXTENDED);
int16_t key = 0;
union REGS regs;
static int32_t lastX = 0;
static int32_t lastY = 0;
// Read mouse motion.
regs.x.ax = MOUSE_GETMOTION;
int86(MOUSE_INT, &regs, &regs);
dx = regs.x.cx; // Temporary assignment changes values to signed.
dy = regs.x.dx; // Don't skip this step. :-)
x = event->x + dx;
y = event->y + dy;
x = lastX + dx;
y = lastY + dy;
if (x < 0) x = 0;
if (x > w - 1) x = w - 1;
if (y < 0) y = 0;
if (y > h - 1) y = h - 1;
lastX = x;
lastY = y;
event->x = x;
event->y = y;

View file

@ -1,7 +1,7 @@
#ifdef BACKEND_SDL2
#include "sdl2.h"
#include <SDL2/SDL.h>
#include "platform.h"
@ -104,7 +104,7 @@ void platformShutdown(void) {
}
void platformStartup(int16_t width, int16_t height, int16_t depth) {
uint8_t platformStartup(int16_t width, int16_t height, int16_t depth) {
SDL_PixelFormatEnum pixelFormat;
(void)depth;
@ -159,6 +159,8 @@ void platformStartup(int16_t width, int16_t height, int16_t depth) {
_height = height;
surfaceStartup(depth);
return SUCCESS;
}