Получение позиции курсора через escape-последовательность VT-100 консоли Windows 10

Я играю с новой (ограниченной) поддержкой escape-последовательностей VT-100 в консоли Windows 10. Поддерживаемые последовательности задокументированы по адресу https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx.

В частности, меня интересует следующая последовательность, которая сообщает о текущем положении курсора.

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

Ответ передается как ввод с клавиатуры и отображается на экране, но я понятия не имею, как программно использовать эту информацию. В идеале я хотел бы получить значения <n> и <m> в переменных среды из командного файла.

Но если кто-нибудь сможет продемонстрировать, как фиксировать переменные, используя любой язык, то я, возможно, смогу использовать эти знания для разработки эффективной стратегии пакетного файла.

Я могу приблизиться к следующему простому скрипту под названием 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!

--ВЫХОД--

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

C:\test>

Я могу легко проанализировать значения с помощью FOR / F, как только у меня будет ответ в переменной. Проблема, с которой я столкнулся, заключается в том, что я должен вручную нажать клавишу <Enter> после того, как ответ появится на экране, чтобы завершить ввод для моего оператора SET / P. Я не понимаю, куда идти дальше ...

РЕДАКТИРОВАТЬ - последнее требование: я не хочу, чтобы ответ на запрос появлялся на экране, так как это нарушает работу экрана и меняет положение курсора. Я подозреваю, что это самый сложный орешек, что, возможно, невозможно с чистой партией.


person dbenham    schedule 07.07.2016    source источник
comment
Вы проверяли xcopy метод чтения вывода? КогдаSET/p работает, я подозреваю, что xcopy тоже должно работать, вы можете остановиться на R   -  person jeb    schedule 07.07.2016


Ответы (2)


Существенное изменение через три года

Он работает с чтением ответа с использованием XCOPY или REPLACE.

Я использую здесь replace, чтобы избежать языковых проблем.

@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

Основная проблема в том, что XCOPY или REPLACE позволяют мне прочитать один символ из входного потока, но затем очищают оставшийся буфер.

И наоборот, PAUSE считывает один символ, сохраняя оставшийся буфер, но не показывает, какой символ был прочитан.

Чтобы решить эту проблему, я отправляю запрос несколько раз, каждый раз читая разные символы ответа. Для каждой итерации я использую комбинацию из 2 или более операторов PAUSE, за которыми следует REPLACE, чтобы прочитать определенный символ ответа. Каждая итерация использует на PAUSE больше, чем предыдущая итерация, пока я не смогу прочитать завершающий R.

Я разработал эту технику и первоначально разместил ее на странице DosTips - состояния запросов с использованием последовательностей виртуальных терминалов консоли .

person jeb    schedule 07.07.2016
comment
Ура! Я оставил надежду когда-либо получить чистое пакетное решение. Отлично сделано. - person dbenham; 20.02.2020
comment
@dbenham Спасибо за редактирование. Теперь даже я могу понимать свой собственный текст :-) - person jeb; 20.02.2020

У меня НЕ Windows 10, поэтому я не могу пройти ни один тест. Однако, если ответом последовательности Ansi ESC[6n является заполнить буфер ввода с клавиатуры символами ESC[<n>;<m>R, тогда просто необходимо добавить клавишу Enter к такому вводу, чтобы прочитать его с помощью команды SET /P, и это можно сделать с помощью метода SendKeys JScript.

Я также использовал более простой метод, чтобы получить символ ESC в переменной.

ИЗМЕНИТЬ: я изменил код в соответствии с комментариями ...

@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}");

Пожалуйста, выложите результат ...

person Aacini    schedule 07.07.2016
comment
Это должно работать, но это раздражает, когда в фокусе находится другое приложение. - person jeb; 07.07.2016
comment
+1, отлично работает. Я подумал об этом после того, как лег спать. Но решение все еще не окончено. Я забыл упомянуть, что не хочу, чтобы ответ на запрос появлялся на экране - я хочу получить информацию, не нарушая работу экрана и не меняя положение курсора. Я отредактировал свой вопрос. - person dbenham; 07.07.2016
comment
@dbenham Если в фокусе находится другое окно, оно получит ENTER от SendKeys вместо командного окна (только что протестировано) - person jeb; 07.07.2016
comment
@jeb - Ох, интересно. Я сейчас на работе, без W10, поэтому тестировать не могу. Но я подозреваю, что ответ на запрос постигнет та же участь, когда в фокусе появится другое окно! - person dbenham; 07.07.2016
comment
@dbenham Проблема sendkey присутствует в каждой версии Windows (я думаю, но по крайней мере в Win7). Сам запрос должен быть безопасным, поскольку он разработан таким образом (по крайней мере, в нормальном мире Linux) - person jeb; 07.07.2016
comment
Чтобы на экране не отображался ответ на запрос, попробуйте set /p "pos=" > NUL (может не сработать). Проблем с фокусом можно избежать с помощью ActivateApp метода JScript ... - person Aacini; 07.07.2016
comment
Опс! Я имею в виду AppActivate ... - person Aacini; 07.07.2016
comment
Я только что вставил эти две модификации в код - person Aacini; 07.07.2016
comment
Перенаправление не сработало. AppActivate тоже не работает - другое окно сохраняет фокус. В сообщении, которое вы связали, говорится, что он не работает на Win10. Кроме того, я видел это на msdn.microsoft.com /en-us/library/wzcddbek(v=vs.84).aspx Метод может возвращать значение False, если внутренний вызов SetForegroundWindow завершается успешно, но внутренний вызов SetFocus терпит неудачу. Известно, что это происходит, когда целевое окно присоединяется к очереди сообщений другого потока, что может происходить, когда окно размещается другим процессом. Примером является окно командной строки, которое размещается в процессе хоста окон консоли. - person dbenham; 08.07.2016
comment
@jeb - Я подтвердил ваше утверждение - ответ на запрос остается в родительском окне, даже если другое окно имеет фокус. Спасибо. - person dbenham; 08.07.2016