235 lines
4.8 KiB
C
235 lines
4.8 KiB
C
|
/*
|
||
|
term_posix: POSIX-specifc terminal functionality
|
||
|
|
||
|
HAVE_TERMIOS is a prerequisite.
|
||
|
|
||
|
copyright 2008-2023 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||
|
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
||
|
initially written by Thomas Orgis
|
||
|
*/
|
||
|
|
||
|
// ctermid
|
||
|
#define _XOPEN_SOURCE 600
|
||
|
#define _POSIX_C_SOURCE 200112L
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#ifdef __OS2__
|
||
|
// Hoping for properly working termios in some future (?!), but until then,
|
||
|
// we need keyboard access bypassing that.
|
||
|
#define INCL_KBD
|
||
|
#define INCL_DOSPROCESS
|
||
|
#include <os2.h>
|
||
|
#endif
|
||
|
|
||
|
#include "compat/compat.h"
|
||
|
|
||
|
#ifndef HAVE_TERMIOS
|
||
|
#error "No TERMIOS? Here?"
|
||
|
#endif
|
||
|
|
||
|
// for param struct
|
||
|
#include "mpg123app.h"
|
||
|
|
||
|
#include <termios.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#include "terms.h"
|
||
|
|
||
|
#include "common/debug.h"
|
||
|
|
||
|
static int term_is_fun = -1;
|
||
|
// This now always refers to a ;freshly opened terminal descriptor (e.g. /dev/tty).
|
||
|
// Printouts to stderr are independent of this.
|
||
|
static int term_fd = -1;
|
||
|
static struct termios old_tio;
|
||
|
|
||
|
/* Buffered key from a signal or whatnot.
|
||
|
We ignore the null character... */
|
||
|
static char prekey = 0;
|
||
|
|
||
|
int term_have_fun(int fd, int want_visuals)
|
||
|
{
|
||
|
if(term_is_fun > -1)
|
||
|
return term_is_fun;
|
||
|
else
|
||
|
term_is_fun = 0;
|
||
|
if(term_width(fd) > 0 && want_visuals)
|
||
|
{
|
||
|
/* Only play with non-dumb terminals. */
|
||
|
char *tname = INT123_compat_getenv("TERM");
|
||
|
if(tname)
|
||
|
{
|
||
|
if(strcmp(tname, "") && strcmp(tname, "dumb"))
|
||
|
term_is_fun = 1;
|
||
|
free(tname);
|
||
|
}
|
||
|
}
|
||
|
return term_is_fun;
|
||
|
}
|
||
|
|
||
|
/* Also serves as a way to detect if we have an interactive terminal. */
|
||
|
int term_width(int fd)
|
||
|
{
|
||
|
#ifdef __OS2__
|
||
|
int s[2];
|
||
|
_scrsize (s);
|
||
|
// It seems like we cannot really use the last character of the
|
||
|
// term and have to stop one short to avoid advancing a line.
|
||
|
if (s[0] >= 0)
|
||
|
return s[0] - 1;
|
||
|
#else
|
||
|
struct winsize geometry;
|
||
|
geometry.ws_col = 0;
|
||
|
if(ioctl(fd, TIOCGWINSZ, &geometry) >= 0)
|
||
|
return (int)geometry.ws_col;
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int term_setup_detail(struct termios *pattern);
|
||
|
|
||
|
static void term_sigcont(int sig)
|
||
|
{
|
||
|
if(term_setup_detail(&old_tio) < 0)
|
||
|
{
|
||
|
debug("Can't set terminal attributes");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void term_sigusr(int sig)
|
||
|
{
|
||
|
switch(sig)
|
||
|
{
|
||
|
case SIGUSR1: prekey=*param.term_usr1; break;
|
||
|
case SIGUSR2: prekey=*param.term_usr2; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* This must call only functions safe inside a signal handler. */
|
||
|
static int term_setup_detail(struct termios *pattern)
|
||
|
{
|
||
|
mdebug("setup on fd %d", term_fd);
|
||
|
|
||
|
/* One might want to use sigaction instead. */
|
||
|
signal(SIGCONT, term_sigcont);
|
||
|
signal(SIGUSR1, term_sigusr);
|
||
|
signal(SIGUSR2, term_sigusr);
|
||
|
struct termios tio = *pattern;
|
||
|
tio.c_lflag &= ~(ICANON|ECHO);
|
||
|
tio.c_cc[VMIN] = 1;
|
||
|
tio.c_cc[VTIME] = 0;
|
||
|
#ifdef __OS2__
|
||
|
// Do not care for the error until OS/2 is known to work.
|
||
|
tcsetattr(term_fd,TCSANOW,&tio);
|
||
|
return 0;
|
||
|
#else
|
||
|
return tcsetattr(term_fd,TCSANOW,&tio);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int term_setup(void)
|
||
|
{
|
||
|
if(term_fd < 0)
|
||
|
{
|
||
|
const char *term_name;
|
||
|
#ifdef HAVE_CTERMID
|
||
|
term_name = ctermid(NULL);
|
||
|
#else
|
||
|
term_name = "/dev/tty";
|
||
|
#endif
|
||
|
if(term_name)
|
||
|
mdebug("accessing terminal for control via %s", term_name);
|
||
|
else
|
||
|
{
|
||
|
error("no controlling terminal");
|
||
|
return -1;
|
||
|
}
|
||
|
term_fd = open(term_name, O_RDONLY);
|
||
|
if(term_fd < 0)
|
||
|
{
|
||
|
merror("failed to open terminal: %s", INT123_strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(tcgetattr(term_fd, &old_tio) < 0)
|
||
|
{
|
||
|
// For now, this always fails on OS/2, but they might fix things.
|
||
|
// So just try to move on.
|
||
|
#ifndef __OS2__
|
||
|
merror("failed to get terminal attributes: %s", INT123_strerror(errno));
|
||
|
return -1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
errno = 0;
|
||
|
if(term_setup_detail(&old_tio) < 0)
|
||
|
{
|
||
|
close(term_fd);
|
||
|
term_fd = -1;
|
||
|
if(errno)
|
||
|
merror("failure setting terminal attributes: %s", INT123_strerror(errno));
|
||
|
else
|
||
|
error("failure setting terminal attributes");
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Get the next pressed key, if any.
|
||
|
Returns 1 when there is a key, 0 if not. */
|
||
|
int term_get_key(int stopped, int do_delay, char *val)
|
||
|
{
|
||
|
#ifdef __OS2__
|
||
|
KBDKEYINFO key;
|
||
|
key.chChar = 0;
|
||
|
key.chScan = 0;
|
||
|
if(do_delay)
|
||
|
DosSleep(10);
|
||
|
if(!KbdCharIn(&key,(stopped) ? IO_WAIT : IO_NOWAIT,0) && key.chChar)
|
||
|
{
|
||
|
*val = key.chChar;
|
||
|
return 1;
|
||
|
}
|
||
|
#else
|
||
|
fd_set r;
|
||
|
struct timeval t;
|
||
|
|
||
|
/* Shortcut: If some other means sent a key, use it. */
|
||
|
if(prekey)
|
||
|
{
|
||
|
debug1("Got prekey: %c\n", prekey);
|
||
|
*val = prekey;
|
||
|
prekey = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
t.tv_sec=0;
|
||
|
t.tv_usec=(do_delay) ? 10*1000 : 0;
|
||
|
|
||
|
FD_ZERO(&r);
|
||
|
FD_SET(term_fd,&r);
|
||
|
/* No timeout if stopped */
|
||
|
if(select(term_fd+1,&r,NULL,NULL,(stopped) ? NULL : &t) > 0 && FD_ISSET(term_fd,&r))
|
||
|
{
|
||
|
if(read(term_fd,val,1) <= 0)
|
||
|
return 0; /* Well, we couldn't read the key, so there is none. */
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
#endif
|
||
|
else return 0;
|
||
|
}
|
||
|
|
||
|
void term_restore(void)
|
||
|
{
|
||
|
debug("reset attrbutes");
|
||
|
tcsetattr(term_fd,TCSAFLUSH,&old_tio);
|
||
|
|
||
|
if(term_fd > -1)
|
||
|
close(term_fd);
|
||
|
term_fd = -1;
|
||
|
}
|