#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <SDL2/SDL.h>
+#include "stty_sane.h"
#include "vrEmu6502/src/vrEmu6502.h"
#define APPLE_WIDTH 560
}
// Open the slave side ot the PTY
- int fd_slave;
- {
- char *p = ptsname(fd_master);
- fd_slave = open(p, O_RDWR);
- if (fd_slave == -1) {
- perror(p);
- exit(EXIT_FAILURE);
- }
+ char *slave_name = ptsname(fd_master);
+ int fd_slave = open(slave_name, O_RDWR);
+ if (fd_slave == -1) {
+ perror(slave_name);
+ exit(EXIT_FAILURE);
}
signal(SIGCHLD, sigchld_handler);
child_pid = fork();
if (child_pid == 0) {
+ close(fd_master);
+
+ // make the current process a new session leader
+ // see http://www.rkoucha.fr/tech_corner/pty_pdip.html
+ if (setsid() == -1) {
+ perror("setsid()");
+ exit(EXIT_FAILURE);
+ }
+
+ // as the child is a session leader, set the controlling terminal to be
+ // the slave side of the pty (mandatory for programs like the shell to
+ // make them manage correctly their outputs)
+ // see http://www.rkoucha.fr/tech_corner/pty_pdip.html
+ if (ioctl(fd_slave, TIOCSCTTY, 1) == -1) {
+ perror("TIOCSCTTY");
+ exit(EXIT_FAILURE);
+ }
+
+ // inform applications like /bin/ls to provide 40-column output
+ struct winsize winsize = {.ws_row = 24, .ws_col = 40};
+ if (ioctl(fd_slave, TIOCSWINSZ, &winsize) == -1) {
+ perror("TIOCSWINSZ");
+ exit(EXIT_FAILURE);
+ }
+
+ // for some reason, the termios configuration MUST be done after fork()
+ struct termios attr;
+ if (tcgetattr(fd_slave, &attr) == -1) {
+ perror("tcgetattr()");
+ exit(EXIT_FAILURE);
+ }
+
+#if 1
+ stty_sane(&attr);
+ attr.c_cc[VERASE] = 8; // ctrl-h
+#elif 1 // see cfmakeraw() in termios man page
+ // with minimal cooked mode (+ICRNL, +OPOST, +ECHO, +ICANON, +ISIG)
+ attr.c_iflag &= ~(
+ IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | IXON
+ );
+ attr.c_iflag |= ICRNL;
+ attr.c_oflag |= OPOST;
+ attr.c_lflag &= ~(ECHONL | IEXTEN);
+ attr.c_lflag |= ECHO | ICANON | ISIG;
+ attr.c_cflag &= ~(CSIZE | PARENB);
+ attr.c_cflag |= CS8;
+#else // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
+ // with minimal cooked mode (+ICRNL, +OPOST, +ECHO, +ICANON, +ISIG)
+ attr.c_iflag &= ~(BRKINT | INPCK | ISTRIP | IXON);
+ attr.c_iflag |= ICRNL;
+ attr.c_oflag |= OPOST;
+ attr.c_cflag &= ~(CSIZE | PARENB);
+ attr.c_cflag |= CS8;
+ attr.c_lflag &= ~IEXTEN;
+ attr.c_lflag |= ECHO | ICANON | ISIG;
+ attr.c_cc[VMIN] = 1;
+ attr.c_cc[VTIME] = 0;
+#endif
+ if (tcsetattr(fd_slave, TCSAFLUSH, &attr) == -1) {
+ perror("tcsetattr()");
+ exit(EXIT_FAILURE);
+ }
+
+ int fd_err = dup(STDERR_FILENO);
+ if (fd_err == -1) {
+ perror("dup()");
+ exit(EXIT_FAILURE);
+ }
+ if (fcntl(fd_err, F_SETFD, FD_CLOEXEC) == -1) {
+ perror("fcntl()");
+ exit(EXIT_FAILURE);
+ }
+
dup2(fd_slave, STDIN_FILENO);
dup2(fd_slave, STDOUT_FILENO);
dup2(fd_slave, STDERR_FILENO);
+ close(fd_slave);
int n = argc - argn;
char **p = malloc((n + 1) * sizeof(char *));
p[n] = NULL;
execve(argv[argn], p, environ);
+ dup2(fd_err, STDERR_FILENO);
perror(argv[argn]);
exit(EXIT_FAILURE);
}
+ close(fd_slave);
fd_in = fd_master;
fd_out = fd_master;
- close(fd_slave);
}
// do this before creating the CPU
}
atexit(termios_atexit);
- // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
struct termios attr = termios_attr;
+#if 1 // see cfmakeraw() in termios man page
+ attr.c_iflag &= ~(
+ IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON
+ );
+ attr.c_oflag &= ~OPOST;
+ attr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ attr.c_cflag &= ~(CSIZE | PARENB);
+ attr.c_cflag |= CS8;
+#else // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
attr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
attr.c_oflag &= ~OPOST;
attr.c_cflag &= ~(CSIZE | PARENB);
attr.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
attr.c_cc[VMIN] = 1;
attr.c_cc[VTIME] = 0;
+#endif
if (tcsetattr(fd_in, TCSAFLUSH, &attr) == -1) {
perror("tcsetattr()");
exit(EXIT_FAILURE);
if (i < 0x80) {
//switch (i) {
//default:
- if (i >= 'a' && i <= 'z')
- i -= 0x20;
- if (i >= 0x40 && i < 0x60 && (e->keysym.mod & KMOD_CTRL))
- i -= 0x40;
+ if ((i >= 0x40 && i < 0x80) && (e->keysym.mod & KMOD_CTRL))
+ i &= 0x1f;
key_waiting = i | 0x80;
// break;
//}
--- /dev/null
+// see https://github.com/wertarbyte/coreutils/blob/master/src/stty.c
+// this is a cut-down version which changes the data tables into inline code
+
+#include "stty_sane.h"
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE 0
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+# define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control ('u')
+#endif
+#ifndef CEOF
+# define CEOF Control ('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control ('z')
+#endif
+#if defined VEOL2 && !defined CEOL2
+# define CEOL2 _POSIX_VDISABLE
+#endif
+/* Some platforms have VSWTC, others VSWTCH. In both cases, this control
+ character is initialized by CSWTCH, if present. */
+#if defined VSWTC && !defined VSWTCH
+# define VSWTCH VSWTC
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name. */
+#if defined VSUSP && !defined VSWTCH
+# define VSWTCH VSUSP
+# if defined CSUSP && !defined CSWTCH
+# define CSWTCH CSUSP
+# endif
+#endif
+#if defined VSWTCH && !defined CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
+ So the default is to disable `swtch.' */
+#if defined __sparc__ && defined __svr4__
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined VWERSE && !defined VWERASE /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined VDSUSP && !defined CDSUSP
+# define CDSUSP Control ('y')
+#endif
+#if !defined VREPRINT && defined VRPRNT /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined VREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined CREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined VWERASE && !defined CWERASE
+# define CWERASE Control ('w')
+#endif
+#if defined VLNEXT && !defined CLNEXT
+# define CLNEXT Control ('v')
+#endif
+#if defined VDISCARD && !defined VFLUSHO
+# define VFLUSHO VDISCARD
+#endif
+#if defined VFLUSH && !defined VFLUSHO /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined CTLECH && !defined ECHOCTL /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined TCTLECH && !defined ECHOCTL /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined CRTKIL && !defined ECHOKE /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined VFLUSHO && !defined CFLUSHO
+# define CFLUSHO Control ('o')
+#endif
+#if defined VSTATUS && !defined CSTATUS
+# define CSTATUS Control ('t')
+#endif
+
+void stty_sane(struct termios *attr) {
+ attr->c_cflag |= CREAD;
+
+ attr->c_iflag &= ~IGNBRK;
+ attr->c_iflag |= BRKINT;
+ attr->c_iflag &= ~INLCR;
+ attr->c_iflag &= ~IGNCR;
+ attr->c_iflag |= ICRNL;
+ attr->c_iflag &= ~IXOFF;
+#ifdef IUCLC
+ attr->c_iflag &= ~IUCLC;
+#endif
+#ifdef IXANY
+ attr->c_iflag &= ~IXANY;
+#endif
+#ifdef IMAXBEL
+ attr->c_iflag |= IMAXBEL;
+#endif
+#ifdef IUTF8
+ attr->c_iflag &= ~IUTF8;
+#endif
+
+ attr->c_oflag |= OPOST;
+#ifdef OLCUC
+ attr->c_oflag &= ~OLCUC;
+#endif
+#ifdef OCRNL
+ attr->c_oflag &= ~OCRNL;
+#endif
+#ifdef ONLCR
+ attr->c_oflag |= ONLCR;
+#endif
+#ifdef ONOCR
+ attr->c_oflag &= ~ONOCR;
+#endif
+#ifdef ONLRET
+ attr->c_oflag &= ~ONLRET;
+#endif
+#ifdef OFILL
+ attr->c_oflag &= ~OFILL;
+#endif
+#ifdef OFDEL
+ attr->c_oflag &= ~OFDEL;
+#endif
+#ifdef NLDLY
+ attr->c_oflag = (attr->c_oflag & ~NLDLY) | NL0;
+#endif
+#ifdef CRDLY
+ attr->c_oflag = (attr->c_oflag & ~CRDLY) | CR0;
+#endif
+#ifdef TABDLY
+# ifdef TAB0
+ attr->c_oflag = (attr->c_oflag & ~TABDLY) | TAB0;
+# endif
+#else
+# ifdef OXTABS
+ attr->c_oflag &= ~OXTABS;
+# endif
+#endif
+#ifdef BSDLY
+ attr->c_oflag = (attr->c_oflag & ~BSDLY) | BS0;
+#endif
+#ifdef VTDLY
+ attr->c_oflag = (attr->c_oflag & ~VTDLY) | VT0;
+#endif
+#ifdef FFDLY
+ attr->c_oflag = (attr->c_oflag & ~FFDLY) | FF0;
+#endif
+
+ attr->c_lflag |= ISIG;
+ attr->c_lflag |= ICANON;
+#ifdef IEXTEN
+ attr->c_lflag |= IEXTEN;
+#endif
+ attr->c_lflag |= ECHO;
+ attr->c_lflag |= ECHOE;
+ attr->c_lflag |= ECHOK;
+ attr->c_lflag &= ~ECHONL;
+ attr->c_lflag &= ~NOFLSH;
+#ifdef XCASE
+ attr->c_lflag &= ~XCASE;
+#endif
+#ifdef TOSTOP
+ attr->c_lflag &= ~TOSTOP;
+#endif
+#ifdef ECHOPRT
+ attr->c_lflag &= ~ECHOPRT;
+#endif
+#ifdef ECHOCTL
+ attr->c_lflag |= ECHOCTL;
+#endif
+#ifdef ECHOKE
+ attr->c_lflag |= ECHOKE;
+#endif
+
+ attr->c_cc[VINTR] = CINTR;
+ attr->c_cc[VQUIT] = CQUIT;
+ attr->c_cc[VERASE] = CERASE;
+ attr->c_cc[VKILL] = CKILL;
+ attr->c_cc[VEOF] = CEOF;
+ attr->c_cc[VEOL] = CEOL;
+#ifdef VEOL2
+ attr->c_cc[VEOL2] = CEOL2;
+#endif
+#ifdef VSWTCH
+ attr->c_cc[VSWTCH] = CSWTCH;
+#endif
+ attr->c_cc[VSTART] = CSTART;
+ attr->c_cc[VSTOP] = CSTOP;
+ attr->c_cc[VSUSP] = CSUSP;
+#ifdef VDSUSP
+ attr->c_cc[VDSUSP] = CDSUSP;
+#endif
+#ifdef VREPRINT
+ attr->c_cc[VREPRINT] = CRPRNT;
+#else
+# ifdef CREPRINT /* HPUX 10.20 needs this */
+ attr->c_cc[CREPRINT] = CRPRNT;
+# endif
+#endif
+#ifdef VWERASE
+ attr->c_cc[VWERASE] = CWERASE;
+#endif
+#ifdef VLNEXT
+ attr->c_cc[VLNEXT] = CLNEXT;
+#endif
+#ifdef VFLUSHO
+ attr->c_cc[VFLUSHO] = CFLUSHO;
+#endif
+#ifdef VSTATUS
+ attr->c_cc[VSTATUS] = CSTATUS;
+#endif
+
+ attr->c_cc[VMIN] = 1;
+ attr->c_cc[VTIME] = 0;
+}