tron@2186: /* $Id$ */ tron@2186: rubidium@10429: /** @file console.cpp Handling of the in-game console. */ belugas@6449: darkvater@135: #include "stdafx.h" Darkvater@1891: #include "openttd.h" rubidium@8603: #include "textbuf_gui.h" rubidium@8603: #include "window_gui.h" rubidium@10684: #include "console_gui.h" darkvater@135: #include darkvater@289: #include rubidium@10684: #include "console_internal.h" rubidium@8627: #include "window_func.h" rubidium@8710: #include "string_func.h" rubidium@8720: #include "gfx_func.h" rubidium@10687: #include "core/math_func.hpp" smatz@10402: #include "rev.h" signde@274: rubidium@8760: #include "table/strings.h" rubidium@8760: darkvater@301: #define ICON_BUFFER 79 Darkvater@1739: #define ICON_HISTORY_SIZE 20 darkvater@301: #define ICON_LINE_HEIGHT 12 truelight@543: #define ICON_RIGHT_BORDERWIDTH 10 truelight@543: #define ICON_BOTTOM_BORDERWIDTH 12 truelight@634: #define ICON_MAX_ALIAS_LINES 40 Darkvater@1739: #define ICON_TOKEN_COUNT 20 darkvater@289: rubidium@10685: /* console modes */ rubidium@8764: IConsoleModes _iconsole_mode; rubidium@8764: belugas@6449: /* ** main console ** */ Darkvater@1739: static char *_iconsole_buffer[ICON_BUFFER + 1]; truelight@543: static uint16 _iconsole_cbuffer[ICON_BUFFER + 1]; Darkvater@1390: static Textbuf _iconsole_cmdline; darkvater@135: belugas@6449: /* ** main console cmd buffer ** */ Darkvater@1739: static char *_iconsole_history[ICON_HISTORY_SIZE]; Darkvater@1739: static byte _iconsole_historypos; signde@220: belugas@6449: /* *************** * belugas@6449: * end of header * belugas@6449: * *************** */ signde@274: rubidium@6573: static void IConsoleClearCommand() darkvater@135: { Darkvater@1390: memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE); Darkvater@1390: _iconsole_cmdline.length = 0; Darkvater@1390: _iconsole_cmdline.width = 0; Darkvater@1390: _iconsole_cmdline.caretpos = 0; Darkvater@1390: _iconsole_cmdline.caretxoffs = 0; Darkvater@5143: SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); darkvater@135: } darkvater@135: rubidium@6573: static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;} Darkvater@1739: tron@2817: belugas@4171: static void IConsoleHistoryAdd(const char *cmd); tron@2817: static void IConsoleHistoryNavigate(int direction); tron@2817: rubidium@10592: struct IConsoleWindow : Window darkvater@135: { rubidium@10592: static byte scroll; Darkvater@5143: rubidium@10592: IConsoleWindow(const WindowDesc *desc) : Window(desc) rubidium@10592: { rubidium@10592: _iconsole_mode = ICONSOLE_OPENED; rubidium@10592: SetBit(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll truelight@543: rubidium@10592: this->height = _screen.height / 3; rubidium@10592: this->width = _screen.width; rubidium@10592: } Darkvater@1390: rubidium@10592: ~IConsoleWindow() rubidium@10592: { rubidium@10592: _iconsole_mode = ICONSOLE_CLOSED; rubidium@10592: ClrBit(_no_scroll, SCROLL_CON); rubidium@10592: } rubidium@10592: rubidium@10592: virtual void OnPaint() rubidium@10592: { rubidium@10592: int i = IConsoleWindow::scroll; rubidium@10592: int max = (this->height / ICON_LINE_HEIGHT) - 1; rubidium@10592: int delta = 0; rubidium@10592: GfxFillRect(this->left, this->top, this->width, this->height - 1, 0); rubidium@10592: while ((i > 0) && (i > IConsoleWindow::scroll - max) && (_iconsole_buffer[i] != NULL)) { rubidium@10592: DoDrawString(_iconsole_buffer[i], 5, rubidium@10592: this->height - (IConsoleWindow::scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); rubidium@10592: i--; darkvater@135: } rubidium@10592: /* If the text is longer than the window, don't show the starting ']' */ rubidium@10592: delta = this->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; rubidium@10592: if (delta > 0) { rubidium@10685: DoDrawString("]", 5, this->height - ICON_LINE_HEIGHT, CC_COMMAND); rubidium@10592: delta = 0; rubidium@10592: } darkvater@672: rubidium@10685: DoDrawString(_iconsole_cmdline.buf, 10 + delta, this->height - ICON_LINE_HEIGHT, CC_COMMAND); rubidium@10592: rubidium@10592: if (_iconsole_cmdline.caret) { rubidium@10592: DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, this->height - ICON_LINE_HEIGHT, TC_WHITE); darkvater@289: } darkvater@135: } rubidium@10592: rubidium@10592: virtual void OnMouseLoop() rubidium@10592: { rubidium@10592: if (HandleCaret(&_iconsole_cmdline)) this->SetDirty(); rubidium@10592: } rubidium@10592: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@10592: { rubidium@10592: switch (keycode) { rubidium@10592: case WKC_UP: rubidium@10592: IConsoleHistoryNavigate(+1); rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_DOWN: rubidium@10592: IConsoleHistoryNavigate(-1); rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_SHIFT | WKC_PAGEUP: rubidium@10592: if (IConsoleWindow::scroll - (this->height / ICON_LINE_HEIGHT) - 1 < 0) { rubidium@10592: IConsoleWindow::scroll = 0; rubidium@10592: } else { rubidium@10592: IConsoleWindow::scroll -= (this->height / ICON_LINE_HEIGHT) - 1; rubidium@10592: } rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_SHIFT | WKC_PAGEDOWN: rubidium@10592: if (IConsoleWindow::scroll + (this->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) { rubidium@10592: IConsoleWindow::scroll = ICON_BUFFER; rubidium@10592: } else { rubidium@10592: IConsoleWindow::scroll += (this->height / ICON_LINE_HEIGHT) - 1; rubidium@10592: } rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_SHIFT | WKC_UP: rubidium@10592: if (IConsoleWindow::scroll <= 0) { rubidium@10592: IConsoleWindow::scroll = 0; rubidium@10592: } else { rubidium@10592: --IConsoleWindow::scroll; rubidium@10592: } rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_SHIFT | WKC_DOWN: rubidium@10592: if (IConsoleWindow::scroll >= ICON_BUFFER) { rubidium@10592: IConsoleWindow::scroll = ICON_BUFFER; rubidium@10592: } else { rubidium@10592: ++IConsoleWindow::scroll; rubidium@10592: } rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_BACKQUOTE: rubidium@10592: IConsoleSwitch(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_RETURN: case WKC_NUM_ENTER: rubidium@10685: IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf); rubidium@10592: IConsoleHistoryAdd(_iconsole_cmdline.buf); rubidium@10592: rubidium@10592: IConsoleCmdExec(_iconsole_cmdline.buf); rubidium@10592: IConsoleClearCommand(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_CTRL | WKC_RETURN: rubidium@10592: _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; rubidium@10592: IConsoleResize(this); rubidium@10592: MarkWholeScreenDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case (WKC_CTRL | 'V'): rubidium@10592: if (InsertTextBufferClipboard(&_iconsole_cmdline)) { rubidium@10592: IConsoleResetHistoryPos(); rubidium@10592: this->SetDirty(); rubidium@10592: } rubidium@10592: break; rubidium@10592: rubidium@10592: case (WKC_CTRL | 'L'): rubidium@10592: IConsoleCmdExec("clear"); rubidium@10592: break; rubidium@10592: rubidium@10592: case (WKC_CTRL | 'U'): rubidium@10592: DeleteTextBufferAll(&_iconsole_cmdline); rubidium@10592: this->SetDirty(); rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_BACKSPACE: case WKC_DELETE: rubidium@10592: if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) { rubidium@10592: IConsoleResetHistoryPos(); rubidium@10592: this->SetDirty(); rubidium@10592: } rubidium@10592: break; rubidium@10592: rubidium@10592: case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: rubidium@10592: if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) { rubidium@10592: IConsoleResetHistoryPos(); rubidium@10592: this->SetDirty(); rubidium@10592: } rubidium@10592: break; rubidium@10592: rubidium@10592: default: rubidium@10592: if (IsValidChar(key, CS_ALPHANUMERAL)) { rubidium@10592: IConsoleWindow::scroll = ICON_BUFFER; rubidium@10592: InsertTextBufferChar(&_iconsole_cmdline, key); rubidium@10592: IConsoleResetHistoryPos(); rubidium@10592: this->SetDirty(); rubidium@10592: } else { rubidium@10607: return ES_NOT_HANDLED; rubidium@10592: } rubidium@10592: } rubidium@10607: return ES_HANDLED; rubidium@10592: } rubidium@10592: }; rubidium@10592: rubidium@10592: byte IConsoleWindow::scroll = ICON_BUFFER; darkvater@135: Darkvater@1390: static const Widget _iconsole_window_widgets[] = { Darkvater@1390: {WIDGETS_END} Darkvater@1390: }; Darkvater@1390: Darkvater@1390: static const WindowDesc _iconsole_window_desc = { rubidium@7837: 0, 0, 2, 2, 2, 2, rubidium@6144: WC_CONSOLE, WC_NONE, belugas@8515: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, Darkvater@1390: _iconsole_window_widgets, Darkvater@1390: }; Darkvater@1390: rubidium@10687: void IConsoleGUIInit() darkvater@135: { Darkvater@1739: _iconsole_historypos = ICON_HISTORY_SIZE - 1; darkvater@289: _iconsole_mode = ICONSOLE_CLOSED; darkvater@1243: Darkvater@1739: memset(_iconsole_history, 0, sizeof(_iconsole_history)); darkvater@1243: memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer)); darkvater@1243: memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer)); KUDr@5860: _iconsole_cmdline.buf = CallocT(ICON_CMDLN_SIZE); // create buffer and zero it Darkvater@3458: _iconsole_cmdline.maxlength = ICON_CMDLN_SIZE; darkvater@1243: rubidium@10685: IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision); rubidium@10685: IConsolePrint(CC_WHITE, "------------------------------------"); rubidium@10685: IConsolePrint(CC_WHITE, "use \"help\" for more information"); rubidium@10685: IConsolePrint(CC_WHITE, ""); signde@220: IConsoleClearCommand(); Darkvater@1739: IConsoleHistoryAdd(""); darkvater@135: } darkvater@135: rubidium@6573: void IConsoleClearBuffer() darkvater@135: { darkvater@289: uint i; Darkvater@1827: for (i = 0; i <= ICON_BUFFER; i++) { signde@220: free(_iconsole_buffer[i]); Darkvater@1827: _iconsole_buffer[i] = NULL; Darkvater@1827: } Darkvater@1827: } Darkvater@1390: rubidium@10687: void IConsoleGUIFree() Darkvater@1827: { Darkvater@1390: free(_iconsole_cmdline.buf); Darkvater@1827: IConsoleClearBuffer(); darkvater@135: } darkvater@135: Darkvater@5143: void IConsoleResize(Window *w) signde@220: { truelight@543: switch (_iconsole_mode) { truelight@543: case ICONSOLE_OPENED: Darkvater@5143: w->height = _screen.height / 3; Darkvater@5143: w->width = _screen.width; truelight@543: break; truelight@543: case ICONSOLE_FULL: Darkvater@5143: w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH; Darkvater@5143: w->width = _screen.width; truelight@543: break; Darkvater@5120: default: return; darkvater@135: } darkvater@986: darkvater@986: MarkWholeScreenDirty(); darkvater@135: } darkvater@135: rubidium@6573: void IConsoleSwitch() darkvater@135: { darkvater@289: switch (_iconsole_mode) { rubidium@10592: case ICONSOLE_CLOSED: rubidium@10592: new IConsoleWindow(&_iconsole_window_desc); rubidium@10592: break; rubidium@10592: Darkvater@1397: case ICONSOLE_OPENED: case ICONSOLE_FULL: darkvater@289: DeleteWindowById(WC_CONSOLE, 0); truelight@543: break; darkvater@289: } darkvater@986: darkvater@986: MarkWholeScreenDirty(); darkvater@135: } darkvater@135: rubidium@6573: void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();} rubidium@6573: void IConsoleOpen() {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();} darkvater@135: Darkvater@1739: /** Darkvater@1739: * Add the entered line into the history so you can look it back Darkvater@1739: * scroll, etc. Put it to the beginning as it is the latest text Darkvater@1739: * @param cmd Text to be entered into the 'history' Darkvater@1739: */ belugas@4171: static void IConsoleHistoryAdd(const char *cmd) signde@220: { Darkvater@1739: free(_iconsole_history[ICON_HISTORY_SIZE - 1]); darkvater@141: Darkvater@1739: memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1)); Darkvater@1739: _iconsole_history[0] = strdup(cmd); Darkvater@1739: IConsoleResetHistoryPos(); signde@220: } signde@220: Darkvater@1739: /** Darkvater@1739: * Navigate Up/Down in the history of typed commands Darkvater@1739: * @param direction Go further back in history (+1), go to recently typed commands (-1) Darkvater@1739: */ tron@2817: static void IConsoleHistoryNavigate(int direction) signde@220: { Darkvater@1739: int i = _iconsole_historypos + direction; Darkvater@1739: belugas@6449: /* watch out for overflows, just wrap around */ Darkvater@1739: if (i < 0) i = ICON_HISTORY_SIZE - 1; Darkvater@1739: if (i >= ICON_HISTORY_SIZE) i = 0; Darkvater@1739: rubidium@10687: if (direction > 0) { Darkvater@1739: if (_iconsole_history[i] == NULL) i = 0; rubidium@10687: } Darkvater@1739: Darkvater@1739: if (direction < 0) { Darkvater@1739: while (i > 0 && _iconsole_history[i] == NULL) i--; Darkvater@1739: } Darkvater@1739: Darkvater@1739: _iconsole_historypos = i; signde@220: IConsoleClearCommand(); belugas@6449: /* copy history to 'command prompt / bash' */ skidd13@8450: assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE)); Darkvater@1739: ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength); Darkvater@1390: UpdateTextBufferSize(&_iconsole_cmdline); darkvater@141: } darkvater@141: Darkvater@1588: /** Darkvater@1588: * Handle the printing of text entered into the console or redirected there Darkvater@1588: * by any other means. Text can be redirected to other players in a network game Darkvater@1588: * as well as to a logfile. If the network server is a dedicated server, all activities Darkvater@1588: * are also logged. All lines to print are added to a temporary buffer which can be Darkvater@1588: * used as a history to print them onscreen Darkvater@1588: * @param color_code the colour of the command. Red in case of errors, etc. Darkvater@1588: * @param string the message entered or output on the console (notice, error, etc.) Darkvater@1588: */ rubidium@10687: void IConsoleGUIPrint(ConsoleColour color_code, char *str) darkvater@135: { Darkvater@1588: /* move up all the strings in the buffer one place and do the same for colour Darkvater@1588: * to accomodate for the new command/message */ Darkvater@1588: free(_iconsole_buffer[0]); Darkvater@1588: memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER); Darkvater@5182: _iconsole_buffer[ICON_BUFFER] = str; Darkvater@1588: Darkvater@1588: memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER); Darkvater@1588: _iconsole_cbuffer[ICON_BUFFER] = color_code; darkvater@289: Darkvater@5143: SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); darkvater@135: }