Page 1 of 1

Move cursor to screen home with TIMEOUT command!

Posted: 30 Oct 2015 22:40
by Aacini
Some time ago I realized that "timeout" command moves the cursor in order to show the seconds countdown in the same place. I tried to make good use of this point, with no luck.

Today I read this SO question entitled "ask set /p variable on a different line" that request to get user input with this format:

Code: Select all

enter your name
name:_(input would be here)
enter name above

I remembered timeout behavior and after several tests I discovered that when timeout is started in a separate cmd.exe session (i.e. at any side of a pipeline or in a FOR /F command), then the "previous" cursor position is lost and timeout show the seconds countdown at screen home (position 0,0)! I used this behavior to develop the program below:

Code: Select all

@echo off
setlocal

cls
echo/
echo Enter your name
echo First:
echo Enter first name above
echo/
echo Last:
echo Enter last name above

rem Move cursor to one line below screen size,
rem you must adjust this value to fit your screen.
for /L %%i in (1,1,30) do echo/

rem The line below do this:
rem 1. Show "Waiting for  1 seconds, press a key..." at current line.
rem 2. Move cursor to screen home (position 0,0) and show a "0" there;
rem    this scrolls screen contents and previous "Waiting" line disappear.
rem 3. Timeout command terminate; the cursor remains at second screen line.

for /F %%a in ('timeout /T 1 ^> CON') do rem

echo/
set /P "first=First: "
echo/
echo/
set /P "last=Last:  "
echo/
echo/
echo Name read: "%first% %last%"

The only disadvantage of this method is that a "0" will always appear at screen home. Tested on Windows 8.1.

Antonio

Re: Move cursor to screen home with TIMEOUT command!

Posted: 31 Oct 2015 05:49
by jeb
:o :D Impressive, I lost to hope that it's possible with "normal" commands.

Btw. You don't need the for loop, this works also

Code: Select all

timeout /T 1 > CON

But how it works, why there is the difference with COn and without :?:

And redirecting to a file shows that it seems only be a backspace character.

Re: Move cursor to screen home with TIMEOUT command!

Posted: 31 Oct 2015 07:18
by aGerman
jeb wrote:But how it works, why there is the difference with COn and without :?:

And redirecting to a file shows that it seems only be a backspace character.

I can't explain that behavior but I had a look into timeout.exe. Two API functions can be found that come in handy: WriteConsoleW and SetConsoleCursorPosition. I wasn't able to reproduce this effect with WriteConsoleW and backspace only. Thus, most likely SetConsoleCursorPosition is involved as well even if I still don't understand how this influences the redirection to CON.

Regards
aGerman

Re: Move cursor to screen home with TIMEOUT command!

Posted: 31 Oct 2015 08:59
by penpen
With mode to ensure a specific amount of lines:

Code: Select all

@echo off
setlocal
set "oldModeLines="
for /F "skip=3 tokens=2 delims=:" %%a in ('mode con /status') do if not defined oldModeLines set "oldModeLines=%%~a"
set "oldModeLines=%oldModeLines: =%"
set "modelines=30"

mode con lines=%modelines%

set /A "lines=%modelines%-7-2"

:: ...

rem Move cursor to one line below screen size,
rem you must adjust this value to fit your screen.
:: for /L %%i in (1,1,30) do echo/
for /L %%i in (1,1,%lines%) do echo/

:: ...

rem restore mode
mode con lines=%oldModeLines%


penpen

Edit: I've overseen the important note: "Move cursor to one line below screen size", so i've replaced my post.

Re: Move cursor to screen home with TIMEOUT command!

Posted: 31 Oct 2015 09:57
by penpen
I guess it's a doublebuffer issue:
All echoes print to a buffer, not changing the curor position.
When timeout is executed it reads the cursor position (0, 0).
The timeout.exe flushes the buffer restores the cursor position (0,0) and writes "0" at the cursor position.

But i'm unsure, because of that:
If you don't wait and press a key (for example a) before the timeout expires the cursor position is set after the text.
(Why should the command line interpreter flush the outputstream to the console when input arrives?)
(Edit: I really hope cmd.exe doesn't use the same buffer for both: Input and output... .)


penpen

Re: Move cursor to screen home with TIMEOUT command!

Posted: 05 Nov 2015 04:13
by jeb
I played a bit with the timeout trick and it's possible to delete the first output of the timout command.
This line can be replaced with any content, in the expamle dots are used.
Gewartet wird 1 Sekunden. Weiter mit beliebiger Taste...
.........................................................


And it's also possible to access the top line of the screen, but currently I can't remove the 0 at the very end

Code: Select all

@echo off
setlocal EnableDelayedExpansion
call :backspace
call :CR
(SET LF=^
%=EMPTY=%
)
if "%1" == "out" (
  (echo(!CR!..................................................................)
  ping -n 2 localhost > NUL
  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=
  echo **** 2-
  exit /b
)
cls
for /L %%n in ( 1 1 30) do echo .... %%n

timeout /t 4 > con | "%~f0" out
echo **** 3-
echo !LF!!LF!**** 6-
for /L %%n in ( 1 1 30) do echo(
exit /b

:backspace
for /F %%A in ('"prompt $H & echo on & FOR %%B in (1) do REM"') do (
    set "BS=%%A"
)
exit /b

:CR
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "cr=%%C"
exit /b

Re: Move cursor to screen home with TIMEOUT command!

Posted: 05 Nov 2015 05:19
by penpen
To avoid the unwanted behaviour when pressing a key immediately after the timeout is executed, just use the "/nobreak" parameter.

penpen

Re: Move cursor to screen home with TIMEOUT command!

Posted: 16 Nov 2015 06:42
by Ed Dyreen
aGerman wrote:I can't explain that behavior but I had a look into timeout.exe. Two API functions can be found that come in handy: WriteConsoleW and SetConsoleCursorPosition. I wasn't able to reproduce this effect with WriteConsoleW and backspace only. Thus, most likely SetConsoleCursorPosition is involved as well even if I still don't understand how this influences the redirection to CON.
How did you figure out timeout.exe uses WriteConsoleW and SetConsoleCursorPosition ? Did you decompile it ? how ? :shock:

Re: Move cursor to screen home with TIMEOUT command!

Posted: 16 Nov 2015 09:17
by Aacini
The .exe file format include a table of the functions that the program uses; this table is used by the program loader when the program start execution. The good news is that function names are stored in plain Ascii, so you only need to open the .exe file with any text editor (like Notepad) in order to see this table.

For example, this is the table of timeout.exe:

Code: Select all

x  «w  ♠w  ­v  µv  ▄v  ╬v  ∟y  tw      és  ls      Þ♣VerSetConditionMask e♣RtlVe
rifyVersionInfo  ntdll.dll 0☻LoadStringW < CharUpperW  USER32.dll  WS2_32.dll  ←
☺StrChrIW  ▲☺StrChrW O☺StrStrIW  R☺StrStrW SHLWAPI.dll ♠ GetFileVersionInfoSizeE
xW ♥ GetFileVersionInfoExW ► VerQueryValueW  VERSION.dll Q☺ExitProcess ä☻GetNumb
erOfConsoleInputEvents æ☺FlushConsoleInputBuffer 9♣SetThreadUILanguage ¯☺GetCons
oleMode  R♣Sleep Ë♦SetConsoleMode  Ã♦SetConsoleCursorPosition  └☻GetStdHandle  ♂
♣SetLastError  ☻♦PeekConsoleInputW ¶☺GetConsoleScreenBufferInfo  >☻GetFileType ├
♦SetConsoleCtrlHandler 7♥HeapSetInformation  H♦ReadConsoleInputW P☻GetLastError
 Ü☺FormatMessageW  ▓♥LocalFree ═♣WideCharToMultiByte ♂♠lstrlenW  ô CompareString
W  Ù☻GetThreadLocale É CompareStringA  Ó♣WriteConsoleW ‗☺GetConsoleOutputCP  ó☻G
etProcessHeap  3♥HeapFree  ;♥HeapValidate  /♥HeapAlloc 6♥HeapReAlloc 8♥HeapSize
 c☻GetModuleFileNameW  KERNEL32.dll  Ñ __iob_func  i☺_errno  ë♣wcstol  ¯♥_vsnwpr
intf U♣time  ■☻_memicmp  å♣wcstod  î♣wcstoul é☺_fileno ┐☺_get_osfhandle  └♦fprin
tf Â♦fflush  q _XcptFilter ╦ __p__commode  ‼☺_amsg_exit  ¾ __wgetmainargs  õ __s
et_app_type  ░♦exit  u☺_exit &☺_cexit  ð __p__fmode  µ __setusermatherr  Û☺_init
term msvcrt.dll  7 ?terminate@@YAXXZ 9☺_controlfp  l☺_except_handler4_common C♣S
etUnhandledExceptionFilter d☻GetModuleHandleA  -♦QueryPerformanceCounter
☻GetCurrentProcessId ♫☻GetCurrentThreadId  Í☻GetSystemTimeAsFileTime ‗☻GetTickCo
unt  é♣UnhandledExceptionFilter         ☻GetCurrentProcess a♣TerminateProcess  ☼
♣memset

This method also allows to discover undocumented switches in a program! 8)

Antonio

Re: Move cursor to screen home with TIMEOUT command!

Posted: 16 Nov 2015 09:52
by Ed Dyreen
Aacini wrote:This method also allows to discover undocumented switches in a program! 8)
Excellent :mrgreen:

Re: Move cursor to screen home with TIMEOUT command!

Posted: 16 Nov 2015 10:47
by Squashman
We discussed some of that in this thread as well.
viewtopic.php?f=3&t=6137

Re: Move cursor to screen home with TIMEOUT command!

Posted: 06 Dec 2015 12:44
by einstein1969
jeb wrote:I played a bit with the timeout trick and it's possible to delete the first output of the timout command.
This line can be replaced with any content, in the expamle dots are used.
Gewartet wird 1 Sekunden. Weiter mit beliebiger Taste...
.........................................................


And it's also possible to access the top line of the screen, but currently I can't remove the 0 at the very end

Code: Select all

@echo off
setlocal EnableDelayedExpansion
call :backspace
call :CR
(SET LF=^
%=EMPTY=%
)
if "%1" == "out" (
  (echo(!CR!..................................................................)
  ping -n 2 localhost > NUL
  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=
  echo **** 2-
  exit /b
)
cls
for /L %%n in ( 1 1 30) do echo .... %%n

timeout /t 4 > con | "%~f0" out
echo **** 3-
echo !LF!!LF!**** 6-
for /L %%n in ( 1 1 30) do echo(
exit /b

:backspace
for /F %%A in ('"prompt $H & echo on & FOR %%B in (1) do REM"') do (
    set "BS=%%A"
)
exit /b

:CR
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "cr=%%C"
exit /b


Hi jeb,

why there is a "set /P v=" ?

Code: Select all

  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=


Einstein1969

Re: Move cursor to screen home with TIMEOUT command!

Posted: 06 Dec 2015 13:33
by einstein1969
It's safe this?

Code: Select all

@echo off
mode 80,34

cls
echo(

For /L %%k in ( 31 -1 1) do (
    for /L %%n in ( 2 1 %%k) do echo(
    echo %%k
    for /L %%n in (%%k 1 30) do echo(
    call :goto_home
)
for /L %%n in (1 1 31) do echo(
pause>nul

exit /b

:goto_home
 timeout /t 1 > con | cmd /Q /C"for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set/p".=.%%C                                                                  "
exit /b


Einstein1969

Re: Move cursor to screen home with TIMEOUT command!

Posted: 25 Mar 2018 18:32
by Aacini
I was really bored, so I take the boat scheme posted at this SO question and used it to complete this program:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

set "n=0"
for %%a in ("                                     |__                                       "
            "                                     |\/                                       "
            "                                     ---                                       "
            "                                     / | [                                     "
            "                              |      | |||                                     "
            "                            _/|     _/|-++'                                    "
            "                        +  +--|    |--|--|_ |-                                 "
            "                     { /|__|  |/\__|  |--- |||__/                              "
            "                    +---------------___[}-_===_.'____                 /\       "
            "                ____`-' ||___-{]_| _[}-  |     |_[___\==--            \/   _   "
            " __..._____--==/___]_|__|_____________________________[___\==--____,------' 7  "
            "|                                                                     BB-61/   "
            "\_________________________________________________________________________/    "
           ) do (
   set /A n+=1
   set "line[!n!]=%%~a"
)
set "spaces="
for /L %%i in (1,1,79) do set "spaces=!spaces! "
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"
for /F "skip=4 delims=pR tokens=2" %%a in ('reg query hkcu\environment /v temp' ) do set "TAB=%%a"
for /F "tokens=2 delims=0" %%a in ('shutdown /? ^| findstr /BC:E') do if not defined TAB set "TAB=%%a"

mode 80
color 1F

for /L %%j in (78,-1,0) do (
   for /L %%i in (1,1,%n%) do echo/!line[%%i]:~%%j!
   call :MoveCursorHome
)
for /L %%j in (78,-1,0) do (
   for /L %%i in (1,1,%n%) do echo/!spaces:~%%j!!line[%%i]:~0^,%%j!
   call :MoveCursorHome
)
goto :EOF


:MoveCursorHome
timeout /T 1 > CON | cmd /Q /C for /F %%C in ('copy /Z "%~F0" NUL') do set /P "=.%%C%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%" ^& set /P "=.%%C%TAB%%BS%%BS%%%C %%C"
exit /B
Antonio