From c67edb15e47535b1c088f05032ebb785dcd6edf9 Mon Sep 17 00:00:00 2001 From: Iswenzz Date: Tue, 7 Mar 2023 20:45:51 +0100 Subject: [PATCH] Win32: TTY console --- makefile | 6 +- src/common.c | 9 +- src/sec_update.c | 3 - src/win32/sys_win32.c | 104 +---- src/win32/sys_win32.h | 2 +- src/win32/win_syscon.c | 909 +++++++++++++++++++---------------------- 6 files changed, 426 insertions(+), 607 deletions(-) diff --git a/makefile b/makefile index 67a6463e..ca7f6b51 100644 --- a/makefile +++ b/makefile @@ -53,7 +53,7 @@ else DCFLAGS=-fno-pie -O1 -DNDEBUG endif -WIN_LFLAGS=-m32 -g -Wl,--nxcompat,--stack,0x800000 -mwindows -static-libgcc -static -lm +WIN_LFLAGS=-m32 -g -Wl,--nxcompat,--stack,0x800000 -static-libgcc -static -lm WIN_LLIBS=tomcrypt mbedtls mbedcrypto mbedx509 ws2_32 wsock32 iphlpapi gdi32 winmm crypt32 stdc++ LINUX_LFLAGS=-m32 -g -static-libgcc -rdynamic -Wl,-rpath=./ LINUX_LLIBS=tomcrypt mbedtls mbedcrypto mbedx509 dl pthread m stdc++ @@ -168,7 +168,7 @@ gittagging: ifneq ($(OS),Windows_NT) git tag -a v$(VERSION) git push origin --tags -endif +endif ################################# # A rule to make mbedtls library. @@ -284,7 +284,7 @@ clean_all: docker: $(TARGET) @docker build . -t cod4x/bleeding -plugins: +plugins: @$(MAKE) -C $(PLUGIN_DIR)/screenshotsender #@$(MAKE) -C $(PLUGIN_DIR)/antispam @$(MAKE) -C $(PLUGIN_DIR)/censor diff --git a/src/common.c b/src/common.c index b617586c..f9c11306 100644 --- a/src/common.c +++ b/src/common.c @@ -401,9 +401,6 @@ Returns last event time */ void Com_EventLoop( void ) { sysEvent_t *ev; -#ifdef _WIN32 - char consoleline[1024]; -#endif while ( 1 ) { ev = Com_GetSystemEvent(); @@ -415,11 +412,7 @@ void Com_EventLoop( void ) { switch(ev->evType) { case SE_CONSOLE: -#ifdef _WIN32 - Com_sprintf(consoleline, sizeof(consoleline), "]%s", (char *)ev->evPtr); - Sys_Print(consoleline); -#endif - Cbuf_AddText( (char *)ev->evPtr ); + Cbuf_AddText( (char *)ev->evPtr ); Cbuf_AddText("\n"); break; default: diff --git a/src/sec_update.c b/src/sec_update.c index 8c946378..0e99d384 100644 --- a/src/sec_update.c +++ b/src/sec_update.c @@ -881,9 +881,6 @@ void Sec_Update( qboolean getbasefiles ){ { Com_PrintError(CON_CHANNEL_SYSTEM,"Update has failed. Trying to recover...\n"); Sec_UndoBackup(files.next); - -// MessageBoxA(NULL, "Couldn't install update", "Couldn't install update", MB_OK); - Sec_AutoupdateUnlock(lockfile); Com_Quit_f(); return; diff --git a/src/win32/sys_win32.c b/src/win32/sys_win32.c index 921cdd23..608ce510 100644 --- a/src/win32/sys_win32.c +++ b/src/win32/sys_win32.c @@ -37,7 +37,6 @@ #include #include -void Sys_ShowErrorDialog(const char* functionName); void Sys_InitThreadContext(); WinVars_t g_wv; @@ -190,7 +189,7 @@ qboolean Sys_MemoryProtectWrite(void* startoffset, int len) if(VirtualProtect((LPVOID)startoffset, len, PAGE_READWRITE, &oldProtect) == 0) { - Sys_ShowErrorDialog("Sys_MemoryProtectWrite"); + fprintf(stderr, "Sys_MemoryProtectWrite"); return qfalse; } @@ -204,7 +203,7 @@ qboolean Sys_MemoryProtectExec(void* startoffset, int len) if(VirtualProtect((LPVOID)startoffset, len, PAGE_EXECUTE_READ, &oldProtect) == 0) { - Sys_ShowErrorDialog("Sys_MemoryProtectExec"); + fprintf(stderr, "Sys_MemoryProtectExec"); return qfalse; } @@ -218,32 +217,13 @@ qboolean Sys_MemoryProtectReadonly(void* startoffset, int len) if(VirtualProtect((LPVOID)startoffset, len, PAGE_READONLY, &oldProtect) == 0) { - Sys_ShowErrorDialog("Sys_MemoryProtectReadonly"); + fprintf(stderr, "Sys_MemoryProtectReadonly"); return qfalse; } return qtrue; } -void Sys_ShowErrorDialog(const char* functionName) -{ - void* HWND = NULL; - char errMessageBuf[1024]; - char displayMessageBuf[1024]; - DWORD lastError = GetLastError(); - - if(lastError != 0) - { - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lastError, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), errMessageBuf, sizeof(errMessageBuf) -1, NULL); - }else{ - Q_strncpyz(errMessageBuf, "Unknown Error", sizeof(errMessageBuf)); - } - - Com_sprintf(displayMessageBuf, sizeof(displayMessageBuf), "Error in function: %s\nThe error is: %s", functionName, errMessageBuf); - - MessageBoxA(HWND, displayMessageBuf, "System Error", MB_OK | MB_ICONERROR); -} - const char *Sys_DefaultHomePath( void ) { return NULL; } @@ -610,7 +590,7 @@ void Sys_PlatformInit( void ) if(received_mem != allocptr) { Com_sprintf(errormsg, sizeof(errormsg), "Sys_PlatformInit: Allocate memory @ %p failed Received: %p", allocptr, received_mem); - Sys_ShowErrorDialog(errormsg); + fprintf(stderr, errormsg); exit(1); } #endif @@ -641,7 +621,7 @@ void* Sys_LoadLibrary(const char* dlfile) /* if(handle == NULL) { - Sys_ShowErrorDialog("Sys_LoadLibrary"); + fprintf(stderr, "Sys_LoadLibrary"); } */ return handle; @@ -782,79 +762,13 @@ char** GetStrTable(void* filebuf, int len, sharedlib_data_t *text) return PE32_GetStrTable(filebuf, len, text); } -void CON_InitInternal(); - -/* Win32 message loop thread */ -void* Sys_EventLoopThread(void* nullarg){ - MSG msg; - - CON_InitInternal(); - g_wv.windowsCreated = qtrue; - - - // pump the message loop - while ( GetMessageA( &msg, NULL, 0, 0 ) > 0) - { - // save the msg time, because wndprocs don't have access to the timestamp - g_wv.sysMsgTime = msg.time; - - TranslateMessage( &msg ); - DispatchMessageA( &msg ); - } - MessageBoxA(NULL, "GetMessageA has failed", "GetMessageA has failed", MB_OK); - Com_Quit_f(); - return NULL; -} - void Sys_EventLoop() { } -void CON_Init() -{ - static qboolean messageThreadActive = 0; - threadid_t tid; - - if(messageThreadActive) - { - return; - } - - messageThreadActive = Sys_CreateNewThread(Sys_EventLoopThread, &tid, NULL); - while(messageThreadActive && !g_wv.windowsCreated) - { - Sys_SleepSec(0); - } -} - - -void* Sys_ErrorBoxThread(void* message) -{ - MessageBoxA(NULL, (char*)message, CLIENT_WINDOW_TITLE " - System Crash", MB_OK | MB_ICONERROR | MB_TOPMOST ); - return NULL; -} - void Sys_WaitForErrorConfirmation(const char* error) { - MSG msg; - unsigned int maxwait; - threadid_t tid; - - CON_Show( 1, qtrue ); - - Sys_CreateNewThread(Sys_ErrorBoxThread, &tid, (void*)error); - - // wait for the user to quit or wait for max 60 seconds - maxwait = Sys_Milliseconds() + 60000; - do{ - if ( !GetMessage( &msg, NULL, 0, 0 ) ) { - break; - } - TranslateMessage( &msg ); - DispatchMessage( &msg ); - }while( Sys_Milliseconds() < maxwait ); - } @@ -879,7 +793,7 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin Sys_SetExeFile( "" ); Sys_SetExeFileShort( "" ); Sys_SetBinaryPath( "" ); - MessageBoxA(NULL, "Path is too long. The whole path to location of this .exe file must not exceed 254 characters", CLIENT_WINDOW_TITLE " Error", MB_OK | MB_ICONERROR); + fprintf(stderr, "Path is too long. The whole path to location of this .exe file must not exceed 254 characters"); return 1; }else{ Sys_SetExeFile( lpFilename ); @@ -890,13 +804,13 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin *lastSep = '\0'; if(strlen(lastSep +1) > MAX_QPATH) { - MessageBoxA(NULL, ".EXE filename exceeds " "64" " characters.", CLIENT_WINDOW_TITLE " Error", MB_OK | MB_ICONERROR); + fprintf(stderr, ".EXE filename exceeds " "64" " characters."); return 1; } Sys_SetBinaryPath( lpFilename ); Sys_SetExeFileShort( lastSep +1 ); }else{ - MessageBoxA(NULL, "GetModuleFileName() returned an unexpected filepath.", CLIENT_WINDOW_TITLE " Error", MB_OK | MB_ICONERROR); + fprintf(stderr, "GetModuleFileName() returned an unexpected filepath."); return 1; } } @@ -964,7 +878,7 @@ void Sys_SetThreadLocalStorage(void** localvar) { if(TlsSetValue(tlsKey, localvar) == FALSE) { - Sys_ShowErrorDialog("Sys_SetThreadLocalStorage"); + fprintf(stderr, "Sys_SetThreadLocalStorage"); ExitProcess(-1); } } diff --git a/src/win32/sys_win32.h b/src/win32/sys_win32.h index c9fd3e79..13b0af54 100644 --- a/src/win32/sys_win32.h +++ b/src/win32/sys_win32.h @@ -51,7 +51,7 @@ unsigned sysMsgTime; qboolean windowsCreated; } WinVars_t; -void CON_Show( int visLevel, qboolean quitOnClose ); +void CON_Show(void); char** PE32_GetStrTable(void *buff, int len, sharedlib_data_t *text); extern WinVars_t g_wv; diff --git a/src/win32/win_syscon.c b/src/win32/win_syscon.c index 5db402e6..16d864b2 100644 --- a/src/win32/win_syscon.c +++ b/src/win32/win_syscon.c @@ -19,557 +19,472 @@ =========================================================================== */ -#include "../q_shared.h" #include "../sys_main.h" -#include "../qcommon.h" -#include "../cmd.h" -#include "../sys_cod4defs.h" -#include "sys_win32.h" - +#include "../q_shared.h" +#include "../cmd_completion.h" +#include "../qcommon_io.h" +#include "../cvar.h" -#include -#include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include -#define SYSCON_DEFAULT_WIDTH 540 -#define SYSCON_DEFAULT_HEIGHT 450 +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif -#define COPY_ID 1 -#define QUIT_ID 2 -#define CLEAR_ID 3 +// TTY console routines. +// NOTE: if the user is editing a line when something gets printed to the early +// console then it won't look good so we provide CON_Hide and CON_Show to be +// called before and after a stdout or stderr output +static cvar_t* com_ansiColor; + +// General flag to tell about tty console mode +static qboolean ttycon_on = qfalse; +static qboolean stdin_active = qtrue; +static int ttycon_hide = 0; + +// Some key codes that the terminal may be using, initialised on start up +static field_t TTY_con; +HANDLE hStdout = NULL; +HANDLE hStdin = NULL; +DWORD stdoutOriginalMode = 0; +DWORD stdinOriginalMode = 0; +DWORD stdinConsoleMode = 0; + +// This is somewhat of aduplicate of the graphical console history +// But it's safer more modular to have our own here +#define CON_HISTORY 32 +static field_t ttyEditLines[CON_HISTORY]; +static int hist_current = -1, hist_count = 0; + +void Field_AutoComplete(field_t *field); +field_t *Hist_Prev(void); + +#ifndef MAXPRINTMSG +#define MAXPRINTMSG 1024 +#endif -#define ERRORBOX_ID 10 -#define ERRORTEXT_ID 11 +/** + * @brief Output a backspace. + * @remarks it seems on some terminals just sending '\b' is not enough so instead wesend "\b \b". + * @todo - FIXME there may be a way to find out if '\b' alone would work though. + */ +static void CON_Back(void) +{ + char key; + + key = '\b'; + write(STDOUT_FILENO, &key, 1); + key = ' '; + write(STDOUT_FILENO, &key, 1); + key = '\b'; + write(STDOUT_FILENO, &key, 1); +} -#define EDIT_ID 100 -#define INPUT_ID 101 +/** + * @brief Console move back. + */ +static void CON_MoveBack(void) +{ + write(STDOUT_FILENO, "\033[D", 3); +} +/** + * @brief Console move forward. + */ +static void CON_MoveForward(void) +{ + write(STDOUT_FILENO, "\033[C", 3); +} +/** + * @brief History add. + * @param field - The history field. + */ +void Hist_Add(field_t *field) +{ + int i; + assert(hist_count <= CON_HISTORY); + assert(hist_count >= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 130 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1005 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif + if ((hist_count > 0 && strcmp(field->buffer, ttyEditLines[0].buffer) == 0) || field->len == 0) + { + // Don't add duplicate entries to history (same as HISTCONTROL = ignoredups in bash) + // Reinit + hist_current = -1; + return; + } + // Make some room + for (i = CON_HISTORY - 1; i > 0; i--) + ttyEditLines[i] = ttyEditLines[i - 1]; + ttyEditLines[0] = *field; + if (hist_count < CON_HISTORY) + hist_count++; + + // Reinit + hist_current = -1; +} -//******************************************************** +/** + * @brief History prev. + * @return field_t* + */ +field_t *Hist_Prev(void) +{ + int hist_prev; + assert(hist_count <= CON_HISTORY); + assert(hist_count >= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); + + hist_prev = hist_current + 1; + if (hist_prev >= hist_count) + return NULL; + hist_current++; + return &(ttyEditLines[hist_current]); +} +/** + * @brief History next. + * @return field_t* + */ +field_t *Hist_Next(void) +{ + assert(hist_count <= CON_HISTORY); + assert(hist_count >= 0); + assert(hist_current >= -1); + assert(hist_current <= hist_count); + + if (hist_current >= 0) + hist_current--; + if (hist_current == -1) + return NULL; + return &(ttyEditLines[hist_current]); +} -typedef struct +/** + * @brief Create console. + */ +void CON_Init(void) { - HWND hWnd; - HWND hwndBuffer; + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdout, &stdoutOriginalMode); + GetConsoleMode(hStdin, &stdinOriginalMode); - HWND hwndButtonClear; - HWND hwndButtonCopy; - HWND hwndButtonQuit; + // Detect ansi support + qboolean ansiSupport = !!(stdoutOriginalMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); - HWND hwndErrorBox; - HWND hwndErrorText; + // Disable line buffering and echoing of input characters + stdinConsoleMode = stdinOriginalMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); + SetConsoleMode(hStdin, stdinConsoleMode); - HBITMAP hbmLogo; - HBITMAP hbmClearBitmap; + Field_Clear(&TTY_con); + ttycon_on = qtrue; - HBRUSH hbrEditBackground; + com_ansiColor = Cvar_RegisterBool("ttycon_ansiColor", ansiSupport, CVAR_ARCHIVE, "Use ansi colors for sysconsole output"); +} - HFONT hfBufferFont; - HFONT hfButtonFont; +/** + * @brief Shutdown console. + */ +void CON_Shutdown(void) +{ + if (ttycon_on) + CON_Back(); + // Restore blocking to stdin reads + SetConsoleMode(hStdin, stdinOriginalMode); +} + +/** + * @brief Console show. + */ +void CON_Show(void) +{ + if (ttycon_on) + { + int i; + assert(ttycon_hide > 0); + ttycon_hide--; + + if (ttycon_hide == 0) + { + write(STDOUT_FILENO, "]", 1); + for (i = 0; i < TTY_con.len; i++) + write(STDOUT_FILENO, TTY_con.buffer + i, 1); + for (i = 0; i < TTY_con.len - TTY_con.cursor; ++i) + CON_MoveBack(); + } + } +} - HWND hwndInputLine; +/** + * @brief Clear the display of the line currently edited bring cursor back to beginning of line. + */ +static void CON_Hide(void) +{ + if (ttycon_on) + { + int i; - char errorString[80]; + if (ttycon_hide) + { + ttycon_hide++; + return; + } + for (i = TTY_con.cursor; i < TTY_con.len; ++i) + CON_MoveForward(); + for (i = 0; i < TTY_con.len; i++) + CON_Back(); - char consoleText[512], returnedText[512]; - int visLevel; - qboolean quitOnClose; - int windowWidth, windowHeight; + CON_Back(); + ttycon_hide++; + } +} - WNDPROC SysInputLineWndProc; +/** + * @brief Console input. + * @return char* + */ +char *CON_Input(void) +{ + // We use this when sending back commands + static char text[MAX_EDIT_LINE]; + field_t *history; -} WinConData; + if (ttycon_on) + { + DWORD numEventsRead; + DWORD lpNumberOfEvents; + INPUT_RECORD inputRecord; -static WinConData s_wcd; + GetNumberOfConsoleInputEvents(hStdin, &lpNumberOfEvents); + if (!lpNumberOfEvents) + return NULL; -static LONG WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + ReadConsoleInput(hStdin, &inputRecord, 1, &numEventsRead); + if (inputRecord.EventType != KEY_EVENT || !inputRecord.Event.KeyEvent.bKeyDown) + return NULL; - static qboolean s_timePolarity; - int cx, cy; - float sx; /*, sy*/ - float x, y, w, h; + WORD event = inputRecord.Event.KeyEvent.wVirtualKeyCode; + char key = inputRecord.Event.KeyEvent.uChar.AsciiChar; - switch ( uMsg ) - { - case WM_SIZE: - // NERVE - SMF - cx = LOWORD( lParam ); - cy = HIWORD( lParam ); -/* - if ( cx < SYSCON_DEFAULT_WIDTH ) - cx = SYSCON_DEFAULT_WIDTH; - if ( cy < SYSCON_DEFAULT_HEIGHT ) - cy = SYSCON_DEFAULT_HEIGHT; -*/ - sx = (float)cx / SYSCON_DEFAULT_WIDTH; - //sy = (float)cy / SYSCON_DEFAULT_HEIGHT; - - x = 8; - y = 6; - w = cx - 15; - h = cy - 60; - SetWindowPos( s_wcd.hwndBuffer, NULL, x, y, w, h, 0 ); - - y = y + h + 4; - h = 20; - SetWindowPos( s_wcd.hwndInputLine, NULL, x, y, w, h, 0 ); - - y = y + h + 4; - w = 72 * sx; - h = 24; - SetWindowPos( s_wcd.hwndButtonCopy, NULL, x, y, w, h, 0 ); - - x = x + w + 2; - SetWindowPos( s_wcd.hwndButtonClear, NULL, x, y, w, h, 0 ); - - x = cx - 6 - w; - SetWindowPos( s_wcd.hwndButtonQuit, NULL, x, y, w, h, 0 ); - - s_wcd.windowWidth = cx; - s_wcd.windowHeight = cy; - // -NERVE - SMF - break; - case WM_ACTIVATE: - if ( LOWORD( wParam ) != WA_INACTIVE ) { - SetFocus( s_wcd.hwndInputLine ); + // We have something, backspace ? + // NOTE: TTimo testing a lot of values .. seems it's the only way to get it to work everywhere + if (event == VK_BACK || event == VK_DELETE) + { + if (TTY_con.len > 0 && TTY_con.cursor > 0) + { + CON_Hide(); + memmove(TTY_con.buffer + TTY_con.cursor - 1, TTY_con.buffer + TTY_con.cursor, TTY_con.len - TTY_con.cursor + 2); + TTY_con.len--; + TTY_con.cursor--; + CON_Show(); + } + return NULL; } - break; - - case WM_CLOSE: - return 0; - case WM_CTLCOLORSTATIC: - if ( ( HWND ) lParam == s_wcd.hwndBuffer ) { - SetBkColor( ( HDC ) wParam, RGB( 0, 0, 0 ) ); - SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0xff ) ); - -#if 0 // this draws a background in the edit box, but there are issues with this - if ( ( hdcScaled = CreateCompatibleDC( ( HDC ) wParam ) ) != 0 ) { - if ( SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo ) ) { - StretchBlt( ( HDC ) wParam, 0, 0, 512, 384, - hdcScaled, 0, 0, 512, 384, - SRCCOPY ); + // Delete word + if (event == VK_ESCAPE) + { + CON_Hide(); + bool trailing_spaces = TTY_con.cursor > 0 && TTY_con.buffer[TTY_con.cursor - 1] == ' '; + while (TTY_con.len > 0 && TTY_con.cursor > 0) + { + if (TTY_con.buffer[TTY_con.cursor - 1] == ' ') + { + if (!trailing_spaces) + break; } - DeleteDC( hdcScaled ); + else + trailing_spaces = false; + memmove(TTY_con.buffer + TTY_con.cursor - 1, TTY_con.buffer + TTY_con.cursor, TTY_con.len - TTY_con.cursor + 2); + TTY_con.len--; + TTY_con.cursor--; } -#endif - return ( long ) s_wcd.hbrEditBackground; + CON_Show(); + return NULL; } - break; - - case WM_COMMAND: - if ( wParam == COPY_ID ) { - SendMessageA( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); - SendMessageA( s_wcd.hwndBuffer, WM_COPY, 0, 0 ); - } else if ( wParam == QUIT_ID ) { - if ( s_wcd.quitOnClose ) { - PostQuitMessage( 0 ); - } else + if (event == VK_RETURN) + { + // Push it in history + TTY_con.cursor = TTY_con.len; + Hist_Add(&TTY_con); + Q_strncpyz(text, TTY_con.buffer, sizeof(text)); + Field_Clear(&TTY_con); + write(STDOUT_FILENO, "\n]", 2); + return text; + } + if (event == VK_TAB) + { + CON_Hide(); + Field_AutoComplete(&TTY_con); + CON_Show(); + return NULL; + } + if (event == VK_UP) + { + history = Hist_Prev(); + if (history) { - Cbuf_AddText("quit\n"); - + CON_Hide(); + TTY_con = *history; + CON_Show(); } - } else if ( wParam == CLEAR_ID ) { - SendMessageA( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); - SendMessageA( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" ); - UpdateWindow( s_wcd.hwndBuffer ); + return NULL; } - break; - case WM_CREATE: -// s_wcd.hbmLogo = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP1 ) ); -// s_wcd.hbmClearBitmap = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP2 ) ); - s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0, 0, 0 ) ); - SetTimer( hWnd, 1, 1000, NULL ); - break; - case WM_ERASEBKGND: -#if 0 - HDC hdcScaled; - HGDIOBJ oldObject; - -#if 1 // a single, large image - hdcScaled = CreateCompatibleDC( ( HDC ) wParam ); - assert( hdcScaled != 0 ); - - if ( hdcScaled ) { - oldObject = SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo ); - assert( oldObject != 0 ); - if ( oldObject ) { - StretchBlt( ( HDC ) wParam, 0, 0, s_wcd.windowWidth, s_wcd.windowHeight, - hdcScaled, 0, 0, 512, 384, - SRCCOPY ); - } - DeleteDC( hdcScaled ); - hdcScaled = 0; + if (event == VK_DOWN) + { + history = Hist_Next(); + CON_Hide(); + if (history) + TTY_con = *history; + else + Field_Clear(&TTY_con); + CON_Show(); + return NULL; } -#else // a repeating brush + if (event == VK_LEFT) { - HBRUSH hbrClearBrush; - RECT r; - - GetWindowRect( hWnd, &r ); - - r.bottom = r.bottom - r.top + 1; - r.right = r.right - r.left + 1; - r.top = 0; - r.left = 0; - - hbrClearBrush = CreatePatternBrush( s_wcd.hbmClearBitmap ); - - assert( hbrClearBrush != 0 ); - - if ( hbrClearBrush ) { - FillRect( ( HDC ) wParam, &r, hbrClearBrush ); - DeleteObject( hbrClearBrush ); + if (TTY_con.cursor > 0) + { + TTY_con.cursor--; + CON_MoveBack(); } + return NULL; } -#endif - return 1; -#endif - return DefWindowProc( hWnd, uMsg, wParam, lParam ); - case WM_TIMER: - if ( wParam == 1 ) { - s_timePolarity = !s_timePolarity; - if ( s_wcd.hwndErrorBox ) { - InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE ); + if (event == VK_RIGHT) + { + if (TTY_con.cursor < TTY_con.len) + { + TTY_con.cursor++; + CON_MoveForward(); } + return NULL; } - break; + if (!key || TTY_con.len >= sizeof(text) - 1) + return NULL; + + // Push regular character + CON_Hide(); + memmove(TTY_con.buffer + TTY_con.cursor + 1, TTY_con.buffer + TTY_con.cursor, TTY_con.len - TTY_con.cursor + 1); + TTY_con.buffer[TTY_con.cursor] = key; + TTY_con.len++; + TTY_con.cursor++; + CON_Show(); + return NULL; } - - return DefWindowProc( hWnd, uMsg, wParam, lParam ); -} - -LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { - char inputBuffer[1024]; - - switch ( uMsg ) + else if (stdin_active) { - case WM_KILLFOCUS: - if ( ( HWND ) wParam == s_wcd.hWnd || - ( HWND ) wParam == s_wcd.hwndErrorBox ) { - SetFocus( hWnd ); - return 0; - } - break; - - case WM_CHAR: - if ( wParam == 13 ) { - GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer ) ); - strncat( s_wcd.consoleText, inputBuffer, sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 5 ); - strcat( s_wcd.consoleText, "\n" ); - SetWindowText( s_wcd.hwndInputLine, "" ); - - //CON_Print( va( "]%s\n", inputBuffer ) ); - - return 0; + int len; + fd_set fdset; + struct timeval timeout; + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(STDIN_FILENO, &fdset)) + return NULL; + + len = read(STDIN_FILENO, text, sizeof(text)); + if (len == 0) + { + // EOF + stdin_active = qfalse; + return NULL; } - } - - return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam ); -} - -/* -** Sys_CreateConsole -*/ -void CON_InitInternal( void ) { - HDC hDC; - WNDCLASS wc; - RECT rect; - const char *DEDCLASS = CLIENT_WINDOW_TITLE " Console"; - const char *WINDOWNAME = CLIENT_WINDOW_TITLE " Console"; - - int nHeight; - int swidth, sheight; - int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | WS_SIZEBOX; - - memset( &wc, 0, sizeof( wc ) ); - - wc.style = 0; - wc.lpfnWndProc = (WNDPROC) ConWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = g_wv.hInstance; - wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE( IDI_ICON1 ) ); - wc.hCursor = LoadCursor( NULL,IDC_ARROW ); - wc.hbrBackground = (void *)COLOR_WINDOW; - wc.lpszMenuName = 0; - wc.lpszClassName = DEDCLASS; - - if ( !RegisterClass( &wc ) ) { - return; - } - - rect.left = 0; - rect.right = SYSCON_DEFAULT_WIDTH; - rect.top = 0; - rect.bottom = SYSCON_DEFAULT_HEIGHT; - AdjustWindowRect( &rect, DEDSTYLE, FALSE ); - - hDC = GetDC( GetDesktopWindow() ); - swidth = GetDeviceCaps( hDC, HORZRES ); - sheight = GetDeviceCaps( hDC, VERTRES ); - ReleaseDC( GetDesktopWindow(), hDC ); - - s_wcd.windowWidth = rect.right - rect.left + 1; - s_wcd.windowHeight = rect.bottom - rect.top + 1; - - s_wcd.hWnd = CreateWindowEx( 0, - DEDCLASS, - WINDOWNAME, - DEDSTYLE, - ( swidth - 600 ) / 2, ( sheight - 450 ) / 2, rect.right - rect.left + 1, rect.bottom - rect.top + 1, - NULL, - NULL, - g_wv.hInstance, - NULL ); - - if ( s_wcd.hWnd == NULL ) { - return; - } - - // - // create fonts - // - hDC = GetDC( s_wcd.hWnd ); - nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); - - s_wcd.hfBufferFont = CreateFont( nHeight, - 0, - 0, - 0, - FW_LIGHT, - 0, - 0, - 0, - DEFAULT_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - FF_MODERN | FIXED_PITCH, - "Courier New" ); - - ReleaseDC( s_wcd.hWnd, hDC ); - - // - // create the input line - // - s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | - ES_LEFT | ES_AUTOHSCROLL, - 6, 400, 528, 20, - s_wcd.hWnd, - ( HMENU ) INPUT_ID, // child window ID - g_wv.hInstance, NULL ); - - // - // create the buttons - // - s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, - 5, 425, 72, 24, - s_wcd.hWnd, - ( HMENU ) COPY_ID, // child window ID - g_wv.hInstance, NULL ); - SendMessageA( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" ); - - s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, - 82, 425, 72, 24, - s_wcd.hWnd, - ( HMENU ) CLEAR_ID, // child window ID - g_wv.hInstance, NULL ); - SendMessageA( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" ); - - s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, - 462, 425, 72, 24, - s_wcd.hWnd, - ( HMENU ) QUIT_ID, // child window ID - g_wv.hInstance, NULL ); - SendMessageA( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" ); - - - // - // create the scrollbuffer - // - s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | - ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, - 6, 6, 526, 390, - s_wcd.hWnd, - ( HMENU ) EDIT_ID, // child window ID - g_wv.hInstance, NULL ); - SendMessageA( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); - - s_wcd.SysInputLineWndProc = ( WNDPROC ) SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, ( long ) InputLineWndProc ); - SendMessageA( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); - - ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT ); - UpdateWindow( s_wcd.hWnd ); - SetForegroundWindow( s_wcd.hWnd ); - SetFocus( s_wcd.hwndInputLine ); - - s_wcd.visLevel = 1; -} - -/* -** Sys_DestroyConsole -*/ -void CON_Shutdown( void ) { - if ( s_wcd.hWnd ) { - ShowWindow( s_wcd.hWnd, SW_HIDE ); - CloseWindow( s_wcd.hWnd ); - DestroyWindow( s_wcd.hWnd ); - s_wcd.hWnd = 0; - } -} - -/* -** Sys_ShowConsole -*/ -void CON_Show( int visLevel, qboolean quitOnClose ) { - s_wcd.quitOnClose = quitOnClose; - mvabuf; - - if ( visLevel == s_wcd.visLevel ) { - return; - } - - s_wcd.visLevel = visLevel; + if (len < 1) + return NULL; - if ( !s_wcd.hWnd ) { - return; - } - - switch ( visLevel ) - { - case 0: - ShowWindow( s_wcd.hWnd, SW_HIDE ); - break; - case 1: - ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL ); - SendMessageA( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); - break; - case 2: - ShowWindow( s_wcd.hWnd, SW_MINIMIZE ); - break; - default: - Sys_Error( va("Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel )); - break; - } -} - -/* -** Sys_ConsoleInput -*/ -char *CON_Input( void ) { - - if ( s_wcd.consoleText[0] == 0 ) { - return NULL; + // Rip off the /n and terminate + text[len-1] = 0; + return text; } - - strcpy( s_wcd.returnedText, s_wcd.consoleText ); - s_wcd.consoleText[0] = 0; - - return s_wcd.returnedText; + return NULL; } -/* -** Conbuf_AppendText -*/ -void CON_Print( const char *pMsg ) { -#define CONSOLE_BUFFER_SIZE 16384 - - char buffer[CONSOLE_BUFFER_SIZE * 2]; - char *b = buffer; - const char *msg; - int bufLen; - int i = 0; - static unsigned long s_totalChars; - - // - // if the message is REALLY long, use just the last portion of it - // - if ( strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 ) { - msg = pMsg + strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1; - } else - { - msg = pMsg; - } - - // - // copy into an intermediate buffer - // - while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) ) +/** + * @brief Transform Q3 colour codes to ANSI escape sequences. + * @param msg - The message to transform. + */ +void Sys_AnsiColorPrint(const char *msg) +{ + static char buffer[MAXPRINTMSG]; + int length = 0; + + static int q3ToAnsi[8] = { + 30, // COLOR_BLACK + 31, // COLOR_RED + 32, // COLOR_GREEN + 33, // COLOR_YELLOW + 34, // COLOR_BLUE + 36, // COLOR_CYAN + 35, // COLOR_MAGENTA + 0 // COLOR_WHITE + }; + + while (*msg) { - if ( msg[i] == '\n' && msg[i + 1] == '\r' ) { - b[0] = '\r'; - b[1] = '\n'; - b += 2; - i++; - } else if ( msg[i] == '\r' ) { - b[0] = '\r'; - b[1] = '\n'; - b += 2; - } else if ( msg[i] == '\n' ) { - b[0] = '\r'; - b[1] = '\n'; - b += 2; - } else if ( Q_IsColorString( &msg[i] ) ) { - i++; - } else + if (Q_IsColorString(msg) || *msg == '\n') { - *b = msg[i]; - b++; + // First empty the buffer + if (length > 0) + { + buffer[length] = '\0'; + fputs(buffer, stderr); + length = 0; + } + if (*msg == '\n') + { + // Issue a reset and then the newline + fputs("\033[0m\n", stderr); + msg++; + } + else + { + // Print the color code + Com_sprintf(buffer, sizeof(buffer), "\033[1;%dm", q3ToAnsi[ColorIndex(*(msg + 1))]); + fputs(buffer, stderr); + msg += 2; + } + } + else + { + if (length >= MAXPRINTMSG - 1) + break; + + buffer[length] = *msg; + length++; + msg++; } - i++; } - *b = 0; - bufLen = b - buffer; - - s_totalChars += bufLen; - - // - // replace selection instead of appending if we're overflowing - // - if ( s_totalChars > CONSOLE_BUFFER_SIZE ) { - SendMessageA( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); - s_totalChars = bufLen; - } else { - // NERVE - SMF - always append at the bottom of the textbox - SendMessageA( s_wcd.hwndBuffer, EM_SETSEL, 0xFFFF, 0xFFFF ); + // Empty anything still left in the buffer + if (length > 0) + { + buffer[length] = '\0'; + fputs(buffer, stderr); } - - // - // put this text into the windows console - // - SendMessageA( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); - SendMessageA( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 ); - - - SendMessageA( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer ); - // - //InvalidateRect(s_wcd.hwndBuffer, NULL, TRUE); } -void CON_DisableDraw() +/** + * @brief Print to the console. + * @param msg - The message. + */ +void CON_Print(const char *msg) { - SendMessage( s_wcd.hwndBuffer, WM_SETREDRAW, (WPARAM) FALSE, 0); -} + CON_Hide(); -void CON_EnableDraw() -{ - SendMessageA( s_wcd.hwndBuffer, WM_SETREDRAW, (WPARAM) TRUE, 0); -} \ No newline at end of file + if (com_ansiColor && com_ansiColor->integer) + Sys_AnsiColorPrint(msg); + else + fputs(msg, stderr); + + CON_Show(); +}