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 = \