/* ==================== Key_Console Interactive line editing and console scrollback ==================== */ void Key_Console (int key) { switch ( key ) { case K_KP_SLASH: key = '/'; break; case K_KP_MINUS: key = '-'; break; case K_KP_PLUS: key = '+'; break; case K_KP_HOME: key = '7'; break; case K_KP_UPARROW: key = '8'; break; case K_KP_PGUP: key = '9'; break; case K_KP_LEFTARROW: key = '4'; break; case K_KP_5: key = '5'; break; case K_KP_RIGHTARROW: key = '6'; break; case K_KP_END: key = '1'; break; case K_KP_DOWNARROW: key = '2'; break; case K_KP_PGDN: key = '3'; break; case K_KP_INS: key = '0'; break; case K_KP_DEL: key = '.'; break; } if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) || ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) ) { char *cbd; if ( ( cbd = Sys_GetClipboardData() ) != 0 ) { int i; char *p, *s; p = cbd; while ((p = strchr (p, '\r')) != NULL) p[0] = ' '; //r1: multiline paste p = strrchr (cbd, '\n'); if (p) { s = strrchr (p, '\n'); if (s) { s[0] = 0; p++; if (cbd[0]) { Cbuf_AddText (cbd); Cbuf_AddText ("\n"); Com_Printf ("%s\n", LOG_GENERAL, cbd); } } } else p = cbd; i = (int)strlen( p ); //r1: save byte for null terminator!! if ( i + key_linepos >= MAXCMDLINE - 1) i = MAXCMDLINE - key_linepos - 1; if ( i > 0 ) { p[i]=0; strcat( key_lines[edit_line], p ); key_linepos += i; } free( cbd ); } return; } if ( key == 'l' ) { if ( keydown[K_CTRL] ) { Cbuf_AddText ("clear\n"); return; } } if ( key == K_ENTER || key == K_KP_ENTER ) { // backslash text are commands, else chat if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/') Cbuf_AddText (key_lines[edit_line]+2); // skip the > else Cbuf_AddText (key_lines[edit_line]+1); // valid command Cbuf_AddText ("\n"); Com_Printf ("%s\n", LOG_CLIENT,key_lines[edit_line]); edit_line = (edit_line + 1) & 31; history_line = edit_line; memset (key_lines[edit_line], 0, sizeof(key_lines[edit_line])); key_lines[edit_line][0] = ']'; //key_lines[edit_line][1] = '\0'; key_linepos = 1; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command // may take some time return; } //r1: command completion stolen from proquake if (key == K_TAB) { // command completion if (cl_cmdcomplete->intvalue == 1) { const char *cmd; int len, i; char *fragment; cvar_t *var; const char *best = "~"; const char *least = "~"; len = (int)strlen(key_lines[edit_line]); for (i = 0 ; i < len - 1 ; i++) { if (key_lines[edit_line][i] == ' ') return; } fragment = key_lines[edit_line] + 1; len--; for (var = cvar_vars->next ; var ; var = var->next) { if (strcmp(var->name, fragment) >= 0 && strcmp(best, var->name) > 0) best = var->name; if (strcmp(var->name, least) < 0) least = var->name; } cmd = Cmd_CompleteCommand(fragment); //if (strcmp(cmd, fragment) >= 0 && strcmp(best, cmd) > 0) if (cmd) best = cmd; if (best[0] == '~') { cmd = Cmd_CompleteCommand(" "); if (strcmp(cmd, least) < 0) best = cmd; else best = least; } //r1: maybe completing cvar/cmd from net? snprintf(key_lines[edit_line], sizeof(key_lines[edit_line])-1, "]%s ", best); key_lines[edit_line][sizeof(key_lines[edit_line])-1] = 0; key_linepos = (int)strlen(key_lines[edit_line]); } else if (cl_cmdcomplete->value == 2) { Key_CompleteCommand(); } else { Key_CompleteCommandOld(); } return; } if ( key == K_LEFTARROW ) { if (key_linepos > 1) key_linepos--; return; } if (key == K_RIGHTARROW) { if (key_lines[edit_line][key_linepos]) key_linepos++; return; } if ( ( key == K_BACKSPACE ) || ( ( key == 'h' ) && ( keydown[K_CTRL] ) ) ) { if (key_linepos > 1) { memmove (key_lines[edit_line] + key_linepos-1, key_lines[edit_line] + key_linepos, sizeof(key_lines[edit_line])-key_linepos); key_linepos--; } return; } if ( key == K_DEL ) { memmove (key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1, sizeof(key_lines[edit_line])-key_linepos-1); return; } if ( ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) || ( ( key == 'p' ) && keydown[K_CTRL] ) ) { do { history_line = (history_line - 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) history_line = (edit_line+1)&31; strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = (int)strlen(key_lines[edit_line]); return; } if ( ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) || ( ( key == 'n' ) && keydown[K_CTRL] ) ) { if (history_line == edit_line) return; do { history_line = (history_line + 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = '\0'; key_linepos = 1; } else { strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = (int)strlen(key_lines[edit_line]); } return; } if (key == K_PGUP || key == K_KP_PGUP ) { con.display -= 2; return; } if (key == K_PGDN || key == K_KP_PGDN ) { con.display += 2; if (con.display > con.current) con.display = con.current; return; } if (key == K_HOME || key == K_KP_HOME ) { if (keydown[K_CTRL] || !key_lines[edit_line][1] || key_linepos == 1) con.display = con.current - con.totallines + 10; else key_linepos = 1; return; } if (key == K_END || key == K_KP_END ) { int len; len = (int)strlen(key_lines[edit_line]); if (keydown[K_CTRL] || !key_lines[edit_line][1] || key_linepos == len) con.display = con.current; else key_linepos = len; return; } if (key < 32 || key > 127) return; // non printable if (key_linepos < MAXCMDLINE-1) { int last; int length; length = (int)strlen(key_lines[edit_line]); if (length >= MAXCMDLINE-1) return; last = key_lines[edit_line][length]; memmove (key_lines[edit_line] + key_linepos+1, key_lines[edit_line] + key_linepos, length - key_linepos); key_lines[edit_line][key_linepos] = (char)key; key_linepos++; if (!last) key_lines[edit_line][length+1] = 0; } }
/** * @brief Console completion for command and variables * @sa Key_CompleteCommand * @sa Cmd_CompleteCommand * @sa Cvar_CompleteVariable * @param[in] s The string to complete * @param[out] target The target buffer of the completed command/cvar * @param[in] bufSize the target buffer size - might not be bigger than MAXCMDLINE * @param[out] pos The position in the buffer after command completion * @param[in] offset The input buffer position to put the completed command to */ bool Com_ConsoleCompleteCommand (const char* s, char* target, size_t bufSize, uint32_t* pos, uint32_t offset) { const char* cmd = nullptr, *cvar = nullptr, *use = nullptr; char cmdLine[MAXCMDLINE] = ""; char cmdBase[MAXCMDLINE] = ""; bool append = true; if (!s[0] || s[0] == ' ') return false; if (s[0] == '\\' || s[0] == '/') { /* maybe we are using the same buffers - and we want to keep the slashes */ if (s == target) offset++; s++; } assert(bufSize <= MAXCMDLINE); assert(pos); /* don't try to search a command or cvar if we are already in the * parameter stage */ if (strstr(s, " ")) { int cntParams; char* tmp; Q_strncpyz(cmdLine, s, sizeof(cmdLine)); /* remove the last whitespace */ cmdLine[strlen(cmdLine) - 1] = '\0'; tmp = cmdBase; while (*s != ' ') *tmp++ = *s++; /* get rid of the whitespace */ s++; /* terminate the string at whitespace position to separate the cmd */ *tmp = '\0'; /* now strip away that part that is not yet completed */ tmp = strrchr(cmdLine, ' '); if (tmp) *tmp = '\0'; cntParams = Cmd_CompleteCommandParameters(cmdBase, s, &cmd); if (cntParams > 1) Com_Printf("\n"); if (cmd) { /* append the found parameter */ Q_strcat(cmdLine, sizeof(cmdLine), " %s", cmd); append = false; use = cmdLine; } else return false; } else { /* Cmd_GenericCompleteFunction uses one static buffer for output, so backup one completion here if available */ static char cmdBackup[MAX_QPATH]; int cntCvar; int cntCmd = Cmd_CompleteCommand(s, &cmd); if (cmd) Q_strncpyz(cmdBackup, cmd, sizeof(cmdBackup)); cntCvar = Cvar_CompleteVariable(s, &cvar); /* complete as much as possible, append only if one single match is found */ if (cntCmd > 0 && !cntCvar) { use = cmd; if (cntCmd != 1) append = false; } else if (!cntCmd && cntCvar > 0) { use = cvar; if (cntCvar != 1) append = false; } else if (cmd && cvar) { int maxLength = std::min(strlen(cmdBackup),strlen(cvar)); int idx = 0; /* try to find similar content of cvar and cmd match */ Q_strncpyz(cmdLine, cmdBackup,sizeof(cmdLine)); for (; idx < maxLength; idx++) { if (cmdBackup[idx] != cvar[idx]) { cmdLine[idx] = '\0'; break; } } if (idx == maxLength) cmdLine[idx] = '\0'; use = cmdLine; append = false; } } if (use) { Q_strncpyz(&target[offset], use, bufSize - offset); *pos = strlen(target); if (append) target[(*pos)++] = ' '; target[*pos] = '\0'; return true; } return false; }
char *Sys_ConsoleInput (void) { int c; if (consolewindowhandle) { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage (&msg, NULL, 0, 0)) return NULL; TranslateMessage (&msg); DispatchMessage (&msg); } return NULL; } #ifdef SUBSERVERS if (SSV_IsSubServer()) { DWORD avail; static char text[1024]; static int textpos = 0; HANDLE input = GetStdHandle(STD_INPUT_HANDLE); if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL)) { SV_FinalMessage("Cluster shut down\n"); Cmd_ExecuteString("quit force", RESTRICT_LOCAL); } else if (avail) { if (avail > sizeof(text)-1-textpos) avail = sizeof(text)-1-textpos; if (ReadFile(input, text+textpos, avail, &avail, NULL)) { textpos += avail; while(textpos >= 2) { unsigned short len = text[0] | (text[1]<<8); if (textpos >= len && len >= 2) { memcpy(net_message.data, text+2, len-2); net_message.cursize = len-2; MSG_BeginReading (msg_nullnetprim); SSV_ReadFromControlServer(); memmove(text, text+len, textpos - len); textpos -= len; } else break; } } } return NULL; } #endif // read a line out while (_kbhit()) { c = _getch(); if (c == '\r') { coninput_text[coninput_len] = 0; putch ('\n'); putch (']'); coninput_len = 0; return coninput_text; } if (c == 8) { if (coninput_len) { putch (c); putch (' '); putch (c); coninput_len--; coninput_text[coninput_len] = 0; } continue; } if (c == '\t') { int i; char *s = Cmd_CompleteCommand(coninput_text, true, true, 0, NULL); if(s) { for (i = 0; i < coninput_len; i++) putch('\b'); for (i = 0; i < coninput_len; i++) putch(' '); for (i = 0; i < coninput_len; i++) putch('\b'); strcpy(coninput_text, s); coninput_len = strlen(coninput_text); printf("%s", coninput_text); } continue; } putch (c); coninput_text[coninput_len] = c; coninput_len++; coninput_text[coninput_len] = 0; if (coninput_len == sizeof(coninput_text)) coninput_len = 0; } return NULL; }