diff --git a/client/build.sh b/client/build.sh index 194c575..d1efe43 100755 --- a/client/build.sh +++ b/client/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # # Kangaroo Punch MultiPlayer Game Server Mark II @@ -23,3 +23,21 @@ source /opt/cross/djgpp/setenv make -f Makefile.djgpp rm bin/client cp data/* bin/. + +:<<'SKIP' +[[ -f bin/CLIENT.LOG ]] && rm bin/CLIENT.LOG + +cat <<-'BATCH' > bin/test.bat + @echo off + :wait + if not exist flag.dat goto wait + del flag.dat + client.exe 800 600 16 +BATCH + +touch bin/flag.dat +while [[ ! -f bin/CLIENT.LOG ]]; do + /bin/true +done +tail -f bin/CLIENT.LOG +SKIP diff --git a/client/src/dos/keyboard.c b/client/src/dos/keyboard.c index 34f7de4..ee4f219 100644 --- a/client/src/dos/keyboard.c +++ b/client/src/dos/keyboard.c @@ -46,8 +46,9 @@ enum MetaBitsE { }; -static int16_t _key = 0; -static int16_t _meta = 0; +static int8_t _extended = 0; +static int16_t _key = 0; +static int16_t _meta = 0; uint8_t keyAlt(void) { @@ -56,7 +57,7 @@ uint8_t keyAlt(void) { uint8_t keyASCII(void) { - return (uint8_t)(_key & 0x00FF); + return LOW_BYTE(_key); } @@ -65,22 +66,43 @@ uint8_t keyControl(void) { } +uint8_t keyExtended(void) { + return _extended; +} + + uint8_t keyHit(void) { int16_t result = bioskey(KEYBOARD_CHECK_EXTENDED); int16_t meta = bioskey(KEYBOARD_META_EXTENDED); if (result > 0) { - _key = bioskey(KEYBOARD_READ_EXTENDED);; + _key = bioskey(KEYBOARD_READ_EXTENDED); _meta = meta; + + // The value returned is a combination of the key's scan code in the high 8 bits + // and its ASCII code in the low 8 bits. For non-alphanumeric keys, such as the + // arrow keys, the low 8 bits are zeroed. + // Extended keys have the E0h prefix in the low 8 bits. + + //logWrite("Raw key = %04X High = %02X Low = %02X\n", _key, HIGH_BYTE(_key), LOW_BYTE(_key)); + + if (LOW_BYTE(_key) == 0xE0) { + _extended = 1; + _key = HIGH_BYTE(_key); + } else { + _extended = 0; + } return 1; } + _extended = 0; + return 0; } uint8_t keyScanCode(void) { - return (uint8_t)((_key & 0xFF00) >> 8); + return HIGH_BYTE(_key); } diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 81cd7af..3aacf2a 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -58,7 +58,7 @@ static char *_magicDebugNames[MAGIC_COUNT] = { static void guiPaintBoundsGet(WidgetT *widget, RectT *pos); -static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); +static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse); @@ -255,17 +255,24 @@ WidgetT *guiFocusGet(void) { void guiFocusSet(WidgetT *widget) { + // Is this widget on the active window? + if (widget->window) { + if (!GUI_GET_FLAG(W(widget->window), WIDGET_FLAG_ACTIVE)) { + // Cancel focus change. + return; + } + } // Did the focus change? if (widget != _guiFocused) { // Remove focus from current control. if (_guiFocused) { - if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 0); + if (_guiFocused->focusMethod) _guiFocused->focusMethod(_guiFocused, 0); } // Change focus. _guiFocused = widget; // Tell new control it has focus. if (_guiFocused) { - if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 1); + if (_guiFocused->focusMethod) _guiFocused->focusMethod(_guiFocused, 1); } } } @@ -406,33 +413,33 @@ static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse) { } -void guiProcessKeyboard(uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { +void guiProcessKeyboard(uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { // Does the focused widget want events? Check for global keyboard so it doesn't get double events. if (_guiFocused) { if (_guiFocused->keyboardEventMethod && !GUI_GET_FLAG(_guiFocused, WIDGET_FLAG_ALWAYS_RECEIVE_KEYBOARD_EVENTS)) { - _guiFocused->keyboardEventMethod(_guiFocused, ascii, scancode, shift, control, alt); + _guiFocused->keyboardEventMethod(_guiFocused, ascii, extended, scancode, shift, control, alt); } } // Check everyone for global keyboard handling. - guiProcessKeyboardChildren((WidgetT *)_guiDesktop, ascii, scancode, shift, control, alt); + guiProcessKeyboardChildren((WidgetT *)_guiDesktop, ascii, extended, scancode, shift, control, alt); } -static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { +static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { size_t len; size_t x; // Process children of this widget. len = arrlenu(widget->children); for (x=0; xchildren[x], ascii, scancode, shift, control, alt); + guiProcessKeyboardChildren(widget->children[x], ascii, extended, scancode, shift, control, alt); } // Does this widget want events? Check for global keyboard so it doesn't get double events. if (widget) { if (widget->keyboardEventMethod && GUI_GET_FLAG(widget, WIDGET_FLAG_ALWAYS_RECEIVE_KEYBOARD_EVENTS)) { - widget->keyboardEventMethod(widget, ascii, scancode, shift, control, alt); + widget->keyboardEventMethod(widget, ascii, extended, scancode, shift, control, alt); } } } diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 85f9fa5..67b483a 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -144,7 +144,7 @@ void guiFocusSet(WidgetT *widget); void guiMousePositionOnWidgetGet(WidgetT *widget, MouseT *mouse, uint16_t *x, uint16_t *y); void guiPaint(WidgetT *widget); void guiProcessMouse(MouseT *mouse); -void guiProcessKeyboard(uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); +void guiProcessKeyboard(uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); WidgetT *guiRootGet(void); void guiSetWidgetAndChildrenDirty(WidgetT *widget); DesktopT *guiStartup(void); diff --git a/client/src/gui/keyboard.h b/client/src/gui/keyboard.h index f4bcd2d..b3fcf17 100644 --- a/client/src/gui/keyboard.h +++ b/client/src/gui/keyboard.h @@ -28,6 +28,7 @@ uint8_t keyAlt(void); uint8_t keyASCII(void); uint8_t keyControl(void); +uint8_t keyExtended(void); uint8_t keyHit(void); uint8_t keyScanCode(void); uint8_t keyShift(void); diff --git a/client/src/gui/os.h b/client/src/gui/os.h index 8600740..c62e262 100644 --- a/client/src/gui/os.h +++ b/client/src/gui/os.h @@ -55,6 +55,8 @@ long biostime(int cmd, long newtime); #define DIVISIBLE_BY_EIGHT(x) ((((x) >> 3) << 3) == (x)) +#define HIGH_BYTE(b) ((uint8_t)(((b) & 0xFF00) >> 8)) +#define LOW_BYTE(b) ((uint8_t)((b) & 0x00FF)) #endif // OS_H diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index c031f6d..46606f5 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -19,11 +19,12 @@ #include "textbox.h" +#include "timer.h" static void textboxFocusEvent(WidgetT *widget, uint8_t focused); static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); -static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); +static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); static void textboxPaint(WidgetT *widget, RectT pos); @@ -38,7 +39,10 @@ void textboxDel(WidgetT **widget) { static void textboxFocusEvent(WidgetT *widget, uint8_t focused) { - + // Make sure cursor disappears when we lose focus. + if (!focused) { + GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY); + } } @@ -59,6 +63,7 @@ WidgetT *textboxInit(WidgetT *widget, char *title) { t->maxLength = 256; t->value = (char *)malloc(t->maxLength); t->caret = 0; + t->offset = 0; if (!t->value) return NULL; t->value[0] = 0; @@ -70,8 +75,115 @@ WidgetT *textboxInit(WidgetT *widget, char *title) { } -static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { +static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) { + TextboxT *t = (TextboxT *)widget; + uint16_t x; + + (void)scancode; + (void)shift; + (void)control; + (void)alt; + + if (extended) { + + switch (ascii) { + case 75: // LEFT + // Can we move left in the value? + if (t->caret + t->offset > 0) { + // Is the caret on the left edge of the box? + if (t->caret == 0) { + // Can we move the string offset? + if (t->offset > 0) { + t->offset--; + } + } else { + // Move the caret left. + t->caret--; + } + } + break; + + case 77: // RIGHT + // Can we move right in the value? + if (t->caret + t->offset < strlen(t->value)) { + // Is the caret on the right edge of the box? + if (t->caret == t->visible - 1) { + // Can we move the string offset? + if (t->offset < strlen(t->value)) { + t->offset++; + } + } else { + // Move the caret right. + t->caret++; + } + } + break; + + case 83: // DELETE + // Is there more data to the right in the value? + if (t->caret + t->offset < strlen(t->value)) { + // Delete character under caret. + for (x = t->caret + t->offset; xvalue); x++) { + t->value[x] = t->value[x+1]; + } + } + break; + + } // switch + + } else { // extended + + switch (ascii) { + case 8: // BACKSPACE + // Can we delete left in the value? + if (t->caret + t->offset > 0) { + // Is the caret on the left edge of the box? + if (t->caret == 0) { + // Can we move the string offset? + if (t->offset > 0) { + t->offset--; + } + } else { + // Move the caret left. + t->caret--; + } + // Delete the character to the left of the caret. + for (x = t->caret + t->offset; xvalue); x++) { + t->value[x] = t->value[x+1]; + } + } + break; + + default: // Other keys + if (ascii >= 32 && ascii <= 126) { + // Insert character, if room. + if (strlen(t->value) < t->maxLength - 1) { + // Move existing characters over, if needed. + if (t->caret + t->offset < strlen(t->value)) { + for (x=strlen(t->value) + 1; x>t->caret + t->offset; x--) { + t->value[x] = t->value[x-1]; + } + } + // Place typed character at caret. + t->value[t->caret + t->offset] = ascii; + // Is the caret on the right edge of the box? + if (t->caret == t->visible - 1) { + // Can we move the string offset? + if (t->offset < strlen(t->value)) { + t->offset++; + } + } else { + // Move the caret right. + t->caret++; + } + } + } + break; + + } // switch + + } // extended } @@ -82,7 +194,10 @@ static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16 (void)y; (void)mouse; - //***TODO*** Allow dragging text cursor with mouse. + //***TODO*** Allow dragging/positioning text cursor with mouse. + if (event == MOUSE_EVENT_LEFT_HOLD) { + + } } @@ -110,8 +225,12 @@ static void textboxPaint(WidgetT *widget, RectT pos) { char *draw = NULL; uint16_t labelWidth; uint16_t valueWidth; + uint16_t caretPos; + uint16_t textX; + uint16_t textY; + char cursor[2] = { 0xb1, 0 }; - if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) { + if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) || guiFocusGet() == widget) { labelWidth = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING]; valueWidth = (t->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2); @@ -125,12 +244,23 @@ static void textboxPaint(WidgetT *widget, RectT pos) { // Draw background. guiDrawRectangleFilled(pos.x + labelWidth + 2, pos.y + 2, pos.x + labelWidth + valueWidth, pos.y + pos.h - 2, _guiColor[COLOR_TEXTBOX_BACKGROUND]); - // Draw value. ***TODO*** This needs much more! - draw = strdup(t->value); + // Where's the text display start? + textX = pos.x + labelWidth + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING]; + textY = pos.y + 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]; + + // Draw value. ***TODO*** This needs much more! Do it without strdup? + draw = strdup(&t->value[t->offset]); if (strlen(t->value) > t->visible) draw[t->visible] = 0; - fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], pos.x + labelWidth + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING], pos.y + 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]); + fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY); free(draw); + // Draw cursor. + if (guiFocusGet() == widget && timerQuarterSecondOn) { + caretPos = textX + fontWidthGet(_guiFont) * t->caret; + //logWrite("textX %d textY %d caretPos %d\n", textX, textY, caretPos); + fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], caretPos, textY); + } + GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY); } } @@ -140,6 +270,14 @@ void textboxSetValue(TextboxT *textbox, char *value) { // Copy it & truncate if needed. strncpy(textbox->value, value, textbox->maxLength - 1); + // Is this longer than the area we have to display it in? + if (strlen(textbox->value) > textbox->visible - 1) { + textbox->offset = strlen(textbox->value) - (textbox->visible - 1); + } + + // Set caret position. + textbox->caret = strlen(&textbox->value[textbox->offset]); + GUI_SET_FLAG((WidgetT *)textbox, WIDGET_FLAG_DIRTY); } diff --git a/client/src/gui/textbox.h b/client/src/gui/textbox.h index 6b45f68..4b9e660 100644 --- a/client/src/gui/textbox.h +++ b/client/src/gui/textbox.h @@ -34,7 +34,6 @@ typedef struct TextboxS { uint16_t caret; // Cursor position in control (caret + offset = cursor positition in value) uint16_t offset; // First character position to display in control uint8_t visible; // How many characters are visible in control - uint8_t blink; } TextboxT; diff --git a/client/src/gui/timer.c b/client/src/gui/timer.c index c0eb1ca..8092f9c 100644 --- a/client/src/gui/timer.c +++ b/client/src/gui/timer.c @@ -26,12 +26,14 @@ #define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND) -uint8_t timerHalfSecondOn = 0; -uint8_t timerSecondOn = 0; +uint8_t timerQuarterSecondOn = 0; +uint8_t timerHalfSecondOn = 0; +uint8_t timerSecondOn = 0; -static long _timerLast = 0; -static uint8_t _timerSecond = 2; +static long _timerLast = 0; +static uint8_t _timerHalfSecond = 2; +static uint8_t _timerSecond = 2; void timerShutdown(void) { @@ -58,16 +60,25 @@ void timerUpdate(void) { delta = (now + TICKS_PER_DAY) - _timerLast; } - if (delta > TICKS_PER_SECOND * 0.5) { + // Everything ticks off the quarter second. + if (delta > TICKS_PER_SECOND * 0.25) { _timerLast = now; - // Half Second timer - timerHalfSecondOn = !timerHalfSecondOn; + // Quarter Second timer. + timerQuarterSecondOn = !timerQuarterSecondOn; - // Second timer - if (--_timerSecond == 0) { - _timerSecond = 2; - timerSecondOn = !timerSecondOn; - } - } + // Half Second timer. + if (--_timerHalfSecond == 0) { + _timerHalfSecond = 2; + timerHalfSecondOn = !timerHalfSecondOn; + + // Second timer + if (--_timerSecond == 0) { + _timerSecond = 2; + timerSecondOn = !timerSecondOn; + } // Second. + + } // Half Second. + + } // Quarter Second. } diff --git a/client/src/gui/timer.h b/client/src/gui/timer.h index c60ff87..ffdd57a 100644 --- a/client/src/gui/timer.h +++ b/client/src/gui/timer.h @@ -25,6 +25,7 @@ #include "os.h" +extern uint8_t timerQuarterSecondOn; extern uint8_t timerHalfSecondOn; extern uint8_t timerSecondOn; diff --git a/client/src/gui/widget.h b/client/src/gui/widget.h index 73903f8..cf75abe 100644 --- a/client/src/gui/widget.h +++ b/client/src/gui/widget.h @@ -40,7 +40,7 @@ typedef struct WindowS WindowT; typedef void (*widgetCallback)(struct WidgetS *widget); typedef void (*widgetDelMethod)(struct WidgetS **widget); typedef void (*widgetFocusMethod)(struct WidgetS *widget, uint8_t focused); -typedef void (*widgetKeyboardEventMethod)(struct WidgetS *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); +typedef void (*widgetKeyboardEventMethod)(struct WidgetS *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); typedef void (*widgetPaintMethod)(struct WidgetS *widget, RectT pos); typedef void (*widgetMouseEventMethod)(struct WidgetS *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); diff --git a/client/src/linux/linux.c b/client/src/linux/linux.c index 3481d7f..09216d3 100644 --- a/client/src/linux/linux.c +++ b/client/src/linux/linux.c @@ -46,6 +46,7 @@ static uint8_t _control = 0; static uint8_t _shift = 0; static uint8_t _ASCII = 0; static uint8_t _scanCode = 0; +static uint8_t _extended = 0; static uint8_t _ASCIIKeep = 0; static uint8_t _scanCodeKeep = 0; static uint8_t _keyPressed = 0; @@ -84,6 +85,11 @@ uint8_t keyControl(void) { } +uint8_t keyExtended(void) { + return _extended; +} + + uint8_t keyHit(void) { uint8_t result = 0; @@ -91,10 +97,19 @@ uint8_t keyHit(void) { result = _keyPressed; if (result) { - if (_scanCode == 41) _ASCII = 27; // Fix ESC mapping. + // Default mapping. + _extended = 0; _ASCIIKeep = _ASCII; _scanCodeKeep = _scanCode; _keyPressed = 0; + // Fix mappings to match DOS bioskey() call. + if (_scanCodeKeep == SDL_SCANCODE_ESCAPE) _ASCIIKeep = 27; + if (_scanCodeKeep == SDL_SCANCODE_BACKSPACE) _ASCIIKeep = 8; + if (_scanCodeKeep == SDL_SCANCODE_DELETE) { _extended = 1; _ASCIIKeep = 83; } + if (_scanCodeKeep == SDL_SCANCODE_LEFT) { _extended = 1; _ASCIIKeep = 75; } + if (_scanCodeKeep == SDL_SCANCODE_RIGHT) { _extended = 1; _ASCIIKeep = 77; } + if (_scanCodeKeep == SDL_SCANCODE_UP) { _extended = 1; _ASCIIKeep = 72; } + if (_scanCodeKeep == SDL_SCANCODE_DOWN) { _extended = 1; _ASCIIKeep = 80; } } return result; diff --git a/client/src/main.c b/client/src/main.c index 2d51a00..084a974 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -26,6 +26,7 @@ * - More widget states: Ghosted, hidden * - Move setup math for paint events inside the dirty check * - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well + * - Focus events are firing for controls not in top level window * */ @@ -97,8 +98,8 @@ void mainLoop(void *data) { mouse = mouseRead(); if (keyHit()) { key = keyASCII(); - guiProcessKeyboard(keyASCII(), keyScanCode(), keyShift(), keyControl(), keyAlt()); - //logWrite("Key '%d' Control '%d'\n", key, keyControl()); + guiProcessKeyboard(keyASCII(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt()); + //logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", key, keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt()); } else { key = 0; } @@ -112,7 +113,7 @@ void mainLoop(void *data) { if (debugState > 2) debugState = 0; } if (debugState > 0) drawWidgetDebug(guiRootGet(), debugState - 1); - if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255)); + //if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255)); vbeWaitVBlank(); vbePresent(); @@ -139,6 +140,7 @@ void test(void *data) { PictureT *p1 = NULL; FrameT *f1 = NULL; TextboxT *t1 = NULL; + TextboxT *t2 = NULL; (void)data; @@ -170,8 +172,11 @@ void test(void *data) { radioSetSelected(r1a); radioSetSelected(r2b); t1 = textboxNew(10, 60, 265, "Test Textbox"); - textboxSetValue(t1, "Text to edit!"); + textboxSetValue(t1, "Really long text string to edit!"); guiAttach(W(w2), W(t1)); + t2 = textboxNew(10, 85, 265, "Test Textbox"); + textboxSetValue(t2, "Short string."); + guiAttach(W(w2), W(t2)); // Window 3 f1 = frameNew(10, 5, 175, 125, "Test Frame");