www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Get single keystroke?

reply Sean Eskapp <eatingstaples gmail.com> writes:
Is there a way to get a single keystroke in D2? Any method I've tried requires
pushing Enter before the stroke is registered.
Mar 21 2011
next sibling parent reply Lars Holowko <lars.holowko gmail.com> writes:
On Mon, Mar 21, 2011 at 1:33 PM, Sean Eskapp <eatingstaples gmail.com> wrote:
 Is there a way to get a single keystroke in D2? Any method I've tried requires
 pushing Enter before the stroke is registered.

Hi Sean, what you want to do is OS dependent. I needed something similar ('press key to continue') e.g.: for Windows import core.sys.windows.windows; bool kbHit() { // inspired by http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1045691686&id=1043284392 HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD saveMode; GetConsoleMode(stdIn, &saveMode); SetConsoleMode(stdIn, ENABLE_PROCESSED_INPUT); bool ret = false; if (WaitForSingleObject(stdIn, INFINITE) == WAIT_OBJECT_0) { uint num; char ch; ReadConsoleA(stdIn, &ch, 1, &num, cast(void *) 0L); ret = true; } SetConsoleMode(stdIn, saveMode); return ret; } void wait_for_key() { writeln("\nPress any key to continue"); while (!kbHit()) {} } for Linux/Unix something like http://www.linuxquestions.org/questions/programming-9/kbhit-34027/ should work. Hope that helps, Lars
Mar 21 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Lars Holowko:

 what you want to do is OS dependent.

But the need to get a keystroke is simple and not so uncommon, so I think Phobos needs a function to do that that works on both Windows/Linux (and Mac too). Bye, bearophile
Mar 21 2011
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/21/2011 02:42 PM, bearophile wrote:
 Lars Holowko:

 what you want to do is OS dependent.

But the need to get a keystroke is simple and not so uncommon, so I

Windows/Linux (and Mac too). If D sticks to the C (and C++) heritage, there is no guarantee that there is a keyboard around. The interface is stdin, stdout, and stderr; which are just character streams. But I agree: some subset of ncurses would be nice when a keyboard is available.
 Bye,
 bearophile

Ali
Mar 21 2011
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Here's something simpler:

import std.stdio : writefln;

extern(C) int kbhit();
extern(C) int getch();

void main()
{
    while(!kbhit())
    {
        // keep polling
        // might use thread.sleep here.
    }

    writefln("Key hit was %s.", getch());
}
Mar 21 2011
parent reply Sean Eskapp <eatingstaples gmail.com> writes:
== Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 Here's something simpler:
 import std.stdio : writefln;
 extern(C) int kbhit();
 extern(C) int getch();
 void main()
 {
     while(!kbhit())
     {
         // keep polling
         // might use thread.sleep here.
     }
     writefln("Key hit was %s.", getch());
 }

What extra linker dependencies does this add?
Mar 21 2011
parent reply Sean Eskapp <eatingstaples gmail.com> writes:
== Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 On 3/21/11, Sean Eskapp <eatingstaples gmail.com> wrote:
 == Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s


 Here's something simpler:
 import std.stdio : writefln;
 extern(C) int kbhit();
 extern(C) int getch();
 void main()
 {
     while(!kbhit())
     {
         // keep polling
         // might use thread.sleep here.
     }
     writefln("Key hit was %s.", getch());
 }

What extra linker dependencies does this add?


What about on Linux?
Mar 21 2011
next sibling parent Sean Eskapp <hotmail microsoft.com> writes:
== Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 I believe I've found you a solution:
 import std.c.stdio;
 import std.c.linux.termios;
 extern(C) void cfmakeraw(termios *termios_p);
 void main() {
 termios  ostate;                 /* saved tty state */
 termios  nstate;                 /* values for editor mode */
     // Open stdin in raw mode
     /* Adjust output channel        */
 tcgetattr(1, &ostate);                       /* save old state */
 tcgetattr(1, &nstate);                       /* get base of new

 cfmakeraw(&nstate);
 tcsetattr(1, TCSADRAIN, &nstate);      /* set mode */
    // Read characters in raw mode
 writefln("The key hit is %s", cast(char)fgetc(stdin));
     // Close
 tcsetattr(1, TCSADRAIN, &ostate);       // return to original mode
 }
 I've tested this under Ubuntu and DMD 2.052 and it works.

Great, thanks!
Mar 21 2011
prev sibling parent =?ISO-8859-1?Q?Ali_=C7ehreli?= <acehreli yahoo.com> writes:
On 03/21/2011 03:37 PM, Andrej Mitrovic wrote:
 On 3/21/11, teo<teo.ubuntu yahoo.com>  wrote:
 On Mon, 21 Mar 2011 23:22:17 +0100, Andrej Mitrovic wrote:

 Woops forgot to add import std.stdio, here's a fixed version:

 import std.stdio : writefln;
 import std.c.stdio;
 import std.c.linux.termios;

 extern(C) void cfmakeraw(termios *termios_p);

 void main()
 {
      termios  ostate;                 /* saved tty state */ termios
      nstate;                 /* values for editor mode */

         // Open stdin in raw mode
         /* Adjust output channel        */
      tcgetattr(1,&ostate);                       /* save old state */
      tcgetattr(1,&nstate);                       /* get base of new
      state */ cfmakeraw(&nstate);
      tcsetattr(1, TCSADRAIN,&nstate);      /* set mode */

        // Read characters in raw mode
      writefln("The key hit is %s", cast(char)fgetc(stdin));

         // Close
      tcsetattr(1, TCSADRAIN,&ostate);       // return to original mode
 }

It looks like this can be dangerous, because the terminal can be left in an unusable state. Please read this: http://groups.google.com/group/comp.os.linux.development.apps/ browse_thread/thread/0667d16089e2b6fc

H mentions using tcgetattr to save old state and restoring it with tcsetattr. This is what this code does. Btw this code is not mine, Walter posted it and I just added the cfmakeraw prototype. So kudos goes to him.

Thanks for posting this. It can be improved by moving the last tcsetattr to an earlier scope(exit) statement: termios ostate; tcgetattr(1, &ostate); scope (exit) tcsetattr(1, TCSADRAIN, &ostate); Ali
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Replace that writefln with this:

writefln("Key hit was %s.", cast(char)getch());

You should get the right key displayed then.
Mar 21 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
 Lars Holowko:
 what you want to do is OS dependent.

But the need to get a keystroke is simple and not so uncommon, so I think Phobos needs a function to do that that works on both Windows/Linux (and Mac too).

Assuming that it could be done cleanly, it would be a good function to add, but honestly, I dispute that it's a common need in this day and age. If all you need is something like "press any key to continue," you can easily do that with the current functions by having "press enter to continue," and if you need to do something other than that, odds are you need something more powerful like ncurses anyway. Not to mention, I don't think that interactive console apps are all that common these days. They definitely exist, but most console apps essentially run a single command for you and then quit. Most of the types of applications which would have have been interactive console apps in the past are now GUI apps. So, while it certainly wouldn't hurt to add a function like this to Phobos (assuming that it could be done cleanly), I really don't think that it's a common need anymore. - Jonathan M Davis
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/21/11, Sean Eskapp <eatingstaples gmail.com> wrote:
 == Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 Here's something simpler:
 import std.stdio : writefln;
 extern(C) int kbhit();
 extern(C) int getch();
 void main()
 {
     while(!kbhit())
     {
         // keep polling
         // might use thread.sleep here.
     }
     writefln("Key hit was %s.", getch());
 }

What extra linker dependencies does this add?

snn.lib
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/21/11, Sean Eskapp <eatingstaples gmail.com> wrote:
 What about on Linux?

I'm not sure. Both DMD and GDC can run the example on Windows. Perhaps they've got these functions implemented for Linux as well. But according to this: https://secure.wikimedia.org/wikipedia/en/wiki/Conio.h it seems these functions are usually only available for compilers targeting Windows. Have you tried compiling it on Linux, or does it fail to link?
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
It looks like its not available on Linux, I've just tried with DMD.
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I believe I've found you a solution:

import std.c.stdio;
import std.c.linux.termios;

extern(C) void cfmakeraw(termios *termios_p);

void main() {
termios  ostate;                 /* saved tty state */
termios  nstate;                 /* values for editor mode */

    // Open stdin in raw mode
    /* Adjust output channel        */
tcgetattr(1, &ostate);                       /* save old state */
tcgetattr(1, &nstate);                       /* get base of new state */
cfmakeraw(&nstate);
tcsetattr(1, TCSADRAIN, &nstate);      /* set mode */

   // Read characters in raw mode
writefln("The key hit is %s", cast(char)fgetc(stdin));

    // Close
tcsetattr(1, TCSADRAIN, &ostate);       // return to original mode
}

I've tested this under Ubuntu and DMD 2.052 and it works.
Mar 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Woops forgot to add import std.stdio, here's a fixed version:

import std.stdio : writefln;
import std.c.stdio;
import std.c.linux.termios;

extern(C) void cfmakeraw(termios *termios_p);

void main()
{
    termios  ostate;                 /* saved tty state */
    termios  nstate;                 /* values for editor mode */

       // Open stdin in raw mode
       /* Adjust output channel        */
    tcgetattr(1, &ostate);                       /* save old state */
    tcgetattr(1, &nstate);                       /* get base of new state */
    cfmakeraw(&nstate);
    tcsetattr(1, TCSADRAIN, &nstate);      /* set mode */

      // Read characters in raw mode
    writefln("The key hit is %s", cast(char)fgetc(stdin));

       // Close
    tcsetattr(1, TCSADRAIN, &ostate);       // return to original mode
}
Mar 21 2011
prev sibling next sibling parent teo <teo.ubuntu yahoo.com> writes:
On Mon, 21 Mar 2011 23:22:17 +0100, Andrej Mitrovic wrote:

 Woops forgot to add import std.stdio, here's a fixed version:
 
 import std.stdio : writefln;
 import std.c.stdio;
 import std.c.linux.termios;
 
 extern(C) void cfmakeraw(termios *termios_p);
 
 void main()
 {
     termios  ostate;                 /* saved tty state */ termios 
     nstate;                 /* values for editor mode */
 
        // Open stdin in raw mode
        /* Adjust output channel        */
     tcgetattr(1, &ostate);                       /* save old state */
     tcgetattr(1, &nstate);                       /* get base of new
     state */ cfmakeraw(&nstate);
     tcsetattr(1, TCSADRAIN, &nstate);      /* set mode */
 
       // Read characters in raw mode
     writefln("The key hit is %s", cast(char)fgetc(stdin));
 
        // Close
     tcsetattr(1, TCSADRAIN, &ostate);       // return to original mode
 }

It looks like this can be dangerous, because the terminal can be left in an unusable state. Please read this: http://groups.google.com/group/comp.os.linux.development.apps/ browse_thread/thread/0667d16089e2b6fc
Mar 21 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/21/11, teo <teo.ubuntu yahoo.com> wrote:
 On Mon, 21 Mar 2011 23:22:17 +0100, Andrej Mitrovic wrote:

 Woops forgot to add import std.stdio, here's a fixed version:

 import std.stdio : writefln;
 import std.c.stdio;
 import std.c.linux.termios;

 extern(C) void cfmakeraw(termios *termios_p);

 void main()
 {
     termios  ostate;                 /* saved tty state */ termios
     nstate;                 /* values for editor mode */

        // Open stdin in raw mode
        /* Adjust output channel        */
     tcgetattr(1, &ostate);                       /* save old state */
     tcgetattr(1, &nstate);                       /* get base of new
     state */ cfmakeraw(&nstate);
     tcsetattr(1, TCSADRAIN, &nstate);      /* set mode */

       // Read characters in raw mode
     writefln("The key hit is %s", cast(char)fgetc(stdin));

        // Close
     tcsetattr(1, TCSADRAIN, &ostate);       // return to original mode
 }

It looks like this can be dangerous, because the terminal can be left in an unusable state. Please read this: http://groups.google.com/group/comp.os.linux.development.apps/ browse_thread/thread/0667d16089e2b6fc

H mentions using tcgetattr to save old state and restoring it with tcsetattr. This is what this code does. Btw this code is not mine, Walter posted it and I just added the cfmakeraw prototype. So kudos goes to him.
Mar 21 2011