2023-05-20 04:13:13 +09:00

969 lines
33 KiB
HolyC
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Telnet client for ZealOS by y4my4m
// Public Domain
Cd(__DIR__);;
#define TELNET_PORT 23
// #define BUF_SIZE 8192 // way too big?
#define BUF_SIZE 819200 // way too big?
#define INPUT_BUF_SIZE 32
#define TIMEOUT_DURATION 500000
#define NEGOTIATE 0xFF
#define ANSI_ESC 0x1B
#define ANSI_CSI 0x5B // [
#define MAX_ANSI_PARAMS 32
#include "TelnetClass"
#include "TelnetHelpers"
// If you're using a custom palette, the colors might not seem right.
CBGR24 original_palette[COLORS_NUM];
Bool dark_mode = TRUE; // since ZealOS is dark by default
Bool original_colors = FALSE;
Bool force_disconnect = FALSE;
I64 TelnetOpen(U8 *host, U16 port) {
I64 sock;
if (host == NULL) {
return -1;
}
DocPrint(term.doc, "$$GREEN$$Connecting to %s:%d.$$FG$$$$BG$$\n", host, port);
sock = TCPConnectionCreate(host, port);
if (sock <= 0) {
// PrintErr("Failed to connect to %s:%d\n", host, port);
PopUpOk("\n\n\tFailed to connect\n\n");
return sock;
}
// sock(CTCPSocket *)->timeout = 0;
// sock(CTCPSocket *)->timeout = TCP_TIMEOUT;
return sock;
}
U0 HandleControlCodes(U8 ch) {
if (ch < 32) { // ASCII code below 32 (control character)
switch (ch) {
case 0: // NUL (Null) - Typically ignored
break;
case 7: // BEL (Bell)
Beep;
break;
case 8: // BS (Backspace)
DocPrint(term.doc, "$$CM,-1,0$$");
break;
case 9: // HT (Horizontal Tab)
DocPrint(term.doc, "$$CM,8,0$$");
break;
case 10: // LF (Line Feed)
// DocPrint(term.doc, "\n");
break;
case 11: // VT (Vertical Tab)
SysLog("Vertical Tab\n");
break;
case 12: // FF (Form Feed)
// SysLog("form feed\n");
DocClear(term.doc);
// DocPrint(term.doc, "\f");
break;
case 13: // CR (Carriage Return)
DocPrint(term.doc, "\r");
// DocPrint(term.doc, "$$CM+LX+PRY,LE=0,RE=1$$");
break;
case 14: // SO (Shift Out) - Switch to an alternate character set
case 15: // SI (Shift In) - Switch back to the default character set
SysLog("Shift In/Out\n");
break;
case 22:
SysLog("Synchronous Idle\n");
break;
case 23:
SysLog("End of Transmission Block\n");
break;
case 24:
SysLog("Cancel\n");
break;
case 25:
SysLog("End of Medium\n");
break;
case 26:
SysLog("Sub\n");
break;
case 27:
SysLog("Esc\n");
break;
case 28:
SysLog("Fs\n");
break;
case 29:
SysLog("Gs\n");
break;
case 30:
SysLog("Rs\n");
break;
case 31:
SysLog("Unit Separator\n");
break;
default:
// some ch make Zeal crash or behave weird because they're commands?
// SysLog("CC %c happened\n", ch);
SysLog("CC 0x%X happened\n", ch);
break;
}
}
else {
if (ch == 127) {
SysLog("case 127");
}
if (ch == 0x24) {
DocPrint(term.doc, "$$$$");
}
if (ch >= 32 && ch < 256) // ZealOS's ASCII is up to 255
{
DocPrint(term.doc, "%c", ch);
}
else {
DocPrint(term.doc, "?"); // unrecognized character
}
}
}
U0 ANSIParse()
{
// Basic Telnet protocol parser
U8 *ptr = term.buffer;
while (ptr < term.buffer + term.buffer_len) {
// disable all SAUCE00 art signature? dsnt work
// if (StrNCompare(ptr, "\033SAUCE", 6) == 0)
// {
// SysLog("SAUCE found\n");
// term.buffer_len = ptr - term.buffer;
// }
// Telnet negotiation sequence
if (*ptr == NEGOTIATE) {
/*telnet negotiation seems proper...however i don't really see any BBS systems relying on this too much...
for instance, the screen size tends to be reported using the Curser Report and not Telnet's NAWS */
if (term.sock_ready) TelnetNegotiate(term.sock, ptr);
ptr += 3;
}
else if (*ptr == ANSI_ESC) {
// ANSI escape sequence
ptr++;
if (*ptr == ANSI_CSI) {
ptr++;
I64 ansi_code[MAX_ANSI_PARAMS], counter;
for (counter = 0; counter < MAX_ANSI_PARAMS; counter++) {
ansi_code[counter] = 0; // Initialize all elements to 0
}
I64 ansi_param_count = 0;
while (*ptr != '\0') {
if (IsDigit(*ptr)) {
ansi_code[ansi_param_count] = ansi_code[ansi_param_count] * 10 + (*ptr - '0');
ptr++;
}
else if (*ptr == ';') {
ansi_param_count++;
ptr++;
if (!IsDigit(*ptr) && *ptr != ';') {
break;
}
}
else {
// If the next character is not a digit or semicolon, break out
break;
}
}
// Handle specific ANSI escape sequences
switch (*ptr) {
case 'n':
SysLog("Case n, %d\n",ansi_code[0]);
if (ansi_code[0] == 5) {
// Respond with terminal readiness
SysLog("reported terminal readiness\n");
U8 deviceStatusResponse[5];
deviceStatusResponse[0] = ANSI_ESC;
deviceStatusResponse[1] = ANSI_CSI;
deviceStatusResponse[2] = 0x30; // '0'
deviceStatusResponse[3] = 0x6E; // 'n'
deviceStatusResponse[4] = 0x00; // Null-terminator
if (term.sock_ready) TCPSocketSend(term.sock, deviceStatusResponse, 4);
}
else if (ansi_code[0] == 6) {
// Respond with cursor position
SysLog("reported cursor position\n");
// TODO: position 24rows x 80cols is hardcoded, should actually report the real cursor position
// U8 cursorResponse[9] = "\x1B[24;80R";
U8 cursorResponse[9];
cursorResponse[0] = ANSI_ESC;
cursorResponse[1] = '['; // Start of CSI
cursorResponse[2] = '2'; // First digit of "24"
cursorResponse[3] = '4'; // Second digit of "24"
cursorResponse[4] = ';'; // Separator
cursorResponse[5] = '8'; // First digit of "80"
cursorResponse[6] = '0'; // Second digit of "80"
cursorResponse[7] = 'R'; // End of CPR
cursorResponse[8] = 0x00; // Null-terminator
if (term.sock_ready) TCPSocketSend(term.sock, cursorResponse, 9);
}
else if (ansi_code[0] == 255) {
// https://github.com/NuSkooler/enigma-bbs/blob/97cd0c3063b0c9f93a0fa4a44a85318ca81aef43/core/ansi_term.js#L140
SysLog("TODO: reported screensize?\n");
// SendWindowSize(term.sock, 80, 25);
}
ptr++;
break;
case 'c':
// Respond with device attributes
SysLog("reported device attributes\n");
// Reports at VT101 (not sure why though)
U8 deviceAttributesResponse[8];
deviceAttributesResponse[0] = ANSI_ESC;
deviceAttributesResponse[1] = ANSI_CSI;
deviceAttributesResponse[2] = 0x3F; // '?'
deviceAttributesResponse[3] = 0x31; // '1'
deviceAttributesResponse[4] = 0x3B; // ';'
deviceAttributesResponse[5] = 0x32; // '0'
deviceAttributesResponse[6] = 0x63; // 'c'
deviceAttributesResponse[7] = 0x00; // Null-terminator
if (term.sock_ready) TCPSocketSend(term.sock, deviceAttributesResponse, 7);
ptr++;
break;
case 'm':
// this is where colors are being set
// TODO: what happens in this case??? --> [0;1;34;44m
I64 m;
Bool isBright = FALSE;
for (m = 0; m <= ansi_param_count; m++) {
if (ansi_code[m] <= 10) {
switch (ansi_code[m]) {
case 0:
if (dark_mode)
DocPrint(term.doc, "$$BG,WHITE$$$$BLACK$$");
else
DocPrint(term.doc, "$$BG$$$$FG$$");
isBright = FALSE;
break; // reset
case 1: isBright = TRUE; break;
case 2: isBright = FALSE; break;
default: break;
}
}
else if ((ansi_code[m] >= 30 && ansi_code[m] <= 39) || (ansi_code[m] >= 90 && ansi_code[m] <= 97)) {
// Set foreground color
// SysLog("ansi_code[%d] = %d\n", m, ansi_code[m]);
if(!isBright){
switch (ansi_code[m]) {
case 30:
if (dark_mode) DocPrint(term.doc, "$$WHITE$$");
else DocPrint(term.doc, "$$BLACK$$");
break;
case 31:
DocPrint(term.doc, "$$RED$$");
break;
case 32:
DocPrint(term.doc, "$$GREEN$$");
break;
case 33:
DocPrint(term.doc, "$$YELLOW$$");
break;
case 34:
DocPrint(term.doc, "$$BLUE$$");
break;
case 35:
DocPrint(term.doc, "$$PURPLE$$");
break;
case 36:
DocPrint(term.doc, "$$CYAN$$");
break;
case 37:
if (dark_mode) DocPrint(term.doc, "$$BLACK$$");
else DocPrint(term.doc, "$$WHITE$$");
break;
case 39:
if (dark_mode) DocPrint(term.doc, "$$WHITE$$");
else DocPrint(term.doc, "$$FG$$");
break;
default: break;
}
}
else {
switch (ansi_code[m]) {
case 90:
case 30:
if (dark_mode) DocPrint(term.doc, "$$LTGRAY$$");
else DocPrint(term.doc, "$$DKGRAY$$");
break;
case 91:
case 31:
DocPrint(term.doc, "$$LTRED$$");
break;
case 92:
case 32:
DocPrint(term.doc, "$$LTGREEN$$");
break;
case 93:
case 33:
DocPrint(term.doc, "$$YELLOW$$");
break;
case 94:
case 34:
DocPrint(term.doc, "$$LTBLUE$$");
break;
case 95:
case 35:
DocPrint(term.doc, "$$LTPURPLE$$");
break;
case 96:
case 36:
DocPrint(term.doc, "$$LTCYAN$$");
break;
case 97:
case 37:
if (dark_mode) DocPrint(term.doc, "$$DKGRAY$$");
else DocPrint(term.doc, "$$LTGRAY$$");
break;
case 39:
if (dark_mode) DocPrint(term.doc, "$$WHITE$$");
else DocPrint(term.doc, "$$FG$$");
break;
default: break;
}
}
}
// this is a dumb approach, just do a CatPrint or something
// until we properly catch the `;` it will stay buggy
else if ((ansi_code[m] >= 40 && ansi_code[m] <= 49) || (ansi_code[m] >= 100 && ansi_code[m] <= 107)) {
// Set background color
// SysLog("ansi_code[%d] = %d\n", m, ansi_code[m]);
if(!isBright){
switch (ansi_code[m]) {
case 40:
if (dark_mode) DocPrint(term.doc, "$$BG,WHITE$$");
else DocPrint(term.doc, "$$BG,BLACK$$");
break;
case 41:
DocPrint(term.doc,"$$BG,RED$$");
break;
case 42:
DocPrint(term.doc,"$$BG,GREEN$$");
break;
case 43:
DocPrint(term.doc,"$$BG,YELLOW$$");
break;
case 44:
DocPrint(term.doc,"$$BG,BLUE$$");
break;
case 45:
DocPrint(term.doc,"$$BG,PURPLE$$");
break;
case 46:
DocPrint(term.doc,"$$BG,CYAN$$");
break;
case 47:
if (dark_mode) DocPrint(term.doc, "$$BG,BLACK$$");
else DocPrint(term.doc, "$$BG,WHITE$$");
break;
case 49:
if (dark_mode) DocPrint(term.doc, "$$BG,WHITE$$");
else DocPrint(term.doc, "$$BG$$");
break;
default: break;
}
}
else {
switch (ansi_code[m]) {
case 100:
case 40:
if (dark_mode) DocPrint(term.doc, "$$BG,LTGRAY$$");
else DocPrint(term.doc, "$$BG,DKGRAY$$");
break;
case 101:
case 41:
DocPrint(term.doc,"$$BG,LTRED$$");
break;
case 102:
case 42:
DocPrint(term.doc,"$$BG,LTGREEN$$");
break;
case 103:
case 43:
DocPrint(term.doc,"$$BG,YELLOW$$");
break;
case 104:
case 44:
DocPrint(term.doc,"$$BG,LTBLUE$$");
break;
case 105:
case 45:
DocPrint(term.doc,"$$BG,LTPURPLE$$");
break;
case 106:
case 46:
DocPrint(term.doc,"$$BG,LTCYAN$$");
break;
case 107:
case 47:
if (dark_mode) DocPrint(term.doc, "$$BG,DKGRAY$$");
else DocPrint(term.doc, "$$BG,LTGRAY$$");
break;
case 49:
if (dark_mode) DocPrint(term.doc, "$$BG,LTGRAY$$");
else DocPrint(term.doc, "$$BG$$");
break;
// reset
default: break;
}
}
}
}
ptr++;
break;
case 'A':
// Cursor Up
SysLog("Cursor Up\n");
// "$$CM+TY,0,-%d$$", ansi_code[0];
DocPrint(term.doc, "$$CM,0,-%d$$", ansi_code[0]);
ptr++;
break;
case 'B':
// Cursor Down
SysLog("Cursor Down\n");
DocPrint(term.doc, "$$CM,0,%d$$", ansi_code[0]);
ptr++;
break;
case 'C':
// Cursor Right
// SysLog("Cursor Right %d %d\n", ansi_param_count, ansi_code[0]);
// DocPrint(term.doc, "$$CM,%d,0$$", ansi_code[0]);
// seems to be less prone to bugs?
I64 cr;
for (cr=0; cr<ansi_code[0]; cr++){
DocPrint(term.doc, " ");
}
ptr++;
break;
case 'D':
// Cursor Left
SysLog("Cursor Left\n");
DocPrint(term.doc, "$$CM,-%d,0$$", ansi_code[0]);
ptr++;
break;
case 'E':
// Cursor Next Line
SysLog("Cursor Next Line\n");
DocPrint(term.doc, "\n");
ptr++;
break;
case 'F':
// Cursor Previous Line
SysLog("Cursor Previous Line\n");
DocPrint(term.doc, "$$CM+LY,0,-%d$$", ansi_code[0]);
ptr++;
break;
case 'G':
// Cursor Horizontal Absolute
SysLog("Cursor Horizontal Absolute\n");
DocPrint(term.doc, "$$CM,%d,0$$", ansi_code[0]);
ptr++;
break;
case 'H':
case 'f':
I64 row = 1, col = 1; // default values
// Parse the row number
if(ansi_code[0] != 1)
row = ansi_code[0];
if(ansi_code[1] != 1)
col = ansi_code[1];
// If we're already at the right position, no need to move
// if (row == term.current_row && col == term.current_col) {
// ptr++;
// break;
// }
// SysLog("H or f row:%d, col:%d, cnt:%d\n", row, col, ansi_param_count);
// Adjust the position based on the window size
if (row >= term.window_height) {
row = term.window_height - 1;
} else if (row < 1) {
row = 1;
}
if (col >= term.window_width) {
col = term.window_width - 1;
} else if (col < 1) {
col = 1;
}
// if (row == term.window_height) {
// term.current_row = row;
// term.current_col = col;
// DocPrint(term.doc, "$$CM+LX+TY,LE=%d,RE=%d$$", term.current_col-1, term.current_row-1);
// DocPrint(term.doc, "\n");
// ptr++;
// break;
// }
// If row or col are at their max value, reset the current position to 1
// if (row == term.window_height || col == term.window_width) {
// if (row == term.window_height) term.current_row = 1;
// if (col == term.window_width) term.current_col = 1;
// ptr++;
// break;
// }
term.current_row = row;
term.current_col = col;
DocPrint(term.doc, "$$CM+LX+TY,LE=%d,RE=%d$$", term.current_col-1, term.current_row-1);
//DocCursorPosSet(term.doc, col, row);
ptr++;
break;
case 'J':
SysLog("J code, %d %d\n", ansi_param_count, ansi_code[0]);
// Erase in Display
if (ansi_code[0] == 0) {
// Erase from cursor to end of display
// DocDelToNum(Fs->display_doc, Fs->display_doc->cur_entry->line_num);
} else if (ansi_code[0] == 1) {
// Erase from cursor to beginning of display
// DocDelToEntry(Fs->display_doc, Fs->display_doc->cur_entry, FALSE);
} else if (ansi_code[0] == 2) {
// Erase entire display
DocClear(term.doc);
// Clear the buffer
// term.buffer_len = 0;
// MemSet(term.buffer, 0, BUF_SIZE);
// Sleep(100);
}
ptr++;
break;
case 'K':
// TODO: I have no idea if this actually works
// if (ansi_param_count == 0 || ansi_code[0] == 0) {
// // Erase from cursor to end of line
// LineDeleteToEnd(term.doc->cur_entry, doc.term->cur_col);
// } else if (ansi_code[0] == 1) {
// // Erase from cursor to beginning of line
// LineDeleteToStart(term.doc->cur_entry, doc.term->cur_col);
// } else if (ansi_code[0] == 2) {
// // Erase entire line
// LineDeleteEntire(term.doc->cur_entry);
// }
ptr++;
break;
case 'L':
SysLog("L code\n");
// DocPrint(term.doc, "\n");
ptr++;
break;
case 'S':
// TODO: Scroll Up
SysLog("Scroll Up");
ptr++;
break;
case 'T':
// TODO: Scroll Down
SysLog("Scroll Down");
ptr++;
break;
case 'M':
SysLog("Case M\n");
// TODO: is this correct? cursor should go one line up
DocPrint(term.doc, "$$CM,0,-1$$");
ptr++;
break;
case '?':
ptr++;
I64 code = 0;
while (IsDigit(*ptr)) {
code = code * 10 + (*ptr - '0');
ptr++;
}
switch (code) {
case 25:
// need to specify which doc?
if (*ptr == 'l') DocCursor(OFF); // Hide cursor
if (*ptr == 'h') DocCursor(ON); // Show cursor
ptr++; // Move past 'l' or 'h'
break;
case 47:
if (*ptr == 'l') SysLog("code 47l\n"); // restore screen
if (*ptr == 'h') SysLog("code 47h\n"); // save screen
ptr++; // Move past 'l' or 'h'
break;
case 1049:
if (*ptr == 'l') SysLog("code 1049l\n"); // enables the alternative buffer
if (*ptr == 'h') SysLog("code 1049h\n"); // disables the alternative buffer
ptr++; // Move past 'l' or 'h'
break;
default:
ptr++;
break;
}
break;
case 's':
SysLog("SaveCurrentCursorPosition\n");
ptr++;
break;
case 'u':
SysLog("RestoreCurrentCursorPosition\n");
ptr++;
break;
case 'r':
// self.restoreCursorPosition();
SysLog("r case \n");
ptr++;
break;
case 'h':
case 'l':
// TODO: Handle 'h' (set mode) or 'l' (reset mode) codes
SysLog("h or l case \n");
ptr++; // Skip 'h' or 'l'
break;
case 't':
// for (m = 0; m <= ansi_param_count; m++) {
SysLog("ansi_code[%d]: %d\n", m, ansi_code[m]);
// }
if (ansi_param_count == 3) {
if (ansi_code[0] == 8) {
term.window_width = ansi_code[1];
term.window_height = ansi_code[2];
Fs->win_width = term.window_width;
WinHorz((TEXT_COLS / 2) - (Fs->win_width / 2),
(TEXT_COLS / 2) - (Fs->win_width / 2) +
(Fs->win_width - 1),
Fs);
Fs->win_height = term.window_height;
WinVert((TEXT_ROWS / 2) - (Fs->win_height / 2),
(TEXT_ROWS / 2) - (Fs->win_height / 2) +
(Fs->win_height - 1),
Fs);
}
}
ptr++;
break;
case '=':
SysLog("ScreenMode attempt\n");
ptr++;
break;
default:
// if(!IsDigit(*ptr)) {
if(*ptr > 32) {
SysLog("Unknown code: 0x%X\n", *ptr);
}
ptr++;
break;
}
}
}
else {
// Print the received character
HandleControlCodes(*ptr);
ptr++;
}
}
}
U0 TerminalTask() {
while (!term.sock_ready) {
Sleep(100); // Avoid busy waiting
}
while (term.sock_ready && !force_disconnect) {
receive_data:
term.buffer_len = TCPSocketReceive(term.sock, term.buffer, BUF_SIZE - 1);
if (term.buffer_len > 0) {
term.buffer[term.buffer_len] = '\0';
// parse the buffer
ANSIParse;
} else {
//SysLog("BUF_SIZE: %d\n", BUF_SIZE);
if (!term.sock_ready || force_disconnect)
DocPrint(term.doc, "Error: Connection closed by the remote host.\n");
else {
SysLog("goto received_data\n");
goto receive_data;
}
break;
}
}
}
U0 Telnet(U8 *host=NULL, U16 port=TELNET_PORT) {
CHostForm form;
term.window_width = 80;
term.window_height = 25;
term.doc = Fs->display_doc;
term.waiting_for_input = TRUE;
term.sock_ready = 0;
I64 art_path = "::/Home/Net/Programs/Telnet/Art/TelnetSplash.ans";
I64 message_code, arg1, arg2;
GrPaletteGet(original_palette);
AutoComplete(OFF);
SettingsPush;
MenuPush(
"Telnet {"
" Connect(,CH_CTRLN);"
" Exit(,CH_SHIFT_ESC);"
"}"
"Load {"
" ANSIArt(,CH_CTRLO);"
"}"
"Config {"
" ToggleDarkMode(,CH_CTRLD);"
" SetDarkColors(,CH_CTRLT);"
"}"
"About {"
" Info(,CH_CTRLL);"
"}"
);
StrCopy(Fs->task_title, "TELNET");
Fs->border_src = BDS_CONST;
Fs->border_attr = LTGREEN << 4 + DriveTextAttrGet(':') & 15;
if (dark_mode) Fs->text_attr = WHITE << 4 + BLACK;
else Fs->text_attr = BLACK << 4 + WHITE;
Fs->title_src = TTS_LOCKED_CONST;
DocClear(Fs->border_doc, TRUE);
Fs->win_width = term.window_width;
WinHorz((TEXT_COLS / 2) - (Fs->win_width / 2),
(TEXT_COLS / 2) - (Fs->win_width / 2) +
(Fs->win_width - 1),
Fs);
Fs->win_height = term.window_height;
WinVert((TEXT_ROWS / 2) - (Fs->win_height / 2),
(TEXT_ROWS / 2) - (Fs->win_height / 2) +
(Fs->win_height - 1),
Fs);
show_splash:
// SplashScreen
DocMax;
DocClear;
// probably should use word wrap?
DocPrint(, "$$WW,1$$");
DocCursor(OFF);
// Load the file into the buffer and get its size
term.buffer_len = ANSIArtLoad(art_path, term.buffer);
if (term.buffer_len > 0) {
term.buffer[term.buffer_len] = '\0';
// parse the buffer
ANSIParse;
// Free(term.buffer);
}
else {
Print("Error: Could not load splash screen.\n");
}
init_connection:
while (!term.waiting_for_input || host != NULL)
{
// if(term.sock != NULL) TCPSocketClose(term.sock);
// TODO: should probably kill the task if it already exists too?
// Spawn a task to receive data from the socket
term.task = Spawn(&TerminalTask, NULL, "Telnet");
term.sock = TelnetOpen(host, port);
if (term.sock <= 0) {
// return;
term.waiting_for_input = TRUE;
host = NULL;
goto show_splash;
}
term.sock_ready = 1; // Signal that the socket is ready
term.waiting_for_input = FALSE;
"$$BG,GREEN$$$$WHITE$$Connected$$FG$$$$BG$$\n";
Sleep(1000);
DocClear;
break;
}
I64 sc;
try
{
while (!force_disconnect) {
U8 key = KeyGet(&sc);
// fix the bug of holding Ctrl triggers all the CH_CTRL, needs to be handled like in Demo/Graphics/FontEd.ZC:104
switch (key)
{
case 0:
switch (sc.u8[0])
{
case SC_CURSOR_LEFT:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x1B[D");
break;
case SC_CURSOR_RIGHT:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x1B[C");
break;
case SC_CURSOR_UP:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x1B[A");
break;
case SC_CURSOR_DOWN:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x1B[B");
break;
default:
break;
}
break;
case 9:
switch (sc.u8[0])
{
case SC_TAB:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x09");
break;
default:
break;
}
case 255:
switch (sc.u8[0])
{
case SC_PAGE_UP: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[V"); break;
case SC_PAGE_DOWN:if (term.sock_ready) TCPSocketSendString(term.sock, "\033[U"); break;
case SC_HOME: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[H"); break;
case SC_END: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[K"); break;
case SC_DELETE: if (term.sock_ready) TCPSocketSendString(term.sock, "\x7F"); break;
case SC_F1: if (term.sock_ready) TCPSocketSendString(term.sock, "\033OP"); break;
case SC_F2: if (term.sock_ready) TCPSocketSendString(term.sock, "\033OQ"); break;
case SC_F3: if (term.sock_ready) TCPSocketSendString(term.sock, "\033OR"); break;
case SC_F4: if (term.sock_ready) TCPSocketSendString(term.sock, "\033OS"); break;
case SC_F5: if (term.sock_ready) TCPSocketSendString(term.sock, "\033Ot"); break;
case SC_F6: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[17~"); break;
case SC_F7: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[18~"); break;
case SC_F8: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[19~"); break;
case SC_F9: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[20~"); break;
case SC_F10: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[21~"); break;
case SC_F11: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[23~"); break;
case SC_F12: if (term.sock_ready) TCPSocketSendString(term.sock, "\033[24~"); break;
default:
break;
}
case CH_BACKSPACE:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x08\x7F");
break;
case CH_ESC:
if (term.sock_ready) TCPSocketSendString(term.sock, "\x1B");
break;
case CH_SHIFT_ESC:
if (term.sock_ready)
{
if (term.sock_ready) TCPSocketClose(term.sock);
host = NULL;
term.sock_ready = 0;
term.waiting_for_input = TRUE;
"Telnet connection closed.\n";
Sleep(100);
goto show_splash;
}
else
force_disconnect = TRUE;
break;
// send buffer on enter
case '\n':
if (term.sock_ready) TCPSocketSendString(term.sock, "\r\n");
break;
case CH_CTRLN:
TelnetPrompt(&form);
host = form.host;
port = form.port;
DocClear;
term.waiting_for_input = FALSE;
goto init_connection;
break;
case CH_CTRLO:
art_path = ANSIArtBrowser;
if (art_path != NULL) {
term.sock_ready = 0;
term.waiting_for_input = TRUE;
// SysLog("%s\n", art_path);
goto show_splash;
}
else {
"Error: Could not load art.\n";
}
break;
case CH_CTRLS:
// Save as DD image.
StrCopy(term.doc->filename.name, "::/Home/Wallpapers/1024/Default.DD");
DocWrite(term.doc, TRUE);
break;
case CH_CTRLD:
dark_mode = !dark_mode;
if (dark_mode) Fs->text_attr = WHITE << 4 + BLACK;
else Fs->text_attr = BLACK << 4 + WHITE;
break;
case CH_CTRLT:
original_colors = !original_colors;
if (original_colors) PaletteSetStd(FALSE);
else {
GrPaletteSet(original_palette);
LFBFlush;
}
break;
case CH_CTRLU:
if(term.sock_ready) TCPSocketSendString(term.sock, "\x15");
break;
// case CH_CTRLP:
// DocFlagsToggle(doc, DOCF_PLAIN_TEXT);
case CH_CTRLL:
PopUpOk("\n\n Not all BBS will work perfectly (yet). \n\n You can load ANSi artwork with Ctrl+O","\n\n\n\t\tMade by y4my4m\n\n");
break;
default:
if (key >= ' ' && key <= '~') {
// Handle regular keys
U8 input_buf[2];
input_buf[0] = key;
input_buf[1] = '\0';
if (term.sock_ready) TCPSocketSend(term.sock, input_buf, 1);
}
break;
}
}
}
catch
PutExcept;
// ideally go back to the splashscreen and wait for another input?
// term.sock_ready = 0;
// term.waiting_for_input = TRUE;
// goto show_splash;
MenuPop;
SettingsPop;
GrPaletteSet(original_palette);
LFBFlush;
}
// dev server
// Telnet("localhost", 8888);
//Telnet;
/*
TODO:
- refactor ANSIParser's TCPSocketSends to send everything to a "TelnetSend" function
- TelnetSend will handle whether there's a connection or not
- This will make easier to transform this software into an ANSI viewer / Telnet client/ general purpose Term.
- There's a lot more to fix, but this is a good start.
*/