yaft/x/yaftx.c
uobikiemukot ca711f1d1a Feature/parse command line arg at startup (#28)
* feature: change argument type

* feature: pass command line arguments to execvp (yaft)

* feature: remove comment related to SHELL env

* doc: update README.md

* feature: pass command line arguments to execvp (yaftx)
2018-08-13 17:16:45 +09:00

239 lines
5.5 KiB
C

/* See LICENSE for licence details. */
#include "../yaft.h"
#include "../conf.h"
#include "../util.h"
#include "x.h"
#include "../terminal.h"
#include "../ctrlseq/esc.h"
#include "../ctrlseq/csi.h"
#include "../ctrlseq/osc.h"
#include "../ctrlseq/dcs.h"
#include "../parse.h"
void sig_handler(int signo)
{
extern volatile sig_atomic_t child_alive;
if (signo == SIGCHLD) {
child_alive = false;
wait(NULL);
}
}
bool sig_set(int signo, void (*handler)(int signo), int flags)
{
struct sigaction sigact;
memset(&sigact, 0, sizeof(struct sigaction));
sigact.sa_handler = handler;
sigact.sa_flags = flags;
if (esigaction(signo, &sigact, NULL) == -1) {
logging(WARN, "sigaction: signo %d failed\n", signo);
return false;
}
return true;
}
void check_fds(fd_set *fds, struct timeval *tv, int master)
{
FD_ZERO(fds);
FD_SET(master, fds);
tv->tv_sec = 0;
tv->tv_usec = SELECT_TIMEOUT;
eselect(master + 1, fds, NULL, NULL, tv);
}
char *keyremap(KeySym keysym, unsigned int state)
{
int length;
unsigned int mask;
length = sizeof(keymap) / sizeof(keymap[0]);
for (int i = 0; i < length; i++) {
mask = keymap[i].mask;
if (keymap[i].keysym == keysym &&
((state & mask) == mask || (mask == XK_NO_MOD && !state)))
return (char *) keymap[i].str;
}
return NULL;
}
void xkeypress(struct xwindow_t *xw, struct terminal_t *term, XEvent *ev)
{
int size;
char buf[BUFSIZE], *customkey;
XKeyEvent *e = &ev->xkey;
KeySym keysym;
//size = XmbLookupString(xw->ic, e, buf, BUFSIZE, &keysym, NULL);
(void) xw;
size = XLookupString(e, buf, BUFSIZE, &keysym, NULL);
if ((customkey = keyremap(keysym, e->state))) {
ewrite(term->fd, customkey, strlen(customkey));
} else {
if (size == 1 && (e->state & Mod1Mask)) {
buf[1] = buf[0];
buf[0] = '\033';
size = 2;
}
ewrite(term->fd, buf, size);
}
}
void xresize(struct xwindow_t *xw, struct terminal_t *term, XEvent *ev)
{
XConfigureEvent *e = &ev->xconfigure;
struct winsize ws;
logging(DEBUG, "xresize() term.width:%d term.height:%d width:%d height:%d\n",
term->width, term->height, e->width, e->height);
(void ) xw; /* unused */
if (e->width == term->width && e->height == term->height)
return;
term->width = e->width;
term->height = e->height;
term->cols = term->width / CELL_WIDTH;
term->lines = term->height / CELL_HEIGHT;
term->scroll.top = 0;
term->scroll.bottom = term->lines - 1;
ws.ws_col = term->cols;
ws.ws_row = term->lines;
ws.ws_xpixel = CELL_WIDTH * term->cols;
ws.ws_ypixel = CELL_HEIGHT * term->lines;
ioctl(term->fd, TIOCSWINSZ, &ws);
}
void xredraw(struct xwindow_t *xw, struct terminal_t *term, XEvent *ev)
{
XExposeEvent *e = &ev->xexpose;
int i, lines, update_from;
logging(DEBUG, "xredraw() count:%d x:%d y:%d width:%d height:%d\n",
e->count, e->x, e->y, e->width, e->height);
update_from = e->y / CELL_HEIGHT;
lines = my_ceil(e->height, CELL_HEIGHT);
for (i = 0; i < lines; i++) {
if ((update_from + i) < term->lines)
term->line_dirty[update_from + i] = true;
}
if (e->count == 0)
refresh(xw, term);
}
void (*event_func[LASTEvent])(struct xwindow_t *xw, struct terminal_t *term, XEvent *ev) = {
[KeyPress] = xkeypress,
[ConfigureNotify] = xresize,
[Expose] = xredraw,
};
bool fork_and_exec(int *master, const char *cmd, char *const argv[], int lines, int cols)
{
pid_t pid;
struct winsize ws = {.ws_row = lines, .ws_col = cols,
/* XXX: this variables are UNUSED (man tty_ioctl),
but useful for calculating terminal cell size */
.ws_ypixel = CELL_HEIGHT * lines, .ws_xpixel = CELL_WIDTH * cols};
pid = eforkpty(master, NULL, NULL, &ws);
if (pid < 0)
return false;
else if (pid == 0) { /* child */
esetenv("TERM", term_name, 1);
eexecvp(cmd, argv);
/* never reach here */
exit(EXIT_FAILURE);
}
return true;
}
int main(int argc, char *const argv[])
{
extern const char *shell_cmd; /* defined in conf.h */
const char *cmd;
uint8_t buf[BUFSIZE];
ssize_t size;
fd_set fds;
struct timeval tv;
struct xwindow_t xw;
struct terminal_t term;
XEvent ev;
XConfigureEvent confev;
extern volatile sig_atomic_t child_alive;
/* init */
if (!setlocale(LC_ALL, ""))
logging(WARN, "setlocale falied\n");
if (!sig_set(SIGCHLD, sig_handler, SA_RESTART))
logging(ERROR, "signal initialize failed\n");
if (!xw_init(&xw)) {
logging(FATAL, "xwindow initialize failed\n");
goto xw_init_failed;
}
if (!term_init(&term, xw.width, xw.height)) {
logging(FATAL, "terminal initialize failed\n");
goto term_init_failed;
}
/* fork and exec shell */
cmd = (argc < 2) ? shell_cmd: argv[1];
if (!fork_and_exec(&term.fd, cmd, argv + 1, term.lines, term.cols)) {
logging(FATAL, "forkpty failed\n");
goto fork_failed;
}
child_alive = true;
/* initial terminal size defined in x.h */
confev.width = TERM_WIDTH;
confev.height = TERM_HEIGHT;
xresize(&xw, &term, (XEvent *) &confev);
/* main loop */
while (child_alive) {
while(XPending(xw.display)) {
XNextEvent(xw.display, &ev);
if (XFilterEvent(&ev, None))
continue;
if (event_func[ev.type])
event_func[ev.type](&xw, &term, &ev);
}
check_fds(&fds, &tv, term.fd);
if (FD_ISSET(term.fd, &fds)) {
size = read(term.fd, buf, BUFSIZE);
if (size > 0) {
if (DEBUG)
ewrite(STDOUT_FILENO, buf, size);
parse(&term, buf, size);
refresh(&xw, &term);
}
}
}
/* die */
term_die(&term);
xw_die(&xw);
sig_set(SIGCHLD, SIG_DFL, 0);
return EXIT_SUCCESS;
fork_failed:
term_die(&term);
term_init_failed:
xw_die(&xw);
xw_init_failed:
return EXIT_FAILURE;
}