1463 lines
39 KiB
C
1463 lines
39 KiB
C
/* terminal.c
|
|
* (c) 2002 Mikulas Patocka
|
|
* This file is a part of the Links program, released under GPL.
|
|
*/
|
|
|
|
#include "links.h"
|
|
|
|
static void in_term(void *);
|
|
static void check_if_no_terminal(void);
|
|
|
|
int hard_write(int fd, const unsigned char *p, int l)
|
|
{
|
|
int t = 0;
|
|
while (l > 0) {
|
|
int w;
|
|
EINTRLOOP(w, (int)write(fd, p, l));
|
|
if (w < 0)
|
|
return -1;
|
|
if (!w) {
|
|
errno = ENOSPC;
|
|
break;
|
|
}
|
|
t += w;
|
|
p += w;
|
|
l -= w;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
int hard_read(int fd, unsigned char *p, int l)
|
|
{
|
|
int r = 1;
|
|
int t = 0;
|
|
while (l > 0 && r) {
|
|
EINTRLOOP(r, (int)read(fd, p, l));
|
|
if (r < 0)
|
|
return -1;
|
|
t += r;
|
|
p += r;
|
|
l -= r;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
unsigned char *get_cwd(void)
|
|
{
|
|
int bufsize = 128;
|
|
unsigned char *buf;
|
|
unsigned char *gcr;
|
|
while (1) {
|
|
buf = mem_alloc(bufsize);
|
|
ENULLLOOP(gcr, cast_uchar getcwd(cast_char buf, bufsize));
|
|
if (gcr) return buf;
|
|
mem_free(buf);
|
|
if (errno != ERANGE) break;
|
|
if ((unsigned)bufsize > MAXINT - 128) overalloc();
|
|
bufsize += 128;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void set_cwd(unsigned char *path)
|
|
{
|
|
int rs;
|
|
if (path)
|
|
EINTRLOOP(rs, chdir(cast_const_char path));
|
|
}
|
|
|
|
unsigned char get_attribute(int fg, int bg)
|
|
{
|
|
return ((bg & 7) << 3) | (fg & 7) | ((fg & 8) << 3);
|
|
}
|
|
|
|
struct list_head terminals = {&terminals, &terminals};
|
|
|
|
static void set_margin(struct terminal *term)
|
|
{
|
|
struct term_spec *ts = term->spec;
|
|
if (term->real_x <= ts->left_margin + ts->right_margin) {
|
|
term->left_margin = 0;
|
|
term->x = term->real_x;
|
|
} else {
|
|
term->left_margin = ts->left_margin;
|
|
term->x = term->real_x - (ts->left_margin + ts->right_margin);
|
|
}
|
|
if (term->real_y <= ts->top_margin + ts->bottom_margin) {
|
|
term->top_margin = 0;
|
|
term->y = term->real_y;
|
|
} else {
|
|
term->top_margin = ts->top_margin;
|
|
term->y = term->real_y - (ts->top_margin + ts->bottom_margin);
|
|
}
|
|
}
|
|
|
|
|
|
static void alloc_term_screen(struct terminal *term)
|
|
{
|
|
chr *s, *t;
|
|
NO_GFX;
|
|
if (term->x < 0) term->x = 1;
|
|
if (term->y < 0) term->y = 1;
|
|
if (term->x && (unsigned)term->x * (unsigned)term->y / (unsigned)term->x != (unsigned)term->y) overalloc();
|
|
if ((unsigned)term->x * (unsigned)term->y > MAXINT / sizeof(*term->screen)) overalloc();
|
|
s = mem_realloc(term->screen, term->x * term->y * sizeof(*term->screen));
|
|
t = mem_realloc(term->last_screen, term->x * term->y * sizeof(*term->screen));
|
|
memset(t, -1, term->x * term->y * sizeof(*term->screen));
|
|
term->last_screen = t;
|
|
memset(s, 0, term->x * term->y * sizeof(*term->screen));
|
|
term->screen = s;
|
|
term->dirty = 1;
|
|
term->lcx = -1;
|
|
term->lcy = -1;
|
|
}
|
|
|
|
static void clear_terminal(struct terminal *term)
|
|
{
|
|
NO_GFX;
|
|
fill_area(term, 0, 0, term->x, term->y, ' ', 0);
|
|
set_cursor(term, 0, 0, 0, 0);
|
|
}
|
|
|
|
void redraw_below_window(struct window *win)
|
|
{
|
|
int tr;
|
|
struct terminal *term = win->term;
|
|
struct window *w;
|
|
struct list_head *lw;
|
|
struct links_event ev = { EV_REDRAW, 0, 0, 0 };
|
|
NO_GFX;
|
|
ev.x = term->x;
|
|
ev.y = term->y;
|
|
if (term->redrawing >= 2) return;
|
|
tr = term->redrawing;
|
|
win->term->redrawing = 2;
|
|
foreachback(struct window, w, lw, term->windows) {
|
|
if (w == win)
|
|
break;
|
|
w->handler(w, &ev, 0);
|
|
}
|
|
term->redrawing = tr;
|
|
}
|
|
|
|
static void redraw_terminal_ev(struct terminal *term, int e)
|
|
{
|
|
struct window *win;
|
|
struct list_head *lwin;
|
|
struct links_event ev = {0, 0, 0, 0};
|
|
NO_GFX;
|
|
ev.ev = e;
|
|
ev.x = term->x;
|
|
ev.y = term->y;
|
|
clear_terminal(term);
|
|
term->redrawing = 2;
|
|
foreachback(struct window, win, lwin, term->windows) win->handler(win, &ev, 0);
|
|
term->redrawing = 0;
|
|
}
|
|
|
|
static void redraw_terminal(struct terminal *term)
|
|
{
|
|
NO_GFX;
|
|
redraw_terminal_ev(term, EV_REDRAW);
|
|
}
|
|
|
|
static void redraw_terminal_all(struct terminal *term)
|
|
{
|
|
NO_GFX;
|
|
redraw_terminal_ev(term, EV_RESIZE);
|
|
}
|
|
|
|
static void erase_screen(struct terminal *term)
|
|
{
|
|
NO_GFX;
|
|
if (!term->master || !is_blocked()) {
|
|
if (term->master) want_draw();
|
|
hard_write(term->fdout, cast_uchar "\033[2J\033[1;1H", 10);
|
|
if (term->master) done_draw();
|
|
}
|
|
}
|
|
|
|
static void redraw_terminal_cls(struct terminal *term)
|
|
{
|
|
NO_GFX;
|
|
erase_screen(term);
|
|
set_margin(term);
|
|
alloc_term_screen(term);
|
|
redraw_terminal_all(term);
|
|
}
|
|
|
|
void cls_redraw_all_terminals(void)
|
|
{
|
|
struct terminal *term;
|
|
struct list_head *lterm;
|
|
foreach(struct terminal, term, lterm, terminals) {
|
|
if (!F) redraw_terminal_cls(term);
|
|
#ifdef G
|
|
else term->dev->resize_handler(term->dev);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void draw_to_window(struct window *win, void (*fn)(struct terminal *term, void *), void *data)
|
|
{
|
|
struct terminal * volatile term = win->term; /* volatile because of setjmp */
|
|
struct window *w;
|
|
struct list_head *lw;
|
|
if (!F) {
|
|
pr(fn(term, data)) {};
|
|
term = win->term;
|
|
if (win->list_entry.prev == &term->windows || term->redrawing) return;
|
|
term->redrawing = 1;
|
|
{
|
|
struct links_event ev = { EV_REDRAW, 0, 0, 0 };
|
|
ev.x = term->x;
|
|
ev.y = term->y;
|
|
foreachbackfrom(struct window, w, lw, term->windows, win->list_entry.prev)
|
|
w->handler(w, &ev, 0);
|
|
}
|
|
term->redrawing = 0;
|
|
#ifdef G
|
|
} else {
|
|
struct rect r1, *r;
|
|
struct rect_set *s;
|
|
volatile int i; /* volatile because of setjmp */
|
|
int a;
|
|
if (win->list_entry.prev == &term->windows) {
|
|
pr(fn(term, data)) {};
|
|
return;
|
|
}
|
|
s = init_rect_set();
|
|
intersect_rect(&r1, &win->pos, &term->dev->clip);
|
|
add_to_rect_set(&s, &r1);
|
|
foreachbackfrom(struct window, w, lw, term->windows, win->list_entry.prev)
|
|
exclude_rect_from_set(&s, &w->pos);
|
|
a = 0;
|
|
memcpy(&r1, &term->dev->clip, sizeof(struct rect));
|
|
for (i = 0; i < s->m; i++) if (is_rect_valid(r = &s->r[i])) {
|
|
set_clip_area(term->dev, r);
|
|
pr(fn(term, data)) {
|
|
#ifdef OOPS
|
|
return;
|
|
#endif
|
|
}
|
|
a = 1;
|
|
}
|
|
if (!a) {
|
|
struct rect empty = { 0, 0, 0, 0 };
|
|
set_clip_area(term->dev, &empty);
|
|
fn(term, data);
|
|
}
|
|
set_clip_area(term->dev, &r1);
|
|
mem_free(s);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void redraw_window(struct window *win)
|
|
{
|
|
struct links_event ev = { EV_REDRAW, 0, 0, 0 };
|
|
ev.x = win->term->x;
|
|
ev.y = win->term->y;
|
|
win->handler(win, &ev, 0);
|
|
}
|
|
|
|
#ifdef G
|
|
|
|
static void redraw_windows(void *term_)
|
|
{
|
|
struct terminal *term = (struct terminal *)term_;
|
|
struct terminal *t1;
|
|
struct list_head *lt1;
|
|
struct window *win;
|
|
struct list_head *lwin;
|
|
foreach(struct terminal, t1, lt1, terminals) if (t1 == term) goto ok;
|
|
return;
|
|
ok:
|
|
foreach(struct window, win, lwin, term->windows) {
|
|
struct links_event ev = { EV_REDRAW, 0, 0, 0 };
|
|
ev.x = term->x;
|
|
ev.y = term->y;
|
|
set_clip_area(term->dev, &win->redr);
|
|
memset(&win->redr, 0, sizeof(struct rect));
|
|
win->handler(win, &ev, 0);
|
|
}
|
|
set_clip_area(term->dev, &term->dev->size);
|
|
}
|
|
|
|
void set_window_pos(struct window *win, int x1, int y1, int x2, int y2)
|
|
{
|
|
struct terminal *term = win->term;
|
|
struct rect r;
|
|
NO_TXT;
|
|
r.x1 = x1, r.y1 = y1, r.x2 = x2, r.y2 = y2;
|
|
if (is_rect_valid(&win->pos) && (x1 > win->pos.x1 || x2 < win->pos.x2 || y1 > win->pos.y1 || y2 < win->pos.y2) && term->redrawing < 2) {
|
|
struct window *w;
|
|
struct list_head *lw;
|
|
foreachfrom(struct window, w, lw, term->windows, win->list_entry.next) unite_rect(&w->redr, &win->pos, &w->redr);
|
|
register_bottom_half(redraw_windows, term);
|
|
}
|
|
memcpy(&win->pos, &r, sizeof(struct rect));
|
|
}
|
|
|
|
#endif
|
|
|
|
void add_window(struct terminal *term, void (*handler)(struct window *, struct links_event *, int), void *data)
|
|
{
|
|
struct links_event ev = { EV_INIT, 0, 0, 0 };
|
|
struct window *win;
|
|
ev.x = term->x;
|
|
ev.y = term->y;
|
|
win = mem_calloc(sizeof(struct window));
|
|
win->handler = handler;
|
|
win->data = data;
|
|
win->term = term;
|
|
win->xp = win->yp = 0;
|
|
add_to_list(term->windows, win);
|
|
win->handler(win, &ev, 0);
|
|
}
|
|
|
|
void delete_window(struct window *win)
|
|
{
|
|
#ifdef G
|
|
struct list_head *nxw;
|
|
#endif
|
|
struct terminal *term = win->term;
|
|
struct links_event ev = { EV_ABORT, 0, 0, 0 };
|
|
win->handler(win, &ev, 1);
|
|
#ifdef G
|
|
nxw = win->list_entry.next;
|
|
#endif
|
|
del_from_list(win);
|
|
if (win->data) mem_free(win->data);
|
|
if (!F) redraw_terminal(term);
|
|
#ifdef G
|
|
else {
|
|
struct window *w;
|
|
struct list_head *lw;
|
|
foreachfrom(struct window, w, lw, term->windows, nxw) unite_rect(&w->redr, &win->pos, &w->redr);
|
|
register_bottom_half(redraw_windows, term);
|
|
}
|
|
#endif
|
|
mem_free(win);
|
|
}
|
|
|
|
void delete_window_ev(struct window *win, struct links_event *ev)
|
|
{
|
|
struct terminal *term = win->term;
|
|
struct list_head *lw = win->list_entry.next;
|
|
delete_window(win);
|
|
if (ev && lw != &term->windows) {
|
|
struct window *w = list_struct(lw, struct window);
|
|
w->handler(w, ev, 1);
|
|
}
|
|
}
|
|
|
|
void set_window_ptr(struct window *win, int x, int y)
|
|
{
|
|
if (win->xp == x && win->yp == y) return;
|
|
win->xp = x;
|
|
win->yp = y;
|
|
#ifdef G
|
|
{
|
|
struct terminal *term = win->term;
|
|
if (F && win->list_entry.prev != &term->windows) {
|
|
struct window *prev = list_struct(win->list_entry.prev, struct window);
|
|
memcpy(&prev->redr, &term->dev->size, sizeof(struct rect));
|
|
register_bottom_half(redraw_windows, term);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void get_parent_ptr(struct window *win, int *x, int *y)
|
|
{
|
|
if (win->list_entry.next != &win->term->windows) {
|
|
struct window *next = list_struct(win->list_entry.next, struct window);
|
|
*x = next->xp;
|
|
*y = next->yp;
|
|
} else {
|
|
*x = *y = 0;
|
|
}
|
|
}
|
|
|
|
struct ewd {
|
|
void (*fn)(void *);
|
|
void *data;
|
|
int b;
|
|
};
|
|
|
|
static void empty_window_handler(struct window *win, struct links_event *ev, int fwd)
|
|
{
|
|
struct terminal *term = win->term;
|
|
struct list_head *ln;
|
|
struct ewd *ewd = win->data;
|
|
int x, y;
|
|
void (*fn)(void *) = ewd->fn;
|
|
void *data = ewd->data;
|
|
if (ewd->b) return;
|
|
switch ((int)ev->ev) {
|
|
case EV_INIT:
|
|
case EV_RESIZE:
|
|
case EV_REDRAW:
|
|
get_parent_ptr(win, &x, &y);
|
|
set_window_ptr(win, x, y);
|
|
return;
|
|
case EV_ABORT:
|
|
fn(data);
|
|
return;
|
|
}
|
|
ewd->b = 1;
|
|
ln = win->list_entry.next;
|
|
delete_window(win);
|
|
fn(data);
|
|
if (ln != &term->windows) {
|
|
struct window *n = list_struct(ln, struct window);
|
|
n->handler(n, ev, fwd);
|
|
}
|
|
}
|
|
|
|
void add_empty_window(struct terminal *term, void (*fn)(void *), void *data)
|
|
{
|
|
struct ewd *ewd;
|
|
ewd = mem_alloc(sizeof(struct ewd));
|
|
ewd->fn = fn;
|
|
ewd->data = data;
|
|
ewd->b = 0;
|
|
add_window(term, empty_window_handler, ewd);
|
|
}
|
|
|
|
struct list_head term_specs = { &term_specs, &term_specs };
|
|
|
|
void free_term_specs(void)
|
|
{
|
|
free_list(struct term_spec, term_specs);
|
|
}
|
|
|
|
#if defined(OS2) || defined(DOS)
|
|
static struct term_spec dumb_term = { init_list_1st(NULL) "", 2, 1, 1, 0, 1, 0, -1, 0, 0, 0, 0, init_list_last(NULL) };
|
|
#else
|
|
static struct term_spec dumb_term = { init_list_1st(NULL) "", 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, init_list_last(NULL) };
|
|
#endif
|
|
static struct term_spec cygwin_term = { init_list_1st(NULL) "", 2, 1, 1, 0, 1, 0, -1, 0, 0, 0, 0, init_list_last(NULL) };
|
|
|
|
static struct term_spec *default_term_spec(unsigned char *term)
|
|
{
|
|
if (!casestrcmp(term, cast_uchar "cygwin"))
|
|
return &cygwin_term;
|
|
#ifdef DOS
|
|
{
|
|
static int is_bw = -1;
|
|
if (is_bw == -1) {
|
|
is_bw = dos_is_bw();
|
|
if (is_bw)
|
|
dumb_term.col = 0;
|
|
}
|
|
}
|
|
#endif
|
|
return &dumb_term;
|
|
}
|
|
|
|
static struct term_spec *get_term_spec(unsigned char *term)
|
|
{
|
|
struct term_spec *t;
|
|
struct list_head *lt;
|
|
NO_GFX;
|
|
foreach(struct term_spec, t, lt, term_specs) if (!casestrcmp(t->term, term)) return t;
|
|
return default_term_spec(term);
|
|
}
|
|
|
|
static void sync_term_specs(void)
|
|
{
|
|
struct terminal *term;
|
|
struct list_head *lterm;
|
|
foreach(struct terminal, term, lterm, terminals) term->spec = get_term_spec(term->term);
|
|
}
|
|
|
|
struct term_spec *new_term_spec(unsigned char *term)
|
|
{
|
|
struct term_spec *t;
|
|
struct list_head *lt;
|
|
foreach(struct term_spec, t, lt, term_specs) if (!casestrcmp(t->term, term)) return t;
|
|
t = mem_alloc(sizeof(struct term_spec));
|
|
memcpy(t, default_term_spec(term), sizeof(struct term_spec));
|
|
if (strlen(cast_const_char term) < MAX_TERM_LEN) strcpy(cast_char t->term, cast_const_char term);
|
|
else memcpy(t->term, term, MAX_TERM_LEN - 1), t->term[MAX_TERM_LEN - 1] = 0;
|
|
add_to_list(term_specs, t);
|
|
sync_term_specs();
|
|
return t;
|
|
}
|
|
|
|
struct terminal *init_term(int fdin, int fdout, void (*root_window)(struct window *, struct links_event *, int))
|
|
{
|
|
static tcount terminal_count = 0;
|
|
struct terminal *term;
|
|
struct window *win;
|
|
NO_GFX;
|
|
term = mem_calloc(sizeof(struct terminal));
|
|
term->count = terminal_count++;
|
|
term->fdin = fdin;
|
|
term->fdout = fdout;
|
|
term->master = term->fdout == get_output_handle();
|
|
term->lcx = -1;
|
|
term->lcy = -1;
|
|
term->dirty = 1;
|
|
term->blocked = -1;
|
|
term->screen = DUMMY;
|
|
term->last_screen = DUMMY;
|
|
term->spec = default_term_spec(cast_uchar "");
|
|
term->input_queue = DUMMY;
|
|
init_list(term->windows);
|
|
term->handle_to_close = -1;
|
|
win = mem_calloc(sizeof(struct window));
|
|
win->handler = root_window;
|
|
win->term = term;
|
|
add_to_list(term->windows, win);
|
|
add_to_list(terminals, term);
|
|
set_handlers(fdin, in_term, NULL, term);
|
|
return term;
|
|
}
|
|
|
|
static int process_utf_8(struct terminal *term, struct links_event *ev)
|
|
{
|
|
#if defined(G) || defined(ENABLE_UTF8)
|
|
if (ev->ev == EV_KBD) {
|
|
if ((!F && term_charset(term) == utf8_table)
|
|
#ifdef G
|
|
|| (F && !(drv->flags & GD_UNICODE_KEYS) && g_kbd_codepage(drv) == utf8_table)
|
|
#endif
|
|
) {
|
|
size_t l;
|
|
unsigned char *p;
|
|
unsigned c;
|
|
if (ev->x <= 0 || ev->x >= 0x100) goto direct;
|
|
if ((term->utf8_paste_mode ^ ev->y) & KBD_PASTING) {
|
|
term->utf8_paste_mode = ev->y & KBD_PASTING;
|
|
term->utf8_buffer[0] = 0;
|
|
}
|
|
if ((l = strlen(cast_const_char term->utf8_buffer)) >= sizeof(term->utf8_buffer) - 1 || ev->x < 0x80 || ev->x >= 0xc0)
|
|
term->utf8_buffer[0] = 0, l = 0;
|
|
term->utf8_buffer[l] = (unsigned char)ev->x;
|
|
term->utf8_buffer[l + 1] = 0;
|
|
p = term->utf8_buffer;
|
|
GET_UTF_8(p, c);
|
|
if (!c) return 0;
|
|
ev->x = c;
|
|
}
|
|
direct:
|
|
term->utf8_buffer[0] = 0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
#ifdef G
|
|
|
|
void t_redraw(struct graphics_device *, struct rect *);
|
|
static void t_resize(struct graphics_device *);
|
|
static void t_kbd(struct graphics_device *, int, int);
|
|
static void t_mouse(struct graphics_device *, int, int, int);
|
|
static void t_extra(struct graphics_device *, int, unsigned char *);
|
|
|
|
static struct term_spec gfx_term = { init_list_1st(NULL) "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, init_list_last(NULL) };
|
|
|
|
struct terminal *init_gfx_term(void (*root_window)(struct window *, struct links_event *, int), unsigned char *cwd, void *info, int len)
|
|
{
|
|
static tcount terminal_count = 0;
|
|
struct terminal *term;
|
|
struct graphics_device *dev;
|
|
struct window *win;
|
|
NO_TXT;
|
|
term = mem_calloc(sizeof(struct terminal));
|
|
term->count = terminal_count++;
|
|
term->fdin = -1;
|
|
if (!(term->dev = dev = drv->init_device())) {
|
|
mem_free(term);
|
|
check_if_no_terminal();
|
|
return NULL;
|
|
}
|
|
dev->user_data = term;
|
|
term->master = 1;
|
|
term->blocked = -1;
|
|
term->x = dev->size.x2;
|
|
term->y = dev->size.y2;
|
|
term->last_mouse_x = term->last_mouse_y = term->last_mouse_b = MAXINT;
|
|
term->environment = !(drv->flags & GD_ONLY_1_WINDOW) ? ENV_G : 0;
|
|
if (!casestrcmp(drv->name, cast_uchar "x")) term->environment |= ENV_XWIN;
|
|
term->spec = &gfx_term;
|
|
term->default_character_set = get_default_charset();
|
|
safe_strncpy(term->cwd, cwd, MAX_CWD_LEN);
|
|
gfx_term.character_set = utf8_table;
|
|
if (gfx_term.character_set == -1) gfx_term.character_set = 0;
|
|
init_list(term->windows);
|
|
term->handle_to_close = -1;
|
|
win = mem_calloc(sizeof (struct window));
|
|
win->handler = root_window;
|
|
win->term = term;
|
|
win->pos.x2 = dev->size.x2;
|
|
win->pos.y2 = dev->size.y2;
|
|
add_to_list(term->windows, win);
|
|
add_to_list(terminals, term);
|
|
dev->redraw_handler = t_redraw;
|
|
dev->resize_handler = t_resize;
|
|
dev->keyboard_handler = t_kbd;
|
|
dev->mouse_handler = t_mouse;
|
|
dev->extra_handler = t_extra;
|
|
{
|
|
int *ptr;
|
|
struct links_event ev = { EV_INIT, 0, 0, 0 };
|
|
ev.x = dev->size.x2;
|
|
ev.y = dev->size.y2;
|
|
if ((unsigned)len > MAXINT - sizeof(int)) overalloc();
|
|
ptr = mem_alloc(sizeof(int) + len);
|
|
*ptr = len;
|
|
memcpy(ptr + 1, info, len);
|
|
ev.b = (my_intptr_t)ptr;
|
|
root_window(win, &ev, 0);
|
|
mem_free(ptr);
|
|
}
|
|
return term;
|
|
}
|
|
|
|
void t_redraw(struct graphics_device *dev, struct rect *r)
|
|
{
|
|
struct terminal *term = dev->user_data;
|
|
struct window *win;
|
|
struct list_head *lwin;
|
|
foreach(struct window, win, lwin, term->windows) unite_rect(&win->redr, r, &win->redr);
|
|
register_bottom_half(redraw_windows, term);
|
|
}
|
|
|
|
static void t_resize(struct graphics_device *dev)
|
|
{
|
|
struct terminal *term = dev->user_data;
|
|
struct window *win;
|
|
struct list_head *lwin;
|
|
struct links_event ev = { EV_RESIZE, 0, 0, 0 };
|
|
term->x = ev.x = dev->size.x2;
|
|
term->y = ev.y = dev->size.y2;
|
|
set_clip_area(dev, &dev->size);
|
|
foreach(struct window, win, lwin, term->windows) {
|
|
win->handler(win, &ev, 0);
|
|
}
|
|
set_clip_area(dev, &dev->size);
|
|
}
|
|
|
|
static void t_kbd(struct graphics_device *dev, int key, int flags)
|
|
{
|
|
struct terminal *term = dev->user_data;
|
|
struct links_event ev = { EV_KBD, 0, 0, 0 };
|
|
struct rect r = { 0, 0, 0, 0 };
|
|
r.x2 = dev->size.x2, r.y2 = dev->size.y2;
|
|
ev.x = key;
|
|
ev.y = flags;
|
|
if (upcase(ev.x) == 'L' && !(ev.y & KBD_PASTING) && ev.y & KBD_CTRL) {
|
|
t_redraw(dev, &r);
|
|
return;
|
|
} else {
|
|
if (ev.x == KBD_STOP) {
|
|
abort_background_connections();
|
|
}
|
|
set_clip_area(dev, &r);
|
|
if (list_empty(term->windows)) return;
|
|
if (ev.x == KBD_CTRL_C || ev.x == KBD_CLOSE) {
|
|
struct window *prev = list_struct(term->windows.prev, struct window);
|
|
prev->handler(prev, &ev, 0);
|
|
} else {
|
|
if (process_utf_8(term, &ev)) {
|
|
struct window *next = list_struct(term->windows.next, struct window);
|
|
next->handler(next, &ev, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void t_mouse(struct graphics_device *dev, int x, int y, int b)
|
|
{
|
|
struct terminal *term = dev->user_data;
|
|
struct links_event ev = { EV_MOUSE, 0, 0, 0 };
|
|
struct rect r = {0, 0, 0, 0};
|
|
int bt, ac;
|
|
struct window *next;
|
|
if (x == term->last_mouse_x && y == term->last_mouse_y && b == term->last_mouse_b) {
|
|
return;
|
|
}
|
|
bt = b & BM_BUTT;
|
|
ac = b & BM_ACT;
|
|
if ((ac == B_MOVE || ac == B_DRAG) &&
|
|
(bt == B_LEFT || bt == B_MIDDLE || bt == B_RIGHT || bt == B_FOURTH || bt == B_FIFTH || bt == B_SIXTH)) {
|
|
term->last_mouse_x = x;
|
|
term->last_mouse_y = y;
|
|
term->last_mouse_b = b;
|
|
} else {
|
|
term->last_mouse_x = term->last_mouse_y = term->last_mouse_b = MAXINT;
|
|
}
|
|
r.x2 = dev->size.x2, r.y2 = dev->size.y2;
|
|
ev.x = x, ev.y = y, ev.b = b;
|
|
set_clip_area(dev, &r);
|
|
if (list_empty(term->windows)) return;
|
|
next = list_struct(term->windows.next, struct window);
|
|
next->handler(next, &ev, 0);
|
|
}
|
|
|
|
static void t_extra(struct graphics_device *dev, int type, unsigned char *str)
|
|
{
|
|
struct terminal *term = dev->user_data;
|
|
struct links_event ev = { EV_EXTRA, 0, 0, 0 };
|
|
struct window *prev;
|
|
|
|
ev.x = type;
|
|
ev.b = (my_intptr_t)str;
|
|
|
|
if (list_empty(term->windows)) return;
|
|
prev = list_struct(term->windows.prev, struct window);
|
|
prev->handler(prev, &ev, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void in_term(void *term_)
|
|
{
|
|
struct terminal *term = (struct terminal *)term_;
|
|
struct links_event *ev;
|
|
int r;
|
|
unsigned char *iq;
|
|
NO_GFX;
|
|
if ((unsigned)term->qlen + ALLOC_GR > MAXINT) overalloc();
|
|
iq = mem_realloc(term->input_queue, term->qlen + ALLOC_GR);
|
|
term->input_queue = iq;
|
|
EINTRLOOP(r, (int)read(term->fdin, iq + term->qlen, ALLOC_GR));
|
|
if (r <= 0) {
|
|
if (r == -1 && errno != ECONNRESET) error("ERROR: error %d on terminal: could not read event", errno);
|
|
destroy_terminal(term);
|
|
return;
|
|
}
|
|
term->qlen += r;
|
|
test_queue:
|
|
if ((size_t)term->qlen < sizeof(struct links_event)) return;
|
|
ev = (struct links_event *)iq;
|
|
r = sizeof(struct links_event);
|
|
if (ev->ev != EV_INIT && ev->ev != EV_RESIZE && ev->ev != EV_REDRAW && ev->ev != EV_KBD && ev->ev != EV_MOUSE && ev->ev != EV_ABORT) {
|
|
error("ERROR: error on terminal: bad event %d", ev->ev);
|
|
goto mm;
|
|
}
|
|
if (ev->ev == EV_INIT) {
|
|
int init_len;
|
|
if ((size_t)term->qlen < sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + 3 * sizeof(int)) return;
|
|
init_len = *(int *)(iq + sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + 2 * sizeof(int));
|
|
if ((size_t)term->qlen < sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + 3 * sizeof(int) + init_len) return;
|
|
memcpy(term->term, iq + sizeof(struct links_event), MAX_TERM_LEN);
|
|
term->term[MAX_TERM_LEN - 1] = 0;
|
|
memcpy(term->cwd, iq + sizeof(struct links_event) + MAX_TERM_LEN, MAX_CWD_LEN);
|
|
term->cwd[MAX_CWD_LEN - 1] = 0;
|
|
term->environment = *(int *)(iq + sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN);
|
|
term->default_character_set = *(int *)(iq + sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + sizeof(int));
|
|
ev->b = (my_intptr_t)(iq + sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + 2 * sizeof(int));
|
|
r = (int)sizeof(struct links_event) + MAX_TERM_LEN + MAX_CWD_LEN + 3 * (int)sizeof(int) + init_len;
|
|
sync_term_specs();
|
|
}
|
|
if (ev->ev == EV_REDRAW || ev->ev == EV_RESIZE || ev->ev == EV_INIT) {
|
|
struct window *win;
|
|
struct list_head *lwin;
|
|
|
|
term->real_x = ev->x;
|
|
term->real_y = ev->y;
|
|
|
|
set_margin(term);
|
|
|
|
send_redraw:
|
|
if (ev->x < 0 || ev->y < 0) {
|
|
error("ERROR: bad terminal size: %d, %d", (int)ev->x, (int)ev->y);
|
|
goto mm;
|
|
}
|
|
alloc_term_screen(term);
|
|
clear_terminal(term);
|
|
erase_screen(term);
|
|
term->redrawing = 1;
|
|
foreachback(struct window, win, lwin, term->windows) win->handler(win, ev, 0);
|
|
term->redrawing = 0;
|
|
}
|
|
if (ev->ev == EV_MOUSE) {
|
|
ev->x -= term->left_margin;
|
|
ev->y -= term->top_margin;
|
|
}
|
|
if (ev->ev == EV_KBD || ev->ev == EV_MOUSE) {
|
|
if (ev->ev == EV_KBD && upcase(ev->x) == 'L' && !(ev->y & KBD_PASTING) && ev->y & KBD_CTRL) {
|
|
ev->ev = EV_REDRAW;
|
|
ev->x = term->x;
|
|
ev->y = term->y;
|
|
goto send_redraw;
|
|
}
|
|
if (ev->ev == EV_KBD && ev->x == KBD_STOP) {
|
|
abort_background_connections();
|
|
}
|
|
if (!list_empty(term->windows)) {
|
|
if (ev->ev == EV_KBD && ev->x == KBD_CTRL_C && !(ev->y & KBD_PASTING)) {
|
|
struct window *prev = list_struct(term->windows.prev, struct window);
|
|
prev->handler(prev, ev, 0);
|
|
} else {
|
|
if (process_utf_8(term, ev)) {
|
|
struct window *next = list_struct(term->windows.next, struct window);
|
|
next->handler(next, ev, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ev->ev == EV_ABORT) {
|
|
destroy_terminal(term);
|
|
return;
|
|
}
|
|
mm:
|
|
if (term->qlen == r) term->qlen = 0;
|
|
else memmove(iq, iq + r, term->qlen -= r);
|
|
goto test_queue;
|
|
}
|
|
|
|
static inline int getcompcode(int c)
|
|
{
|
|
return (c<<1 | (c&4)>>2) & 7;
|
|
}
|
|
|
|
unsigned char frame_dumb[49] = " ||||++||++++++--|-+||++--|-+----++++++++ ";
|
|
static const unsigned char frame_vt100[49] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla ";
|
|
static const unsigned char frame_koi[48] = {
|
|
144,145,146,129,135,178,180,167,
|
|
166,181,161,168,174,173,172,131,
|
|
132,137,136,134,128,138,175,176,
|
|
171,165,187,184,177,160,190,185,
|
|
186,182,183,170,169,162,164,189,
|
|
188,133,130,141,140,142,143,139,
|
|
};
|
|
static const unsigned char frame_freebsd[48] = {
|
|
130,138,128,153,150,150,150,140,
|
|
140,150,153,140,139,139,139,140,
|
|
142,151,152,149,146,143,149,149,
|
|
142,141,151,152,149,146,143,151,
|
|
151,152,152,142,142,141,141,143,
|
|
143,139,141,128,128,128,128,128,
|
|
};
|
|
static const unsigned short frame_utf8[48] = {
|
|
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
|
|
0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
|
|
0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
|
|
0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
|
|
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
|
|
0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
|
|
};
|
|
static const unsigned char frame_restrict[48] = {
|
|
0, 0, 0, 0, 0,179,186,186,
|
|
205, 0, 0, 0, 0,186,205, 0,
|
|
0, 0, 0, 0, 0, 0,179,186,
|
|
0, 0, 0, 0, 0, 0, 0,205,
|
|
196,205,196,186,205,205,186,186,
|
|
179, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
#if defined(ENABLE_UTF8) && defined(WIN)
|
|
static inline char_t utf8_hack(char_t c)
|
|
{
|
|
/*
|
|
* These characters produce beeps on Cygwin.
|
|
*/
|
|
switch (c) {
|
|
case 0xb7:
|
|
case 0x2022:
|
|
case 0x2024:
|
|
case 0x2026:
|
|
case 0x2219:
|
|
case 0x22c5:
|
|
case 0x30fb:
|
|
return '.';
|
|
default:
|
|
return c;
|
|
}
|
|
}
|
|
#else
|
|
#define utf8_hack(x) (x)
|
|
#endif
|
|
|
|
#define SETPOS(x, y) \
|
|
{ \
|
|
add_to_str(&a, &l, cast_uchar "\033["); \
|
|
add_num_to_str(&a, &l, (y) + 1 + term->top_margin); \
|
|
add_chr_to_str(&a, &l, ';'); \
|
|
add_num_to_str(&a, &l, (x) + 1 + term->left_margin); \
|
|
add_chr_to_str(&a, &l, 'H'); \
|
|
n_chars = 0; \
|
|
}
|
|
|
|
#define PRINT_CHAR(p) \
|
|
{ \
|
|
char_t c = term->screen[p].ch; \
|
|
unsigned char A = term->screen[p].at & 0x7f; \
|
|
unsigned char frm = !!(term->screen[p].at & ATTR_FRAME); \
|
|
if (s->restrict_852 && frm && c >= 176 && c < 224) { \
|
|
if (frame_restrict[c - 176]) c = frame_restrict[c - 176];\
|
|
} \
|
|
if (s->mode == TERM_LINUX) { \
|
|
if (s->m11_hack) { \
|
|
if (frm != mode) { \
|
|
if (!(mode = frm)) add_to_str(&a, &l, cast_uchar "\033[10m");\
|
|
else add_to_str(&a, &l, cast_uchar "\033[11m");\
|
|
} \
|
|
} \
|
|
} else if (s->mode == TERM_VT100) { \
|
|
if (frm != mode) { \
|
|
if (!(mode = frm)) add_to_str(&a, &l, cast_uchar "\017");\
|
|
else add_to_str(&a, &l, cast_uchar "\016"); \
|
|
} \
|
|
if (frm && c >= 176 && c < 224) c = frame_vt100[c - 176];\
|
|
} else if (s->mode == TERM_KOI8 && frm && c >= 176 && c < 224) { c = frame_koi[c - 176];\
|
|
} else if (s->mode == TERM_FREEBSD && frm && c >= 176 && c < 224) { c = frame_freebsd[c - 176];\
|
|
} else if (s->mode == TERM_UTF8 && frm && c >= 176 && c < 224 && term_charset(term) == utf8_table) { c = frame_utf8[c - 176]; frm = 0;\
|
|
} else if (frm && c >= 176 && c < 224) c = frame_dumb[c - 176];\
|
|
if (!(A & 0100) && (A >> 3) == (A & 7)) A = (A & 070) | 7 * !(A & 020);\
|
|
if (A != attrib) { \
|
|
attrib = A; \
|
|
add_to_str(&a, &l, cast_uchar "\033[0"); \
|
|
if (s->col) { \
|
|
unsigned char m[4]; \
|
|
m[0] = ';'; m[1] = '3'; m[3] = 0; \
|
|
m[2] = (attrib & 7) + '0'; \
|
|
add_to_str(&a, &l, m); \
|
|
m[1] = '4'; \
|
|
m[2] = ((attrib >> 3) & 7) + '0'; \
|
|
add_to_str(&a, &l, m); \
|
|
} else if (getcompcode(attrib & 7) < getcompcode(attrib >> 3 & 7))\
|
|
add_to_str(&a, &l, cast_uchar ";7"); \
|
|
if (attrib & 0100) add_to_str(&a, &l, cast_uchar ";1"); \
|
|
add_chr_to_str(&a, &l, 'm'); \
|
|
} \
|
|
if (c >= ' ' && c != 127 && (c != 155 || \
|
|
(term_charset(term) != utf8_table && cp2u(155, term_charset(term)) != -1))) {\
|
|
if (c < 128 || frm || term_charset(term) != utf8_table) {\
|
|
add_chr_to_str(&a, &l, (unsigned char)c); \
|
|
} else { \
|
|
/* \
|
|
* Linux UTF-8 console is broken and doesn't advance cursor\
|
|
* on some characters. So we first print an one-byte \
|
|
* replacement, then set the cursor back, then print \
|
|
* the UTF-8 character and finally set the cursor again.\
|
|
*/ \
|
|
unsigned char *r; \
|
|
c = utf8_hack(c); \
|
|
r = u2cp(c, 0, 1); \
|
|
if (!(r && r[0] >= 32 && r[0] < 127 && !r[1])) r = cast_uchar "*";\
|
|
add_chr_to_str(&a, &l, r[0]); \
|
|
if (term->x - cx > 1) \
|
|
add_chr_to_str(&a, &l, 8); \
|
|
else \
|
|
SETPOS(cx, y); \
|
|
add_to_str(&a, &l, encode_utf_8(c)); \
|
|
SETPOS(cx + 1, y); \
|
|
print_next = 1; \
|
|
} \
|
|
} \
|
|
else if (!c || c == 1) add_chr_to_str(&a, &l, ' '); \
|
|
else add_chr_to_str(&a, &l, '.'); \
|
|
cx++; \
|
|
n_chars++; \
|
|
}
|
|
|
|
static void redraw_screen(struct terminal *term)
|
|
{
|
|
int x, y, p = 0;
|
|
int cx = term->lcx, cy = term->lcy;
|
|
unsigned n_chars = MAXINT / 2;
|
|
unsigned char *a;
|
|
int attrib = -1;
|
|
int mode = -1;
|
|
int l = 0;
|
|
int print_next = 0;
|
|
struct term_spec *s;
|
|
NO_GFX;
|
|
if (!term->dirty || (term->master && is_blocked())) return;
|
|
a = init_str();
|
|
s = term->spec;
|
|
for (y = 0; y < term->y; y++) {
|
|
if (!memcmp(&term->screen[p], &term->last_screen[p], sizeof(chr) * term->x)) {
|
|
p += term->x;
|
|
continue;
|
|
}
|
|
for (x = 0; x < term->x; x++, p++) {
|
|
int i;
|
|
if (y == term->y - 1 && x == term->x - 1 && term->left_margin + term->x == term->real_x && term->top_margin + term->y == term->real_y) break;
|
|
if (term->screen[p].ch == term->last_screen[p].ch && term->screen[p].at == term->last_screen[p].at) {
|
|
/* make sure that padding is identical */
|
|
if (chr_has_padding)
|
|
memcpy(&term->last_screen[p], &term->screen[p], sizeof(chr));
|
|
if (print_next) {
|
|
print_next = 0;
|
|
goto must_print_next;
|
|
}
|
|
continue;
|
|
}
|
|
/*if ((term->screen[p].at & 0x38) == (term->last_screen[p].at & 0x38) && (term->screen[p].ch == 0 || term->screen[p].ch == 1 || term->screen[p].ch == ' ') && (term->last_screen[p].ch == 0 || term->last_screen[p].ch == 1 || term->last_screen[p].ch == ' ') && (x != term->cx || y != term->cy)) continue;*/
|
|
/*fprintf(stderr, "%d.%d : %d-%d -> %d-%d\n", x, y, term->last_screen[p].ch, term->last_screen[p].at, term->screen[p].ch, term->screen[p].at);*/
|
|
memcpy(&term->last_screen[p], &term->screen[p], sizeof(chr));
|
|
must_print_next:
|
|
#ifdef OPENVMS
|
|
if (n_chars >= term->x - 6) cy = -1;
|
|
#endif
|
|
if (cx == x && cy == y) goto pc;/*PRINT_CHAR(p)*/
|
|
else if (cy == y && x - cx < 10 && x - cx > 0) {
|
|
for (i = x - cx; i >= 0; i--) {
|
|
ppc:
|
|
PRINT_CHAR(p - i);
|
|
}
|
|
} else {
|
|
SETPOS(x, y);
|
|
cx = x; cy = y;
|
|
pc:
|
|
i = 0;
|
|
goto ppc;
|
|
}
|
|
}
|
|
if (print_next && term->left_margin + term->x < term->real_x) {
|
|
add_to_str(&a, &l, cast_uchar "\033[0m ");
|
|
attrib = -1;
|
|
print_next = 0;
|
|
}
|
|
}
|
|
if (l) {
|
|
if (s->col) add_to_str(&a, &l, cast_uchar "\033[37;40m");
|
|
add_to_str(&a, &l, cast_uchar "\033[0m");
|
|
if (s->mode == TERM_LINUX && s->m11_hack) add_to_str(&a, &l, cast_uchar "\033[10m");
|
|
if (s->mode == TERM_VT100) add_to_str(&a, &l, cast_uchar "\017");
|
|
}
|
|
term->lcx = cx;
|
|
term->lcy = cy;
|
|
if (term->cx != term->lcx || term->cy != term->lcy) {
|
|
term->lcx = term->cx;
|
|
term->lcy = term->cy;
|
|
add_to_str(&a, &l, cast_uchar "\033[");
|
|
add_num_to_str(&a, &l, term->cy + 1 + term->top_margin);
|
|
add_chr_to_str(&a, &l, ';');
|
|
add_num_to_str(&a, &l, term->cx + 1 + term->left_margin);
|
|
add_chr_to_str(&a, &l, 'H');
|
|
}
|
|
if (l && term->master) want_draw();
|
|
#ifdef OPENVMS
|
|
{
|
|
/*
|
|
* OpenVMS/VAX has some bug in the terminal driver and corrupts long strings.
|
|
* Also, we need to avoid breaking escape sequences.
|
|
*/
|
|
#define PRINT_BATCH 2000
|
|
int i, q;
|
|
for (i = 0; i < l; i += q) {
|
|
int qq;
|
|
q = PRINT_BATCH;
|
|
if (q > l - i) q = l - i;
|
|
for (qq = q - 1; qq > 0; qq--) {
|
|
if (a[i + qq] == 27) {
|
|
q = qq;
|
|
break;
|
|
}
|
|
}
|
|
hard_write(term->fdout, a + i, q);
|
|
}
|
|
}
|
|
#else
|
|
hard_write(term->fdout, a, l);
|
|
#endif
|
|
if (l && term->master) done_draw();
|
|
mem_free(a);
|
|
term->dirty = 0;
|
|
}
|
|
|
|
void redraw_all_terminals(void)
|
|
{
|
|
struct terminal *term;
|
|
struct list_head *lterm;
|
|
foreach(struct terminal, term, lterm, terminals) redraw_screen(term);
|
|
}
|
|
|
|
void flush_terminal(struct terminal *term)
|
|
{
|
|
if (!F) {
|
|
redraw_screen(term);
|
|
#ifdef G
|
|
} else {
|
|
if (drv->flush)
|
|
drv->flush(term->dev);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void destroy_terminal(void *term_)
|
|
{
|
|
struct terminal *term = (struct terminal *)term_;
|
|
int rs;
|
|
unregister_bottom_half(destroy_terminal, term);
|
|
#ifdef G
|
|
unregister_bottom_half(redraw_windows, term);
|
|
#endif
|
|
while (!list_empty(term->windows)) {
|
|
delete_window(list_struct(term->windows.next, struct window));
|
|
}
|
|
if (!F && !casestrcmp(term->term, cast_uchar "cygwin")) {
|
|
clear_terminal(term);
|
|
redraw_screen(term);
|
|
}
|
|
del_from_list(term);
|
|
close_socket(&term->blocked);
|
|
if (term->title) mem_free(term->title);
|
|
if (!F) {
|
|
mem_free(term->screen);
|
|
mem_free(term->last_screen);
|
|
mem_free(term->input_queue);
|
|
set_handlers(term->fdin, NULL, NULL, NULL);
|
|
EINTRLOOP(rs, close(term->fdin));
|
|
if (!term->master) {
|
|
if (term->fdout != term->fdin)
|
|
EINTRLOOP(rs, close(term->fdout));
|
|
} else {
|
|
unhandle_terminal_signals(term);
|
|
free_all_itrms();
|
|
if (!list_empty(terminals)) {
|
|
need_detach_console = 1;
|
|
}
|
|
}
|
|
#ifdef G
|
|
} else {
|
|
drv->shutdown_device(term->dev);
|
|
#endif
|
|
}
|
|
if (term->handle_to_close != -1) {
|
|
hard_write(term->handle_to_close, cast_uchar "x", 1);
|
|
close_socket(&term->handle_to_close);
|
|
}
|
|
mem_free(term);
|
|
check_if_no_terminal();
|
|
}
|
|
|
|
void destroy_all_terminals(void)
|
|
{
|
|
while (!list_empty(terminals)) {
|
|
destroy_terminal(list_struct(terminals.next, struct terminal));
|
|
}
|
|
}
|
|
|
|
static void check_if_no_terminal(void)
|
|
{
|
|
if (!list_empty(terminals)) return;
|
|
terminate_loop = 1;
|
|
}
|
|
|
|
void set_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at)
|
|
{
|
|
NO_GFX;
|
|
t->dirty = 1;
|
|
if ((unsigned)x < (unsigned)t->x && (unsigned)y < (unsigned)t->y) {
|
|
chr *cc = &t->screen[x + t->x * y];
|
|
cc->ch = ch;
|
|
cc->at = at;
|
|
}
|
|
}
|
|
|
|
const chr *get_char(struct terminal *t, int x, int y)
|
|
{
|
|
int lx, ly;
|
|
NO_GFX;
|
|
lx = t->x - 1;
|
|
ly = t->y - 1;
|
|
if ((lx | ly) < 0) {
|
|
static_const chr empty = { ' ', 070 };
|
|
return ∅
|
|
}
|
|
if (x > lx) x = lx;
|
|
else if (x < 0) x = 0;
|
|
if (y > ly) y = ly;
|
|
else if (y < 0) y = 0;
|
|
return &t->screen[x + t->x * y];
|
|
}
|
|
|
|
void set_color(struct terminal *t, int x, int y, unsigned char c)
|
|
{
|
|
NO_GFX;
|
|
t->dirty = 1;
|
|
if (x >= 0 && x < t->x && y >= 0 && y < t->y) t->screen[x + t->x * y].at = (t->screen[x + t->x * y].at & ATTR_FRAME) | (c & ~ATTR_FRAME);
|
|
}
|
|
|
|
void set_only_char(struct terminal *t, int x, int y, unsigned ch, unsigned char at)
|
|
{
|
|
const chr *cc;
|
|
NO_GFX;
|
|
t->dirty = 1;
|
|
cc = get_char(t, x, y);
|
|
at = (at & ATTR_FRAME) | (cc->at & ~ATTR_FRAME);
|
|
set_char(t, x, y, ch, at);
|
|
}
|
|
|
|
void set_line(struct terminal *t, int x, int y, int l, chr *line)
|
|
{
|
|
int i;
|
|
chr *cc;
|
|
NO_GFX;
|
|
t->dirty = 1;
|
|
if (y < 0 || y >= t->y) return;
|
|
i = x >= 0 ? 0 : -x;
|
|
cc = &t->screen[x+i + t->x * y];
|
|
line = &line[i];
|
|
i = (x+l <= t->x ? l : t->x-x) - i;
|
|
if (i <= 0) return;
|
|
memcpy(cc, line, i * sizeof(chr));
|
|
}
|
|
|
|
void set_line_color(struct terminal *t, int x, int y, int l, unsigned char c)
|
|
{
|
|
int i;
|
|
NO_GFX;
|
|
t->dirty = 1;
|
|
if (y < 0 || y >= t->y) return;
|
|
for (i = x >= 0 ? 0 : -x; i < (x+l <= t->x ? l : t->x-x); i++)
|
|
t->screen[x+i + t->x * y].at = (t->screen[x+i + t->x * y].at & ATTR_FRAME) | (c & ~ATTR_FRAME);
|
|
}
|
|
|
|
void fill_area(struct terminal *t, int x, int y, int xw, int yw, unsigned ch, unsigned char at)
|
|
{
|
|
int i;
|
|
chr *p, *ps;
|
|
NO_GFX;
|
|
if (x < 0) xw += x, x = 0;
|
|
if (x + xw > t->x) xw = t->x - x;
|
|
if (xw <= 0) return;
|
|
if (y < 0) yw += y, y = 0;
|
|
if (y + yw > t->y) yw = t->y - y;
|
|
if (yw <= 0) return;
|
|
t->dirty = 1;
|
|
p = ps = &t->screen[x + t->x * y];
|
|
for (i = 0; i < xw; i++) {
|
|
p->ch = ch;
|
|
p->at = at;
|
|
p++;
|
|
}
|
|
p = ps;
|
|
for (i = 1; i < yw; i++) {
|
|
p += t->x;
|
|
memcpy(p, ps, xw * sizeof(chr));
|
|
}
|
|
}
|
|
|
|
static int p1[] = { 218, 191, 192, 217, 179, 196 };
|
|
static int p2[] = { 201, 187, 200, 188, 186, 205 };
|
|
|
|
void draw_frame(struct terminal *t, int x, int y, int xw, int yw, unsigned char c, int w)
|
|
{
|
|
int *p = w > 1 ? p2 : p1;
|
|
NO_GFX;
|
|
c |= ATTR_FRAME;
|
|
set_char(t, x, y, p[0], c);
|
|
set_char(t, x+xw-1, y, p[1], c);
|
|
set_char(t, x, y+yw-1, p[2], c);
|
|
set_char(t, x+xw-1, y+yw-1, p[3], c);
|
|
fill_area(t, x, y+1, 1, yw-2, p[4], c);
|
|
fill_area(t, x+xw-1, y+1, 1, yw-2, p[4], c);
|
|
fill_area(t, x+1, y, xw-2, 1, p[5], c);
|
|
fill_area(t, x+1, y+yw-1, xw-2, 1, p[5], c);
|
|
}
|
|
|
|
void print_text(struct terminal *t, int x, int y, int l, unsigned char *text, unsigned char c)
|
|
{
|
|
NO_GFX;
|
|
for (; l--; x++) {
|
|
unsigned u = GET_TERM_CHAR(t, &text);
|
|
if (!u) break;
|
|
set_char(t, x, y, u, c);
|
|
}
|
|
}
|
|
|
|
void set_cursor(struct terminal *term, int x, int y, int altx, int alty)
|
|
{
|
|
NO_GFX;
|
|
term->dirty = 1;
|
|
if (term->spec->block_cursor && !term->spec->braille) x = altx, y = alty;
|
|
if (x >= term->x) x = term->x - 1;
|
|
if (y >= term->y) y = term->y - 1;
|
|
if (x < 0) x = 0;
|
|
if (y < 0) y = 0;
|
|
term->cx = x;
|
|
term->cy = y;
|
|
}
|
|
|
|
void exec_thread(void *path_, int p)
|
|
{
|
|
unsigned char *path = (unsigned char *)path_;
|
|
int rs;
|
|
#if defined(HAVE_SETPGID) && !defined(EXEC_IN_THREADS)
|
|
if (path[0] == 2)
|
|
EINTRLOOP(rs, setpgid(0, 0));
|
|
#endif
|
|
exe(path + 1, path[0]);
|
|
if (path[1 + strlen(cast_const_char(path + 1)) + 1])
|
|
EINTRLOOP(rs, unlink(cast_const_char(path + 1 + strlen(cast_const_char(path + 1)) + 1)));
|
|
}
|
|
|
|
void close_handle(void *p)
|
|
{
|
|
int h = (int)(my_intptr_t)p;
|
|
close_socket(&h);
|
|
}
|
|
|
|
static void unblock_terminal(void *term_)
|
|
{
|
|
struct terminal *term = (struct terminal *)term_;
|
|
close_handle((void *)(my_intptr_t)term->blocked);
|
|
term->blocked = -1;
|
|
if (!F) {
|
|
set_handlers(term->fdin, in_term, NULL, term);
|
|
unblock_itrm(term->fdin);
|
|
/* clear the dirty flag because unblock_itrm queued a resize
|
|
event - so avoid double redraw */
|
|
term->dirty = 0;
|
|
/*redraw_terminal_cls(term);*/
|
|
#ifdef G
|
|
} else if (drv->unblock) {
|
|
drv->unblock(term->dev);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef G
|
|
int have_extra_exec(void)
|
|
{
|
|
#ifdef NO_FG_EXEC
|
|
return 0;
|
|
#else
|
|
return F && drv->exec;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void exec_on_terminal(struct terminal *term, unsigned char *path, unsigned char *delet, unsigned char fg)
|
|
{
|
|
int rs;
|
|
if (path && !*path) return;
|
|
if (!path) path = cast_uchar "";
|
|
#ifdef HAVE_EXE_ON_BACKGROUND
|
|
if (*path) {
|
|
rs = exe_on_background(path, delet, fg);
|
|
if (!rs) return;
|
|
}
|
|
#endif
|
|
#ifdef NO_FG_EXEC
|
|
fg = 0;
|
|
#endif
|
|
if (term->master) {
|
|
if (!*path) {
|
|
if (!F) dispatch_special(delet);
|
|
} else {
|
|
int blockh;
|
|
unsigned char *param;
|
|
int paraml;
|
|
if (is_blocked() && fg) {
|
|
if (*delet)
|
|
EINTRLOOP(rs, unlink(cast_const_char delet));
|
|
return;
|
|
}
|
|
param = init_str();
|
|
paraml = 0;
|
|
add_chr_to_str(¶m, ¶ml, fg);
|
|
add_to_str(¶m, ¶ml, path);
|
|
add_chr_to_str(¶m, ¶ml, 0);
|
|
add_to_str(¶m, ¶ml, delet);
|
|
if (fg == 1) {
|
|
if (!F) block_itrm(term->fdin);
|
|
#ifdef G
|
|
else if (drv->block && drv->block(term->dev)) {
|
|
mem_free(param);
|
|
if (*delet)
|
|
EINTRLOOP(rs, unlink(cast_const_char delet));
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
heap_trim();
|
|
if ((blockh = start_thread(exec_thread, param, paraml + 1, *delet != 0)) == -1) {
|
|
if (fg == 1) {
|
|
if (!F) unblock_itrm(term->fdin);
|
|
#ifdef G
|
|
else if (drv->unblock) drv->unblock(term->dev);
|
|
#endif
|
|
}
|
|
mem_free(param);
|
|
return;
|
|
}
|
|
mem_free(param);
|
|
if (fg == 1
|
|
#ifdef G
|
|
&& !have_extra_exec()
|
|
#endif
|
|
) {
|
|
term->blocked = blockh;
|
|
#ifdef DOS
|
|
unblock_terminal(term);
|
|
#else
|
|
set_handlers(blockh, unblock_terminal, NULL, term);
|
|
if (!F) set_handlers(term->fdin, NULL, NULL, term);
|
|
/*block_itrm(term->fdin);*/
|
|
#endif
|
|
} else {
|
|
set_handlers(blockh, close_handle, NULL, (void *)(my_intptr_t)blockh);
|
|
}
|
|
}
|
|
} else {
|
|
unsigned char *data;
|
|
int datal;
|
|
data = init_str();
|
|
datal = 0;
|
|
add_chr_to_str(&data, &datal, 0);
|
|
add_chr_to_str(&data, &datal, fg);
|
|
add_to_str(&data, &datal, path);
|
|
add_chr_to_str(&data, &datal, 0);
|
|
add_to_str(&data, &datal, delet);
|
|
hard_write(term->fdout, data, datal + 1);
|
|
mem_free(data);
|
|
}
|
|
}
|
|
|
|
void do_terminal_function(struct terminal *term, unsigned char code, unsigned char *data)
|
|
{
|
|
unsigned char *x_data;
|
|
int x_datal;
|
|
NO_GFX;
|
|
x_data = init_str();
|
|
x_datal = 0;
|
|
add_chr_to_str(&x_data, &x_datal, code);
|
|
add_to_str(&x_data, &x_datal, data);
|
|
exec_on_terminal(term, NULL, x_data, 0);
|
|
mem_free(x_data);
|
|
}
|
|
|
|
void set_terminal_title(struct terminal *term, unsigned char *title)
|
|
{
|
|
if (strlen(cast_const_char title) > 10000) title[10000] = 0;
|
|
if (strchr(cast_const_char title, 1)) {
|
|
unsigned char *a, *b;
|
|
for (a = title, b = title; *a; a++) if (*a != 1) *b++ = *a;
|
|
*b = 0;
|
|
}
|
|
if (term->title && !strcmp(cast_const_char title, cast_const_char term->title)) goto ret;
|
|
if (term->title) mem_free(term->title);
|
|
term->title = stracpy(title);
|
|
#ifdef SET_WINDOW_TITLE_UTF_8
|
|
{
|
|
mem_free(title);
|
|
title = convert(term_charset(term), utf8_table, term->title, NULL);
|
|
}
|
|
#endif
|
|
if (!F) do_terminal_function(term, TERM_FN_TITLE, title);
|
|
#ifdef G
|
|
else if (drv->set_title) drv->set_title(term->dev, title);
|
|
#endif
|
|
ret:
|
|
mem_free(title);
|
|
}
|
|
|
|
struct terminal *find_terminal(tcount count)
|
|
{
|
|
struct terminal *term;
|
|
struct list_head *lterm;
|
|
foreach(struct terminal, term, lterm, terminals) if (term->count == count) return term;
|
|
return NULL;
|
|
}
|