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