Author Topic: getting a single keypress?  (Read 9015 times)

Dave Yeo

  • Hero Member
  • *****
  • Posts: 5033
  • Karma: +114/-1
    • View Profile
getting a single keypress?
« on: May 16, 2022, 06:00:21 am »
Trying to fix the terminal control in mpg123 so things like pause work.
Example program which works on other systems but not on OS/2, at that sometimes hangs the system.
Ideas to get this to work?
Code: [Select]

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <termios.h>

int term_fd = -1;

int get_key(int do_delay, char *val)
{
fd_set r;
struct timeval t;
t.tv_sec=0;
t.tv_usec=(do_delay) ? 1000 : 0;
FD_ZERO(&r);
FD_SET(term_fd,&r);
int n = select(term_fd+1,&r,NULL,NULL,&t);
if(n > 0 && FD_ISSET(term_fd,&r) && read(term_fd,val,1) == 1)
return 1;
return 0;
}


int main(int argc, char **argv)
{
if(argc >= 2)
term_fd = open("/dev/tty", O_RDONLY);
else
term_fd = STDIN_FILENO;
if(term_fd < 0)
{
perror("failure opening terminal input");
return 1;
}
fprintf(stderr, "terminal fd: %d\n", term_fd);

struct termios old_tio;
int termsetup = 0;
if(!tcgetattr(term_fd, &old_tio))
{
fprintf(stderr, "proper terminal setup\n");
struct termios tio = old_tio;
tio.c_lflag &= ~(ICANON|ECHO);
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tcsetattr(term_fd,TCSANOW,&tio);
termsetup = 1;
}

char val = 0;
while(val != 'q')
{
if(get_key(1, &val))
fprintf(stderr, "got key: %c\n", val);
}

if(termsetup)
tcsetattr(term_fd,TCSAFLUSH,&old_tio);
if(term_fd > 0)
close(term_fd);
return 0;
}


Pete

  • Hero Member
  • *****
  • Posts: 1343
  • Karma: +11/-0
    • View Profile
Re: getting a single keypress?
« Reply #1 on: May 21, 2022, 02:11:45 am »
Hi Dave

Not really a "c" coder myself but this does not look right

int get_key(int do_delay, char *val)
{
.
.
.
   int n = select(term_fd+1,&r,NULL,NULL,&t);
   if(n > 0 && FD_ISSET(term_fd,&r) && read(term_fd,val,1) == 1)
      return 1;
   return 0;
}


I would expect an If/Else statement:-

   if(n > 0 && FD_ISSET(term_fd,&r) && read(term_fd,val,1) == 1)
      return 1
           else
           return 0;
}


As I said I am not a "c" coder...


Regards

Pete


« Last Edit: May 21, 2022, 02:13:21 am by Pete »

Dave Yeo

  • Hero Member
  • *****
  • Posts: 5033
  • Karma: +114/-1
    • View Profile
Re: getting a single keypress?
« Reply #2 on: May 21, 2022, 03:45:54 am »
Yea, I'm not a C coder either. This is the code we ended up with, modded to also show the hex value,
Code: [Select]
#define INCL_KBD
#define INCL_DOSPROCESS
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <termios.h>

int term_fd = -1;

int get_key(int do_delay, char *val)
{
KBDKEYINFO key;
key.chChar = 0;
key.chScan = 0;
// optionally: sleep for a tiny bit
        DosSleep(5);
if(!KbdCharIn(&key,IO_NOWAIT,0) && key.chChar)
{
*val = key.chChar;
return 1;
}
return 0;
}


int main(int argc, char **argv)
{
if(argc >= 2)
term_fd = open("CON:", O_RDONLY);
else
term_fd = STDIN_FILENO;
if(term_fd < 0)
{
perror("failure opening terminal input");
return 1;
}
fprintf(stderr, "terminal fd: %d\n", term_fd);

struct termios old_tio;
int termsetup = 0;
if(!tcgetattr(term_fd, &old_tio))
{
fprintf(stderr, "proper terminal setup\n");
struct termios tio = old_tio;
tio.c_lflag &= ~(ICANON|ECHO);
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tcsetattr(term_fd,TCSANOW,&tio);
termsetup = 1;
}

char val = 0;
while(val != 'q')
{
if(get_key(1, &val))
fprintf(stderr, "got key: %c %x\n", val, val);
}

if(termsetup)
tcsetattr(term_fd,TCSAFLUSH,&old_tio);
if(term_fd > 0)
close(term_fd);
return 0;
}
Only problem is that it gets a scan code first, which is ignored. Executable attached.
[/code]

Lars

  • Hero Member
  • *****
  • Posts: 1362
  • Karma: +70/-0
    • View Profile
Re: getting a single keypress?
« Reply #3 on: May 21, 2022, 07:26:45 am »
And why can't you use the standard RTL function "getchar"? Is that not available?

Dave Yeo

  • Hero Member
  • *****
  • Posts: 5033
  • Karma: +114/-1
    • View Profile
Re: getting a single keypress?
« Reply #4 on: May 21, 2022, 05:32:15 pm »
It didn't seem to work while a program was running.

Rich Walsh

  • Sr. Member
  • ****
  • Posts: 358
  • Karma: +24/-0
  • ONU! (OS/2 is NOT Unix!)
    • View Profile
Re: getting a single keypress?
« Reply #5 on: May 21, 2022, 06:44:21 pm »
Dave, I saw your post a few days ago but was unable to respond until now. I'm glad you recognized that using select() to get a keystroke on OS/2 is just a crazy-bad idea. And, since this will never be ported back to *nix, the use of KbdCharIn() may be the simplest possible to way avoid using 'stdin' (a method I would never have thought of!).

The good news is that it allows you discard just about every other line of code in your test program. There's no need to open anything - notice that it doesn't require a file descriptor. Unless termio is required elsewhere in the app, it's totally unnecessary as well. The only question I have is whether keyboard input occurs on the same thread as 'mpg123's decode/play code. If they're on separate threads, there's no need for a pointless polling loop - just let it block until a keystroke comes in, no DosSleep() needed. The final version of 'test.exe' should require no more than 8-10 lines of actual code, mostly just to display the keystroke on the console.

Dave Yeo

  • Hero Member
  • *****
  • Posts: 5033
  • Karma: +114/-1
    • View Profile
Re: getting a single keypress?
« Reply #6 on: May 21, 2022, 07:21:31 pm »
Hi Rich, the test program was written by Thomas the maintainer, based on the mpg123 code. It does run on the same thread so the DosSleep()  is required and the file itself is term_posix.c with HAVE_TERMIOS a requirement and Thomas did add the comment,
Code: [Select]
#ifdef __OS2__
// Hoping for properly working termios in some future (?!), but until then,
// we need keyboard access bypassing that.

It does seem to work well.