Files
neutrino/src/gui/widget/yaft/yaft_class.cpp
2018-01-13 20:07:27 +01:00

207 lines
5.0 KiB
C++

/*
* yaft framebuffer terminal as C++ class for embedding in neutrino-MP
* (C) 2018 Stefan Seyfried
* License: GPL-2.0
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* derived from yaft/yaft.c,
* original code
* Copyright (c) 2012 haru <uobikiemukot at gmail dot com>
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*/
#include "yaft.h"
#include "conf.h"
#include "util.h"
#include "fb/common.h"
#include "terminal.h"
#include "ctrlseq/esc.h"
#include "ctrlseq/csi.h"
#include "ctrlseq/osc.h"
#include "parse.h"
#include "yaft_class.h"
#include <driver/framebuffer.h>
static void sig_handler(int signo)
{
/* global */
extern volatile sig_atomic_t child_alive;
logging(DEBUG, "caught signal! no:%d\n", signo);
if (signo == SIGCHLD) {
child_alive = false;
wait(NULL);
}
}
bool tty_init(void)
{
struct sigaction sigact;
memset(&sigact, 0, sizeof(struct sigaction));
sigact.sa_handler = sig_handler;
sigact.sa_flags = SA_RESTART;
esigaction(SIGCHLD, &sigact, NULL);
#if 0
if (VT_CONTROL) {
esigaction(SIGUSR1, &sigact, NULL);
esigaction(SIGUSR2, &sigact, NULL);
struct vt_mode vtm;
vtm.mode = VT_PROCESS;
vtm.waitv = 0;
vtm.acqsig = SIGUSR1;
vtm.relsig = SIGUSR2;
vtm.frsig = 0;
if (ioctl(STDIN_FILENO, VT_SETMODE, &vtm))
logging(WARN, "ioctl: VT_SETMODE failed (maybe here is not console)\n");
if (FORCE_TEXT_MODE == false) {
if (ioctl(STDIN_FILENO, KDSETMODE, KD_GRAPHICS))
logging(WARN, "ioctl: KDSETMODE failed (maybe here is not console)\n");
}
}
etcgetattr(STDIN_FILENO, termios_orig);
set_rawmode(STDIN_FILENO, termios_orig);
ewrite(STDIN_FILENO, "\033[?25l", 6); /* make cusor invisible */
#endif
return true;
}
static const char * const *yaft_argv;
static bool fork_and_exec(int *master, int lines, int cols)
{
struct winsize ws;
ws.ws_row = lines;
ws.ws_col = cols;
/* XXX: this variables are UNUSED (man tty_ioctl),
but useful for calculating terminal cell size */
ws.ws_ypixel = CELL_HEIGHT * lines;
ws.ws_xpixel = CELL_WIDTH * cols;
pid_t pid;
pid = eforkpty(master, NULL, NULL, &ws);
if (pid < 0)
return false;
else if (pid == 0) { /* child */
esetenv("TERM", term_name, 1);
execvp(yaft_argv[0], (char * const *)yaft_argv);
/* never reach here */
exit(EXIT_FAILURE);
}
return true;
}
static int check_fds(fd_set *fds, struct timeval *tv, int input, int master)
{
FD_ZERO(fds);
if (input != STDIN_FILENO)
FD_SET(input, fds);
FD_SET(master, fds);
tv->tv_sec = 0;
tv->tv_usec = SELECT_TIMEOUT;
return eselect(master + 1, fds, NULL, NULL, tv);
}
YaFT::YaFT(const char * const *argv)
{
yaft_argv = argv;
}
int YaFT::run(void)
{
uint8_t buf[BUFSIZE];
ssize_t size;
fd_set fds;
struct timeval tv;
struct framebuffer_t fb;
struct terminal_t term;
/* global */
extern volatile sig_atomic_t need_redraw;
extern volatile sig_atomic_t child_alive;
term.cfb = fb.info.cfb = CFrameBuffer::getInstance();
memset(&term, 0, sizeof(term));
/* init */
if (setlocale(LC_ALL, "") == NULL) /* for wcwidth() */
logging(WARN, "setlocale falied\n");
if (!fb_init(&fb)) {
logging(FATAL, "framebuffer initialize failed\n");
goto fb_init_failed;
}
if (!term_init(&term, fb.info.width, fb.info.height)) {
logging(FATAL, "terminal initialize failed\n");
goto term_init_failed;
}
if (!tty_init()) {
logging(FATAL, "tty initialize failed\n");
goto tty_init_failed;
}
/* fork and exec shell */
if (!fork_and_exec(&term.fd, term.lines, term.cols)) {
logging(FATAL, "forkpty failed\n");
goto tty_init_failed;
}
child_alive = true;
/* main loop */
while (child_alive) {
if (need_redraw) {
need_redraw = false;
redraw(&term);
refresh(&fb, &term);
}
if (check_fds(&fds, &tv, STDIN_FILENO, term.fd) == -1)
continue;
#if 0
if (FD_ISSET(STDIN_FILENO, &fds)) {
if ((size = read(STDIN_FILENO, buf, BUFSIZE)) > 0)
ewrite(term.fd, buf, size);
}
#endif
if (FD_ISSET(term.fd, &fds)) {
while ((size = read(term.fd, buf, BUFSIZE)) > 0) {
if (VERBOSE)
ewrite(STDOUT_FILENO, buf, size);
parse(&term, buf, size);
if (LAZY_DRAW && size == BUFSIZE)
continue; /* maybe more data arrives soon */
refresh(&fb, &term);
}
}
}
refresh(&fb, &term);
/* normal exit */
term_die(&term);
fb_die(&fb);
return EXIT_SUCCESS;
/* error exit */
tty_init_failed:
term_die(&term);
term_init_failed:
fb_die(&fb);
fb_init_failed:
return EXIT_FAILURE;
}