From a4a8534aa61bf3b29aba2198aa30d708c68d4e23 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 28 Nov 2021 21:32:07 -0600 Subject: [PATCH] Message Box added to GUI. Connection code and timers are having issues. --- client/client.pro | 8 +- client/data/IconError.png | 3 + client/data/IconInformation.png | 3 + client/data/IconMessage.png | 3 + client/data/IconQuestion.png | 3 + client/data/IconWarning.png | 3 + client/data/MessageBoxIcon.png | 3 + client/data/mbie32.png | 3 + client/data/mbii32.png | 3 + client/data/mbim32.png | 3 + client/data/mbiq32.png | 3 + client/data/mbiw32.png | 3 + client/src/gui/gui.c | 58 ++++++++- client/src/gui/gui.h | 1 + client/src/gui/msgbox.c | 219 ++++++++++++++++++++++++++++++++ client/src/gui/msgbox.h | 57 +++++++++ client/src/gui/textbox.c | 2 +- client/src/gui/updown.c | 2 +- client/src/gui/widget.c | 20 +++ client/src/gui/widget.h | 4 + client/src/linux/linux.c | 1 + client/src/settings.c | 39 +++--- client/src/system/comport.c | 80 ++++++++++++ client/src/system/comport.h | 4 + client/src/system/os.h | 1 + client/src/system/task.c | 5 +- client/src/system/timer.c | 71 +++++++---- client/src/system/timer.h | 13 +- client/src/welcome.c | 160 ++++++++++++++++++++++- shared/util.c | 31 +++++ shared/util.h | 2 + 31 files changed, 739 insertions(+), 72 deletions(-) create mode 100644 client/data/IconError.png create mode 100644 client/data/IconInformation.png create mode 100644 client/data/IconMessage.png create mode 100644 client/data/IconQuestion.png create mode 100644 client/data/IconWarning.png create mode 100644 client/data/MessageBoxIcon.png create mode 100644 client/data/mbie32.png create mode 100644 client/data/mbii32.png create mode 100644 client/data/mbim32.png create mode 100644 client/data/mbiq32.png create mode 100644 client/data/mbiw32.png create mode 100644 client/src/gui/msgbox.c create mode 100644 client/src/gui/msgbox.h create mode 100644 client/src/system/comport.c diff --git a/client/client.pro b/client/client.pro index 25a88ef..2b5def8 100644 --- a/client/client.pro +++ b/client/client.pro @@ -27,7 +27,10 @@ CONFIG -= qt DESTDIR = $$OUT_PWD/bin SHARED = $$PWD/../shared -DOS_HEADERS = +QMAKE_CFLAGS += -O0 + +DOS_HEADERS = \ + src/thirdparty/serial/serial.h DOS_SOURCES = \ src/thirdparty/serial/serial.c \ @@ -62,6 +65,7 @@ HEADERS = \ $$SHARED/primes.h \ src/config.h \ $$SHARED/util.h \ + src/gui/msgbox.h \ src/thirdparty/minicoro/minicoro.h \ src/system/comport.h \ src/settings.h \ @@ -103,7 +107,9 @@ SOURCES = \ $$SHARED/thirdparty/ini/src/ini.c \ src/config.c \ $$SHARED/memory.c \ + src/gui/msgbox.c \ src/settings.c \ + src/system/comport.c \ src/system/surface.c \ src/system/taglist.c \ $$SHARED/util.c \ diff --git a/client/data/IconError.png b/client/data/IconError.png new file mode 100644 index 0000000..68d99d4 --- /dev/null +++ b/client/data/IconError.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b0308e42689aedda4a83f1f8bd63a1dad4e1d24aad2b88595a0bac7e6d9ad72 +size 6934 diff --git a/client/data/IconInformation.png b/client/data/IconInformation.png new file mode 100644 index 0000000..9d43ec9 --- /dev/null +++ b/client/data/IconInformation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78d4d4025b23b3320c124318874c9d0ac591d58593e1288657af6ae45f12b30e +size 7394 diff --git a/client/data/IconMessage.png b/client/data/IconMessage.png new file mode 100644 index 0000000..4e2afa6 --- /dev/null +++ b/client/data/IconMessage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6d369a27ae6e1f1bfb26575be59a95751aa24e1b8b0b72bd963521a67cb98ca +size 6303 diff --git a/client/data/IconQuestion.png b/client/data/IconQuestion.png new file mode 100644 index 0000000..1fbe017 --- /dev/null +++ b/client/data/IconQuestion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d07aeb5fb9ea56edc270969730ff737a7e807395eb4740f563216ab6fbbbf17 +size 9000 diff --git a/client/data/IconWarning.png b/client/data/IconWarning.png new file mode 100644 index 0000000..0ccc2e2 --- /dev/null +++ b/client/data/IconWarning.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78039b04052a5e5d249bc188742aec473c8d295ed58234ffaad276982f05abee +size 4577 diff --git a/client/data/MessageBoxIcon.png b/client/data/MessageBoxIcon.png new file mode 100644 index 0000000..c5a1376 --- /dev/null +++ b/client/data/MessageBoxIcon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c29dca4d2d74a66383d1806a391ddd6056c080317e9dfec25dcb945fc67e282e +size 35902 diff --git a/client/data/mbie32.png b/client/data/mbie32.png new file mode 100644 index 0000000..c4f4840 --- /dev/null +++ b/client/data/mbie32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a90ad5688a89e6a72c478a84fa33959152398f30cf3d442bf91db342acfa5c92 +size 2601 diff --git a/client/data/mbii32.png b/client/data/mbii32.png new file mode 100644 index 0000000..ab09615 --- /dev/null +++ b/client/data/mbii32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c98c9c95d9a4c98a649403826d1f79ecff77db1dd3495aab9f251163ce75643 +size 2636 diff --git a/client/data/mbim32.png b/client/data/mbim32.png new file mode 100644 index 0000000..82444bf --- /dev/null +++ b/client/data/mbim32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89bcbd0990b3dd7fd335f1518b9ef75403fdf8b156d3a08daf93209cb1abdcc0 +size 2176 diff --git a/client/data/mbiq32.png b/client/data/mbiq32.png new file mode 100644 index 0000000..8847e32 --- /dev/null +++ b/client/data/mbiq32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54a817ee9fb5f1d4393f045cbb67f26a9d9948082715c5f7a34ea8c0ec2735fd +size 3041 diff --git a/client/data/mbiw32.png b/client/data/mbiw32.png new file mode 100644 index 0000000..b66a2b9 --- /dev/null +++ b/client/data/mbiw32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7cfc4f4ffaef29bfe0fec812b56abe3d5850e74f1d897f1000e05aa537f43f2 +size 1921 diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 8fb51d3..3dbeadb 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -65,11 +65,12 @@ static char *_magicDebugNames[MAGIC_COUNT] = { }; -static void guiDeleteList(void); -static void guiDeleteListItem(WidgetT **widget); -static void guiPaintBoundsGet(WidgetT *widget, RectT *pos); -static void guiKeyboardChildrenProcess(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); -static uint8_t guiMouseChildrenProcess(WidgetT *widget, MouseT *mouse); +static void guiDeleteList(void); +static void guiDeleteListItem(WidgetT **widget); +static void guiPaintBoundsGet(WidgetT *widget, RectT *pos); +static void guiKeyboardChildrenProcess(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); +static uint8_t guiMouseChildrenProcess(WidgetT *widget, MouseT *mouse); +static WindowT *guiWindowFindTopChildrenProcess(WidgetT *widget); void guiAttach(WidgetT *parent, WidgetT *child) { @@ -153,7 +154,12 @@ void guiDebugAreaShow(WidgetT *widget) { RectT r; guiWidgetBoundsDrawableOnScreenGet(widget, &r); - logWrite("%s: %dx%d\n", _magicDebugNames[widget->magic], r.w, r.h); + logWrite("%s: Size %dx%d Area %dx%d TL %dx%d BR %dx%d\n", + _magicDebugNames[widget->magic], + widget->pos.w, widget->pos.h, + r.w, r.h, + widget->margin.x, widget->margin.y, + widget->margin.w, widget->margin.h); } @@ -191,7 +197,8 @@ void guiDelete(WidgetT **widget) { static void guiDeleteList(void) { - WidgetT **w = NULL; + WidgetT **w = NULL; + WindowT *win = NULL; while (arrlen(_guiDeleteList) > 0) { w = arrpop(_guiDeleteList); @@ -200,6 +207,14 @@ static void guiDeleteList(void) { arrfree(_guiDeleteList); _guiDeleteList = NULL; + + // Is the top level window no longer active? If so, select it. + win = guiWindowFindTop(); + if (win) { + if (!GUI_GET_FLAG(W(win), WIDGET_FLAG_ACTIVE)) { + windowActiveSet(win); + } + } } @@ -686,3 +701,32 @@ void guiWidgetPositionOnScreenGet(WidgetT *widget, RectT *pos) { } } + + +WindowT *guiWindowFindTop(void) { + if (guiRootGet() == NULL) return NULL; + return guiWindowFindTopChildrenProcess(guiRootGet()); +} + + +static WindowT *guiWindowFindTopChildrenProcess(WidgetT *widget) { + size_t len = arrlenu(widget->children); + size_t x; + WindowT *top = NULL; + WindowT *r = NULL; + + // Is this a window? + if (widget->magic == MAGIC_WINDOW) { + top = (WindowT *)widget; + } + + // Check children. + if (len > 0) { + for (x=0; xchildren[x]); + if (r) top = r; + } + } + + return top; +} diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 84640ba..2b98b9f 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -213,6 +213,7 @@ void guiUserDataSet(WidgetT *widget, void *userData); void guiWidgetAndChildrenDirtySet(WidgetT *widget); void guiWidgetBoundsDrawableOnScreenGet(WidgetT *widget, RectT *bounds); void guiWidgetPositionOnScreenGet(WidgetT *widget, RectT *pos); +WindowT *guiWindowFindTop(void); #endif // GUI_H diff --git a/client/src/gui/msgbox.c b/client/src/gui/msgbox.c new file mode 100644 index 0000000..fd3117e --- /dev/null +++ b/client/src/gui/msgbox.c @@ -0,0 +1,219 @@ +/* + * Kangaroo Punch MultiPlayer 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 "msgbox.h" + +#include "font.h" + +#include "window.h" +#include "button.h" +#include "label.h" +#include "picture.h" + + +#define WIN_MARGIN_TOP 26 +#define WIN_MARGIN_LEFT 7 +#define WIN_MARGIN_RIGHT 8 +#define WIN_MARGIN_BOTTOM 8 + +#define BTN_MARGIN_TOP 4 +#define BTN_MARGIN_LEFT 14 +#define BTN_MARGIN_RIGHT 14 +#define BTN_MARGIN_BOTTOM 4 + +#define MSG_PADDING 10 + +#define ICN_SIZE 32 + + +static msgBoxCallback cbOne = NULL; +static msgBoxCallback cbTwo = NULL; +static msgBoxCallback cbThree = NULL; + +static WindowT *winDialog = NULL; +static PictureT *picIcon = NULL; +static LabelT *lblText = NULL; +static ButtonT *btnOne = NULL; +static ButtonT *btnTwo = NULL; +static ButtonT *btnThree = NULL; + + +static void btnMsgBox(WidgetT *widget); + + +static char *iconFiles[MSGBOX_ICON_COUNT] = { + "data/mbie32.png", + "data/mbiw32.png", + "data/mbii32.png", + "data/mbim32.png", + "data/mbiq32.png" +}; + + +static void btnMsgBox(WidgetT *widget) { + // Remove us from the display. + guiDelete(D(winDialog)); + // Call whoever wanted called. + if (widget == W(btnOne)) { + if (cbOne) cbOne(MSGBOX_BUTTON_ONE); + } + if (widget == W(btnTwo)) { + if (cbTwo) cbTwo(MSGBOX_BUTTON_TWO); + } + if (widget == W(btnThree)) { + if (cbThree) cbThree(MSGBOX_BUTTON_THREE); + } +} + + +void msgBoxOne(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne) { + return msgBoxThree(title, icon, message, buttonOne, callbackOne, NULL, NULL, NULL, NULL); +} + + +void msgBoxThree(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo, char *buttonThree, msgBoxCallback callbackThree) { + uint16_t w = 0; + uint16_t h = 0; + uint16_t x = 0; + uint16_t y = 0; + uint16_t t = 0; + uint16_t textH = 0; + uint16_t textW = 0; + uint16_t iconW = 0; + uint16_t newW = 0; + uint16_t cursorH = 0; + char *token = NULL; + char *text = NULL; + + // Null these for later. + btnOne = NULL; + btnTwo = NULL; + btnThree = NULL; + + // Remember callbacks for later. + cbOne = callbackOne; + cbTwo = callbackTwo; + cbThree = callbackThree; + + // Calculate size of dialog box window. + + // Initial minimum window width is the left and right margins + font width * (title length + 3). + w = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * (strlen(title) + 3)); + + // Initial minimum window height is the top and bottom margins + font height + button height + (padding * 2). + h = WIN_MARGIN_TOP + WIN_MARGIN_BOTTOM + fontHeightGet(_guiFont) + (BTN_MARGIN_TOP + BTN_MARGIN_BOTTOM + fontHeightGet(_guiFont)) + (MSG_PADDING * 2); + + // Count lines of text and add up height. Find widest line. + text = strdup(message); + token = strtok(text, "\n"); + while (token) { + textH += fontHeightGet(_guiFont); + if (strlen(token) > textW) { + textW = strlen(token); + } + token = strtok(NULL, "\n"); + } + free(text); + textW *= fontWidthGet(_guiFont); + + // Initial cursor move and window sizing are based on text height. + cursorH = textH; + + // Is there an icon? + if (icon > MSGBOX_ICON_NONE && icon < MSGBOX_ICON_COUNT) { + // Is the icon taller than the message text? + if (ICN_SIZE > textH) { + // Use icon height. + cursorH = ICN_SIZE; + } + iconW = ICN_SIZE + MSG_PADDING; + } + + // Add icon and/or text height to window. + h += cursorH; + + // Do we need to make the dialog wider to contain the text and icon? + newW = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + iconW + textW + (MSG_PADDING * 2); // Only 2 here because iconW includes one. + if (newW > w) w = newW; + + // Do we need to make the dialog wider to contain the buttons? + newW = WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonOne)); + if (buttonTwo) newW += MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonTwo)); + if (buttonThree) newW += MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonThree)); + if (newW > w) w = newW; + + // Draw dialog. + + h -= 5; // Height is off for some reason. At least, to me, it doesn't look right. + winDialog = windowNew(vbeDisplayWidthGet() / 2 - w / 2, vbeDisplayHeightGet() / 2 - h / 2, w, h, title); + guiAttach(guiRootGet(), W(winDialog)); + + // Initial x cursor is the (implied window left margin +) padding + x = MSG_PADDING; + + // Initial y cursor is the (implied window top margin +) padding + y = MSG_PADDING; + + // Load proper icon, if desired. + if (icon > MSGBOX_ICON_NONE && icon < MSGBOX_ICON_COUNT) { + picIcon = pictureNew(x, y, iconFiles[icon - 1]); + guiAttach(W(winDialog), W(picIcon)); + } + + // Draw message text. + t = y; + x += iconW; + text = strdup(message); + token = strtok(text, "\n"); + while (token) { + lblText = labelNew(x, t, token); + guiAttach(W(winDialog), W(lblText)); + t += fontHeightGet(_guiFont); + token = strtok(NULL, "\n"); + } + free(text); + + // Move to where buttons belong. + x = w - (WIN_MARGIN_LEFT + WIN_MARGIN_RIGHT + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonOne))); + y += cursorH + MSG_PADDING; + + // We always have at least one button. + btnOne = buttonNew(x, y, buttonOne, btnMsgBox); + guiAttach(W(winDialog), W(btnOne)); + + // Two buttons? + if (buttonTwo) { + x -= (MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonTwo))); + btnTwo = buttonNew(x, y, buttonTwo, btnMsgBox); + guiAttach(W(winDialog), W(btnTwo)); + } + + // Three buttons? + if (buttonThree) { + x -= (MSG_PADDING + BTN_MARGIN_LEFT + BTN_MARGIN_RIGHT + (fontWidthGet(_guiFont) * strlen(buttonThree))); + btnThree = buttonNew(x, y, buttonThree, btnMsgBox); + guiAttach(W(winDialog), W(btnThree)); + } +} + + +void msgBoxTwo(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo) { + return msgBoxThree(title, icon, message, buttonOne, callbackOne, buttonTwo, callbackTwo, NULL, NULL); +} diff --git a/client/src/gui/msgbox.h b/client/src/gui/msgbox.h new file mode 100644 index 0000000..3ea7605 --- /dev/null +++ b/client/src/gui/msgbox.h @@ -0,0 +1,57 @@ +/* + * Kangaroo Punch MultiPlayer 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 MSGBOX_H +#define MSGBOX_H + + +#include "os.h" +#include "widget.h" + + +enum MsgBoxIconE { + MSGBOX_ICON_NONE = 0, + MSGBOX_ICON_ERROR, + MSGBOX_ICON_WARNING, + MSGBOX_ICON_INFORMATION, + MSGBOX_ICON_MESSAGE, + MSGBOX_ICON_QUESTION, + MSGBOX_ICON_COUNT +}; + +enum MsgBoxButtonE { + MSGBOX_BUTTON_ONE = 0, + MSGBOX_BUTTON_TWO, + MSGBOX_BUTTON_THREE, + MSGBOX_BUTTON_COUNT +}; + + +typedef enum MsgBoxIconE MsgBoxIconT; +typedef enum MsgBoxButtonE MsgBoxButtonT; +typedef void (*msgBoxCallback)(MsgBoxButtonT button); + + +void msgBoxOne(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne); +void msgBoxThree(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo, char *buttonThree, msgBoxCallback callbackThree); +void msgBoxTwo(char *title, MsgBoxIconT icon, char *message, char *buttonOne, msgBoxCallback callbackOne, char *buttonTwo, msgBoxCallback callbackTwo); + + +#endif // MSGBOX_H diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index 77d040a..ce5d35d 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -289,7 +289,7 @@ static void textboxPaint(WidgetT *widget, uint8_t enabled, RectT pos) { free(draw); // Draw cursor. - if (guiFocusGet() == widget && timerQuarterSecondOn) { + if (guiFocusGet() == widget && _timerQuarterSecondOn) { caretPos = textX + fontWidthGet(_guiFont) * t->caret; fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], caretPos, textY); } diff --git a/client/src/gui/updown.c b/client/src/gui/updown.c index 8a98991..9d08375 100644 --- a/client/src/gui/updown.c +++ b/client/src/gui/updown.c @@ -307,7 +307,7 @@ static void updownPaint(WidgetT *widget, uint8_t enabled, RectT pos) { fontRender(_guiFont, draw, _guiColor[COLOR_UPDOWN_TEXT], _guiColor[COLOR_UPDOWN_BACKGROUND], textX, textY); // Draw cursor. - if (guiFocusGet() == widget && timerQuarterSecondOn) { + if (guiFocusGet() == widget && _timerQuarterSecondOn) { textX += (strlen(draw) - 1) * fontWidthGet(_guiFont); fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY); } diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c index 60f40a6..4e899a4 100644 --- a/client/src/gui/widget.c +++ b/client/src/gui/widget.c @@ -76,6 +76,26 @@ WidgetT *widgetInit(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint } +uint16_t widgetMarginBottomGet(WidgetT *widget) { + return widget->margin.h; +} + + +uint16_t widgetMarginLeftGet(WidgetT *widget) { + return widget->margin.x; +} + + +uint16_t widgetMarginRightGet(WidgetT *widget){ + return widget->margin.w; +} + + +uint16_t widgetMarginTopGet(WidgetT *widget) { + return widget->margin.y; +} + + WidgetT *widgetNew(uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t marginX, uint16_t marginY, uint16_t marginX2, uint16_t marginY2) { WidgetT *widget = (WidgetT *)malloc(sizeof(WidgetT)); diff --git a/client/src/gui/widget.h b/client/src/gui/widget.h index 78400bc..7873e32 100644 --- a/client/src/gui/widget.h +++ b/client/src/gui/widget.h @@ -70,6 +70,10 @@ uint8_t widgetEnableGet(WidgetT *widget); void widgetEnableSet(WidgetT *widget, uint8_t enabled); uint16_t widgetHeightGet(WidgetT *widget); WidgetT *widgetInit(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t marginX, uint16_t marginY, uint16_t marginX2, uint16_t marginY2); +uint16_t widgetMarginBottomGet(WidgetT *widget); +uint16_t widgetMarginLeftGet(WidgetT *widget); +uint16_t widgetMarginRightGet(WidgetT *widget); +uint16_t widgetMarginTopGet(WidgetT *widget); WidgetT *widgetNew(uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t marginX, uint16_t marginY, uint16_t marginX2, uint16_t marginY2); uint8_t widgetVisibleGet(WidgetT *widget); void widgetVisibleSet(WidgetT *widget, uint8_t enabled); diff --git a/client/src/linux/linux.c b/client/src/linux/linux.c index 5aaadc3..a56bb36 100644 --- a/client/src/linux/linux.c +++ b/client/src/linux/linux.c @@ -443,6 +443,7 @@ static void processNetworkEvent(void) { case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: _connected = 0; _modemCommandMode = 1; + comAddToBuffer("\13NO CARRIER\13", 12); break; default: diff --git a/client/src/settings.c b/client/src/settings.c index ff76dbc..a253849 100644 --- a/client/src/settings.c +++ b/client/src/settings.c @@ -55,17 +55,16 @@ typedef struct PortS { } PortT; -static WindowT *winDetecting; -static LabelT *lblOneMoment; - -static WindowT *winSettings; -static FrameT *frmComPorts; -static FrameT *frmServer; -static ButtonT *btnOkay; -static TextboxT *txtServer; -static UpdownT *updPort; - -static PortT port[4]; +static WindowT *winDetecting; +static LabelT *lblOneMoment; +static WindowT *winSettings; +static FrameT *frmComPorts; +static FrameT *frmServer; +static ButtonT *btnOkay; +static TextboxT *txtServer; +static UpdownT *updPort; +static PortT port[4]; +static widgetCallback done; static void btnOkayClick(WidgetT *widget); @@ -90,9 +89,9 @@ static void btnOkayClick(WidgetT *widget) { _configData.serverHost = strdup(textboxValueGet(txtServer)); _configData.serverPort = updownValueGet(updPort); - // Return to welcome dialog. - taskCreate(taskWelcome, NULL); + // Return to calling routine. guiDelete(D(winSettings)); + done(NULL); } @@ -101,10 +100,9 @@ void taskSettings(void *data) { int32_t rc; uint32_t len; char buffer[1024]; - uint8_t quarterSeconds = 0; uint8_t selected = 1; - (void)data; + done = (widgetCallback)data; TagItemT uiDetecting[] = { T_START, @@ -126,15 +124,10 @@ void taskSettings(void *data) { for (int x=0; x<4; x++) { rc = comOpen(x, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); if (rc == SER_SUCCESS) { - snprintf(buffer, 1024, "%s%c", "AT+SOCK1", 13); + snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); comWrite(x, buffer, strlen(buffer)); - // Wait a second. - quarterSeconds = 0; - while (quarterSeconds < 5) { - taskYield(); - if (timerQuarterSecondTick) quarterSeconds++; - } - len = comRead(x, buffer, 1024); +// timerQuarterSecondsWait(4); + len = comRead(x, buffer, 1023); buffer[len] = 0; if (strstr(buffer, "OK")) { snprintf(port[x].title, TITLE_LEN - 1, "COM%d - SoftModem Found!", x + 1); diff --git a/client/src/system/comport.c b/client/src/system/comport.c new file mode 100644 index 0000000..58de140 --- /dev/null +++ b/client/src/system/comport.c @@ -0,0 +1,80 @@ +/* + * Kangaroo Punch MultiPlayer 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 "comport.h" +#include "timer.h" +#include "task.h" + + +int comReceiveBufferFlush(int com) { + char buffer[256]; + int result = 1; + + while (result > 0) { + result = comRead(com, buffer, 255); + } + + return result; +} + + +int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting) { + int r; + int quarterTicks = 0; + int count = 0; + int bufferIndex = 0; + char data[2]; + + while (quarterTicks <= quarterSeconds) { + r = comRead(com, data, 1); + if (r == 1) { + logWrite("Byte\n"); + buffer[bufferIndex++] = data[0]; + buffer[bufferIndex] = 0; + if (data[0] == expecting[count]) { + logWrite("Expect\n"); + count++; + if (count == (int)strlen(expecting)) { + // Found our expect. + break; + } + } else { + count = 0; + } + if (bufferIndex == len - 1) { + // Out of buffer. + break; + } + } else { + sprintf(data, "%c", 0); + } + if (_timerQuarterSecondTick) { + quarterTicks++; + logWrite("Tick\n"); + } + taskYield(); + } + + if (count == (int)strlen(expecting)) { + return bufferIndex; + } + + return -bufferIndex; +} diff --git a/client/src/system/comport.h b/client/src/system/comport.h index 84d845d..ad25130 100644 --- a/client/src/system/comport.h +++ b/client/src/system/comport.h @@ -52,4 +52,8 @@ int comWrite(int com, const char *data, int len); #endif +int comReceiveBufferFlush(int com); +int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting); + + #endif // COMPORT_H diff --git a/client/src/system/os.h b/client/src/system/os.h index d9f75b0..2ce8924 100644 --- a/client/src/system/os.h +++ b/client/src/system/os.h @@ -65,6 +65,7 @@ long biostime(int cmd, long newtime); // Now our headers. #include "log.h" +#include "util.h" // Allocation helpers. diff --git a/client/src/system/task.c b/client/src/system/task.c index eb2c2bc..76f7cc5 100644 --- a/client/src/system/task.c +++ b/client/src/system/task.c @@ -23,9 +23,8 @@ //#define MCO_DEFAULT_STACK_SIZE 114688 // Default is 57344 #include "thirdparty/minicoro/minicoro.h" - -#include "array.h" #include "task.h" +#include "array.h" typedef struct TaskS { @@ -111,6 +110,6 @@ void taskStartup(void) { } -void taskYield(void) { +void __attribute__ ((noinline)) taskYield(void) { mco_yield(mco_running()); } diff --git a/client/src/system/timer.c b/client/src/system/timer.c index 8e31211..00f3c40 100644 --- a/client/src/system/timer.c +++ b/client/src/system/timer.c @@ -19,6 +19,8 @@ #include "timer.h" +#include "task.h" +#include "gui.h" #define SECONDS_IN_DAY 86400 @@ -26,18 +28,18 @@ #define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND) -uint8_t timerQuarterSecondTick = 0; -uint8_t timerHalfSecondTick = 0; -uint8_t timerSecondTick = 0; +volatile uint8_t _timerQuarterSecondTick = 0; +volatile uint8_t _timerHalfSecondTick = 0; +volatile uint8_t _timerSecondTick = 0; -uint8_t timerQuarterSecondOn = 0; -uint8_t timerHalfSecondOn = 0; -uint8_t timerSecondOn = 0; +volatile uint8_t _timerQuarterSecondOn = 0; +volatile uint8_t _timerHalfSecondOn = 0; +volatile uint8_t _timerSecondOn = 0; -static long _timerLast = 0; -static uint8_t _timerHalfSecond = 2; -static uint8_t _timerSecond = 2; +static volatile long timerLast = 0; +static volatile uint8_t timerHalfSecond = 2; +static volatile uint8_t timerSecond = 2; void timerShutdown(void) { @@ -46,7 +48,7 @@ void timerShutdown(void) { void timerStartup(void) { - _timerLast = biostime(0, 0); + timerLast = biostime(0, 0); } @@ -57,40 +59,55 @@ void timerUpdate(void) { now = biostime(0, 0); // Reset ticks. - timerQuarterSecondTick = 0; - timerHalfSecondTick = 0; - timerSecondTick = 0; + _timerQuarterSecondTick = 0; + _timerHalfSecondTick = 0; + _timerSecondTick = 0; // Ensure we haven't rolled past midnight between calls. - if (now >= _timerLast) { - delta = now - _timerLast; + if (now >= timerLast) { + delta = now - timerLast; } else { // Compensate for midnight rollover. - delta = (now + TICKS_PER_DAY) - _timerLast; + delta = (now + TICKS_PER_DAY) - timerLast; } // Everything ticks off the quarter second. if (delta > TICKS_PER_SECOND * 0.25) { - _timerLast = now; + timerLast = now; // Quarter Second timer. - timerQuarterSecondOn = !timerQuarterSecondOn; - timerQuarterSecondTick = 1; + _timerQuarterSecondOn = !_timerQuarterSecondOn; + _timerQuarterSecondTick = 1; // Half Second timer. - if (--_timerHalfSecond == 0) { - _timerHalfSecond = 2; - timerHalfSecondOn = !timerHalfSecondOn; - timerHalfSecondTick = 1; + if (--timerHalfSecond == 0) { + timerHalfSecond = 2; + _timerHalfSecondOn = !_timerHalfSecondOn; + _timerHalfSecondTick = 1; // Second timer - if (--_timerSecond == 0) { - _timerSecond = 2; - timerSecondOn = !timerSecondOn; - timerSecondTick = 1; + if (--timerSecond == 0) { + timerSecond = 2; + _timerSecondOn = !_timerSecondOn; + _timerSecondTick = 1; } // Second. } // Half Second. } // Quarter Second. } + + +/* +void timerQuarterSecondsWait(u_int8_t quarterSeconds) { + uint8_t counter = 0; + + while (counter <= quarterSeconds && !guiHasStopped()) { + if (_timerQuarterSecondTick) { + counter++; + } + logWrite("Waiting %d of %d\n", counter, quarterSeconds); + taskYield(); + } +} +*/ diff --git a/client/src/system/timer.h b/client/src/system/timer.h index f35e8eb..0740077 100644 --- a/client/src/system/timer.h +++ b/client/src/system/timer.h @@ -25,18 +25,19 @@ #include "os.h" -extern uint8_t timerQuarterSecondTick; -extern uint8_t timerHalfSecondTick; -extern uint8_t timerSecondTick; +extern volatile uint8_t _timerQuarterSecondTick; +extern volatile uint8_t _timerHalfSecondTick; +extern volatile uint8_t _timerSecondTick; -extern uint8_t timerQuarterSecondOn; -extern uint8_t timerHalfSecondOn; -extern uint8_t timerSecondOn; +extern volatile uint8_t _timerQuarterSecondOn; +extern volatile uint8_t _timerHalfSecondOn; +extern volatile uint8_t _timerSecondOn; void timerShutdown(void); void timerStartup(void); void timerUpdate(void); +//void timerQuarterSecondsWait(u_int8_t quarterSeconds); #endif // TIMER_H diff --git a/client/src/welcome.c b/client/src/welcome.c index 371ad1f..352be0d 100644 --- a/client/src/welcome.c +++ b/client/src/welcome.c @@ -24,40 +24,175 @@ #include "taglist.h" #include "task.h" #include "config.h" +#include "comport.h" +#include "timer.h" #include "window.h" #include "picture.h" #include "button.h" +#include "msgbox.h" static WindowT *winWelcome = NULL; static PictureT *picLogo = NULL; +static PictureT *picInit = NULL; +static PictureT *picDialing = NULL; +static PictureT *picConnect = NULL; static ButtonT *btnQuit = NULL; static ButtonT *btnSettings = NULL; static ButtonT *btnConnect = NULL; static void btnConnectClick(WidgetT *widget); +static void btnMsgBox(MsgBoxButtonT button); +static void btnMsgBoxQuit(MsgBoxButtonT button); static void btnQuitClick(WidgetT *widget); static void btnSettingsClick(WidgetT *widget); +static void setButtons(uint8_t enabled); +static void settingsFinished(WidgetT *widget); + + +static void btnMsgBox(MsgBoxButtonT button) { + (void)button; + + // Re-enable buttons. + setButtons(1); + + // Reset to Welcome image. + widgetVisibleSet(W(picLogo), 1); + widgetVisibleSet(W(picInit), 0); + widgetVisibleSet(W(picDialing), 0); + widgetVisibleSet(W(picConnect), 0); +} + + +static void btnMsgBoxQuit(MsgBoxButtonT button) { + if (button == MSGBOX_BUTTON_ONE) { + guiStop(); + } else { + setButtons(1); + } +} static void btnConnectClick(WidgetT *widget) { + uint32_t r; + uint32_t len; + char buffer[1024]; + int8_t timeout = 7 * 4; // Seven seconds (of quarter seconds) to connect to server. + uint16_t count = 0; + uint8_t connected = 0; + char *banner = "KPMPGSMKII"; + (void)widget; + + // Ghost all buttons. + widgetEnableSet(W(btnConnect), 0); + widgetEnableSet(W(btnSettings), 0); + widgetEnableSet(W(btnQuit), 0); + + // Hide welcome banner, show init. widgetVisibleSet(W(picLogo), 0); + widgetVisibleSet(W(picInit), 1); + taskYield(); + + // Open COM port. + r = comOpen(_configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS); + if (r != SER_SUCCESS) { + msgBoxOne("COM Problem", MSGBOX_ICON_ERROR, "Unable to open COM port!\nPlease check settings.", "Okay", btnMsgBox); + return; + } + logWrite("COM open\n"); + + // Send a CR to clear anything in the modem. + snprintf(buffer, 1023, "%c", 13); + comWrite(_configData.serialCom - 1, buffer, strlen(buffer)); + logWrite("CR sent\n"); + // Wait roughly a second. + comWaitWithTimeout(_configData.serialCom - 1, buffer, 1023, 4, "weExpectNothing"); + //uint32_t dumbThing = timerQuarterSecondsWait(4); + logWrite("Wait over\n"); + // Flush COM port. + comReceiveBufferFlush(_configData.serialCom - 1); + logWrite("Port flushed\n"); + + // Send actual init + snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13); + comWrite(_configData.serialCom - 1, buffer, strlen(buffer)); + // Wait roughly a second. +// timerQuarterSecondsWait(4); + // Read COM port & check for OK. + len = comRead(_configData.serialCom - 1, buffer, 1023); + buffer[len] = 0; + if (!strstr(buffer, "OK")) { + comClose(_configData.serialCom - 1); + msgBoxOne("Modem Problem", MSGBOX_ICON_ERROR, "Modem does not support ENET!\nPlease check settings.", "Okay", btnMsgBox); + return; + } + + // Show dialing, dial service. + widgetVisibleSet(W(picDialing), 1); + taskYield(); + snprintf(buffer, 1023, "ATDT%s:%d%c", _configData.serverHost, _configData.serverPort, 13); + comWrite(_configData.serialCom - 1, buffer, strlen(buffer)); + while (timeout > 0 && connected == 0) { + len = comRead(_configData.serialCom - 1, buffer, 1); + // Did we get a character? + if (len == 1) { + // Is it the next we expect in the server banner? + if (buffer[0] == banner[count]) { + count++; + } else { + count = 0; + } + // Are we connected? + if (count == strlen(banner)) { + connected = 1; + } + } + // Tick timeout. + if (_timerQuarterSecondTick) timeout--; + taskYield(); + } +// timerQuarterSecondsWait(4); + comReceiveBufferFlush(_configData.serialCom - 1); + // Did we connect? + if (!connected) { + comClose(_configData.serialCom - 1); + msgBoxOne("No Connection", MSGBOX_ICON_INFORMATION, "Unable to connect to server!\nPlease check settings or try later.", "Okay", btnMsgBox); + return; + } + + // Connected! Show icon and negotiate session. + widgetVisibleSet(W(picConnect), 1); + taskYield(); } static void btnQuitClick(WidgetT *widget) { (void)widget; - guiStop(); + setButtons(0); + msgBoxTwo("Quit?", MSGBOX_ICON_QUESTION, "Exit to DOS?", "Okay", btnMsgBoxQuit, "Cancel", btnMsgBoxQuit); } static void btnSettingsClick(WidgetT *widget) { (void)widget; - taskCreate(taskSettings, NULL); - guiDelete(D(winWelcome)); + setButtons(0); + taskCreate(taskSettings, settingsFinished); +} + + +void setButtons(uint8_t enabled) { + widgetEnableSet(W(btnConnect), (_configData.serialCom > 0 && strlen(_configData.serverHost) > 2) ? enabled : 0); + widgetEnableSet(W(btnSettings), enabled); + widgetEnableSet(W(btnQuit), enabled); +} + + +static void settingsFinished(WidgetT *widget) { + (void)widget; + setButtons(1); } @@ -72,10 +207,28 @@ void taskWelcome(void *data) { T_WINDOW, O(winWelcome), T_TITLE, P("Welcome to KangaWorld!"), T_WIDTH, 500, T_HEIGHT, 225, + T_PICTURE, O(picLogo), T_FILENAME, P("data/logo.png"), T_X, 18, T_Y, 18, T_PICTURE, T_DONE, + + T_PICTURE, O(picInit), + T_FILENAME, P("data/dinit.png"), + T_X, 18, T_Y, 18, + T_VISIBLE, T_FALSE, + T_PICTURE, T_DONE, + T_PICTURE, O(picDialing), + T_FILENAME, P("data/ddialing.png"), + T_X, 179, T_Y, 18, + T_VISIBLE, T_FALSE, + T_PICTURE, T_DONE, + T_PICTURE, O(picConnect), + T_FILENAME, P("data/dconnect.png"), + T_X, 339, T_Y, 18, + T_VISIBLE, T_FALSE, + T_PICTURE, T_DONE, + T_BUTTON, O(btnQuit), T_TITLE, P("Quit"), T_X, 30, T_Y, 157, @@ -92,6 +245,7 @@ void taskWelcome(void *data) { T_CLICK, P(btnConnectClick), T_ENABLED, (_configData.serialCom > 0 && strlen(_configData.serverHost) > 2) ? T_TRUE : T_FALSE, T_BUTTON, T_DONE, + T_WINDOW, T_DONE, T_END }; diff --git a/shared/util.c b/shared/util.c index 30b7449..f7852c4 100644 --- a/shared/util.c +++ b/shared/util.c @@ -62,3 +62,34 @@ void utilDie(const char *why, ...) { logWrite("DIE: %s", msg); exit(1); } + + +char *utilCreateString(char *format, ...) { + va_list args; + char *string; + + va_start(args, format); + string = utilCreateStringVArgs(format, args); + va_end(args); + + return string; +} + + +__attribute__((__format__(__printf__, 1, 0))) +char *utilCreateStringVArgs(char *format, va_list args) { + va_list argsCopy; + int32_t size = 0; + char *buffer = NULL; + + va_copy(argsCopy, args); + size = vsnprintf(NULL, 0, format, argsCopy) + 1; + va_end(argsCopy); + buffer = calloc(1, (size_t)size); + if (buffer) { + vsnprintf(buffer, (size_t)size, format, args); + } + + return buffer; +} + diff --git a/shared/util.h b/shared/util.h index 2e1be8b..25e7dbe 100644 --- a/shared/util.h +++ b/shared/util.h @@ -27,6 +27,8 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension); void utilDie(const char *why, ...); +char *utilCreateString(char *format, ...); +char *utilCreateStringVArgs(char *format, va_list args); #endif // UTIL_H