diff --git a/client/client.pro b/client/client.pro index 6eb6df7..dcc4d17 100644 --- a/client/client.pro +++ b/client/client.pro @@ -43,6 +43,7 @@ INCLUDEPATH += \ HEADERS = \ $$LINUX_HEADERS \ + src/gui/button.h \ src/thirdparty/stb_ds.h \ src/thirdparty/stb_leakcheck.h \ src/thirdparty/stb_image.h \ @@ -62,6 +63,7 @@ HEADERS = \ SOURCES = \ $$LINUX_SOURCES \ src/gui/array.c \ + src/gui/button.c \ src/gui/font.c \ src/gui/desktop.c \ src/gui/gui.c \ diff --git a/client/src/dos/vesa.c b/client/src/dos/vesa.c index 2de56b2..cf9f397 100644 --- a/client/src/dos/vesa.c +++ b/client/src/dos/vesa.c @@ -802,6 +802,16 @@ void vbeSurfaceDestroy(SurfaceT **surface) { } +uint16_t vbeSurfaceHeightGet(void) { + return _activeSurface->height; +} + + +uint16_t vbeSurfaceWidthGet(void) { + return _activeSurface->width; +} + + void vbeSurfaceSet(SurfaceT *surface) { if (surface) { _activeSurface = surface; diff --git a/client/src/gui/button.c b/client/src/gui/button.c new file mode 100644 index 0000000..97d74e5 --- /dev/null +++ b/client/src/gui/button.c @@ -0,0 +1,114 @@ +/* + * Kangaroo Punch Multi Player Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "button.h" + + +void buttonDel(WidgetT **widget) { + ButtonT *b = (ButtonT *)*widget; + + if (b->title) free(b->title); + free(b); + b = NULL; +} + + +WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widgetCallback callback) { + ButtonT *b = (ButtonT *)button; + + b->base.magic = MAGIC_BUTTON; + b->base.x = x; + b->base.y = y; + b->base.delMethod = buttonDel; + b->base.paintMethod = buttonPaint; + b->base.mouseEventMethod = buttonMouseEvent; + b->title = NULL; + b->clicked = callback; + + buttonSetTitle(b, title); + + // Width is set in buttonSetTitle + b->base.h = fontHeightGet(_guiFont) + (_guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2); + + return button; +} + + +void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y) { + ButtonT *b = (ButtonT *)widget; + + (void)x; + (void)y; + + // Fire callback on mouse up. + if (!mouse->buttonLeft && mouse->buttonLeftWasDown) { + if (b->clicked) b->clicked(widget); + } +} + + +ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback) { + ButtonT *button = (ButtonT *)malloc(sizeof(ButtonT)); + WidgetT *widget = NULL; + + if (!button) return NULL; + + widget = widgetInit((WidgetT *)button); + if (!widget) { + free(button); + return NULL; + } + + button = (ButtonT *)buttonInit((WidgetT *)button, x, y, title, callback); + + return button; +} + + +void buttonPaint(WidgetT *button) { + ButtonT *b = (ButtonT *)button; + int16_t i; + uint16_t x1 = b->base.x + b->base.marginX; + uint16_t y1 = b->base.y + b->base.marginY; + + if (GUI_GET_FLAG(button, WIDGET_FLAG_DIRTY)) { + vbeSurfaceSet(b->base.surface); + + // Draw bezel. + for (i=0; i<_guiMetric[METRIC_BUTTON_BEZEL_SIZE]; i++) { + guiDrawHighlightFrame(x1 + i, y1 + i, x1 + b->base.w - i, y1 + b->base.h - i, _guiColor[COLOR_BUTTON_HIGHLIGHT], _guiColor[COLOR_BUTTON_SHADOW]); + } + + // Draw background (depends on x from above). + guiDrawFilledRectangle(x1 + i, y1 + i, x1 + b->base.w - i, y1 + b->base.h - i, _guiColor[COLOR_BUTTON_BACKGROUND]); + + // Draw title (depends on x from above). + fontRender(_guiFont, b->title, _guiColor[COLOR_BUTTON_TEXT], _guiColor[COLOR_BUTTON_BACKGROUND], x1 + i + _guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN], y1 + i + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN]); + + GUI_CLEAR_FLAG(button, WIDGET_FLAG_DIRTY); + } +} + + +void buttonSetTitle(ButtonT *button, char *title) { + if (button->title) free(button->title); + button->title = strdup(title); + button->base.w = (strlen(title) * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2); +} diff --git a/client/src/gui/button.h b/client/src/gui/button.h new file mode 100644 index 0000000..bc30449 --- /dev/null +++ b/client/src/gui/button.h @@ -0,0 +1,44 @@ +/* + * Kangaroo Punch Multi Player Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifndef BUTTON_H +#define BUTTON_H + + +#include "gui.h" +#include "widget.h" + + +typedef struct ButtonS { + WidgetT base; // Must be first in every widget + char *title; + widgetCallback clicked; +} ButtonT; + + +void buttonDel(WidgetT **widget); +WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widgetCallback callback); +void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y); +ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback); +void buttonPaint(WidgetT *button); +void buttonSetTitle(ButtonT *button, char *title); + + +#endif // BUTTON_H diff --git a/client/src/gui/desktop.c b/client/src/gui/desktop.c index 5a7a7c0..7e3fade 100644 --- a/client/src/gui/desktop.c +++ b/client/src/gui/desktop.c @@ -42,6 +42,7 @@ WidgetT *desktopInit(WidgetT *desktop) { d->dragOffsetY = 0; d->dragWidget = NULL; + GUI_SET_FLAG(desktop, WIDGET_FLAG_OWNS_SURFACE); d->base.surface = vbeSurfaceCreate(d->base.w, d->base.h); if (!d->base.surface) { free(d); @@ -73,9 +74,9 @@ DesktopT *desktopNew(void) { void desktopPaint(WidgetT *desktop) { DesktopT *d = (DesktopT *)desktop; - if (d->base.dirty) { + if (GUI_GET_FLAG(desktop, WIDGET_FLAG_DIRTY)) { vbeSurfaceSet(d->base.surface); vbeSurfaceClear(_guiColor[COLOR_DESKTOP]); - d->base.dirty = 0; + GUI_CLEAR_FLAG(desktop, WIDGET_FLAG_DIRTY); } } diff --git a/client/src/gui/font.c b/client/src/gui/font.c index 84b627e..6a6db37 100644 --- a/client/src/gui/font.c +++ b/client/src/gui/font.c @@ -21,6 +21,11 @@ #include "font.h" +uint16_t fontHeightGet(FontT *font) { + return font->height; +} + + FontT *fontLoad(char *filename) { FILE *in = NULL; uint16_t size = 0; @@ -59,38 +64,46 @@ FontT *fontLoad(char *filename) { } -void fontRender(FontT *font, uint8_t character, PixelT foreground, PixelT background, uint16_t x, uint16_t y) { +void fontRender(FontT *font, char *string, PixelT foreground, PixelT background, uint16_t x, uint16_t y) { uint8_t cx; uint8_t cy; uint16_t offset; uint8_t yl; uint16_t yp; uint8_t data; + uint8_t character; + uint8_t c; - //***TODO*** This only handles 8x8 fonts. + //***TODO*** This only handles 8xY fonts. - // Find character position in font grid. - cx = character % font->span; - cy = character / font->span; + for (c=0; cspan * font->height + cx; + // Find character position in font grid. + cx = character % font->span; + cy = character / font->span; - // Draw out 8 lines. - yp = y; - for (yl=0; yl<8; yl++) { - // We do 8 pixels unrolled hoping it's fast. - data = font->bits[offset]; - offset += font->span; - vbePutPixel(x, yp, data & 0x80 ? foreground : background); - vbePutPixel(x + 1, yp, data & 0x40 ? foreground : background); - vbePutPixel(x + 2, yp, data & 0x20 ? foreground : background); - vbePutPixel(x + 3, yp, data & 0x10 ? foreground : background); - vbePutPixel(x + 4, yp, data & 0x08 ? foreground : background); - vbePutPixel(x + 5, yp, data & 0x04 ? foreground : background); - vbePutPixel(x + 6, yp, data & 0x02 ? foreground : background); - vbePutPixel(x + 7, yp, data & 0x01 ? foreground : background); - yp++; + // Find offset byte based on font bits. + offset = cy * font->span * font->height + cx; + + // Draw out 8 lines. + yp = y; + for (yl=0; ylheight; yl++) { + // We do 8 pixels unrolled hoping it's fast. + data = font->bits[offset]; + offset += font->span; + vbePutPixel(x, yp, data & 0x80 ? foreground : background); + vbePutPixel(x + 1, yp, data & 0x40 ? foreground : background); + vbePutPixel(x + 2, yp, data & 0x20 ? foreground : background); + vbePutPixel(x + 3, yp, data & 0x10 ? foreground : background); + vbePutPixel(x + 4, yp, data & 0x08 ? foreground : background); + vbePutPixel(x + 5, yp, data & 0x04 ? foreground : background); + vbePutPixel(x + 6, yp, data & 0x02 ? foreground : background); + vbePutPixel(x + 7, yp, data & 0x01 ? foreground : background); + yp++; + } + + x += font->width; } } @@ -102,3 +115,8 @@ void fontUnload(FontT **font) { free(f); f = NULL; } + + +uint16_t fontWidthGet(FontT *font) { + return font->width; +} diff --git a/client/src/gui/font.h b/client/src/gui/font.h index 7f0f702..a652a1d 100644 --- a/client/src/gui/font.h +++ b/client/src/gui/font.h @@ -37,9 +37,11 @@ typedef struct FontS { } FontT; -FontT *fontLoad(char *filename); -void fontRender(FontT *font, uint8_t character, PixelT foreground, PixelT background, uint16_t x, uint16_t y); -void fontUnload(FontT **font); +uint16_t fontHeightGet(FontT *font); +FontT *fontLoad(char *filename); +void fontRender(FontT *font, char *string, PixelT foreground, PixelT background, uint16_t x, uint16_t y); +void fontUnload(FontT **font); +uint16_t fontWidthGet(FontT *font); #endif // FONT_H diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index b28a274..ab9690c 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -26,15 +26,34 @@ int16_t _guiMetric[METRIC_COUNT]; PixelT _guiColor[COLOR_COUNT]; +FontT *_guiFont = NULL; static DesktopT *_guiDesktop = NULL; void guiAttach(WidgetT *parent, WidgetT *child) { + WidgetT *p = NULL; + // Add us to the child list. child->parent = parent; arrput(parent->children, child); + + // If this widget does not own a surface, find one for it to draw on. + if (!GUI_GET_FLAG(child, WIDGET_FLAG_OWNS_SURFACE)) { + p = child; + while (p != NULL && !GUI_GET_FLAG(p, WIDGET_FLAG_OWNS_SURFACE)) { + // Check parent. + p = p->parent; + if (p) { + // Calculate margins as we go. + child->marginX += p->marginX; + child->marginY += p->marginY; + } + } + child->surface = p->surface; + } + // New windows should be active. if (child->magic == MAGIC_WINDOW) { windowSetActive((WindowT *)child); @@ -53,14 +72,16 @@ void guiComposite() { vbeSurfaceSet(NULL); // Render us? - if (widget->surface) { + if (GUI_GET_FLAG(widget, WIDGET_FLAG_OWNS_SURFACE)) { vbeSurfaceBlit(widget->surface, widget->x, widget->y); } + //***TODO*** This is wrong. Should be recursive. + // Now render all surface-containing children to the VBE buffer. if (len > 0) { for (x=0; xchildren[x]->surface) { + if (GUI_GET_FLAG(widget->children[x], WIDGET_FLAG_OWNS_SURFACE)) { vbeSurfaceBlit(widget->children[x]->surface, widget->children[x]->x, widget->children[x]->y); } } @@ -169,6 +190,25 @@ void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, Pixe } +void guiPaint(WidgetT *widget) { + size_t len = arrlenu(widget->children); + size_t x; + + // Paint us, if needed. + if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) && widget->paintMethod) { + widget->paintMethod(widget); + GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY); + } + + // Paint all children. + if (len > 0) { + for (x=0; xchildren[x]); + } + } +} + + void guiProcessMouse(MouseT *mouse) { size_t len; int16_t x; @@ -179,7 +219,7 @@ void guiProcessMouse(MouseT *mouse) { // Is the left button down? if (mouse->buttonLeft) { - // Was it NOT down before? + // Was left button NOT down before? if (!mouse->buttonLeftWasDown) { // Initial click. Are we already dragging something? if (!_guiDesktop->dragWidget) { @@ -196,17 +236,21 @@ void guiProcessMouse(MouseT *mouse) { window = (WindowT *)child; // Is it the active window? if (GUI_GET_FLAG(window, WINDOW_FLAG_ACTIVE)) { - // Start dragging. + // Are we on the draggable area of the titlebar / borders? _guiDesktop->dragOffsetX = mouse->x - window->base.x; _guiDesktop->dragOffsetY = mouse->y - window->base.y; - _guiDesktop->dragWidget = child; + if (_guiDesktop->dragOffsetY < _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]) { + // Start dragging. + _guiDesktop->dragWidget = child; + } } } break; } } } - } else { // Was it NOT down before? + + } else { // Was left button NOT down before? // Still holding left button, but not the initial click. Update dragged widget location. if (_guiDesktop->dragWidget) { @@ -219,7 +263,7 @@ void guiProcessMouse(MouseT *mouse) { if (_guiDesktop->dragWidget->y + _guiDesktop->dragWidget->h > vbeDisplayHeightGet()) _guiDesktop->dragWidget->y = vbeDisplayHeightGet() - _guiDesktop->dragWidget->h; } - } // Was it NOT down before? + } // Was left button NOT down before? } else { // Left button down? @@ -257,39 +301,40 @@ void guiProcessMouse(MouseT *mouse) { } -void guiPaint(WidgetT *widget) { - size_t len = arrlenu(widget->children); - size_t x; - - // Paint us, if needed. - if (widget->dirty && widget->paintMethod) { - widget->paintMethod(widget); - widget->dirty = 0; - } - - // Paint all children, if needed. - if (len > 0) { - for (x=0; xchildren[x]->dirty && widget->children[x]->paintMethod) { - widget->children[x]->paintMethod(widget->children[x]); - widget->children[x]->dirty = 0; - } - - } - } -} - - WidgetT *guiRootGet(void) { return (WidgetT *)_guiDesktop; } +void guiSetWidgetAndChildrenDirty(WidgetT *widget) { + size_t len = arrlenu(widget->children); + size_t x; + + // Mark us dirty. + GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY); + + // Mark all children. + if (len > 0) { + for (x=0; xchildren[x]); + } + } +} + + DesktopT *guiStartup(void) { - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] = 4; // Does not include highlight or shadow lines. - _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] = 17; // Does not include highlight or shadow lines. + _guiMetric[METRIC_BUTTON_BEZEL_SIZE] = 2; + _guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN] = 8; + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] = 2; + _guiMetric[METRIC_WINDOW_BORDER_WIDTH] = 4; // Does not include highlight or shadow lines. + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] = 17; // Does not include highlight or shadow lines. + _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT] = _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4; // Border, highlights, titlebar. + _guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakePixel(168, 168, 168); + _guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakePixel(248, 252, 248); + _guiColor[COLOR_BUTTON_SHADOW] = vbeMakePixel( 80, 84, 80); + _guiColor[COLOR_BUTTON_TEXT] = vbeMakePixel( 0, 0, 0); _guiColor[COLOR_DESKTOP] = vbeMakePixel( 51, 153, 255); _guiColor[COLOR_WINDOW_BACKGROUND] = vbeMakePixel(168, 168, 168); _guiColor[COLOR_WINDOW_HIGHLIGHT] = vbeMakePixel(248, 252, 248); @@ -299,6 +344,8 @@ DesktopT *guiStartup(void) { _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] = vbeMakePixel(248, 252, 248); _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE] = vbeMakePixel( 0, 0, 0); + _guiFont = fontLoad("vga8x14.dat"); + // Create desktop and return it. Remember it for later. _guiDesktop = desktopNew(); @@ -307,6 +354,18 @@ DesktopT *guiStartup(void) { void guiShutdown(void) { + fontUnload(&_guiFont); + // Delete all widgets in GUI tree. guiDelete((WidgetT **)&_guiDesktop); } + + +void *guiUserDataGet(WidgetT *widget) { + return widget->userData; +} + + +void guiUserDataSet(WidgetT *widget, void *userData) { + widget->userData = userData; +} diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 33e1b41..da7da32 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -29,11 +29,14 @@ #include "vesa.h" #include "array.h" #include "mouse.h" +#include "font.h" #define GUI_GET_FLAG(w,f) ((w)->flags & (1 << (f))) -#define GUI_SET_FLAG(w,f) ((w)->flags |= (1 << (f))) -#define GUI_CLEAR_FLAG(w,f) ((w)->flags &= (~(1 << (f)))) +#define GUI_SET_FLAG(w,f) (w)->flags |= (1 << (f)) +#define GUI_CLEAR_FLAG(w,f) (w)->flags &= (~(1 << (f))) + +#define W(w) ((WidgetT *)w) // Widget Magics @@ -47,14 +50,22 @@ enum MagicE { // Widget Metrics enum MetricE { - METRIC_WINDOW_BORDER_WIDTH = 0, + METRIC_BUTTON_BEZEL_SIZE = 0, + METRIC_BUTTON_HORIZONTAL_MARGIN, + METRIC_BUTTON_VERTICAL_MARGIN, + METRIC_WINDOW_BORDER_WIDTH, METRIC_WINDOW_TITLE_HEIGHT, + METRIC_WINDOW_TITLE_GRAB_HEIGHT, METRIC_COUNT }; // Widget Colors enum ColorE { - COLOR_DESKTOP = 0, + COLOR_BUTTON_BACKGROUND = 0, + COLOR_BUTTON_HIGHLIGHT, + COLOR_BUTTON_SHADOW, + COLOR_BUTTON_TEXT, + COLOR_DESKTOP, COLOR_WINDOW_BACKGROUND, COLOR_WINDOW_HIGHLIGHT, COLOR_WINDOW_SHADOW, @@ -70,8 +81,9 @@ typedef struct WidgetS WidgetT; typedef struct DesktopS DesktopT; -extern int16_t _guiMetric[METRIC_COUNT]; -extern PixelT _guiColor[COLOR_COUNT]; +extern int16_t _guiMetric[METRIC_COUNT]; +extern PixelT _guiColor[COLOR_COUNT]; +extern FontT *_guiFont; void guiAttach(WidgetT *parent, WidgetT *child); @@ -83,8 +95,11 @@ void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, void guiPaint(WidgetT *widget); void guiProcessMouse(MouseT *mouse); WidgetT *guiRootGet(void); +void guiSetWidgetAndChildrenDirty(WidgetT *widget); DesktopT *guiStartup(void); void guiShutdown(void); +void *guiUserDataGet(WidgetT *widget); +void guiUserDataSet(WidgetT *widget, void *userData); #endif // GUI_H diff --git a/client/src/gui/vesa.h b/client/src/gui/vesa.h index f618875..27ba8a5 100644 --- a/client/src/gui/vesa.h +++ b/client/src/gui/vesa.h @@ -53,6 +53,8 @@ void vbeSurfaceBlit(SurfaceT *source, uint16_t x, uint16_t y); void vbeSurfaceClear(PixelT color); SurfaceT *vbeSurfaceCreate(uint16_t width, uint16_t height); void vbeSurfaceDestroy(SurfaceT **surface); +uint16_t vbeSurfaceHeightGet(void); +uint16_t vbeSurfaceWidthGet(void); void vbeSurfaceSet(SurfaceT *surface); void vbeWaitVBlank(void); diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c index ebaadf6..c213f5e 100644 --- a/client/src/gui/widget.c +++ b/client/src/gui/widget.c @@ -22,18 +22,23 @@ WidgetT *widgetInit(WidgetT *widget) { - widget->magic = MAGIC_UNKNOWN; - widget->x = 0; - widget->y = 0; - widget->w = 0; - widget->h = 0; - widget->dirty = 1; // Force a paint right away - widget->surface = NULL; - widget->children = NULL; - widget->parent = NULL; - widget->delMethod = NULL; - widget->paintMethod = NULL; - widget->mouseDownMethod = NULL; + widget->magic = MAGIC_UNKNOWN; + widget->flags = 0; + widget->x = 0; + widget->y = 0; + widget->w = 0; + widget->h = 0; + widget->marginX = 0; + widget->marginY = 0; + widget->surface = NULL; + widget->children = NULL; + widget->parent = NULL; + widget->delMethod = NULL; + widget->paintMethod = NULL; + widget->mouseEventMethod = NULL; + widget->userData = NULL; + + GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY); return widget; } diff --git a/client/src/gui/widget.h b/client/src/gui/widget.h index 0944f26..c255512 100644 --- a/client/src/gui/widget.h +++ b/client/src/gui/widget.h @@ -25,27 +25,37 @@ #include "gui.h" +enum WidgetE { + WIDGET_FLAG_DIRTY = 0, + WIDGET_FLAG_OWNS_SURFACE +}; + + struct WidgetS; +typedef void (*widgetCallback)(struct WidgetS *widget); typedef void (*widgetDelMethod)(struct WidgetS **widget); typedef void (*widgetPaintMethod)(struct WidgetS *widget); -typedef void (*widgetMouseDownMethod)(struct WidgetS *widget, uint16_t x, uint16_t y); +typedef void (*widgetMouseEventMethod)(struct WidgetS *widget, MouseT *mouse, uint16_t x, uint16_t y); typedef struct WidgetS { - uint8_t magic; // Widget identifier constant - uint8_t dirty; // Does this widget need redrawn - SurfaceT *surface; // Pointer to compositable surface or NULL - int16_t x; // Position of widget on parent - int16_t y; // Position of widget on parent - uint16_t w; // Width of widget - uint16_t h; // Height of widget - struct WidgetS **children; // List of children - struct WidgetS *parent; // Parent of this widget - widgetDelMethod delMethod; // Delete method - widgetPaintMethod paintMethod; // Paint method - widgetMouseDownMethod mouseDownMethod; // Mouse button down handler + uint8_t magic; // Widget identifier constant + uint8_t flags; // See above enum + SurfaceT *surface; // Pointer to compositable surface or NULL + int16_t x; // Position of widget on parent + int16_t y; // Position of widget on parent + uint16_t w; // Width of widget + uint16_t h; // Height of widget + uint16_t marginX; // Pixels to skip when placing child widgets + uint16_t marginY; // Pixels to skip when placing child widgets + struct WidgetS **children; // List of children + struct WidgetS *parent; // Parent of this widget + widgetDelMethod delMethod; // Delete method + widgetPaintMethod paintMethod; // Paint method + widgetMouseEventMethod mouseEventMethod; // Mouse event handler + void *userData; // Anything the user wants to store } WidgetT; diff --git a/client/src/gui/window.c b/client/src/gui/window.c index 6102be1..630ca20 100644 --- a/client/src/gui/window.c +++ b/client/src/gui/window.c @@ -32,7 +32,7 @@ static void windowDeactivateAll(WidgetT *widget) { if (widget->magic == MAGIC_WINDOW) { // Deactivate it. GUI_CLEAR_FLAG((WindowT *)widget, WINDOW_FLAG_ACTIVE); - widget->dirty = 1; + guiSetWidgetAndChildrenDirty(widget); } // Process any children. @@ -64,10 +64,15 @@ WidgetT *windowInit(WidgetT *window, uint16_t x, uint16_t y, uint16_t w, uint16_ win->base.h = h; win->base.delMethod = windowDel; win->base.paintMethod = windowPaint; + win->flags = 0; win->title = NULL; + win->base.marginX += _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2; + win->base.marginY += _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]; + windowSetTitle(win, title); + GUI_SET_FLAG(window, WIDGET_FLAG_OWNS_SURFACE); win->base.surface = vbeSurfaceCreate(win->base.w, win->base.h); if (!win->base.surface) { free(win->title); @@ -102,9 +107,11 @@ void windowPaint(WidgetT *window) { uint16_t x2 = w->base.w - 1; uint16_t y2 = w->base.h - 1; PixelT background = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_INACTIVE]; - //PixelT text = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE]; + PixelT text = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE]; - if (w->base.dirty) { + //***NOTE*** This doesn't obey margins since desktops don't have any. + + if (GUI_GET_FLAG(window, WIDGET_FLAG_DIRTY)) { vbeSurfaceSet(w->base.surface); // Background. @@ -120,7 +127,9 @@ void windowPaint(WidgetT *window) { guiDrawHighlightFrame(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]); guiDrawFilledRectangle(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 2, background); - w->base.dirty = 0; + fontRender(_guiFont, w->title, text, background, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 16, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 5); + + GUI_CLEAR_FLAG(window, WIDGET_FLAG_DIRTY); } } diff --git a/client/src/linux/linux.c b/client/src/linux/linux.c index 878f5a3..4c56fec 100644 --- a/client/src/linux/linux.c +++ b/client/src/linux/linux.c @@ -34,6 +34,7 @@ static SurfaceT *_activeSurface = NULL; static uint16_t _width = 0; static uint16_t _height = 0; static MouseT _mouse; +static uint8_t _windowScale = 1; void (*vbePutPixel)(uint16_t x, uint16_t y, PixelT pixel); @@ -83,8 +84,8 @@ MouseT *mouseRead(void) { _mouse.buttonRight = ((buttons & SDL_BUTTON_RMASK) != 0); _mouse.buttonMiddle = ((buttons & SDL_BUTTON_MMASK) != 0); - _mouse.x = mouseX; - _mouse.y = mouseY; + _mouse.x = mouseX / _windowScale; + _mouse.y = mouseY / _windowScale; return &_mouse; } @@ -162,6 +163,7 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) { (void)bpp; + _windowScale = 2; vbePutPixel = vbePutPixel32; SDL_Init(SDL_INIT_EVERYTHING); @@ -169,7 +171,9 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) { _window = SDL_CreateWindow("GUI Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xRes, yRes, SDL_WINDOW_ALLOW_HIGHDPI); _surface = SDL_GetWindowSurface(_window); _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); + SDL_RenderSetLogicalSize(_renderer, xRes, yRes); + SDL_SetWindowSize(_window, xRes * _windowScale, yRes * _windowScale); _offScreenBuffer = vbeSurfaceCreate(xRes, yRes); @@ -232,6 +236,16 @@ void vbeSurfaceDestroy(SurfaceT **surface) { } +uint16_t vbeSurfaceHeightGet(void) { + return _activeSurface->height; +} + + +uint16_t vbeSurfaceWidthGet(void) { + return _activeSurface->width; +} + + void vbeSurfaceSet(SurfaceT *surface) { SDL_Texture *texture = NULL; diff --git a/client/src/main.c b/client/src/main.c index c558d80..836a9e5 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -26,18 +26,20 @@ #include "widget.h" #include "desktop.h" #include "window.h" +#include "button.h" void test(void) { MouseT *mouse = NULL; + ImageT *pointer = NULL; + PixelT alpha; DesktopT *desktop = NULL; WindowT *w1 = NULL; WindowT *w2 = NULL; WindowT *w3 = NULL; - ImageT *pointer = NULL; - PixelT alpha; + ButtonT *b1 = NULL; - vbeStartup(800, 600, 8); + vbeStartup(800, 600, 16); mouseStartup(); desktop = guiStartup(); @@ -47,9 +49,12 @@ void test(void) { w1 = windowNew(25, 25, 300, 200, "Window 1"); w2 = windowNew(150, 150, 300, 200, "Window 2"); w3 = windowNew(300, 300, 300, 200, "Window 3"); - guiAttach((WidgetT *)desktop, (WidgetT *)w1); - guiAttach((WidgetT *)desktop, (WidgetT *)w2); - guiAttach((WidgetT *)desktop, (WidgetT *)w3); + guiAttach(W(desktop), W(w1)); + guiAttach(W(desktop), W(w2)); + guiAttach(W(desktop), W(w3)); + + b1 = buttonNew(25, 25, "Test Button", NULL); + guiAttach(W(w3), W(b1)); do { mouse = mouseRead(); @@ -76,7 +81,7 @@ int main(int argc, char *argv[]) { return 0; } - logOpen("test.log", 1); + logOpen("test.log", 0); test(); diff --git a/font/data/vga8x14.png b/font/data/vga8x14.png new file mode 100644 index 0000000..a52f226 --- /dev/null +++ b/font/data/vga8x14.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5330f2c589cd0094131d7b686947fbc1da0a6c3df4388a09f3bb77ba57d53a0c +size 22459 diff --git a/font/data/vga8x16.png b/font/data/vga8x16.png new file mode 100644 index 0000000..f49a9d9 --- /dev/null +++ b/font/data/vga8x16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:170c659c489d3cd1d141dbf12ae37bbf40975941ced925ce6e50817128e5bf97 +size 20257 diff --git a/font/src/main.c b/font/src/main.c index f251045..8339178 100644 --- a/font/src/main.c +++ b/font/src/main.c @@ -23,11 +23,7 @@ #include "stb.h" -#define BITMAP_FILE "/home/scott/code/kpmpgsmkii/font/data/vga8x8.png" -#define FONT_FILE "/home/scott/code/kpmpgsmkii/client/bin/vga8x8.dat" - - -int main(int argc, char *argv[]) { +void makeFont(char *source, char *target, int pixelsW, int pixelsH, int charsW, int charCount) { unsigned char *font = NULL; unsigned char data = 0; FILE *out = NULL; @@ -38,25 +34,22 @@ int main(int argc, char *argv[]) { int w; int h; - (void)argc; - (void)argv; - - // Load 8x8 font from disk. Font is in a 16x16 grid. - font = stbi_load(BITMAP_FILE, (int *)&w, (int *)&h, (int *)&n, 3); - if (!font) return 1; + // Load font atlas from disk. + font = stbi_load(source, (int *)&w, (int *)&h, (int *)&n, 3); + if (!font) return; // Create data file for font. - out = fopen(FONT_FILE, "wb"); + out = fopen(target, "wb"); if (!out) { stbi_image_free(font); - return 2; + return; } // Provide some metadata for enhancement later. - fputc(8, out); // Width of characters - fputc(8, out); // Height of characters - fputc(16, out); // Number of characters per row - fputc(255, out); // Number of characters - 1 + fputc(pixelsW, out); // Width of characters + fputc(pixelsH, out); // Height of characters + fputc(charsW, out); // Number of characters per row + fputc(charCount, out); // Number of characters - 1 // Convert bitmap to actual bits. for (y=0; y. # -TEMPLATE = SUBDIRS +TEMPLATE = subdirs CONFIG *= ORDERED SUBDIRS = \