From ca0b54a3c1f2bc7478325063af35394297eed992 Mon Sep 17 00:00:00 2001 From: Izy Date: Mon, 17 Nov 2014 14:21:17 +0000 Subject: [PATCH] chat patch by Izy Provided by mynick1 on IRC, whitespace- and indent-cleaned, applied against xonotic-v0.7.0 and rebased Patch description for chat: - Added (in the code for the chat) the support for modifiers - Removed the "signed int chat_mode" thing and replaced it with an enum (generally speaking enums allow fast jump tables in switches and the code is easier to update) - TAB completes nicks even if you're completing them from within a substring - Ins mode (overwriting mode) by pressing INS on the keyboard (using modifier ins) - now CTRL+{left|right}ARROW moves the cursor between words (using modifier ctrl) - removed the exception for the commandmode, now all works as in say/say_team - cursor changed to match those used in the console (square = ins, arrow = std) - Support for the right/left arrow buttons and the home/end buttons. - Support for del and backspace. - Edit the text in-place. --- console.c | 60 +++++++++---- keys.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++------ keys.h | 16 +++- 3 files changed, 297 insertions(+), 44 deletions(-) diff --git a/console.c b/console.c index 8dde83d9..622b9fc6 100644 --- a/console.c +++ b/console.c @@ -431,7 +431,7 @@ static void Log_DestBuffer_Flush_NoLock(void) log_dest_buffer[log_dest_buffer_pos++] = 0; if(!NetConn_HaveServerPorts() && !NetConn_HaveClientPorts()) // then temporarily open one - { + { have_opened_temp_sockets = true; NetConn_OpenServerPorts(true); } @@ -710,11 +710,11 @@ Con_MessageMode_f static void Con_MessageMode_f (void) { key_dest = key_message; - chat_mode = 0; // "say" + chat_mode = DP_CHAT_MODE_SAY; // "say" if(Cmd_Argc() > 1) { dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args()); - chat_bufferlen = (unsigned int)strlen(chat_buffer); + chat_bufferlen = chat_cursor = (unsigned int)strlen(chat_buffer); // Izy's patch } } @@ -727,11 +727,11 @@ Con_MessageMode2_f static void Con_MessageMode2_f (void) { key_dest = key_message; - chat_mode = 1; // "say_team" + chat_mode = DP_CHAT_MODE_SAYTEAM; // "say_team" if(Cmd_Argc() > 1) { dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args()); - chat_bufferlen = (unsigned int)strlen(chat_buffer); + chat_bufferlen = chat_cursor = (unsigned int)strlen(chat_buffer); // Izy's patch } } @@ -746,9 +746,9 @@ static void Con_CommandMode_f (void) if(Cmd_Argc() > 1) { dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args()); - chat_bufferlen = (unsigned int)strlen(chat_buffer); + chat_bufferlen = chat_cursor = (unsigned int)strlen(chat_buffer); // Izy's patch } - chat_mode = -1; // command + chat_mode = DP_CHAT_MODE_COMMAND; // command } /* @@ -1830,17 +1830,47 @@ void Con_DrawNotify (void) { //static char *cursor[2] = { "\xee\x80\x8a", "\xee\x80\x8b" }; // { off, on } int colorindex = -1; + // Added by Izy (izy from izysoftware.com) + size_t skiptext; + const char *prefix = ""; const char *cursor; char charbuf16[16]; - cursor = u8_encodech(0xE00A + ((int)(realtime * con_cursorspeed)&1), NULL, charbuf16); - - // LordHavoc: speedup, and other improvements - if (chat_mode < 0) - dpsnprintf(temptext, sizeof(temptext), "]%s%s", chat_buffer, cursor); - else if(chat_mode) - dpsnprintf(temptext, sizeof(temptext), "say_team:%s%s", chat_buffer, cursor); + unsigned onoff; + size_t cursor_size; + size_t sizeofchar_to_replace; + const unsigned magic_numbers[2][2] = { {0xE00A, 0xE00B}, {0xE00A, 0xE08D} }; + onoff = (unsigned)(realtime * con_cursorspeed) & 1; + cursor_size = u8_fromchar(magic_numbers[chat_modifiers.ins ? 0 : 1][onoff], charbuf16, sizeof(charbuf16)); + cursor = charbuf16; + + // Added by Izy (izy from izysoftware.com) + switch(chat_mode) + { + case DP_CHAT_MODE_COMMAND: + prefix = "]"; + break; + case DP_CHAT_MODE_SAY: + prefix = "say:"; + break; + case DP_CHAT_MODE_SAYTEAM: + prefix = "say_team:"; + break; + } + // Added by Izy (izy from izysoftware.com) + if(chat_bufferlen == chat_cursor) + dpsnprintf(temptext, sizeof(temptext), "%s%s%s", prefix, chat_buffer, cursor); else - dpsnprintf(temptext, sizeof(temptext), "say:%s%s", chat_buffer, cursor); + { + dpsnprintf(temptext, sizeof(temptext), "%s%s", prefix, chat_buffer); + if(onoff) + { + skiptext = strlen(prefix); + sizeofchar_to_replace = u8_bytelen(&temptext[chat_cursor+skiptext], 1); + if(sizeofchar_to_replace != cursor_size) + memmove(&temptext[chat_cursor+skiptext+cursor_size], &temptext[chat_cursor+skiptext+sizeofchar_to_replace], chat_bufferlen - (chat_cursor + sizeofchar_to_replace) + 1); + memcpy(&temptext[chat_cursor+skiptext], cursor, cursor_size); + } + } // FIXME word wrap inputsize = (numChatlines ? con_chatsize : con_notifysize).value; diff --git a/keys.c b/keys.c index 32648560..8a57c4b0 100644 --- a/keys.c +++ b/keys.c @@ -1105,7 +1105,7 @@ Key_Console (int key, int unicode) con_backscroll -= ((vid_conheight.integer >> 1) / con_textsize.integer)-3; return; } - + if (key == K_MWHEELUP) { if(keydown[K_CTRL]) @@ -1201,63 +1201,274 @@ Key_Console (int key, int unicode) //============================================================================ -int chat_mode; +dp_chat_mode_t chat_mode; char chat_buffer[MAX_INPUTLINE]; unsigned int chat_bufferlen = 0; +unsigned int chat_cursor = 0; +dp_chat_modifiers_t chat_modifiers = DP_CHAT_MODIFIERS_OFF; + +static void +dp_chat_modifiers_cloning_tool(dp_chat_modifiers_t *dest, const dp_chat_modifiers_t *src) +{ + *dest = *src; +} + +static void +Key_Message_Reset_All_Modifiers (void) // Added by Izy (izy from izysoftware.com) +{ + const dp_chat_modifiers_t off = DP_CHAT_MODIFIERS_OFF; + chat_modifiers = off; +} + +static qboolean +Key_Message_Modifiers_Enabled (void) // Added by Izy (izy from izysoftware.com) +{ + const dp_chat_modifiers_t off = DP_CHAT_MODIFIERS_OFF; + return memcmp(&chat_modifiers, &off, sizeof(off)) != 0; +} + +typedef struct +{ + int key; + int ascii; + qboolean down; +} dp_chat_fakekey_t; + +static void Key_Message_SendFakeKeys(const dp_chat_fakekey_t *fakekeys, const size_t count); static void -Key_Message (int key, int ascii) +Key_Message (int key, int ascii, qboolean down) { char vabuf[1024]; + unsigned int inc; /* Added by Izy (izy from izysoftware.com) - uint */ + size_t dec; /* Added by Izy (izy from izysoftware.com) - size_t */ + unsigned charscounter; + size_t sizeofchar_to_replace; + const dp_chat_fakekey_t fake_backspace[] = {{K_LEFTARROW, 0, true}, {K_DEL, 0, true}}; + const dp_chat_fakekey_t fake_del[] = {{K_DEL, 0, true}}; + + if(key == 133 && ascii == 0) // Added by Izy (izy from izysoftware.com) + chat_modifiers.ctrl = down; + if(key == 147 && ascii == 0 && down) // Added by Izy (izy from izysoftware.com) + chat_modifiers.ins = chat_modifiers.ins == false; + + if(down == false) // Izy's Patch + return; + if (key == K_ENTER || ascii == 10 || ascii == 13) { - if(chat_mode < 0) - Cmd_ExecuteString(chat_buffer, src_command, true); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases! - else - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "%s %s", chat_mode ? "say_team" : "say ", chat_buffer)); + + switch(chat_mode) + { + case DP_CHAT_MODE_COMMAND: + // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases! + Cmd_ExecuteString(chat_buffer, src_command, true); + break; + case DP_CHAT_MODE_SAY: + Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "%s %s", "say", chat_buffer)); + break; + case DP_CHAT_MODE_SAYTEAM: + Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "%s %s", "say_team", chat_buffer)); + break; + } key_dest = key_game; - chat_bufferlen = 0; + chat_bufferlen = chat_cursor = 0; chat_buffer[0] = 0; + Key_Message_Reset_All_Modifiers(); // Izy return; } - // TODO add support for arrow keys and simple editing - if (key == K_ESCAPE) { key_dest = key_game; - chat_bufferlen = 0; + chat_bufferlen = chat_cursor = 0; chat_buffer[0] = 0; + Key_Message_Reset_All_Modifiers(); // Izy return; } - if (key == K_BACKSPACE) { - if (chat_bufferlen) { - chat_bufferlen = (unsigned int)u8_prevbyte(chat_buffer, chat_bufferlen); - chat_buffer[chat_bufferlen] = 0; + // delete char before cursor + if (key == K_BACKSPACE) // Added by Izy (izy from izysoftware.com) + { + if(chat_cursor > 0) + { + // Backspace is always equivalent to K_LEFTARROW + K_DEL + #if 1 // ok, allow the quick fast case + if(chat_cursor == chat_bufferlen) + { + chat_bufferlen = chat_cursor = (unsigned int)u8_prevbyte(chat_buffer, chat_bufferlen); + chat_buffer[chat_bufferlen] = 0; + return; + } + #endif + Key_Message_SendFakeKeys(fake_backspace, sizeof(fake_backspace)/sizeof(fake_backspace[0])); } return; } - if(key == K_TAB) { - chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen); + // delete char on cursor + if (key == K_DEL) // Added by Izy (izy from izysoftware.com) + { + if(chat_cursor < chat_bufferlen) + { + dec = u8_bytelen(&chat_buffer[chat_cursor], 1); + memmove(&chat_buffer[chat_cursor], &chat_buffer[chat_cursor+dec], chat_bufferlen - (chat_cursor + dec) + 1); + chat_bufferlen -= dec; + } return; } - // ctrl+key generates an ascii value < 32 and shows a char from the charmap - if (ascii > 0 && ascii < 32 && utf8_enable.integer) - ascii = 0xE000 + ascii; + // move cursor to the previous character + if (key == K_LEFTARROW) // Added by Izy (izy from izysoftware.com) + { + if (chat_cursor > 0) + { + if(chat_modifiers.ctrl) // move cursor to the previous word + { + chat_cursor = u8_prevbyte(chat_buffer, chat_cursor); + while(chat_cursor > 0 && isspace(chat_buffer[chat_cursor])) + chat_cursor = u8_prevbyte(chat_buffer, chat_cursor); + charscounter = 0; + if(chat_cursor > 0) + do + { + chat_cursor = u8_prevbyte(chat_buffer, chat_cursor); + charscounter++; + } while(chat_cursor > 0 && !isspace(chat_buffer[chat_cursor])); + if(charscounter && chat_cursor) + chat_cursor += u8_bytelen(&chat_buffer[chat_cursor], 1); + return; + } + chat_cursor = u8_prevbyte(chat_buffer, chat_cursor); + } + return; + } + + // move cursor to the next character + if (key == K_RIGHTARROW) // Added by Izy (izy from izysoftware.com) + { + if (chat_cursor < chat_bufferlen) + { + if(chat_modifiers.ctrl) // move cursor to the next word + { + chat_cursor += u8_bytelen(&chat_buffer[chat_cursor], 1); + while(chat_cursor < chat_bufferlen && isspace(chat_buffer[chat_cursor])) + chat_cursor += u8_bytelen(&chat_buffer[chat_cursor], 1); + if(chat_cursor < chat_bufferlen) + do + { + chat_cursor += u8_bytelen(&chat_buffer[chat_cursor], 1); + } while(chat_cursor < chat_bufferlen && !isspace(chat_buffer[chat_cursor])); + return; + } + chat_cursor += u8_bytelen(&chat_buffer[chat_cursor], 1); + } + return; + } + + // move cursor to the first character + if (key == 151 /* HOME */) // Added by Izy (izy from izysoftware.com) + { + chat_cursor = 0; + return; + } + + // move cursor to the last character + if (key == 152 /* END */) // Added by Izy (izy from izysoftware.com) + { + chat_cursor = chat_bufferlen; + return; + } + + if(key == K_TAB) + { + if(chat_cursor == chat_bufferlen) // Added by Izy (izy from izysoftware.com) + chat_cursor = chat_bufferlen = Nicks_CompleteChatLine(chat_buffer, sizeof(chat_buffer), chat_bufferlen); + else + if(chat_cursor > 0) // Added by Izy (izy from izysoftware.com) + { + int nicks; + char nick[sizeof(chat_buffer)]; + memcpy(nick, chat_buffer, chat_cursor); + nick[chat_cursor] = 0; + nicks = Nicks_CompleteChatLine(nick, sizeof(nick), chat_cursor); + if(nicks > 0 && (unsigned)nicks > chat_cursor) + { + inc = nicks - chat_cursor; + if(sizeof(chat_buffer) > chat_bufferlen + inc) + { + memmove(&chat_buffer[chat_cursor+inc], &chat_buffer[chat_cursor], chat_bufferlen - chat_cursor + 1); + memcpy(chat_buffer, nick, nicks); + chat_bufferlen += inc; + chat_cursor += inc; + if(isspace(chat_buffer[chat_cursor])) /* remove the double space */ + Key_Message_SendFakeKeys(fake_del, sizeof(fake_del)/sizeof(fake_del[0])); + } + } + } + return; + } if (chat_bufferlen == sizeof (chat_buffer) - 1) return; // all full - if (!ascii) + if (ascii <= 0) return; // non printable - chat_bufferlen += u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1); + // ctrl+key generates an ascii value < 32 and shows a char from the charmap + if (ascii < 32 && utf8_enable.integer) + ascii = 0xE000 + ascii; - //chat_buffer[chat_bufferlen++] = ascii; - //chat_buffer[chat_bufferlen] = 0; + // Added by Izy (izy from izysoftware.com) + if(chat_cursor == chat_bufferlen) + { + inc = u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1); + chat_bufferlen += inc; + chat_cursor += inc; + } + else + { + char utf8buffer[16]; + inc = u8_fromchar(ascii, utf8buffer, sizeof(utf8buffer)); + if(chat_modifiers.ins) + { + sizeofchar_to_replace = u8_bytelen(&chat_buffer[chat_cursor], 1); + if(sizeofchar_to_replace != inc) + memmove(&chat_buffer[chat_cursor+inc], &chat_buffer[chat_cursor+sizeofchar_to_replace], chat_bufferlen - (chat_cursor + sizeofchar_to_replace) + 1); + memcpy(&chat_buffer[chat_cursor], utf8buffer, inc); + chat_bufferlen = chat_bufferlen - sizeofchar_to_replace + inc; + chat_cursor += inc; + } + else + if(sizeof(chat_buffer) > chat_bufferlen + inc) + { + memmove(&chat_buffer[chat_cursor+inc], &chat_buffer[chat_cursor], chat_bufferlen - chat_cursor + 1); + memcpy(&chat_buffer[chat_cursor], utf8buffer, inc); + chat_bufferlen += inc; + chat_cursor += inc; + } + } +} + + +static void +Key_Message_SendFakeKeys(const dp_chat_fakekey_t *fakekeys, const size_t count) // Added by Izy (izy from izysoftware.com) +{ + dp_chat_modifiers_t modbackup; + unsigned modifiers_enabled; + size_t i; + modifiers_enabled = Key_Message_Modifiers_Enabled(); + if(modifiers_enabled) + { + dp_chat_modifiers_cloning_tool(&modbackup, &chat_modifiers); + Key_Message_Reset_All_Modifiers(); + } + for(i = 0; i < count; i++) + Key_Message(fakekeys[i].key, fakekeys[i].ascii, fakekeys[i].down); + if(modifiers_enabled) + { + dp_chat_modifiers_cloning_tool(&chat_modifiers, &modbackup); + } } //============================================================================ @@ -1853,8 +2064,7 @@ Key_Event (int key, int ascii, qboolean down) break; case key_message: - if (down) - Key_Message (key, ascii); // that'll close the message input + Key_Message (key, ascii, down); // that'll close the message input break; case key_menu: @@ -1953,8 +2163,7 @@ Key_Event (int key, int ascii, qboolean down) switch (keydest) { case key_message: - if (down) - Key_Message (key, ascii); + Key_Message (key, ascii, down); break; case key_menu: case key_menu_grabbed: diff --git a/keys.h b/keys.h index a84eb4c6..d455de41 100644 --- a/keys.h +++ b/keys.h @@ -367,9 +367,23 @@ extern keydest_t key_dest; extern int key_consoleactive; extern char *keybindings[MAX_BINDMAPS][MAX_KEYS]; -extern int chat_mode; // 0 for say, 1 for say_team, -1 for command +typedef enum +{ + DP_CHAT_MODE_COMMAND, + DP_CHAT_MODE_SAY, + DP_CHAT_MODE_SAYTEAM +} dp_chat_mode_t; /* Added by Izy (izy from izysoftware.com) */ +extern dp_chat_mode_t chat_mode; extern char chat_buffer[MAX_INPUTLINE]; extern unsigned int chat_bufferlen; +extern unsigned int chat_cursor; // Added by Izy (izy from izysoftware.com) +typedef struct +{ +#define DP_CHAT_MODIFIERS_OFF { false, false } + qboolean ctrl; + qboolean ins; +} dp_chat_modifiers_t; /* Added by Izy (izy from izysoftware.com) */ +extern dp_chat_modifiers_t chat_modifiers; void Key_ClearEditLine(int edit_line); void Key_WriteBindings(qfile_t *f); -- 2.39.2