diff --git a/client/client.pro b/client/client.pro index 4beb242..600062c 100644 --- a/client/client.pro +++ b/client/client.pro @@ -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 diff --git a/client/src/gui/font.c b/client/src/gui/font.c index c541351..703f03c 100644 --- a/client/src/gui/font.c +++ b/client/src/gui/font.c @@ -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; cspan; - 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; ylheight; 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; ylheight; 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; } diff --git a/client/src/gui/font.h b/client/src/gui/font.h index 0cda234..4c6044c 100644 --- a/client/src/gui/font.h +++ b/client/src/gui/font.h @@ -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); diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 8c7d276..ad00bd3 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -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; } diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index c6ce8dc..b7a58f7 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -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 diff --git a/client/src/gui/surface.c b/client/src/gui/surface.c index 0948ecc..cc2e216 100644 --- a/client/src/gui/surface.c +++ b/client/src/gui/surface.c @@ -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; diff --git a/client/src/gui/surface.h b/client/src/gui/surface.h index f129cc5..924edcd 100644 --- a/client/src/gui/surface.h +++ b/client/src/gui/surface.h @@ -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); diff --git a/client/src/gui/wmwindow.c b/client/src/gui/wmwindow.c index 8a0eb99..7cb320a 100644 --- a/client/src/gui/wmwindow.c +++ b/client/src/gui/wmwindow.c @@ -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; ititle) 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; diff --git a/client/src/gui/wmwindow.h b/client/src/gui/wmwindow.h index 8ec57ee..4d0290f 100644 --- a/client/src/gui/wmwindow.h +++ b/client/src/gui/wmwindow.h @@ -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); diff --git a/client/src/main.c b/client/src/main.c index 35c670a..575c5db 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -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; +} +*/ diff --git a/client/src/platform/djgpp.c b/client/src/platform/djgpp.c index dda63e1..046242b 100644 --- a/client/src/platform/djgpp.c +++ b/client/src/platform/djgpp.c @@ -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, ®s, ®s); 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; diff --git a/client/src/platform/sdl2.c b/client/src/platform/sdl2.c index 3f32b2c..6617846 100644 --- a/client/src/platform/sdl2.c +++ b/client/src/platform/sdl2.c @@ -1,7 +1,7 @@ #ifdef BACKEND_SDL2 -#include "sdl2.h" +#include #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; }