lt

simple terminal emulator
Log | Files | Refs | git clone https://git.ne02ptzero.me/git/lt

commit 5c65c4f7e348f53bfbcbdb3346af51a50e9e5409
Author: Ne02ptzero <louis@ne02ptzero.me>
Date:   Tue,  5 Jun 2018 18:48:14 +0200

ADD: Basic code, slow working terminal emulator

Signed-off-by: Ne02ptzero <louis@ne02ptzero.me>

Diffstat:
A.gitignore | 2++
AMakefile | 18++++++++++++++++++
Amain.c | 28++++++++++++++++++++++++++++
Aopt.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aopt.h | 18++++++++++++++++++
Aterm.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aterm.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ax.c | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ax.h | 6++++++
9 files changed, 468 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +*.o +lt diff --git a/Makefile b/Makefile @@ -0,0 +1,18 @@ +NAME = lt +CC = gcc + +SFLAGS = -Wall -Wextra -Werror -g -O2 +LFLAGS = -lXft -lX11 +IFLAGS = -I/usr/X11R6/include -I/usr/include/freetype2 +CFLAGS = $(SFLAGS) $(IFLAGS) $(LFLAGS) + +SRCS = $(wildcard *.c) +OBJS = $(SRCS:%.c=%.o) + +all: $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $(NAME) + +clean: + rm -f $(OBJS) $(NAME) + +re: clean all diff --git a/main.c b/main.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <locale.h> + +#include "opt.h" +#include "term.h" +#include "x.h" + +int main(int ac, const char **av) +{ + opt_t arg = { 0 }; + term_t term = { 0 }; + + opt_parse(&arg, ac, av); + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + + term_init(&term, &arg); + term_xinit(&term); + term_pty(&term); + term_resize(&term); + term_spawn(&term); + + term_run(&term); + + printf("Hello World!\n"); + return 0; +} diff --git a/opt.c b/opt.c @@ -0,0 +1,72 @@ +#include "opt.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdbool.h> + +static void help(const char *bin_name, int code) +{ + fprintf(stdout, "%s [-c col] [-r row] [-f font] [-t title]\n", bin_name); + exit(code); +} + +static inline bool str_to_int32(const char *in, uint32_t *out) +{ + char *end; + + errno = 0; + *out = strtol(in, &end, 10); + + if (end == in || strlen(end) > 0 || errno) + return false; + + return true; +} + +#define NEED_ARG(ac, av, i) do { \ + if (i + 1 >= ac) \ + { \ + help(av[0], 1); \ + } \ + i++; \ + } while(0) + +void opt_parse(opt_t *ret, int ac, const char **av) +{ + for (int i = 1; i < ac; i++) + { + const char *arg = av[i]; + + if (strlen(arg) > 1 && arg[0] == '-') + { + switch (arg[1]) + { + case 'c': + NEED_ARG(ac, av, i); + if (!str_to_int32(av[i], &ret->cols)) + help(av[0], 1); + break; + case 'r': + NEED_ARG(ac, av, i); + if (!str_to_int32(av[i], &ret->rows)) + help(av[0], 1); + break; + case 't': + NEED_ARG(ac, av, i); + ret->title = av[i]; + break; + case 'f': + NEED_ARG(ac, av, i); + ret->font = av[i]; + break; + case 'h': + help(av[0], 0); + break; + default: + help(av[0], 1); + } + } + } +} diff --git a/opt.h b/opt.h @@ -0,0 +1,18 @@ +#ifndef OPT_H +#define OPT_H + +#include <stdint.h> +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + +typedef struct { + uint32_t cols; + uint32_t rows; + const char *title; + const char *font; + const char *bg; + const char *fg; +} opt_t; + +void opt_parse(opt_t *ret, int ac, const char **av); + +#endif /* OPT_H */ diff --git a/term.c b/term.c @@ -0,0 +1,69 @@ +#define _XOPEN_SOURCE 600 +#include <sys/ioctl.h> +#include <termios.h> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> + +#include "term.h" + +#include <stdio.h> + +void term_init(term_t *term, opt_t *arg) +{ + term->cols = MAX(arg->cols, 80); + term->rows = MAX(arg->rows, 25); + term->font = arg->font; + term->bg = arg->bg == NULL ? "#000000" : arg->fg; + term->fg = arg->fg == NULL ? "#ffffff" : arg->bg; + term->font = arg->font; + term->title = arg->title == NULL ? "lt" : arg->title; +} + +void term_pty(term_t *term) +{ + char *slave_name; + + term->pty.master = posix_openpt(O_RDWR | O_NOCTTY); + assert(term->pty.master != -1); + + grantpt(term->pty.master); + unlockpt(term->pty.master); + + slave_name = ptsname(term->pty.master); + term->pty.slave = open(slave_name, O_RDWR | O_NOCTTY); +} + +void term_resize(term_t *term) +{ + struct winsize ws = { + .ws_col = term->cols, + .ws_row = term->rows + }; + + ioctl(term->pty.master, TIOCSWINSZ, &ws); +} + +#define SHELL "/bin/bash" +void term_spawn(term_t *term) +{ + char *env[] = { NULL }; + pid_t p = fork(); + + if (p == 0) + { + close(term->pty.master); + setsid(); + ioctl(term->pty.slave, TIOCSCTTY, NULL); + dup2(term->pty.slave, 0); + dup2(term->pty.slave, 1); + dup2(term->pty.slave, 2); + close(term->pty.slave); + execle(SHELL, "-" SHELL, NULL, env); + } + else if (p > 0) + { + close(term->pty.slave); + } +} diff --git a/term.h b/term.h @@ -0,0 +1,69 @@ +#ifndef TERM_H +#define TERM_H + +#include <stdint.h> +#include <X11/Xlib.h> +#include "opt.h" + +typedef struct { + uint32_t u; + uint16_t mode; + uint32_t fg; + uint32_t bg; +} char_t; + +typedef struct { + uint32_t cols; + uint32_t rows; + const char *font; + const char *bg; + const char *fg; + const char *title; + + unsigned char *buf; + + struct { + uint32_t x; +#define __x cursor.x + uint32_t y; +#define __y cursor.y + char_t attr; + uint8_t state; + } cursor; + + struct { + int fd; + Display *dsp; +#define __dsp win.dsp + Colormap cmap; +#define __cmap win.cmap + Window win; +#define __win win.win + Window root; + XFontStruct *font; +#define __font win.font + int scr; +#define __scr win.scr + uint32_t font_width; + uint32_t font_height; + + uint32_t bg; + uint32_t fg; + + GC termgc; + } win; + + struct { + int master; + int slave; + } pty; + +} term_t; + +void term_init(term_t *term, opt_t *arg); +void term_pty(term_t *term); +void term_resize(term_t *term); +void term_run(term_t *term); +void term_spawn(term_t *term); + +#endif /* TERM_H */ diff --git a/x.c b/x.c @@ -0,0 +1,186 @@ +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <X11/Xft/Xft.h> +#include <X11/XKBlib.h> +#include <X11/Xatom.h> + +#include <assert.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/select.h> +#include <ctype.h> + +#include "term.h" + +void term_xinit(term_t *term) +{ + XColor color; + Atom wm_name; + XSetWindowAttributes wa = { + .background_pixmap = ParentRelative, + .event_mask = KeyPressMask | KeyReleaseMask | ExposureMask, + }; + + (void)wa; + term->__dsp = XOpenDisplay(NULL); + assert(term->__dsp != NULL); + + term->__scr = XDefaultScreen(term->__dsp); + term->win.root = RootWindow(term->__dsp, term->__scr); + term->win.fd = ConnectionNumber(term->__dsp); + + term->__font = XLoadQueryFont(term->__dsp, term->font == NULL ? "fixed" : NULL); + assert(term->__font != NULL); + term->win.font_width = XTextWidth(term->__font, "m", 1); + term->win.font_height = term->__font->ascent + term->__font->descent; + term->__cmap = DefaultColormap(term->__dsp, term->__scr); + + XAllocNamedColor(term->__dsp, term->__cmap, term->bg, &color, &color); + term->win.bg = color.pixel; + XAllocNamedColor(term->__dsp, term->__cmap, term->fg, &color, &color); + term->win.fg = color.pixel; + + term->buf = calloc(term->cols * term->rows, sizeof(*term->buf)); + assert(term->buf != NULL); + + term->__win = XCreateWindow(term->__dsp, term->win.root, 0, 0, + term->cols * term->win.font_width, term->rows * term->win.font_height, + 0, DefaultDepth(term->__dsp, term->__scr), CopyFromParent, + DefaultVisual(term->__dsp, term->__scr), + CWBackPixmap | CWEventMask, &wa); + + XMapWindow(term->__dsp, term->__win); + term->win.termgc = XCreateGC(term->__dsp, term->__win, 0, NULL); + + wm_name = XInternAtom(term->__dsp, "_NET_WM_NAME", False); + XChangeProperty(term->__dsp, term->__win, wm_name, + XInternAtom(term->__dsp, "UTF8_STRING", False), 8, PropModeReplace, + (unsigned char *)term->title, strlen(term->title)); + + XSync(term->__dsp, False); +} + +static void term_redraw(term_t *term) +{ + char buf[1]; + + XSetForeground(term->__dsp, term->win.termgc, term->win.bg); + XFillRectangle(term->__dsp, term->__win, term->win.termgc, 0, 0, + term->cols * term->win.font_width, term->rows * term->win.font_height); + + XSetForeground(term->__dsp, term->win.termgc, term->win.fg); + + for (uint32_t y = 0; y < term->rows; y++) + { + for (uint32_t x = 0; x < term->cols; x++) + { + buf[0] = term->buf[y * term->cols + x]; + if (!iscntrl(buf[0])) + { + XDrawString(term->__dsp, term->__win, term->win.termgc, + x * term->win.font_width, + y * term->win.font_height + term->win.font->ascent, + buf, 1); + } + } + } + + XSetForeground(term->__dsp, term->win.termgc, term->win.fg); + XFillRectangle(term->__dsp, term->__win, term->win.termgc, + term->__x * term->win.font_width, + term->__y * term->win.font_height, + term->win.font_width, term->win.font_height); + XSync(term->__dsp, False); +} + +static void term_buf(term_t *term, char *buf, bool *just_wrapped) +{ + if (*buf == '\r') + term->__x = 0; + else + { + if (*buf != '\n') + { + term->buf[term->__y * term->cols + term->__x] = *buf; + term->__x++; + + if (term->__x >= term->cols) + { + term->__x = 0; + term->__y++; + *just_wrapped = true; + } + else + *just_wrapped = false; + } + else if (!*just_wrapped) + { + term->__y++; + *just_wrapped = false; + } + + if (term->__y >= term->rows) + { + memmove(term->buf, &term->buf[term->cols], term->cols * (term->rows - 1)); + term->__y = term->rows - 1; + + for (uint32_t i = 0; i < term->cols; i++) + term->buf[term->__y * term->cols + i] = 0; + } + } +} + +static void term_key(term_t *term, XKeyEvent *ev) +{ + char buf[32]; + int num; + KeySym ksym; + + num = XLookupString(ev, buf, sizeof(buf), &ksym, 0); + write(term->pty.master, buf, num); +} + +#define BUF_SIZE 1 +void term_run(term_t *term) +{ + int maxfd; + fd_set readable; + XEvent ev; + char buf[BUF_SIZE]; + bool just_wrapped = false; + + maxfd = MAX(term->pty.master, term->win.fd); + + while (true) + { + FD_ZERO(&readable); + FD_SET(term->pty.master, &readable); + FD_SET(term->win.fd, &readable); + + assert(select(maxfd + 1, &readable, NULL, NULL, NULL) != -1); + + if (FD_ISSET(term->pty.master, &readable)) + { + read(term->pty.master, buf, sizeof(buf)); + term_buf(term, buf, &just_wrapped); + term_redraw(term); + } + + if (FD_ISSET(term->win.fd, &readable)) + { + while (XPending(term->__dsp)) + { + XNextEvent(term->__dsp, &ev); + switch (ev.type) + { + case Expose: + term_redraw(term); + break; + case KeyPress: + term_key(term, &ev.xkey); + break; + } + } + } + } +} diff --git a/x.h b/x.h @@ -0,0 +1,6 @@ +#ifndef _X_H +#define _X_H + +void term_xinit(term_t *term); + +#endif /* _X_H */