Recent

Author Topic: unread input  (Read 1433 times)

jamie

  • Hero Member
  • *****
  • Posts: 6129
Re: unread input
« Reply #15 on: March 18, 2023, 02:38:07 pm »
Code: Pascal  [Select][+][-]
  1. While Not SeekEOF do;
  2.  

Have you tried that?

Otherwise you could simply loop with the same thing using the READ;

The only true wisdom is knowing you know nothing

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #16 on: March 18, 2023, 03:15:54 pm »
target os is linux, the purpose is writing config file for app.
user runs app --config and follows instructions:
Key "A" will be used for "Action A". Press key "A" 
Key "B" will be used for "Action B". Press key "B"
keys A and B may not be readable by readln, so they are read directly from '/dev/input/'
while user presses A and B, they are kept in buffer, and this is a problem for further readln
If you want to read keys, don't read them from /dev/input, use the raw mode directly, this way you don't have different read states between that and readln. Also have you considered using my TermUtils? Because they already said, they support exactly what you are describing:
Code: Pascal  [Select][+][-]
  1. var
  2.   Term: TTerminal;
  3.   k: TTerminalKey;
  4.   ln: String;
  5. begin
  6.   term := TTerminal.Create;
  7.   try
  8.     WriteLn('Interactive Mode:');
  9.     term.Input.DirectRead := True;
  10.     try
  11.       while true do
  12.       begin
  13.         k := Term.Input.ReadKey;
  14.         if k.SpecialKeyCode = skEscape then // Escape: Exit loop
  15.           break;
  16.         if k.CharValue = 'a' then
  17.           term.Output.WriteLn('User Pressed A');
  18.         if k.CharValue = 'b' then
  19.           term.Output.WriteLn('User Pressed B');
  20.       end;
  21.     finally
  22.       term.Input.DirectRead := False;
  23.     end;
  24.     WriteLn('Back to line based mode:');
  25.     ReadLn(ln);
  26.     WriteLn(ln);
  27.   finally
  28.     term.Free;
  29.   end;
  30. end.

Thaddy

  • Hero Member
  • *****
  • Posts: 14364
  • Sensorship about opinions does not belong here.
Re: unread input
« Reply #17 on: March 18, 2023, 03:21:26 pm »
Code: Pascal  [Select][+][-]
  1. While Not SeekEOF do;
  2.  

Have you tried that?

Otherwise you could simply loop with the same thing using the READ;
Much simpler: EOF(<Handle>) is basic Pascal.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #18 on: March 18, 2023, 03:53:07 pm »
Neither EOF nor SeekEOF will work on StdIn/Input, because it is a pipe, and this is only EOF when the other side is closed. So as long as it was not closed (e.g. by using ctrl+D in the console), this will try to continue to read, and therefore be blocking.

It's not usable to check how much is buffered within a pipe. What can be done is to use select to check if something is in buffer, do a non blocking read (set O_NOBLOCK in the file descriptor attributes on Linux), or try to do a read with the PEEK flag (but I don't know if this works for pipes or only for sockets)
« Last Edit: March 18, 2023, 03:56:43 pm by Warfley »

freemind001

  • New Member
  • *
  • Posts: 35
Re: unread input
« Reply #19 on: March 18, 2023, 03:54:07 pm »
Have you tried that?

Code: Pascal  [Select][+][-]
  1. var
  2.   s: string;
  3. begin
  4.   writeln('Program is doing some job, emulating this with sleep');
  5.   sleep(5000); //press A
  6.   while not SeekEOF do
  7.     Read();
  8.   writeln('Now input B');
  9.   Readln(s);
  10.   writeln('s contains ', s);  

program never goes out of while
seems like there is no EOF in input

Also have you considered using my TermUtils?
Do they handle keys like ctrl, win, pause/break?

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #20 on: March 18, 2023, 04:02:08 pm »
Do they handle keys like ctrl, win, pause/break?
ctrl and win (meta) are only readable as modifiers (i.e. you can recognize ctrl + a, but not ctrl seperately). Pause is actually a bug, which I just found and will fix later today.

Also remember that whats possible is often highly dependent on the terminal emulator used, not everything that xterm does is for example supported by gnome terminal and vice versa
« Last Edit: March 18, 2023, 04:05:31 pm by Warfley »

jamie

  • Hero Member
  • *****
  • Posts: 6129
Re: unread input
« Reply #21 on: March 18, 2023, 04:15:06 pm »
Can't help if things don't work as documented.

have you tried this?

Code: Pascal  [Select][+][-]
  1. Flush(Input);
  2.  

The only true wisdom is knowing you know nothing

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #22 on: March 18, 2023, 04:21:49 pm »
EOF works as it should, it's just that the end of the file for a pipe is not when there is no more input in the buffer, it's when the pipe is closed.

And flush will also not work, because flush is for writing the buffer to the filesystem, but this only makes sense for output files not input files.

Pipes are a bit special, they work more like sockets than "real" files (you can see them as sockets lite, because the sockets api has a bit more functionality than is applicable to pipes)

Thaddy

  • Hero Member
  • *****
  • Posts: 14364
  • Sensorship about opinions does not belong here.
Re: unread input
« Reply #23 on: March 18, 2023, 04:37:49 pm »
Neither EOF nor SeekEOF will work on StdIn/Input
Nonsense. Be careful.
SeekEof - who did that!  >:D -should not be there and Eof() is proper Pascal.

And of course both should work in any terminal.
Really strange remark.Works with :Input, Output, StdIn, StdOut, StdErr and whatever I forgot.... Silly.
« Last Edit: March 18, 2023, 04:41:49 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #24 on: March 18, 2023, 04:47:08 pm »
Yes they do work, but not for the purposes as described here, they don't tell you if there is something in the buffer, but if the pipe was closed on the other end.
I sometimes feel like you don't read my posts fully, because this is literally one comma after the part you quoted:
Quote
Neither EOF nor SeekEOF will work on StdIn/Input, because it is a pipe, and this is only EOF when the other side is closed. So as long as it was not closed (e.g. by using ctrl+D in the console), this will try to continue to read, and therefore be blocking.

Sure I could have been more precise, EOF works in general, but it does not work *as described/expected in this thread*

freemind001

  • New Member
  • *
  • Posts: 35
Re: unread input
« Reply #25 on: March 18, 2023, 05:22:29 pm »
Warfley, for couple of reasons we need to read keys directly from '/dev/input' (keyboard is not a keyboard, but a keyboard-like device, it can send exotic scancodes, we need to track them)
is it possible somehow to "close" the pipe and then "reopen" it to clear the buffer?

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: unread input
« Reply #26 on: March 18, 2023, 06:30:59 pm »
As I've mentioned earlier, the consumption of the data through using raw mode should work easiely. I just taken the functions from TermUtils (removed the Windows specific part) and build the following:
Code: Pascal  [Select][+][-]
  1. program Test;
  2. {$mode ObjFPC}{$H+}
  3.  
  4. uses
  5.   SysUtils, termio, baseunix;
  6.  
  7. function EnableDirectRead(Handle: THandle): Termios;
  8. var
  9.   nTIO: Termios;
  10. begin
  11.   TCGetAttr(Handle, Result);
  12.   nTIO := Result;
  13.   // Raw to not echo the characters inputted
  14.   CFMakeRaw(nTIO);
  15.   // Directly read and don't line buffer
  16.   TCSetAttr(Handle, TCSANOW, nTIO);
  17. end;
  18.  
  19. procedure RestoreDirectRead(Handle: THandle; oldState: Termios);
  20. begin
  21.   TCSetAttr(Handle, TCSANOW, oldState);
  22. end;
  23.  
  24. function CharAvailable(Handle: THandle): Boolean;
  25. var
  26.   fdSet: TFDSet;
  27.   timeout: TTimeVal;
  28. begin
  29.   fpFD_ZERO(fdSet);
  30.   fpFD_SET(Handle, fdSet);
  31.   timeout.tv_sec:=0;
  32.   timeout.tv_usec:=0;
  33.   Result := fpSelect(Handle + 1, @fdSet, nil, nil, @timeout) = 1;
  34. end;
  35.  
  36. procedure DiscardBuffered(handle: THandle);
  37. var
  38.   oldState: Termios;
  39.   buff: array[0..1023] of Byte;
  40. begin
  41.   oldState := EnableDirectRead(handle);
  42.   try
  43.     while CharAvailable(Handle) do
  44.       fpRead(handle, buff, sizeOf(Buff));
  45.   finally
  46.     RestoreDirectRead(handle, oldState);
  47.   end;
  48. end;
  49.  
  50. var
  51.   ln: String;
  52. begin
  53.   Sleep(5000);
  54.   WriteLn('test');
  55.   DiscardBuffered(StdInputHandle);
  56.   ReadLn(ln);
  57.   WriteLn(ln);
  58. end.

This works on my linux system. If you don't want the inputs to be printed to the screen, you can slightly modify it:
Code: Pascal  [Select][+][-]
  1. program Test;
  2. {$mode ObjFPC}{$H+}
  3.  
  4. uses
  5.   SysUtils, termio, baseunix;
  6.  
  7. function EnableDirectRead(Handle: THandle): Termios;
  8. var
  9.   nTIO: Termios;
  10. begin
  11.   TCGetAttr(Handle, Result);
  12.   nTIO := Result;
  13.   // Raw to not echo the characters inputted
  14.   CFMakeRaw(nTIO);
  15.   // Directly read and don't line buffer
  16.   TCSetAttr(Handle, TCSANOW, nTIO);
  17. end;
  18.  
  19. procedure RestoreDirectRead(Handle: THandle; oldState: Termios);
  20. begin
  21.   TCSetAttr(Handle, TCSANOW, oldState);
  22. end;
  23.  
  24. function CharAvailable(Handle: THandle): Boolean;
  25. var
  26.   fdSet: TFDSet;
  27.   timeout: TTimeVal;
  28. begin
  29.   fpFD_ZERO(fdSet);
  30.   fpFD_SET(Handle, fdSet);
  31.   timeout.tv_sec:=0;
  32.   timeout.tv_usec:=0;
  33.   Result := fpSelect(Handle + 1, @fdSet, nil, nil, @timeout) = 1;
  34. end;
  35.  
  36. procedure DiscardBuffered(handle: THandle);
  37. var
  38.   buff: array[0..1023] of Byte;
  39. begin
  40.   while CharAvailable(Handle) do
  41.     fpRead(handle, buff, sizeOf(Buff));
  42. end;
  43.  
  44. var
  45.   oldState: Termios;
  46.   ln: String;
  47. begin
  48.   oldState := EnableDirectRead(StdInputHandle);
  49.   try
  50.     Sleep(5000);
  51.   finally
  52.     RestoreDirectRead(StdInputHandle, oldState);
  53.   end;
  54.   WriteLn('test');
  55.   DiscardBuffered(StdInputHandle);
  56.   ReadLn(ln);
  57.   WriteLn(ln);
  58. end.

Between the EnableDirectRead and RestoreDirectRead, all inputs the user makes will not be echoed to the terminal. So you can just surround the block where you don't need the user input by that, then discard the buffer afterwards and then start reading using Readln

freemind001

  • New Member
  • *
  • Posts: 35
Re: unread input
« Reply #27 on: March 18, 2023, 06:44:40 pm »
works, thank you!

Kays

  • Hero Member
  • *****
  • Posts: 574
  • Whasup!?
    • KaiBurghardt.de
Re: unread input
« Reply #28 on: March 18, 2023, 06:45:35 pm »
And flush will also not work, because flush is for writing the buffer to the filesystem, but this only makes sense for output files not input files.
On the contrary, flush is correct. However system.flush will, as you have also correctly stated, only work as desired on buffers managed by our Pascal program, in other words output files. You will need to ask the terminal driver to flush its buffer.
Code: Pascal  [Select][+][-]
  1. uses
  2.         baseUnix;
  3. const
  4.         TCFLSH = $0000540B;
  5. type
  6.         queueSelector = (TCIFLUSH, TCOFLUSH, TCIOFLUSH);
  7. begin
  8.         { Determine first if STDIN is connected to a terminal, then: }
  9.         fpIOCtl(stdInputHandle, TCFLSH, pointer(ord(TCIFLUSH)));
  10. end.
« Last Edit: March 18, 2023, 06:55:18 pm by Kays »
Yours Sincerely
Kai Burghardt

 

TinyPortal © 2005-2018