diff --git a/client/src/gui/surface.c b/client/src/gui/surface.c index dab7450..171f017 100644 --- a/client/src/gui/surface.c +++ b/client/src/gui/surface.c @@ -19,38 +19,39 @@ static void surfacePixelSet8(uint16_t x, uint16_t y, ColorT color); static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color); static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color); -#include "gui.h" -void surfaceBlit(int16_t targetX, int16_t targetY, SurfaceT *source) { +void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source) { int16_t y; int16_t x1 = 0; int16_t y1 = 0; - int16_t x2 = source->width; - int16_t y2 = source->height; size_t bytes; size_t offsetTarget; size_t offsetSource; + // Did they provide a valid width/height? + if (width <= 0 || width > source->width) width = source->width; + if (height <= 0 || height > source->height) height = source->height; + // Clip on top and left. x1 & y1 are pixel locations inside the source bitmap. - if (targetX < 0) x1 = -targetX; - if (targetY < 0) y1 = -targetY; + if (targetX1 < 0) x1 = -targetX1; + if (targetY1 < 0) y1 = -targetY1; // Clip on right and bottom. - if (targetX + x2 > __surfaceActive->width) x2 -= targetX + x2 - __surfaceActive->width; - if (targetY + y2 > __surfaceActive->height) y2 -= targetY + y2 - __surfaceActive->height; + if (targetX1 + width > __surfaceActive->width) width -= targetX1 + width - __surfaceActive->width; + if (targetY1 + height > __surfaceActive->height) height -= targetY1 + height - __surfaceActive->height; // Are we still on the screen? - if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) return; + if (x1 < 0 || y1 < 0 || width < x1 || height < y1) return; - if (targetX == 0 && targetY == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) { + if (targetX1 == 0 && targetY1 == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) { // Direct blit of entire surface. memcpy(__surfaceActive->buffer.bits8, source->buffer.bits8, source->bytes); } else { // Blit into larger surface. - offsetTarget = (targetY + y1) * __surfaceActive->scanline + (targetX + x1) * __surfaceBytesPerPixel; + offsetTarget = (targetY1 + y1) * __surfaceActive->scanline + (targetX1 + x1) * __surfaceBytesPerPixel; offsetSource = y1 * source->scanline + x1 * __surfaceBytesPerPixel; - bytes = (x2 - x1) * __surfaceBytesPerPixel; - for (y=y1; ybuffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], bytes); offsetTarget += __surfaceActive->scanline; offsetSource += source->scanline; diff --git a/client/src/gui/surface.h b/client/src/gui/surface.h index 92a8c57..bd41c71 100644 --- a/client/src/gui/surface.h +++ b/client/src/gui/surface.h @@ -45,7 +45,7 @@ extern ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y); extern void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color); -void surfaceBlit(int16_t targetX, int16_t targetY, SurfaceT *source); +void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source); void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent); void surfaceClear(ColorT color); ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b); diff --git a/client/src/gui/wmwindow.c b/client/src/gui/wmwindow.c index 903c219..2be5cf5 100644 --- a/client/src/gui/wmwindow.c +++ b/client/src/gui/wmwindow.c @@ -5,25 +5,211 @@ 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, ...) { - WindowT *win = NULL; +static void windowCache(WindowT *w); + + +static void windowCache(WindowT *w) { + 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; + ColorT titleBackgroundColor; + ColorT widgetColor; + SurfaceT *target = surfaceGet(); + + // 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? + surfaceSet(w->cached); + if ((surfaceWidthGet() != w->base.r.w) || (surfaceHeightGet() != 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); + + // 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); + } + + // Blit contents. + if (w->content) surfaceBlit(w->bounds.x, w->bounds.y, w->bounds.x2 - w->bounds.x, w->bounds.y2 - w->bounds.y, w->content); + + // Fixup all the widget coordinates. + windowMoveTo(w, originalX, originalY); + + surfaceSet(target); +} + + +// Passing "flags" as a default int provides proper alignment for the following va_args list. +WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...) { + int16_t width; + int16_t height; + va_list args; + WindowT *win = NULL; + SurfaceT *t = surfaceGet(); NEW(WindowT, win); guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h); - win->title = strdup(title); - win->flags = flags; + win->title = strdup(title); + win->flags = (uint8_t)flags; win->close.x = win->close.y = win->close.x2 = win->close.y2 = 0; win->titlebar.x = win->titlebar.y = win->titlebar.x2 = win->titlebar.y2 = 0; win->minimize.x = win->minimize.y = win->minimize.x2 = win->minimize.y2 = 0; 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; + win->content = NULL; - win->cached = NULL; + // If the window is resizable, we need to get two more arguments for the content size. + if (win->flags & WIN_RESIZE) { + va_start(args, flags); + width = va_arg(args, int); + height = va_arg(args, int); + va_end(args); + } else { + // Use whatever the default content area is. This causes an extra draw on create. Oh well. + windowCache(win); + width = win->bounds.x2 - win->bounds.x; + height = win->bounds.y2 - win->bounds.y; + guiWidgetDirtySet(W(win), 1); + } + win->content = surfaceCreate(width, height); + surfaceSet(win->content); + surfaceClear(GUI_LIGHTGRAY); + surfaceSet(t); arrput(_windowList, win); @@ -49,6 +235,8 @@ void windowDestroy(struct WidgetS *widget, ...) { if (_windowList[i]->title) DEL(_windowList[i]->title); // Free cached surface. if (_windowList[i]->cached) surfaceDestroy(&_windowList[i]->cached); + // Free content surface. + if (_windowList[i]->content) surfaceDestroy(&_windowList[i]->content); // Delete the window. DEL(_windowList[i]); // Remove it from window list. @@ -151,172 +339,16 @@ void windowMoveTo(WindowT *w, uint16_t x, uint16_t 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; + WindowT *w = (WindowT *)widget; // 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? - surfaceSet(w->cached); - if ((surfaceWidthGet() != w->base.r.w) || (surfaceHeightGet() != 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); - - // 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); - - // Fixup all the widget coordinates. - windowMoveTo(w, originalX, originalY); + windowCache(w); } // By now we have a valid cached window. Blit it. - surfaceSet(target); - surfaceBlit(w->base.r.x, w->base.r.y, w->cached); + surfaceBlit(w->base.r.x, w->base.r.y, 0, 0, w->cached); } @@ -335,6 +367,19 @@ RegisterT *windowRegister(uint8_t magic) { } +void wmShutdown(void) { + while (arrlen(_windowList) > 0) { + windowDestroy((WidgetT *)_windowList[0]); + } + arrfree(_windowList); +} + + +void wmStartup(void) { + // Nada +} + + void wmUpdate(EventT *event) { int16_t i; int16_t x2; @@ -455,15 +500,3 @@ void wmUpdate(EventT *event) { } // Do we have windows? } - - -void wmShutdown(void) { - while (arrlen(_windowList) > 0) { - windowDestroy((WidgetT *)_windowList[0]); - } - arrfree(_windowList); -} - - -void wmStartup(void) { -} diff --git a/client/src/gui/wmwindow.h b/client/src/gui/wmwindow.h index 38d61b8..332134f 100644 --- a/client/src/gui/wmwindow.h +++ b/client/src/gui/wmwindow.h @@ -21,6 +21,7 @@ typedef struct WindowS { RectT minimize; // Coordinates of minimize box, if any. RectT maximize; // Coordinates of maximize box, if any. RectT bounds; // Inside edge of window frame. + SurfaceT *content; // Actual window contents - widgets and such. SurfaceT *cached; // Once rendered, keep a cached copy for faster redrawing. } WindowT; @@ -28,7 +29,7 @@ typedef struct WindowS { 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, ...); +WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...); void windowDestroy(struct WidgetS *widget, ...); void windowFocusSet(WindowT *win); void windowMoveTo(WindowT *win, uint16_t x, uint16_t y); @@ -36,9 +37,9 @@ void windowPaint(struct WidgetS *widget, ...); RegisterT *windowRegister(uint8_t magic); -void wmUpdate(EventT *event); void wmShutdown(void); void wmStartup(void); +void wmUpdate(EventT *event); #endif // WMWINDOW_H diff --git a/client/src/main.c b/client/src/main.c index 575c5db..81e205b 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { if (guiStartup(800, 600, 32) == SUCCESS) { for (i=1; i<4; i++) { sprintf(title, "Testing %d", i); - windowCreate(i * 50, i * 50, 300, 200, title, WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE | WIN_RESIZE); + windowCreate(i * 50, i * 50, 300, 200, title, WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE); } guiRun(); guiShutdown();