OS2 World Community Forum

OS/2, eCS & ArcaOS - Technical => Programming => Topic started by: Dave Yeo on May 16, 2022, 06:00:21 am

Title: getting a single keypress?
Post by: Dave Yeo 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;
}

Title: Re: getting a single keypress?
Post by: Pete 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


Title: Re: getting a single keypress?
Post by: Dave Yeo 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]
Title: Re: getting a single keypress?
Post by: Lars on May 21, 2022, 07:26:45 am
And why can't you use the standard RTL function "getchar"? Is that not available?
Title: Re: getting a single keypress?
Post by: Dave Yeo on May 21, 2022, 05:32:15 pm
It didn't seem to work while a program was running.
Title: Re: getting a single keypress?
Post by: Rich Walsh 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.
Title: Re: getting a single keypress?
Post by: Dave Yeo 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.