Minecraft-Console-Client icon indicating copy to clipboard operation
Minecraft-Console-Client copied to clipboard

Fix Console IO for Chinese keyboards

Open ElementClock opened this issue 5 years ago • 11 comments

For a Chinese player there is such trouble that sometimes using backspace after entering characters cannot accurately delete characters.

ElementClock avatar Apr 13 '20 12:04 ElementClock

Chinese character takes two spaces on console. When backspace is pressed, only one space is cleared, leaving behind a space on the console that cannot be deleted. Not only backspace, left and right arrow key also not work with Chinese characters. @ORelio I can help you on this issue if you need as I know Chinese :)

ReinforceZwei avatar Apr 13 '20 15:04 ReinforceZwei

The culprit is ConsoleIO.cs. This class handles the chat-like prompt that allows you to type while messages are being printed in chat. I could only test it using European keyboard where each character fits in a single byte. You can start looking at RemoveOneChar(). I think you'd need to check what kind of character is sitting behind the cursor and adjust the deletion mechanism. Then repeat the same process with GoLeft() and GoRight().

ORelio avatar Apr 13 '20 15:04 ORelio

I have wrote a similar console like this but in javascript. My way to handle it is clear the whole line and write them back after finish process the key. It may not be the best way but is the easiest solution.

ReinforceZwei avatar Apr 13 '20 15:04 ReinforceZwei

This is also how ConsoleIO handles this in its Write() method. When new text needs to be printed, user input is temporarily removed and printed back.

ORelio avatar Apr 13 '20 20:04 ORelio

@ORelio It is very hard (for me) to modify the current exists code because I don't understand what it is doing. I might re-write almost every method if I attempt to fix this issue. Do you mind about that?

ReinforceZwei avatar Apr 14 '20 11:04 ReinforceZwei

I'd like to say I don't mind, but many bugs were fixed across the years and ConsoleIO has come to be pretty stable although there is still room for improvement (this issue being a proof 😄). I'm afraid rewriting everything from scratch would reintroduce more bugs than it fixes 😕

Here is a summary of how it works:

  • buffer and buffer2 holds text being typed by the User, buffer2 holds text on the right of the cursor if you move back using the Left arrow key
  • ReadLine() handles the reading prompt. It waits for keys to be pressed and perform actions depending on the key being pressed: AddChar() to add a new character typed by the user, RemoveOneChar() to remove a character on press on Backspace, etc, until the user finally presses Enter to validate the input.
  • DebugReadInput() can be used to debug how MCC sees your keypresses. You can invoke it using --keyboard-debug when launching MCC. It should help you understand how MCC sees your keypresses in the ReadLine() method.
  • Write() handles the writing. It makes a copy of buffer and buffer2, then clears everything using ClearLineAndBuffer(), then erases the > prompt using \b escape sequence in terminal and whitespace character. There is also a special case depending on whether the cursor needs to go back to the previous line or not. This allows going back exactly where the cursor was before spawning the input prompt, and resume printing text that was not finishing with a newline character \n. Finally, after performing the write operation, it restores the prompt and buffers.
  • WriteLineFormatted() takes a minecraft-formatted string like hello§1world and breaks it into several Write() calls separated by console color changes
  • ClearLineAndBuffer() is a shortcut for as many GoRight() right arrow key and RemoveOneChar() backspace as necessary to effectively removes everything typed by the user.
  • RemoveOneChar() is called when using backspace. It goes back \b, writes a whitespace then go back again \b to erase the last character. This might be what does not work properly for Chinese characters where you may need multiple backspace \b characters to effectively go back in the terminal. Additionally, there is a special handling if the cursor is at the beginning of a line because \b does not work and the cursor needs to be manually moved at the end of the previous line before printing the white space . Finally, everything on the right of the cursor needs to be printed back to the console, and the cursor is placed back using multiple GoBack() calls.
  • GoBack() moves the cursor back of 1 character to the left. Here again, there's a special case when the cursor needs to go back to the previous line inside the terminal.
  • GoLeft() is called when using left arrow key. It is the same as GoBack() but adjusts buffer and buffer2 accordingly.
  • GoRight() is called when using right arrow key. It moves the cursor of 1 character to the right by printing back the character and adjusting the buffers.
  • AddChar() is called when inserting a new character inside the user input. As with RemoveOneChar() it needs to print back the text on the right of the cursor.

I think you need to adjust the RemoveOneChar() / AddChar() methods that handle addition and removal of a single character, and maybe GoBack() / GoLeft() / GoRight() for moving the cursor inside the input prompt.

Also any debugger with step-by-step code execution like in Visual Studio would help you understand what each line does by running single instructions and seeing how it reflects in the terminal.

ORelio avatar Apr 14 '20 14:04 ORelio

Actually I don't understand what this part do. (the Console.Cursor and Console.Buffer) Are they for handling multiple lines input?

Edited I figured out it myself. It is for multiple lines handle. But GoLeft() or GoBack() do not have multiple lines handling, hence we cannot go back to the previous row (a bug that no one found/reported across the year)

ReinforceZwei avatar Apr 14 '20 15:04 ReinforceZwei

Ok, Chinese characters worked now :smile: See my repo The bug mentioned above have not been fixed yet.

Edited This is still not perfect. Characters may left over if there are CJK char across multiple lines.

ReinforceZwei avatar Apr 15 '20 08:04 ReinforceZwei

Indeed, this is the multiline handling part, when we need to go back to the previous line:

if (Console.CursorLeft == 0) // We are at beginning of line
{
    Console.CursorLeft = Console.BufferWidth - 1; // Move the cursor at right end of terminal
    if (Console.CursorTop > 0) // ... and move the cursor up of to previous line if not the first line
        Console.CursorTop--;
    Console.Write(' '); // Write a whitespace: This will proceed to next line and erase the first character
    Console.CursorLeft = Console.BufferWidth - 1; // Move back the cursor at right and of terminal
    if (Console.CursorTop > 0) // ... And move up to the line above
        Console.CursorTop--;
}
// Otherwise, move back 1 character, write a whitespace, move back again of 1 character. Done.
else Console.Write("\b \b");

Just had a look to your modified file. Apparently, the \b backspace character was indeed the culprit since you can get around this by using multiple \b. In that case, what if we manually move the cursor back instead of using \b at all? It might fix that kind of issue for any other character set, not just Chinese. Attempted that in commit e4cae97dd77fdb1f4b4876a847aa91f802c28569. It has no downside for Western character sets, but I could not test its effect on Eastern character sets. Could you try it out for me?

ORelio avatar Apr 15 '20 12:04 ORelio

I have tested sending a left arrow key instead of \b in the GoLeft() method but no luck. I will test your code when I get home 😊

ReinforceZwei avatar Apr 15 '20 13:04 ReinforceZwei

@ORelio I have tested the code and unfortunately, it didn't work :cry: It just act like before.

ReinforceZwei avatar Apr 15 '20 14:04 ReinforceZwei

Closing due to being stale. Use the latest release, as since the issue was opened a lot of things have been changed. Re-open the issue if you still have it.

milutinke avatar Oct 22 '22 13:10 milutinke