www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Getch() Problem: C vs D

reply LouisHK <louishk gmail.com> writes:
Hello, I minimize the problem to identify the problem:

Here the C version:

#include <stdio.h>
int main(){
    int c;
    while(c != 27){
        printf("%d\n", (c = getch()));
    }
    return 0;
}

And works fine, but the D version below nothing happens when I 
hit ESCAPE:

import std.stdio;
extern (C) int getch();
int main(){
    int c;
    while(c != 27){
        printf("%d\n", (c = getch()));
    }
    return 0;
}

Is this a bug or there is another approach?

Thanks,

L.
Jan 08
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Sunday, 8 January 2017 at 21:19:15 UTC, LouisHK wrote:
 And works fine, but the D version below nothing happens when I 
 hit ESCAPE:

 Is this a bug or there is another approach?
Could this be because of maybe somehow it handles the console input? Kinda like how shift and different keys are toggles rather than dedicated to specific codes? Regardless, try ^[ ( Ctrl+[ ), which is 27 and ^] is 29.
Jan 08
parent LouisHK <louishk gmail.com> writes:
On Monday, 9 January 2017 at 04:31:04 UTC, Era Scarecrow wrote:
 On Sunday, 8 January 2017 at 21:19:15 UTC, LouisHK wrote:
 ...
  Regardless, try ^[ ( Ctrl+[ ), which is 27 and ^] is 29.
Interesting, I'll try that at home. And maybe it will work. One more thing: I had tried the C version with gcc compiler, and it worked ok. But with DMC (Digital Mars Compiler) like DMD it does nothing when I hit (ESCAPE, INSERT, DELETE...). So I think this is a behaviour from Digital Mars compilers. L.
Jan 09
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 8 January 2017 at 21:19:15 UTC, LouisHK wrote:
 And works fine, but the D version below nothing happens when I 
 hit ESCAPE:
Different runtimes are free to line buffer data, meaning getch won't actually see anything until you hit enter. (Actually, the operating system does the buffering unless the runtime specifically asks it not to.) Looks like your C library (gcc? asks the OS not to buffer, and the D (digital mars or Microsoft, DM on 32 bit, MS on 64 bit) turn it off. Best solution is to skip those C library functions and do it yourself with the OS-level calls. Then you can turn it off and actually control the buffering behavior.
Jan 09
parent reply LouisHK <louishk gmail.com> writes:
On Monday, 9 January 2017 at 13:10:30 UTC, Adam D. Ruppe wrote:
 ...
 Best solution is to skip those C library functions and do it 
 yourself with the OS-level calls. Then you can turn it off and 
 actually control the buffering behavior.
That's what I was afraid of. I even tried your terminal.d, and it worked for those keys above, but unfortunately it's duplicating the values, one little example (From your source): if(input.kbhit()){ auto c = input.getch(); write(c); } As I could see with WinDBG, the condition "input.kbhit()" is true for the first 2 times after I hit a key. L.
Jan 09
parent reply Ivan Kazmenko <gassa mail.ru> writes:
On Monday, 9 January 2017 at 17:22:41 UTC, LouisHK wrote:
 On Monday, 9 January 2017 at 13:10:30 UTC, Adam D. Ruppe wrote:
 ...
 Best solution is to skip those C library functions and do it 
 yourself with the OS-level calls. Then you can turn it off and 
 actually control the buffering behavior.
That's what I was afraid of. I even tried your terminal.d, and it worked for those keys above, but unfortunately it's duplicating the values, one little example (From your source): if(input.kbhit()){ auto c = input.getch(); write(c); } As I could see with WinDBG, the condition "input.kbhit()" is true for the first 2 times after I hit a key.
That's because special keys actually put two characters in the buffer, right? Otherwise, using that buffer alone, you won't be able to distinguish, for example, arrow keys from capital Latin letters with the same codes.
Jan 09
parent reply LouisHK <louishk gmail.com> writes:
On Monday, 9 January 2017 at 18:09:21 UTC, Ivan Kazmenko wrote:
 That's because special keys actually put two characters in the 
 buffer, right?  Otherwise, using that buffer alone, you won't 
 be able to distinguish, for example, arrow keys from capital 
 Latin letters with the same codes.
No, that duplicate problem occurs even on normal keys, if I press "a" it shows "aa", and through the WinDBG, I saw the kbhit() was always true 2x after a key is pressed. And I think the kbhit() (Like in many other libraries) would only return true, when a key is pressed. I even tried to delay using sleep() after a keystroke, but it still duplicates after I press any key. L.
Jan 09
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 9 January 2017 at 19:11:48 UTC, LouisHK wrote:
 No, that duplicate problem occurs even on normal keys, if I 
 press "a" it shows "aa", and through the WinDBG, I saw the 
 kbhit() was always true 2x after a key is pressed.
I think my lib returns on key *release* as well, since it registers that on Windows. Probably a bug, though I don't like using the getch function, I usually use the full input stream.
Jan 09
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Monday, 9 January 2017 at 20:12:38 UTC, Adam D. Ruppe wrote:
 Probably a bug, though I don't like using the getch function, I 
 usually use the full input stream.
For direct interactions (a game menu or similar) getting individual characters makes sense; I can't help but think Rogue-likes. However for data input (per line basis) or doing bulk data/processing, it doesn't work well. Something to comment on, a while back when I was first getting into C and MS-DOS assembly programming, I did a direct file-copy using only one character input/write at a time. A meg sized file probably took a minute or so while if I did something as small as a 4k buffer it took moments (approx 8000x faster). This was back in 1996 or so, still it's obvious the advantages of working in bulk.
Jan 09
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 9 January 2017 at 23:33:45 UTC, Era Scarecrow wrote:
  For direct interactions (a game menu or similar) getting 
 individual characters makes sense; I can't help but think 
 Rogue-likes. However for data input (per line basis) or doing 
 bulk data/processing, it doesn't work well.
Well, line processing depends on which level you're working on. My terminal.d includes a getline() function which gives the high level interface on a line level, but its implementation uses the individual events (and you can hook those for customization btw) because it allows the library to provide a much richer UX - line navigation with arrow keys, editing, insertion, etc. Bulk I/O is a major win too, but with direct user interaction, the bottleneck is (almost always nowadays) the user's actual input - even quick typists are insanely slow sources of data as far as the computer is concerned. And there, being able to arrow, backspace, etc., is a huge productivity win for them and seeing the individual events gives the program the control it needs to make that work well.
Jan 09
prev sibling parent reply LouisHK <louishk gmail.com> writes:
On Monday, 9 January 2017 at 20:12:38 UTC, Adam D. Ruppe wrote:
 Probably a bug, though I don't like using the getch function, I 
 usually use the full input stream.
So, I thought a little bit and I changed the terminal.d to check on the KeyEvent if the KeyEvent.bKeyDown is true, otherwise assigns a null event, and it's working fine and now I can get the ESCAPE key. :) terminal.d is truly amazing, I'm doing a lot of things with it. Thanks, L.
Jan 09
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 10 January 2017 at 01:06:53 UTC, LouisHK wrote:
 So, I thought a little bit and I changed the terminal.d to 
 check on the KeyEvent if the KeyEvent.bKeyDown is true, 
 otherwise assigns a null event, and it's working fine and now I 
 can get the ESCAPE key. :)
What line did you change? Maybe I can merge your change in, though I gotta make sure it doesn't break the key release events (which are supposed to be opt-in, so they should be ignored by default, but they are also supposed to be processable.)
Jan 09
parent reply LouisHK <louishk gmail.com> writes:
On Tuesday, 10 January 2017 at 02:04:07 UTC, Adam D. Ruppe wrote:
 On Tuesday, 10 January 2017 at 01:06:53 UTC, LouisHK wrote:
 So, I thought a little bit and I changed the terminal.d to 
 check on the KeyEvent if the KeyEvent.bKeyDown is true, 
 otherwise assigns a null event, and it's working fine and now 
 I can get the ESCAPE key. :)
What line did you change? ...
So, I was seeing and my version is a bit older than the one in the GitHub, but I looked over it, and I think you should put the code below at the line 1797[1]: if(!buffer[0].KeyEvent.bKeyDown){ return null ;} So, it will stay inside the case KEY_EVENT: and will check the if KEY status is DOWN, otherwise will return null event. Again this worked for me, but my terminal.d version is a bit older, and you may had already fixed, I can't change the version right now to test because I have some legacy code. But I'll try in another computer with the last version. [1]: (https://github.com/adamdruppe/arsd/blob/master/terminal.d#L1797). L.
Jan 09
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 10 January 2017 at 02:37:31 UTC, LouisHK wrote:
 Again this worked for me, but my terminal.d version is a bit 
 older, and you may had already fixed, I can't change the 
 version right now to test because I have some legacy code.
k. I try not to break things very often btw. terminal.d does have a minor change lately but code that worked on like any version over the last i think almost two years should keep working now. If I did it right.
Jan 09
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 10 January 2017 at 02:37:31 UTC, LouisHK wrote:
 So, it will stay inside the case KEY_EVENT: and will check the 
 if KEY status is DOWN, otherwise will return null event.
So, the newest version of terminal.d already has a check for this... but you actually want to REQUEST key release events so kbhit doesn't clash with the system's release events. Try this code on your version: import arsd.terminal; void main() { auto terminal = Terminal(ConsoleOutputType.linear); auto input = RealTimeConsoleInput(&terminal, ConsoleInputFlags.allInputEventsWithRelease); while(true) { if(input.kbhit()) { terminal.write(input.getch()); } else terminal.write("."); terminal.flush(); import core.thread; Thread.sleep(dur!"msecs"(50)); } } It should give a steady series of dots unless you press a key. It should never stop giving dots (that means kbhit returned true when getch wouldn't actually return) and not print extra on release (though note that key repeat may give several down events as you hold it). I might provide a helper function for that input thing eventually... but idk how yet, that RealTimeConsoleInput thing uses RAII to change terminal state so it cannot be copied - you always must pass by pointer, and I can't do that when returning from a function! Maybe I'll do a mixin to make the setup a bit simpler.
Jan 10
parent reply LouisHK <louishk gmail.com> writes:
On Tuesday, 10 January 2017 at 14:48:39 UTC, Adam D. Ruppe wrote:
 ...
 Try this code on your version:
 ...
Well, I'm afraid to say that my version is older than you think: Error: no property 'allInputEventsWithRelease' for type 'int' Unfortunately there is no info in the terminal.d source so I could say how old it's precisely. My main problem is that I can't upgrade DMD right now because the old projects that are running. I'm using DMD: 2.060! Thanks, L.
Jan 10
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 10 January 2017 at 22:14:47 UTC, LouisHK wrote:
 Well, I'm afraid to say that my version is older than you think:

 Error: no property 'allInputEventsWithRelease' for type 'int'
Oh yeah, I added that... I think a year ago.
 My main problem is that I can't upgrade DMD right now because 
 the old projects that are running. I'm using DMD: 2.060!
Oh, I don't think you need to update dmd for the new terminal.d, you can copy yours though, then try the new one, and if it doesn't work just copy back to the old one. But yeah, that's ok either way, your change fixes it for you, so you're good to go! That's one of the reasons why I like to keep my modules simple: so you can can edit them for your own purpose if you like.
Jan 10
parent LouisHK <louishk gmail.com> writes:
On Tuesday, 10 January 2017 at 22:25:04 UTC, Adam D. Ruppe wrote:
 Oh, I don't think you need to update dmd for the new 
 terminal.d, you can copy yours though, then try the new one, 
 and if it doesn't work just copy back to the old one.
I already did that, but unfortunately it didn't work. Lacking some features.
 ... That's one of the reasons why I like to keep my modules 
 simple: so you can can edit them for your own purpose if you 
 like.
And thanks for this. I like your repo because that. It's so easy to import/try/change, like this one. Thanks, L.
Jan 10