looper/subprojects/mpg123/src/term_posix.c

235 lines
4.8 KiB
C
Raw Normal View History

2024-09-28 10:31:06 -07:00
/*
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;
}