Recent

Author Topic: CRT unit kills Ctrl-C - SOLVED  (Read 820 times)

jollytall

  • Sr. Member
  • ****
  • Posts: 303
CRT unit kills Ctrl-C - SOLVED
« on: June 05, 2023, 10:03:31 am »
The simple problem: If I add Crt to the uses list, the program does not receive Ctrl-C and hence an infinite loop cannot be stopped.

A bit more details:
I am writing a linux terminal program that first switches to an alternate screen and there runs in a loop until Ctrl-C is pressed when it returns to the normal screen. To achieve this I use the #27'[?47h' and #27'[?47l' sequences to actually switch between the normal and the alternate screens and the fpSigAction from BaseUnix to capture the Ctrl-C and do the switching back before exiting the program.
This works almost perfectly, the only problem that when I return to the normal screen then the cursor position is not where it was on the normal screen, but where it is last seen on the alternate screen. I tried to fix it, and added the Crt unit (WhereX, WhereY, GotoXY), what works as intended, but then the Ctrl-C is not working any more.
I  tried even without the fpSigAction, just a normal infinite loop and Ctr-C does not work when Crt is in the uses list.
I found in the documentation a CheckBreak variable, but it says that it is not used.

I would need a solution, either to allow Ctrl-C when Crt is used OR get and set the cursor position without using Crt at all.
« Last Edit: June 05, 2023, 01:41:42 pm by jollytall »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: CRT unit kills Ctrl-C
« Reply #1 on: June 05, 2023, 10:25:23 am »
I can't comment on the Crt finalisation issue (which sounds like a bug to be fixed), but the ^C problem is that the input device is being set to "cooked" mode and you need to partially undo that, i.e. take it to a state that we can usefully call "rare" as distinct from the original "raw".

See e.g. https://github.com/MarkMLl/serialcomms/blob/main/Term.pas

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: CRT unit kills Ctrl-C
« Reply #2 on: June 05, 2023, 10:42:59 am »
Welcome to the weird world of CRT. CRT switches the terminal input to RAW mode and then tries to emulate buffered mode, to pretend it didn't (and this is the source of alot of confusion, by people who think they just use some "normal" functionality). This will result in hotkeys being sent to your application (as keycode, in case of CTRL+C it's #3) rather than triggering events.

I don't know how CRT passes these keycodes to your application, but you can just try to read the input

jollytall

  • Sr. Member
  • ****
  • Posts: 303
Re: CRT unit kills Ctrl-C
« Reply #3 on: June 05, 2023, 10:59:59 am »
Thanks,

@Warfley
Checked it and indeed it works like that. However the control characters to switch to and back from the alternate screen do not work. I guess it is again, because it is in Raw mode, and the Crt unit does not implement the concept of an alternate screen. So, to use the alternate screen mode, I need to drop Crt, but I do not see how to get then the cursor position. (The GotoXY I can easily implement.)

@MarkMLI
As above I give up CRT for this purpose. Any other idea to "read" the cursor pos I would appreciate.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: CRT unit kills Ctrl-C
« Reply #4 on: June 05, 2023, 11:17:21 am »
So how CRT works is that it internally creates a buffer that emulates the complete state of the terminal. Meaning if you write something, it internally emulates how this writing would change the cursor position, and uses this to track the "WhereXY". As soon as you switch the screen, the new screen does not match with the CRT buffer anymore, and therefore CRT will break down at this point.

You can checkout the XTerm ANSI Escape Codes: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
Code: Pascal  [Select][+][-]
  1. CSI Ps n  Device Status Report (DSR).
  2.             Ps = 5  ⇒  Status Report.
  3.           Result ("OK") is CSI 0 n
  4.             Ps = 6  ⇒  Report Cursor Position (CPR) [row;column].
  5.           Result is CSI r ; c R
  6.  
  7.           Note: it is possible for this sequence to be sent by a
  8.           function key.  For example, with the default keyboard
  9.           configuration the shifted F1 key may send (with shift-,
  10.           control-, alt-modifiers)
  11.  
  12.             CSI 1 ; 2  R , or
  13.             CSI 1 ; 5  R , or
  14.             CSI 1 ; 6  R , etc.
  15.  
  16.           The second parameter encodes the modifiers; values range from
  17.           2 to 16.  See the section PC-Style Function Keys for the
  18.           codes.  The modifyFunctionKeys and modifyKeyboard resources
  19.           can change the form of the string sent from the modified F1
  20.           key.

So if your terminal supports that (most XTerm compatible terminal emulators should do), you can just send #27'[6n' and then read the output sequence

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: CRT unit kills Ctrl-C
« Reply #5 on: June 05, 2023, 11:23:54 am »
As above I give up CRT for this purpose. Any other idea to "read" the cursor pos I would appreciate.

Short answer: there isn't one.

Longer answer: position relative to what? As soon as Crt has switched the terminal (as described by termcap/terminfo and manipulated by termios) back to the default mode it can scroll, so while you can generally work out where you are on a line any concept of vertical positioning is meaningless.

A library such as Crt will have its own internal state, which includes an indication of whether it's disabled scrolling and in (only) that case the physical Y position. The kernel will have its own internal state. A library such as (n)curses, used by e.g. the shell via termcap/terminfo, will have its own internal state: you /might/ be able to query this by sending it a carefully-crafted escape sequence but if used on an unexpected output device (see $TERM) you're screwed.

Quite frankly, if Crt does most of what you want it would be worth investigating and fixing its shortcomings. You might even find the core team grateful for it.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Re: CRT unit kills Ctrl-C
« Reply #6 on: June 05, 2023, 12:19:46 pm »
I wonder why you have the Ctrl-C problem only with unit CRT. Since windows 10, using Ctrl-C in a console program stopped working for me entirely, it just does nothing at all. I have tried options such as the "Legacy Mode" but in vain. I can only kill a not responding console, or, a bit less onerous, use Ctrl-Pause to kill the IDE and return to the command shell.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: CRT unit kills Ctrl-C
« Reply #7 on: June 05, 2023, 12:35:03 pm »
I wonder why you have the Ctrl-C problem only with unit CRT. Since windows 10, using Ctrl-C in a console program stopped working for me entirely, it just does nothing at all. I have tried options such as the "Legacy Mode" but in vain. I can only kill a not responding console, or, a bit less onerous, use Ctrl-Pause to kill the IDE and return to the command shell.

OP's explicitly said that he's using Linux.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jollytall

  • Sr. Member
  • ****
  • Posts: 303
Re: CRT unit kills Ctrl-C
« Reply #8 on: June 05, 2023, 12:47:22 pm »
@Warfley: Thanks, it almost works.
Code: Pascal  [Select][+][-]
  1. write(#27'[6n');
  2. read(s);
gives back a string like
Code: Pascal  [Select][+][-]
  1. ^[[9;1R
where 9 is the row (counting from the top of the screen) and 1 is the column. So, it works. Two problems:
- The reply string appears on the screen, though I would prefer it hidden.
- The read stops waiting for an enter (I tried writeLN and readLN in every combination, but it is the same).
How can I read the same string hidden and continue running?

@MarkMLI: Yes, it would be great to make CRT "smarter", but I am afraid my knowledge is far too little to improve it to an alternate screen, full escape sequence handling, signal handling, etc.

@Nitorami: As Mark also said, I on Linux, where Ctrl-C works well.


« Last Edit: June 05, 2023, 01:00:27 pm by jollytall »

jollytall

  • Sr. Member
  • ****
  • Posts: 303
Re: CRT unit kills Ctrl-C - SOLVED
« Reply #9 on: June 05, 2023, 01:43:03 pm »
It seems to be much ado about nothing. Sorry for taking your time.

I just found ESC 7 and ESC 8 what saves the cursor position and restores it. So, I can easily store it before moving to the alternate window and restore it once I am back.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: CRT unit kills Ctrl-C
« Reply #10 on: June 05, 2023, 01:44:09 pm »
@Warfley: Thanks, it almost works.
Code: Pascal  [Select][+][-]
  1. write(#27'[6n');
  2. read(s);
gives back a string like
Code: Pascal  [Select][+][-]
  1. ^[[9;1R
where 9 is the row (counting from the top of the screen) and 1 is the column. So, it works. Two problems:
- The reply string appears on the screen, though I would prefer it hidden.
- The read stops waiting for an enter (I tried writeLN and readLN in every combination, but it is the same).
How can I read the same string hidden and continue running?

I'm currently on Windows so I can't try myself, but you could try to turn the terminal into raw mode, send the request, read the response and then return back to buffered mode.
To enable RAW mode you can look at how I did it here: https://github.com/Warfley/LazTermUtils/blob/master/src/compatibility.pas#L143
Code: Pascal  [Select][+][-]
  1. function EnableDirectRead(Handle: THandle): TDirectReadState;
  2. {$IfDef WINDOWS}
  3. begin
  4.   GetConsoleMode(Handle, Result);
  5.   SetConsoleMode(Handle, Result And Not (ENABLE_ECHO_INPUT Or ENABLE_LINE_INPUT or ENABLE_PROCESSED_INPUT));
  6. end;
  7. {$Else}
  8. var
  9.   nTIO: Termios;
  10.   i: SizeInt;
  11. begin
  12.   TCGetAttr(Handle, Result);
  13.   nTIO := Result;
  14.   // Raw to not echo the characters inputted
  15.   CFMakeRaw(nTIO);
  16.   // Directly read and don't line buffer
  17.   TCSetAttr(Handle, TCSANOW, nTIO);
  18. end;
  19. {$EndIf}
  20.  
  21. procedure RestoreDirectRead(Handle: THandle; oldState: TDirectReadState);
  22. {$IfDef WINDOWS}
  23. begin
  24.   SetConsoleMode(Handle, oldState);
  25. end;
  26. {$Else}
  27. begin
  28.   TCSetAttr(Handle, TCSANOW, oldState);
  29. end;
  30. {$EndIf}

So it would be:
Code: Pascal  [Select][+][-]
  1. OldState := EnableDirectRead(InputHandle);
  2. try
  3.     write(#27'[6n');
  4.     read(s);
  5. finally
  6.   RestoreDirectRead(InputHandle, OldState);
  7. end;
« Last Edit: June 05, 2023, 01:45:56 pm by Warfley »

 

TinyPortal © 2005-2018