7

How do I manipulate console cursor position in dotnet core?

As far as I can see in https://github.com/dotnet/corefx/blob/master/src/System.Console/src/System/Console.cs there is no way to manipualte cursor position.

Does it mean that it's impossible on windows and will be possible on linux via ANSI escape sequences?

poke
  • 369,085
  • 72
  • 557
  • 602
AlfeG
  • 1,475
  • 4
  • 18
  • 33
  • You want to move and control the mouse etc? – Slashy Sep 18 '15 at 07:03
  • @Slashy the console cursor is the .... cursor in the console window. The OP knows what he's asking. The escape sequences should also work in Windows BTW – Panagiotis Kanavos Sep 18 '15 at 07:21
  • @PanagiotisKanavos The Windows Console does not support ANSI escape sequences per se. [You need to load some custom "driver"](http://stackoverflow.com/a/16799175/21567). – Christian.K Sep 18 '15 at 08:08
  • @Christian.K duh - it's been 20 years since I had to position the console cursor! This doesn't mean the escape sequences can't be used though, if a .NET core application runs on Linux. A non-Core application on Windows, or Mono on Linux wouldn't need them either, it could use the positioning methods – Panagiotis Kanavos Sep 18 '15 at 08:12
  • @PanagiotisKanavos: I was refering to the part where you state that escape sequences "should also work in Windows BTW". Not to nitpick, but to prevent the OP from going this road in vain. ;-) So in summary, currently, he'd have to use escape sequences for Linux and the Win32 API (like wrapped in the full .NET framework) on Windows. Best wrapped behind some compat layer of course. – Christian.K Sep 18 '15 at 08:14
  • @Christian.K it looks like .NET Core already has part of this implementation, with platform specific overrides .... – Panagiotis Kanavos Sep 18 '15 at 08:15
  • @PanagiotisKanavos Yeah. But it looks like it is tucked away in the _internal_ `ConsolePal`-class with no public API surface. Strange. – Christian.K Sep 18 '15 at 08:19

1 Answers1

4

In ConsolePal class you have private static IntPtr OutputHandle(that is the handle of the console on which you want to move the cursor), so int this class you have to expose a method to set the cursor position. In this method you have to call system API SetConsoleCursorPosition(IntPtr hConsoleOutput, COORD cursorPosition);. COORD is:

[StructLayout(LayoutKind.Sequential)]
internal struct COORD
{
    internal short X;
    internal short Y;
} 

You can add DllImport of the previous method in Interop.mincore class (because it seems that is here where system DllImport are made), so somewhere where you want you can:

internal partial class Interop
{
    internal partial class mincore
    {
        [DllImport("kernel32.dll", SetLastError=true)]
        internal static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, COORD cursorPosition);
    }
}

The method to expose in ConsolePal can look like this:

public static void SetCursorPosition(int left, int top)
{
    IntPtr consoleOutputHandle = OutputHandle;
    COORD cursorPosition = new COORD {
        X = (short) left,
        Y = (short) top
    };
    Interop.mincore.SetConsoleCursorPosition(consoleOutputHandle, cursorPosition;
}

Note: add to the method some input check and some check on Interop.mincore.SetConsoleCursorPosition returned value

And in Console class simply expose a method that call ConsolePal.SetCursorPosition

public static void SetCursorPosition(int left, int top)
{
     ConsolePal.SetCursorPosition(left, top);
}

I didn't test the code above so it may contain errors.

Edit

As @Jcl stated, it may not be welcome to use a custom version of .NET. In this case you can write a simple class to move the cursor(even this solution is only for Windows):

static class MyAwesomeConsoleExtensions
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(int nStdHandle);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, COORD cursorPosition);
    [StructLayout(LayoutKind.Sequential)]
    private struct COORD
    {
        internal short X;
        internal short Y;
    }
    private const int STD_OUTPUT_HANDLE = -11;

    public static void SetCursorPos(int left, int top)
    {
        IntPtr consoleOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD cursorPosition = new COORD
        {
            X = (short)left,
            Y = (short)top
        };
        SetConsoleCursorPosition(consoleOutputHandle, cursorPosition);
    }
}
Matteo Umili
  • 3,412
  • 1
  • 19
  • 31
  • 1
    Not saying it's not supported (it indeed is), but going native with `DllImport` and the Win32 Api on .NET Core might not be an all that good idea – Jcl Sep 18 '15 at 08:18
  • 1
    This would only work on Windows. I guess the OP could use a Linux implementation (perhaps using ANSI sequences) in a different class, just like .NET core does with ConsolePal.Windows and ConsolePal.Unix.cs – Panagiotis Kanavos Sep 18 '15 at 08:18
  • @Jcl check the repository. ConsolePal *is* a Core class with platform-specific specializations. ConsolePal.Windows.cs does uses Interop and ConsolePal.Unix uses POSIX calls – Panagiotis Kanavos Sep 18 '15 at 08:19
  • Uhm... but ConsolePal is an internal class. How should he use that (short of using reflection of course). – Christian.K Sep 18 '15 at 08:21
  • Oh yes, I know it's indeed supported. Just feel (personal feel) that native calls should be avoided whenever possible outside the actual framework for Core – Jcl Sep 18 '15 at 08:22
  • @Jcl `DllImport` are widely [used](https://github.com/dotnet/corefx/search?utf8=%E2%9C%93&q=DllImport&type=Code) in .NET Core. I'm suggesting to add this call in ConsolePal.Windows.cs, so platform specific, I don't think that this can lead troubles – Matteo Umili Sep 18 '15 at 08:23
  • @Christian.K I was assuming that he can recompile the core, so I suggested him to wrap the call to `ConsolePal.SetCursorPosition` in `Console` class (that is obviously public) – Matteo Umili Sep 18 '15 at 08:25
  • @PanagiotisKanavos My limited knowledge can help only on Windows environment. I hope that the community will help improving the answer with Linux/Unix part – Matteo Umili Sep 18 '15 at 08:27
  • @codroipo if I didn't think having calls to native code outside the framework felt right, I'd feel much worse about having a *custom* build of net core for your app. Again, not saying your answer is wrong, or that it can't be done... it just doesn't *feel* right. But of course, this is just a personal subjective oppinion, not dogma. YMMV – Jcl Sep 18 '15 at 08:32
  • You could always fork and make a feature/pull request, of course :-) – Jcl Sep 18 '15 at 08:37
  • @Jcl I updated the answer with code that doesn't need core recompilation – Matteo Umili Sep 18 '15 at 08:59
  • @codroipo all things said, I'd be a supporter of that code for the "old" net framework... again, I'm just not keen (but let me repeat: *it's something personal*) of using `DllImport` on net core (outside the framework itself)... but it is indeed a solution (for Windows) – Jcl Sep 18 '15 at 09:01
  • 1
    Solution "just for windows" is OK, as long as I can use ANSI escape characters for linux version. Thanks! – AlfeG Sep 18 '15 at 19:20