Start of Terminal widget.

This commit is contained in:
Scott Duensing 2021-11-02 21:30:56 -05:00
parent 71c13fdc71
commit fb0d5ad0ac
15 changed files with 2819 additions and 19 deletions

18
LICENSE
View file

@ -22,14 +22,32 @@ Licenses Used By:
Client
======
DOS Serial Library
------------------
https://github.com/kstenerud/DOS-Serial-Library
Attribution
MemWatch
--------
http://www.linkdata.se/sourcecode/memwatch/
GPL2
SDL2
----
https://www.libsdl.org/
BSD 3-Clause
SDL2_Net
--------
https://www.libsdl.org/
BSD 3-Clause
stb_ds.h
--------
https://github.com/nothings/stb
Public Domain
stb_image.h
-----------
https://github.com/nothings/stb
Public Domain

View file

@ -32,7 +32,7 @@ BINDIR = bin
## CHANGE THIS ##
# CFLAGS, LDFLAGS, CPPFLAGS, PREFIX can be overriden on CLI
CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty -I$(SRCDIR)/thirdparty/memwatch
CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty -I$(SRCDIR)/thirdparty/memwatch -I$(SRCDIR)/thirdparty/serial
CPPFLAGS :=
LDFLAGS :=
PREFIX := /usr/local

View file

@ -18,7 +18,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
mkdir -p bin obj/dos obj/gui obj/thirdparty/memwatch
mkdir -p bin obj/dos obj/gui obj/thirdparty/memwatch obj/thirdparty/serial
source /opt/cross/djgpp/setenv
make -f Makefile.djgpp
rm bin/client

View file

@ -21,9 +21,11 @@ CONFIG -= qt
DESTDIR = $$OUT_PWD/bin
DOS_HEADERS =
DOS_HEADERS = \
src/thirdparty/serial/serial.h
DOS_SOURCES = \
src/thirdparty/serial/serial.c \
src/dos/keyboard.c \
src/dos/mouse.c \
src/dos/vesa.c
@ -45,6 +47,7 @@ INCLUDEPATH += \
HEADERS = \
$$LINUX_HEADERS \
src/gui/listbox.h \
src/gui/terminal.h \
src/gui/updown.h \
src/stddclmr.h \
src/thirdparty/stb_ds.h \
@ -77,6 +80,7 @@ HEADERS = \
SOURCES = \
$$LINUX_SOURCES \
src/gui/listbox.c \
src/gui/terminal.c \
src/gui/updown.c \
src/thirdparty/memwatch/memwatch.c \
src/gui/memory.c \
@ -100,7 +104,8 @@ SOURCES = \
src/main.c
LIBS = \
-lSDL2
-lSDL2 \
-lSDL2_net
OTHER_FILES = \
Makefile.djgpp \

1
client/data/kanga.ans Normal file

File diff suppressed because one or more lines are too long

View file

@ -29,6 +29,9 @@
int16_t _guiMetric[METRIC_COUNT];
ColorT _guiColor[COLOR_COUNT];
FontT *_guiFont8 = NULL;
FontT *_guiFont14 = NULL;
FontT *_guiFont16 = NULL;
FontT *_guiFont = NULL;
WidgetT *_guiDragWidget = NULL;
uint16_t _guiDragOffsetX = 0;
@ -533,7 +536,12 @@ DesktopT *guiStartup(void) {
_guiColor[COLOR_LISTBOX_ARROWS_ACTIVE] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_LISTBOX_ARROWS_INACTIVE] = vbeMakeColor( 80, 84, 80);
_guiFont = fontLoad("vga8x14.dat");
// Load all font sizes.
_guiFont8 = fontLoad("vga8x8.dat");
_guiFont14 = fontLoad("vga8x14.dat");
_guiFont16 = fontLoad("vga8x16.dat");
_guiFont = _guiFont14; // Font to use for widgets.
// Create desktop and return it. Remember it for later.
_guiDesktop = desktopNew();
@ -550,7 +558,11 @@ DesktopT *guiStartup(void) {
void guiShutdown(void) {
fontUnload(&_guiFont);
// Unload fonts.
fontUnload(&_guiFont16);
fontUnload(&_guiFont14);
fontUnload(&_guiFont8);
_guiFont = NULL;
// Delete all widgets in GUI tree.
guiDelete((WidgetT **)&_guiDesktop);

View file

@ -51,7 +51,7 @@ enum MagicE {
MAGIC_TEXTBOX,
MAGIC_UPDOWN,
MAGIC_LISTBOX,
//MAGIC_TERMINAL,
MAGIC_TERMINAL,
//MAGIC_DROPDOWN,
MAGIC_COUNT
};
@ -149,6 +149,9 @@ typedef struct WindowS WindowT;
extern int16_t _guiMetric[METRIC_COUNT];
extern ColorT _guiColor[COLOR_COUNT];
extern FontT *_guiFont;
extern FontT *_guiFont8;
extern FontT *_guiFont14;
extern FontT *_guiFont16;
extern WidgetT *_guiDragWidget;
extern uint16_t _guiDragOffsetX;
extern uint16_t _guiDragOffsetY;

View file

@ -48,6 +48,7 @@ ImageT *imageAllocate(uint16_t w, uint16_t h) {
for (y=0; y<x; y++) {
free(image->pixels[y]);
}
free(image->pixels);
free(image);
return NULL;
}

649
client/src/gui/terminal.c Normal file
View file

@ -0,0 +1,649 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
*/
// http://www.ansi-bbs.org/BANSI/bansi.txt
// http://ansi-bbs.org/ansi-bbs-core-server.html
#include "terminal.h"
#define TERMINAL_CELL_GET_FLAG(t,x,y,f) (((t)->cells[(x)][(y)].flags & (1 << (f))) != 0)
#define TERMINAL_CELL_SET_FLAG(t,x,y,f) ((t)->cells[(x)][(y)].flags |= (1 << (f)))
#define TERMINAL_CELL_CLEAR_FLAG(t,x,y,f) ((t)->cells[(x)][(y)].flags &= (~(1 << (f))))
static int16_t terminalConvertToInt(char *number, int16_t defaultValue);
static void terminalDel(WidgetT **widget);
static void terminalMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void terminalPaint(WidgetT *widget, RectT pos);
static void terminalSequenceReset(TerminalT *terminal);
void terminalBackgroundSet(TerminalT *terminal, uint8_t color) {
terminal->background = color;
}
static int16_t terminalConvertToInt(char *number, int16_t defaultValue) {
char *end = NULL;
int16_t result = (int16_t)strtol(number, &end, 10);
if (end == NULL) result = defaultValue;
return result;
}
void terminalCursorMove(TerminalT *terminal, uint16_t col, uint16_t row) {
// Clamp X.
if (col < 1) {
terminal->cursorX = 1;
} else {
if (col > terminal->cols) {
terminal->cursorX = terminal->cols;
} else {
terminal->cursorX = col;
}
}
// Clamp Y.
if (row < 1) {
terminal->cursorY = 1;
} else {
if (row > terminal->rows) {
terminal->cursorY = terminal->rows;
} else {
terminal->cursorY = row;
}
}
}
static void terminalDel(WidgetT **widget) {
TerminalT *t = (TerminalT *)*widget;
uint16_t x;
terminalSequenceReset(t);
for (x=0; x<t->cols; x++) {
free(t->cells[x]);
}
free(t->cells);
free(t);
t = NULL;
}
void terminalForegroundSet(TerminalT *terminal, uint8_t color) {
terminal->foreground = color;
terminal->bold = (color > 7);
}
WidgetT *terminalInit(WidgetT *widget, uint16_t cols, uint16_t rows) {
TerminalT *t = (TerminalT *)widget;
uint16_t x;
uint16_t y;
t->base.delMethod = terminalDel;
t->base.paintMethod = terminalPaint;
t->base.mouseEventMethod = terminalMouseEvent;
t->font = _guiFont8; // Default font. Also see terminalNew.
t->cols = cols;
t->rows = rows;
t->cursorX = 1;
t->cursorY = 1;
t->saveX = 1;
t->saveY = 1;
t->bold = 0;
t->blink = 0;
t->reverse = 0;
t->background = TERMINAL_COLOR_BLACK;
t->foreground = TERMINAL_COLOR_LIGHT_GRAY;
t->escFound = 0;
t->inEscape = 0;
t->numberIndex = 0;
t->number[0] = 0;
t->parameters = NULL;
// Allocate space for column data.
t->cells = (CellT **)malloc(sizeof(CellT *) * cols);
if (!t->cells) return NULL;
// Allocate space for row data.
for (x=0; x<cols; x++) {
t->cells[x] = (CellT *)malloc(sizeof(CellT) * rows);
if (!t->cells[x]) {
for (y=0; y<x; y++) {
free(t->cells[y]);
}
free(t->cells);
return NULL;
}
}
// Set up default palette.
t->palette[TERMINAL_COLOR_BLACK] = vbeMakeColor(0x00, 0x00, 0x00);
t->palette[TERMINAL_COLOR_BLUE] = vbeMakeColor(0x00, 0x00, 0xAA);
t->palette[TERMINAL_COLOR_GREEN] = vbeMakeColor(0x00, 0xAA, 0x00);
t->palette[TERMINAL_COLOR_CYAN] = vbeMakeColor(0x00, 0xAA, 0xAA);
t->palette[TERMINAL_COLOR_RED] = vbeMakeColor(0xAA, 0x00, 0x00);
t->palette[TERMINAL_COLOR_MAGENTA] = vbeMakeColor(0xAA, 0x00, 0xAA);
t->palette[TERMINAL_COLOR_BROWN] = vbeMakeColor(0xAA, 0x55, 0x00);
t->palette[TERMINAL_COLOR_LIGHT_GRAY] = vbeMakeColor(0xAA, 0xAA, 0xAA);
t->palette[TERMINAL_COLOR_DARK_GRAY] = vbeMakeColor(0x55, 0x55, 0x55);
t->palette[TERMINAL_COLOR_BRIGHT_BLUE] = vbeMakeColor(0x55, 0x55, 0xFF);
t->palette[TERMINAL_COLOR_BRIGHT_GREEN] = vbeMakeColor(0x55, 0xFF, 0x55);
t->palette[TERMINAL_COLOR_BRIGHT_CYAN] = vbeMakeColor(0x55, 0xFF, 0xFF);
t->palette[TERMINAL_COLOR_BRIGHT_RED] = vbeMakeColor(0xFF, 0x55, 0x55);
t->palette[TERMINAL_COLOR_BRIGHT_MAGENTA] = vbeMakeColor(0xFF, 0x55, 0xFF);
t->palette[TERMINAL_COLOR_YELLOW] = vbeMakeColor(0xFF, 0xFF, 0x55);
t->palette[TERMINAL_COLOR_WHITE] = vbeMakeColor(0xFF, 0xFF, 0xFF);
// Default attributes is gray on black, no bold, no blink, and dirty.
for (y=0; y<rows; y++) {
for (x=0; x<cols; x++) {
t->cells[x][y].character = ' ';
t->cells[x][y].background = TERMINAL_COLOR_BLACK;
t->cells[x][y].foreground = TERMINAL_COLOR_LIGHT_GRAY;
t->cells[x][y].flags = 0;
TERMINAL_CELL_SET_FLAG(t, x, y, TERMINAL_FLAG_DIRTY);
}
}
return widget;
}
static void terminalMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
TerminalT *t = (TerminalT *)widget;
(void)mouse;
// Fire callback on mouse up.
if (event == MOUSE_EVENT_LEFT_UP) {
}
}
TerminalT *terminalNew(uint16_t x, uint16_t y, uint16_t cols, uint16_t rows) {
TerminalT *terminal = (TerminalT *)malloc(sizeof(TerminalT));
WidgetT *widget = NULL;
uint16_t w;
uint16_t h;
if (!terminal) return NULL;
// Default font. Also see terminalInit.
w = fontWidthGet(_guiFont8) * cols;
h = fontHeightGet(_guiFont8) * rows;
widget = widgetInit(W(terminal), MAGIC_TERMINAL, x, y, w, h, 0, 0, 0, 0);
if (!widget) {
free(terminal);
return NULL;
}
terminal = (TerminalT *)terminalInit((WidgetT *)terminal, cols, rows);
if (!terminal) {
free(terminal);
return NULL;
}
return terminal;
}
static void terminalPaint(WidgetT *widget, RectT pos) {
TerminalT *t = (TerminalT *)widget;
uint16_t x;
uint16_t y;
uint16_t xp;
uint16_t yp;
char c[2];
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
c[1] = 0;
yp = pos.y;
for (y=0; y<t->rows; y++) {
xp = pos.x;
for (x=0; x<t->cols; x++) {
if (TERMINAL_CELL_GET_FLAG(t, x, y, TERMINAL_FLAG_DIRTY)) {
c[0] = t->cells[x][y].character;
fontRender(t->font, c, t->palette[t->cells[x][y].foreground], t->palette[t->cells[x][y].background], xp, yp);
TERMINAL_CELL_CLEAR_FLAG(t, x, y, TERMINAL_FLAG_DIRTY);
}
xp += fontWidthGet(t->font);
}
yp += fontHeightGet(t->font);
}
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
void terminalPrint(TerminalT *terminal, char *string) {
uint16_t i;
for (i=0; i<strlen(string); i++) {
terminalPrintChar(terminal, string[i]);
}
}
void terminalPrintChar(TerminalT *terminal, uint8_t c) {
int16_t x;
int16_t y;
uint16_t p;
// Find leading ESC.
if (c == 27 && !terminal->escFound && !terminal->inEscape) {
terminal->escFound = 1;
return;
}
// Find [.
if (c == '[' && terminal->escFound && !terminal->inEscape) {
terminal->inEscape = 1;
return;
}
// Is this a sequence that is ESC and then not [ ?
if (terminal->escFound && !terminal->inEscape) {
terminalSequenceReset(terminal);
return;
}
if (terminal->inEscape) {
switch (c) {
case '[':
case 27:
// ESC sequence inside sequence. Invalid ANSIBBS.
//***TODO*** Can stick custom sequences here.
break;
// End of a parameter
case ';':
arrput(terminal->parameters, strdup(terminal->number));
terminal->number[0] = 0;
terminal->numberIndex = 0;
break;
// Cursor up.
case 'A':
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY - y);
terminalSequenceReset(terminal);
break;
// Cursor down.
case 'B':
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY + y);
terminalSequenceReset(terminal);
break;
// Cursor forward.
case 'C':
x = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, terminal->cursorX + x, terminal->cursorY);
terminalSequenceReset(terminal);
break;
// Cursor backward.
case 'D':
x = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, terminal->cursorX - x, terminal->cursorY);
terminalSequenceReset(terminal);
break;
// Cursor line down.
case 'E':
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, 1, terminal->cursorY + y);
//***TODO*** This should allow scrolling.
terminalSequenceReset(terminal);
break;
// Cursor line up.
case 'F':
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, 1, terminal->cursorY - y);
//***TODO*** This should allow scrolling.
terminalSequenceReset(terminal);
break;
// Cursor horizontal absolute.
case 'G':
x = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, x, terminal->cursorY);
terminalSequenceReset(terminal);
break;
// Cursor move.
case 'H':
case 'f':
switch (arrlen(terminal->parameters)) {
// Cursor vertical absolute. Kinda. Moves X to 1.
case 0:
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, 1, y);
break;
// Absolute position.
case 1:
x = terminalConvertToInt(terminal->parameters[0], 1);
y = terminalConvertToInt(terminal->number, 1);
terminalCursorMove(terminal, x, y);
break;
// Invalid nonsense.
default:
break;
}
terminalSequenceReset(terminal);
break;
// Clear display.
case 'J':
if (arrlen(terminal->parameters) == 0 && terminalConvertToInt(terminal->number, 1) == 2) {
terminalScreenClear(terminal);
terminalCursorMove(terminal, 1, 1);
}
terminalSequenceReset(terminal);
break;
// Clear from cursor to end of line.
case 'K':
x = terminalConvertToInt(terminal->number, 0);
if (x == 0) {
for (y=terminal->cursorX-1; y<terminal->cols; y++) {
terminal->cells[y][terminal->cursorY - 1].character = ' ';
TERMINAL_CELL_SET_FLAG(terminal, y, terminal->cursorY - 1, TERMINAL_FLAG_DIRTY);
GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY);
}
} else {
//***TODO*** Unimplemented line clear.
}
terminalSequenceReset(terminal);
break;
// Insert lines.
case 'L':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Delete lines.
case 'M':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Delete characters.
case 'P':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Scroll up.
case 'S':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Scroll down.
case 'T':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Clear screen with normal attribute.
case 'U':
x = terminal->background;
terminal->background = TERMINAL_COLOR_BLACK;
terminalScreenClear(terminal);
terminal->background = x;
terminalCursorMove(terminal, 1, 1);
terminalSequenceReset(terminal);
break;
// Back-TAB.
case 'Z':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Set attributes.
case 'm':
arrput(terminal->parameters, strdup(terminal->number));
for (p=0; p<arrlen(terminal->parameters); p++) {
x = terminalConvertToInt(terminal->parameters[p], 0);
switch (x) {
// Reset.
case 0:
terminal->bold = 0;
terminal->blink = 0;
terminal->reverse = 0;
terminal->foreground = TERMINAL_COLOR_LIGHT_GRAY;
terminal->background = TERMINAL_COLOR_BLACK;
break;
// Bold.
case 1:
terminal->bold = 1;
break;
// Blink slow.
case 5:
terminal->blink = TERMINAL_FLAG_BLINK_SLOW;
break;
// Blink fast.
case 6:
terminal->blink = TERMINAL_FLAG_BLINK_FAST;
break;
// Reverse.
case 7:
if (!terminal->reverse) {
x = terminal->foreground;
terminal->foreground = terminal->background;
terminal->background = x;
terminal->reverse = 1;
}
break;
// Normal intensity. (22 is the actual code, unsure exactly what 21 is.)
case 21:
case 22:
terminal->bold = 0;
break;
// Steady.
case 25:
terminal->blink = 0;
break;
// Un-reverse?
case 27:
if (terminal->reverse) {
x = terminal->foreground;
terminal->foreground = terminal->background;
terminal->background = x;
terminal->reverse = 0;
}
break;
// Color change.
default:
if (x > 29 && x < 38) terminal->foreground = x - 30;
if (x > 39 && x < 48) terminal->background = x - 40;
break;
} // switch (x)
}
terminalSequenceReset(terminal);
break;
// Define scroll region.
case 'r':
//***TODO*** Unimplemented.
terminalSequenceReset(terminal);
break;
// Cursor save.
case 's':
terminal->saveX = terminal->cursorX;
terminal->saveY = terminal->cursorY;
terminalSequenceReset(terminal);
break;
// Cursor restore.
case 'u':
terminalCursorMove(terminal, terminal->saveX, terminal->saveY);
terminalSequenceReset(terminal);
break;
// Something else.
default:
// Number?
if (c >= '0' && c <= '9') {
terminal->number[terminal->numberIndex++] = c;
terminal->number[terminal->numberIndex] = 0;
} else {
// Unknown sequence.
terminalSequenceReset(terminal);
}
} // switch (c)
} else { // inEscape
switch (c) {
// Bell.
case 7:
//***TODO*** Unimplemented.
break;
// Backspace.
case 8:
case 128:
//***TODO*** Unimplemented.
break;
// TAB
case 9:
//***TODO*** Unimplemented.
break;
// Line feed.
case 10:
terminal->cursorY++;
if (terminal->cursorY > terminal->rows) {
terminal->cursorY--;
//***TODO*** Scroll.
}
terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY);
break;
// Clear screen.
case 12:
x = terminal->background;
terminal->background = TERMINAL_COLOR_BLACK;
terminalScreenClear(terminal);
terminal->background = x;
terminalCursorMove(terminal, 1, 1);
break;
// Carrage return.
case 13:
terminalCursorMove(terminal, 1, terminal->cursorY);
break;
// Non-special character.
default:
// Render character.
terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].character = c;
terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].background = terminal->background;
terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].foreground = terminal->foreground + (terminal->bold ? 8 : 0);
terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].flags = 0;
if (terminal->blink == TERMINAL_FLAG_BLINK_SLOW) TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_BLINK_SLOW);
if (terminal->blink == TERMINAL_FLAG_BLINK_FAST) TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_BLINK_FAST);
TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_DIRTY);
GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY);
// Move cursor.
terminal->cursorX++;
if (terminal->cursorX > terminal->cols) {
terminal->cursorX = 1;
terminal->cursorY++;
if (terminal->cursorY > terminal->rows) {
terminal->cursorY--;
//***TODO*** Scroll.
}
}
terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY);
break;
} // switch (c)
} // inEscape
}
void terminalScreenClear(TerminalT *terminal) {
uint16_t x;
uint16_t y;
// Does not move cursor.
// Should this clear the blink flags?
for (y=0; y<terminal->rows; y++) {
for (x=0; x<terminal->cols; x++) {
terminal->cells[x][y].character = ' ';
TERMINAL_CELL_SET_FLAG(terminal, x, y, TERMINAL_FLAG_DIRTY);
}
}
GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY);
}
static void terminalSequenceReset(TerminalT *terminal) {
terminal->escFound = 0;
terminal->inEscape = 0;
terminal->number[0] = 0;
terminal->numberIndex = 0;
if (terminal->parameters != NULL) {
while (arrlen(terminal->parameters) > 0) {
free(arrpop(terminal->parameters));
}
terminal->parameters = NULL;
}
}

97
client/src/gui/terminal.h Normal file
View file

@ -0,0 +1,97 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#ifndef TERMINAL_H
#define TERMINAL_H
#include "gui.h"
#include "widget.h"
enum TerminalColorsE {
TERMINAL_COLOR_BLACK = 0,
TERMINAL_COLOR_BLUE,
TERMINAL_COLOR_GREEN,
TERMINAL_COLOR_CYAN,
TERMINAL_COLOR_RED,
TERMINAL_COLOR_MAGENTA,
TERMINAL_COLOR_BROWN,
TERMINAL_COLOR_LIGHT_GRAY,
TERMINAL_COLOR_DARK_GRAY,
TERMINAL_COLOR_BRIGHT_BLUE,
TERMINAL_COLOR_BRIGHT_GREEN,
TERMINAL_COLOR_BRIGHT_CYAN,
TERMINAL_COLOR_BRIGHT_RED,
TERMINAL_COLOR_BRIGHT_MAGENTA,
TERMINAL_COLOR_YELLOW,
TERMINAL_COLOR_WHITE,
TERMINAL_COLOR_COUNT
};
enum TerminalFlagsE {
TERMINAL_FLAG_DIRTY = 0,
TERMINAL_FLAG_BLINK_FAST, // Must be = 1, don't move!
TERMINAL_FLAG_BLINK_SLOW // Must be = 2, don't move!
};
typedef struct CellS {
uint8_t character;
ColorT foreground;
ColorT background;
uint8_t flags;
} CellT;
typedef struct TerminalS {
WidgetT base; // Must be first in every widget
FontT *font;
uint8_t foreground;
uint8_t background;
uint8_t bold;
uint8_t blink;
uint8_t reverse;
uint16_t cursorX; // Cursor positions are 1 based.
uint16_t cursorY; // Cursor positions are 1 based.
uint16_t saveX; // Cursor positions are 1 based.
uint16_t saveY; // Cursor positions are 1 based.
uint16_t cols;
uint16_t rows;
ColorT palette[TERMINAL_COLOR_COUNT];
CellT **cells;
uint8_t escFound;
uint8_t inEscape;
char number[7]; // Maximum 6 digits / sign.
uint8_t numberIndex;
char **parameters;
} TerminalT;
void terminalBackgroundSet(TerminalT *terminal, uint8_t color);
void terminalCursorMove(TerminalT *terminal, uint16_t col, uint16_t row);
void terminalForegroundSet(TerminalT *terminal, uint8_t color);
WidgetT *terminalInit(WidgetT *widget, uint16_t cols, uint16_t rows);
TerminalT *terminalNew(uint16_t x, uint16_t y, uint16_t cols, uint16_t rows);
void terminalPrint(TerminalT *terminal, char *string);
void terminalPrintChar(TerminalT *terminal, uint8_t c);
void terminalScreenClear(TerminalT *terminal);
#endif // TERMINAL_H

View file

@ -27,14 +27,12 @@
* - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well
* - Metrics, colors, etc. should be defined in each widget and not in GUI
* - Widgets should support a "changed" callback that can cancel the change
* - Add pgup/pgdn to UpDown
* - Add home/end to Textbox
* - Move drawing and surface code into it's own file
* - Use LabelT in all widgets that have a label
* - Find a light grey to replace white widget data areas
* - No thumb in listbox scrollbar
*
* - Random crash after adding listbox
* - Random crash after adding listbox - looks memwatch / task related
*/
@ -61,6 +59,7 @@
#include "textbox.h"
#include "updown.h"
#include "listbox.h"
#include "terminal.h"
void buttonClick(WidgetT *widget) {
@ -138,6 +137,7 @@ void test(void *data) {
WindowT *w1 = NULL;
WindowT *w2 = NULL;
WindowT *w3 = NULL;
WindowT *w4 = NULL;
ButtonT *b1 = NULL;
LabelT *l1 = NULL;
CheckboxT *c1 = NULL;
@ -149,20 +149,26 @@ void test(void *data) {
RadioT *r3b = NULL;
PictureT *p1 = NULL;
FrameT *f1 = NULL;
TextboxT *t1 = NULL;
TextboxT *t2 = NULL;
TextboxT *tb1 = NULL;
TextboxT *tb2 = NULL;
UpdownT *u1 = NULL;
ListboxT *lb1 = NULL;
TerminalT *t1 = NULL;
FILE *in = NULL;
char *buffer = NULL;
uint16_t length = 0;
(void)data;
// Windows
w1 = windowNew(25, 25, 300, 200, "Window 1");
w1 = windowNew(300, 25, 300, 200, "Window 1");
guiAttach(W(desktop), W(w1));
w2 = windowNew(150, 150, 300, 200, "Window 2");
guiAttach(W(desktop), W(w2));
w3 = windowNew(300, 300, 300, 200, "Window 3");
guiAttach(W(desktop), W(w3));
w4 = windowNew(10, 10, 7 + 8 + (80 * 8), 26 + 8 + (24 * 8), "Terminal");
guiAttach(W(desktop), W(w4));
// Window 1
p1 = pictureNew(0, 0, "kanga.png");
@ -196,12 +202,12 @@ void test(void *data) {
guiAttach(W(w2), W(r3b));
radioSetSelected(r1a);
radioSetSelected(r2b);
t1 = textboxNew(10, 60, 265, "Test Textbox");
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));
tb1 = textboxNew(10, 60, 265, "Test Textbox");
textboxSetValue(tb1, "Really long text string to edit!");
guiAttach(W(w2), W(tb1));
tb2 = textboxNew(10, 85, 265, "Test Textbox");
textboxSetValue(tb2, "Short string.");
guiAttach(W(w2), W(tb2));
u1 = updownNew(10, 110, 0, 1024, 5, "UpDown");
guiAttach(W(w2), W(u1));
@ -214,6 +220,20 @@ void test(void *data) {
guiAttach(W(f1), W(l1));
c1 = checkboxNew(10, 65, "Test Checkbox");
guiAttach(W(f1), W(c1));
// Window 4 - Terminal
t1 = terminalNew(0, 0, 80, 24);
guiAttach(W(w4), W(t1));
in = fopen("kanga.ans", "rt");
fseek(in, 0, SEEK_END);
length = ftell(in);
fseek(in, 0, SEEK_SET);
buffer = (char *)malloc(length + 1);
fread(buffer, 1, length, in);
fclose(in);
buffer[length] = 0;
terminalPrint(t1, buffer);
free(buffer);
}

48
client/src/thirdparty/serial/README.md vendored Normal file
View file

@ -0,0 +1,48 @@
DOS Serial Library
==================
A serial port (UART) library for DOS written back in the days when 16-bit
systems were still a going concern and far pointers were a thing.
I'm putting it on Github because I still think it's way cool, and who knows...
Perhaps someone, somewhere, is in dire need of a serial library for DOS (I've
needed it from time to time to deal with some DOS based embedded piece of crap).
Supports simultaneous communication over up to 4 serial ports.
Have a look at term.c for an example of how to use it.
How do I build it?
==================
Good question! It was built to compile under Borland C++ 3 and whatever
Microsoft compiler happened to be current at the time. It conforms to ANSI C
(1989) and it doesn't do anything too crazy, so it shouldn't be too bad in a
more modern compiler. Have a go and let me know how it works!
License
=======
Copyright 1998 Karl Stenerud
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1647
client/src/thirdparty/serial/serial.c vendored Normal file

File diff suppressed because it is too large Load diff

195
client/src/thirdparty/serial/serial.h vendored Normal file
View file

@ -0,0 +1,195 @@
/* Serial Library 1.4 (22-Jun-2000) (c) 1998 Karl Stenerud
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SERIAL__HEADER
#define SERIAL__HEADER
/* COM Ports */
#define COM_1 0
#define COM_2 1
#define COM_3 2
#define COM_4 3
/* Handshaking Modes */
#define SER_HANDSHAKING_NONE 0
#define SER_HANDSHAKING_XONXOFF 1
#define SER_HANDSHAKING_RTSCTS 2
#define SER_HANDSHAKING_DTRDSR 3
/* Error Codes */
#define SER_SUCCESS 0 /* Function completed successfully */
#define SER_ERR_UNKNOWN -1 /* An unknown error occured */
#define SER_ERR_NOT_OPEN -2 /* The specified COM port is not opened */
#define SER_ERR_ALREADY_OPEN -3 /* The specified COM port is already opened */
#define SER_ERR_NO_UART -4 /* Could not find a UART for this COM port */
#define SER_ERR_INVALID_COMPORT -5 /* User specified an invalid COM port */
#define SER_ERR_INVALID_BASE -6 /* User specified an invalid base address */
#define SER_ERR_INVALID_IRQ -7 /* User specified an invalid IRQ number */
#define SER_ERR_INVALID_BPS -8 /* User specified an invalid BPS rate */
#define SER_ERR_INVALID_DATA_BITS -9 /* User specified an invalid number of data bits */
#define SER_ERR_INVALID_PARITY -10 /* User specified an invalid parity type */
#define SER_ERR_INVALID_STOP_BITS -11 /* User specified an invalid number of stop bits */
#define SER_ERR_INVALID_HANDSHAKING -12 /* User specified an invalid handshaking type */
#define SER_ERR_INVALID_FIFO_THRESHOLD -13 /* User specified an invalid fifo threshold value */
#define SER_ERR_NULL_PTR -14 /* User specified a buffer address that was NULL */
#define SER_ERR_IRQ_NOT_FOUND -15 /* Could not find an IRQ for the specified COM port */
#define SER_ERR_LOCK_MEM -16 /* Could not lock memory in DPMI mode */
/* Name: serial_open()
*
* Desc: Finds and initializes the specified COM port.
*
* Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4)
* long bps: Bits per second.
* (50, 75, 110, 150, 300, 600, 1200, 2400, 4800,
* 9600, 19200, 38400, 57600, 115200)
* int data_bits: Number of data word bits (5-8)
* char parity: Parity mode ('e', 'o', 'n', 'm', 's')
* int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2,
* actual stop bits is 1.5.
* int handshaking: Handshaking mode
* (SER_HANDSHAKING_NONE, SER_HANDSHAKING_XONXOFF,
* SER_HANDSHAKING_RTSCTS, SER_HANDSHAKING_DTRDSR)
*
* Return: SER_SUCCESS or an error code
*/
int serial_open(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking);
/* Name: serial_close()
*
* Desc: Clean up and close the specified serial port.
*
* Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4)
*
* Return: SER_SUCCESS or an error code
*/
int serial_close(int com);
/* Name: serial_read()
*
* Desc: Read data from the specified serial port buffer.
*
* Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4)
* char* data: Pointer to data buffer
* int len: Number of bytes to read
*
* Return: number of bytes read or an error code
*/
int serial_read(int com, char* data, int len);
/* Name: serial_write() and serial_write_buffered()
*
* Desc: Write data to the serial port.
* serial_write() will write the data directly to serial port and will
* block until it has completely sent the data or handshaking has
* stopped output transmission while serial_write_buffered() will copy
* as much as possible data bytes to the transmit buffer and will return
* immediately enabling asynchronous output.
*
* Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4)
* char* data: Pointer to data buffer
* int len: Number of bytes to write
*
* Return: number of bytes written or an error code
*/
int serial_write (int com, const char* data, int len);
int serial_write_buffered(int com, const char* data, int len);
/* Name: serial_set()
*
* Desc: Change the specified serial port's settings.
*
* Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4)
* long bps: Bits per second.
* (50, 75, 110, 150, 300, 600, 1200, 2400, 4800,
* 9600, 19200, 38400, 57600, 115200)
* int data_bits: Data bits (5, 6, 7, or 8)
* char parity: Parity ('n' = none, 'e' = even, 'o' = odd, 'm' = mark, 's' = space)
* int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2,
* actual stop bits is 1.5.
* int handshaking: Handshaking type (see handshaking modes at top of file)
*
* Return: SER_SUCCESS or an error code
*/
int serial_set(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking);
/* serial_set_xxx() functions. These take the same arguments
* as the corresponding ones in serial_set() and return either
* SER_SUCCESS or an error code.
*/
int serial_set_bps (int com, long bps);
int serial_set_parity (int com, char parity);
int serial_set_data (int com, int data);
int serial_set_stop (int com, int stop);
int serial_set_handshaking (int com, int handshaking);
/* Advanced settings. Use these only if you know what you are doing. */
/* UART's FIFO threshold -- 14, 8, 4, 1, or 0 (off) */
int serial_set_fifo_threshold(int com, int threshold);
/* UART's base address */
int serial_set_base (int com, int base);
/* UART's IRQ */
int serial_set_irq (int com, int base);
int serial_set_rts(int comport, int rts);
int serial_set_dtr(int comport, int dtr);
int serial_set_mcr(int comport, int mcr);
/* serial_get_xxx() functions. These return the same types as are supplied
* to serial_set(). The returned type may be negative, in which case it
* is an error code.
*/
int serial_get_base (int com);
int serial_get_irq (int com);
long serial_get_bps (int com);
char serial_get_parity (int com);
int serial_get_data (int com);
int serial_get_stop (int com);
int serial_get_handshaking (int com);
int serial_get_rts(int comport);
int serial_get_dtr(int comport);
int serial_get_cts(int comport);
int serial_get_dsr(int comport);
int serial_get_mcr(int comport);
int serial_get_msr(int comport);
int serial_get_lsr(int comport);
/* get number of bytes or discard data in TX/RX buffers
*/
int serial_get_tx_buffered(int comport);
int serial_get_rx_buffered(int comport);
int serial_clear_tx_buffer(int comport);
int serial_clear_rx_buffer(int comport);
#endif

104
client/src/thirdparty/serial/term.C vendored Normal file
View file

@ -0,0 +1,104 @@
/* A simple terminal program for testing the serial library by Karl Stenerud
*/
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include "serial.h"
#include <dos.h>
char* errors[] =
{
"Successful",
"Unknown error",
"Port not open",
"Port already open",
"No UART found on that comport",
"Invalid comport",
"Invalid BPS",
"Invalid data bits",
"Invalid parity",
"Invalid stop bits",
"Invalid handshaking",
"Invalid fifo threshold",
"Passed in a NULL pointer",
"",
"",
""
};
char* handshaking[] =
{
"none",
"xon/xoff",
"rts/cts",
"dtr/dsr",
"",
""
};
int main(int argc, char* argv[])
{
int len;
char buff[100];
char ch;
int rc;
int com = COM_1;
if(argc < 2)
{
printf("Usage: term <com number>\n");
return 0;
}
switch(atoi(argv[1]))
{
case 1:
com = COM_1;
break;
case 2:
com = COM_2;
break;
case 3:
com = COM_3;
break;
case 4:
com = COM_4;
break;
default:
printf("%s: invalid com port number\n", argv[1]);
return 0;
}
if((rc=serial_open(com, 19200L, 8, 'n', 1, SER_HANDSHAKING_XONXOFF)) != SER_SUCCESS)
{
printf("Can't open port! (%s)\n", errors[-rc]);
return 0;
}
printf("Opened COM%d. Base 0x%03x, IRQ %d, %ld %d%c%d, Handshaking %s\n", com+1,
serial_get_base(com), serial_get_irq(com), serial_get_bps(com), serial_get_data(com),
(char)serial_get_parity(com), serial_get_stop(com), handshaking[serial_get_handshaking(com)]);
for(;;)
{
if(kbhit())
{
if((ch=(char)getch()) == 0x1b)
break;
if((rc=serial_write(com, &ch, 1)) < 0)
printf("Error writing (%s)\n", errors[-rc]);
}
if((len=serial_read(com, buff, 100)) > 0)
write(1, buff, len);
else if(len < 0)
printf("Error reading (%s)\n", errors[-len]);
}
if((rc=serial_close(com)) != SER_SUCCESS)
printf("Can't close serial port! (%s)\n", errors[-rc]);
return 0;
}