5

I'm playing around with the new (limited) support for VT-100 escape sequences within the Windows 10 console. The supported sequences are documented at https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx.

In particular, the following sequence that reports the current cursor position interests me.

ESC[6n - responds with ESC[<n>;<m>R, 
         where <n> is the row number, and <m> the column number

The response is passed off as keyboard input, and appears on the screen, but I have no idea how to programmatically make use of the information. Ideally I would like to get the <n> and <m> values into environment variables from within a batch file.

But if anyone can demonstrate how to capture the variables using any language, then I may be able to use that knowledge to develop an effective batch file strategy.

I can get close with the following simple script called ANSI.BAT

@echo off
setlocal enableDelayedExpansion

for /f "delims=" %%C in (
  'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x1B"'
) do set "esc=%%C"
set "csi=%esc%["

echo(Inquiry:%csi%6n
set /p "pos="
echo response=!pos:%esc%=ESC!

--OUTPUT--

C:\test>ansi
Inquiry:
^[[3;9R
response=ESC[3;9R

C:\test>

I can easily parse out the values using FOR /F, once I have the response in a variable. The problem I am having is I must manually press the <Enter> key after the response appears on the screen in order to terminate the input for my SET /P statement. I am stumped on where to go from here...

EDIT - One last requirement: I don't want the inquiry response to appear on the screen, as that disrupts the screen, and changes the cursor position. I suspect this may be the toughest nut to crack, perhaps impossible with pure batch.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Did you tested the `xcopy` method to read the output? When`SET/p` works, I suspect `xcopy` should also work, you could stop at he `R` – jeb Jul 07 '16 at 05:12

2 Answers2

6

Major change after three years

It works with reading the response by using XCOPY or REPLACE.

I'm using replace here, to avoid language dependent problems.

@echo off
for /F "delims=#" %%a in ('"prompt #$E# & for %%a in (1) do rem"') do set "ESC=%%a"

call :get_cursor_pos
exit /b

:get_cursor_pos
set "response="
set pos=2

:_get_loop
REM *** Request Cursor position
<nul set /p "=%ESC%[6n" 
FOR /L %%# in (1 1 %pos%) DO pause < CON > NUL

for /F "tokens=1 skip=1 eol=" %%C in ('"REPLACE /W ? . < con"') DO (
    set "char=%%C"
)
set "response=%response%%char%"
set /a pos+=1
if "%char%" NEQ "R" goto :_get_loop

set response
exit /b

The main problem is, XCOPY or REPLACE allows me to read one character from the input stream, but then clears the remaining buffer.

Conversely, PAUSE reads one character, preserving the remaining buffer, but does not reveal what character was read.

To solve this, I issue the query multiple times, reading a different character of the response each time. For each iteration I use a combination of 2 or more PAUSE statements followed by REPLACE to read a specific character of the response. Each iteration uses one more PAUSE than the prior iteration, until I am able to read the terminating R.

I developed this technique and initially posted it at DosTips - Query States using Console Virtual Terminal Sequences.

dbenham
  • 127,446
  • 28
  • 251
  • 390
jeb
  • 78,592
  • 17
  • 171
  • 225
2

I have NOT Windows 10, so I can't complete any test. However, if the response of the Ansi ESC[6n sequence is fill the keyboard input buffer with ESC[<n>;<m>R characters, then it is just necessary to add an Enter key to such input in order to read it via SET /P command, and this can be done via SendKeys JScript method.

I also used a simpler method to get an ESC character in a variable.

EDIT: I modified the code accordingly to comments...

@if (@CodeSegment == @Batch) @then

@echo off
title Ansi Test
setlocal EnableDelayedExpansion

for /F %%a in ('echo prompt $E ^| cmd') do set "esc=%%a"
set "csi=%esc%["

echo Inquiry:%csi%6n
cscript //nologo //E:JScript "%~F0"
set /p "pos=" > NUL
echo response=!pos:%esc%=ESC!

@end

var sh = WScript.CreateObject("WScript.Shell");
sh.AppActivate("Ansi Test");
sh.SendKeys("{ENTER}");

Please, post the result...

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • This should work, but it gets annoying when another application has the focus. – jeb Jul 07 '16 at 09:28
  • +1, Works great. I thought of this after I went to bed. But the solution is still not complete. I forgot to mention that I don't want the inquiry response to appear on the screen - I want to get the info without disrupting the screen or changing the cursor position. I've edited my question. – dbenham Jul 07 '16 at 11:57
  • @dbenham If an other window has the focus, it will receives the ENTER from SendKeys instead of the command window (Just tested) – jeb Jul 07 '16 at 13:34
  • @jeb - Ooh, interesting. I'm at work now, without W10, so I can't test. But I suspect the inquiry response will suffer the same fate when another window has focus! – dbenham Jul 07 '16 at 13:45
  • @dbenham The sendkey problem is present in every Windows version (I guess, but at least in Win7). The inquiry itself should be safe, as it's designed in that way (at least in the _normal_ linux world) – jeb Jul 07 '16 at 13:47
  • To avoid the inquiry response appear on the screen, try `set /p "pos=" > NUL` (may not work). The focus problem may be avoided via `ActivateApp` JScript method... – Aacini Jul 07 '16 at 13:59
  • Ops! I mean [AppActivate](http://stackoverflow.com/questions/36932227/keeping-batch-console-on-the-screen-as-active-window/36944773#36944773)... – Aacini Jul 07 '16 at 14:07
  • I just inserted these two modifications in the code – Aacini Jul 07 '16 at 14:20
  • Redirectiion didn't work. Neither did AppActivate - the other Window retains focus. The post that you linked talks about it not working on Win10. Also, I saw this at https://msdn.microsoft.com/en-us/library/wzcddbek(v=vs.84).aspx *"The method can return False when an internal call to SetForegroundWindow succeeds but an internal call to SetFocus fails. This is known to occur when the target window is attached to another thread's message queue, which can occur when a window is hosted by another process. An example is a Command Prompt window that is hosted by the Console Window Host process."* – dbenham Jul 07 '16 at 23:36
  • @jeb - I confirmed your assertion - the inquiry response remains with the parent window, even if another window has focus. Thanks. – dbenham Jul 07 '16 at 23:38