/* 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 #endif #include "compat/compat.h" #ifndef HAVE_TERMIOS #error "No TERMIOS? Here?" #endif // for param struct #include "mpg123app.h" #include #include #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; }