📄 kbd.c
字号:
/* Support for keyboard interface *//* $Id: kbd.c,v 1.112.6.7 2005/06/11 16:22:29 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#ifdef HAVE_TERMIOS_H#include <termios.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef __hpux__#include <limits.h>#define HPUX_PIPE (len > PIPE_BUF || errno != EAGAIN)#else#define HPUX_PIPE 1#endif#include "elinks.h"#include "config/options.h"#include "intl/gettext/libintl.h"#include "lowlevel/select.h"#include "osdep/ascii.h"#include "osdep/osdep.h"#include "terminal/hardio.h"#include "terminal/kbd.h"#include "terminal/mouse.h"#include "terminal/terminal.h"#include "util/error.h"#include "util/memory.h"#include "util/string.h"#define OUT_BUF_SIZE 16384#define IN_BUF_SIZE 16#define TW_BUTT_LEFT 1#define TW_BUTT_MIDDLE 2#define TW_BUTT_RIGHT 4struct itrm { int std_in; int std_out; int sock_in; int sock_out; int ctl_in; /* Input queue */ unsigned char kqueue[IN_BUF_SIZE]; int qlen; /* Output queue */ unsigned char *ev_queue; int eqlen; int timer; /* ESC timeout timer */ struct termios t; /* For restoring original attributes */ void *mouse_h; /* Mouse handle */ unsigned char *orig_title; /* For restoring window title */ unsigned int blocked:1; /* Whether it was blocked */ unsigned int altscreen:1; /* Whether to use alternate screen */ unsigned int touched_title:1; /* Whether the term title was changed */};static struct itrm *ditrm = NULL;static void free_trm(struct itrm *);static void in_kbd(struct itrm *);static void in_sock(struct itrm *);static int process_queue(struct itrm *);intis_blocked(void){ return ditrm && ditrm->blocked;}voidfree_all_itrms(void){ if (ditrm) free_trm(ditrm);}static voidwrite_ev_queue(struct itrm *itrm){ int written; int qlen = int_min(itrm->eqlen, 128); assertm(qlen, "event queue empty"); if_assert_failed return; written = safe_write(itrm->sock_out, itrm->ev_queue, qlen); if (written <= 0) { if (written < 0) free_trm(itrm); /* write error */ return; } itrm->eqlen -= written; if (itrm->eqlen == 0) { set_handlers(itrm->sock_out, get_handler(itrm->sock_out, H_READ), NULL, get_handler(itrm->sock_out, H_ERROR), get_handler(itrm->sock_out, H_DATA)); } else { assert(itrm->eqlen > 0); memmove(itrm->ev_queue, itrm->ev_queue + written, itrm->eqlen); }}static voidqueue_event(struct itrm *itrm, unsigned char *data, int len){ int w = 0; if (!len) return; if (!itrm->eqlen && can_write(itrm->sock_out)) { w = safe_write(itrm->sock_out, data, len); if (w <= 0 && HPUX_PIPE) { /* free_trm(itrm); */ register_bottom_half((void (*)(void *)) free_trm, itrm); return; } } if (w < len) { int left = len - w; unsigned char *c = mem_realloc(itrm->ev_queue, itrm->eqlen + left); if (!c) { free_trm(itrm); return; } itrm->ev_queue = c; memcpy(itrm->ev_queue + itrm->eqlen, data + w, left); itrm->eqlen += left; set_handlers(itrm->sock_out, get_handler(itrm->sock_out, H_READ), (void (*)(void *)) write_ev_queue, (void (*)(void *)) free_trm, itrm); }}voidkbd_ctrl_c(void){ struct term_event ev = INIT_TERM_EVENT(EVENT_KBD, KBD_CTRL_C, 0, 0); if (!ditrm) return; queue_event(ditrm, (unsigned char *) &ev, sizeof(ev));}#define write_sequence(fd, seq) \ hard_write(fd, seq, sizeof(seq) / sizeof(unsigned char) - 1)#define INIT_TERMINAL_SEQ "\033)0\0337" /* Special Character and Line Drawing Set, Save Cursor */#define INIT_TWIN_MOUSE_SEQ "\033[?9h" /* Send MIT Mouse Row & Column on Button Press */#define INIT_XWIN_MOUSE_SEQ "\033[?1000h" /* Send Mouse X & Y on button press and release */#define INIT_ALT_SCREEN_SEQ "\033[?47h" /* Use Alternate Screen Buffer */static voidsend_init_sequence(int h, int altscreen){ write_sequence(h, INIT_TERMINAL_SEQ); /* If alternate screen is supported switch to it. */ if (altscreen) { write_sequence(h, INIT_ALT_SCREEN_SEQ); }#ifdef CONFIG_MOUSE write_sequence(h, INIT_TWIN_MOUSE_SEQ); write_sequence(h, INIT_XWIN_MOUSE_SEQ);#endif}#define DONE_CLS_SEQ "\033[2J" /* Erase in Display, Clear All */#define DONE_TERMINAL_SEQ "\0338\r \b" /* Restore Cursor (DECRC) + ??? */#define DONE_TWIN_MOUSE_SEQ "\033[?9l" /* Don't Send MIT Mouse Row & Column on Button Press */#define DONE_XWIN_MOUSE_SEQ "\033[?1000l" /* Don't Send Mouse X & Y on button press and release */#define DONE_ALT_SCREEN_SEQ "\033[?47l" /* Use Normal Screen Buffer */static voidsend_done_sequence(int h, int altscreen){ write_sequence(h, DONE_CLS_SEQ);#ifdef CONFIG_MOUSE /* This is a hack to make xterm + alternate screen working, * if we send only DONE_XWIN_MOUSE_SEQ, mouse is not totally * released it seems, in rxvt and xterm... --Zas */ write_sequence(h, DONE_TWIN_MOUSE_SEQ); write_sequence(h, DONE_XWIN_MOUSE_SEQ);#endif /* Switch from alternate screen. */ if (altscreen) { write_sequence(h, DONE_ALT_SCREEN_SEQ); } write_sequence(h, DONE_TERMINAL_SEQ);}#undef write_sequencevoidresize_terminal(void){ struct term_event ev = INIT_TERM_EVENT(EVENT_RESIZE, 0, 0, 0); int width, height; get_terminal_size(ditrm->std_out, &width, &height); ev.info.size.width = width; ev.info.size.height = height; queue_event(ditrm, (char *) &ev, sizeof(ev));}static voidset_terminal_name(unsigned char name[MAX_TERM_LEN]){ unsigned char *term = getenv("TERM"); int i; memset(name, 0, MAX_TERM_LEN); if (!term) return; for (i = 0; term[i] != 0 && i < MAX_TERM_LEN - 1; i++) name[i] = isident(term[i]) ? term[i] : '-';}static intsetraw(int fd, struct termios *p){ struct termios t; memset(&t, 0, sizeof(t)); if (tcgetattr(fd, &t)) return -1; if (p) copy_struct(p, &t); elinks_cfmakeraw(&t); t.c_lflag |= ISIG;#ifdef TOSTOP t.c_lflag |= TOSTOP;#endif t.c_oflag |= OPOST; if (tcsetattr(fd, TCSANOW, &t)) return -1; return 0;}voidhandle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in, void *init_string, int init_len, int remote){ struct itrm *itrm; struct terminal_info info; struct term_event_size *size = &info.event.info.size; unsigned char *ts; memset(&info, 0, sizeof(info)); get_terminal_size(ctl_in, &size->width, &size->height); info.event.ev = EVENT_INIT; info.system_env = get_system_env(); info.length = init_len; if (remote) { info.session_info = remote; info.magic = INTERLINK_REMOTE_MAGIC; } else { info.session_info = get_cmd_opt_int("base-session"); info.magic = INTERLINK_NORMAL_MAGIC; } itrm = mem_calloc(1, sizeof(*itrm)); if (!itrm) return; ditrm = itrm; itrm->std_in = std_in; itrm->std_out = std_out; itrm->sock_in = sock_in; itrm->sock_out = sock_out; itrm->ctl_in = ctl_in; itrm->timer = -1; /* FIXME: Combination altscreen + xwin does not work as it should, * mouse clicks are reportedly partially ignored. */ if (info.system_env & (ENV_SCREEN | ENV_XWIN)) itrm->altscreen = 1; if (ctl_in >= 0) setraw(ctl_in, &itrm->t); set_handlers(std_in, (void (*)(void *)) in_kbd, NULL, (void (*)(void *)) free_trm, itrm); if (sock_in != std_out) set_handlers(sock_in, (void (*)(void *)) in_sock, NULL, (void (*)(void *)) free_trm, itrm); handle_terminal_resize(ctl_in, resize_terminal); set_terminal_name(info.name); ts = get_cwd(); if (ts) { memcpy(info.cwd, ts, int_min(strlen(ts), MAX_CWD_LEN)); mem_free(ts); } queue_event(itrm, (char *) &info, TERMINAL_INFO_SIZE); queue_event(itrm, (char *) init_string, init_len); send_init_sequence(std_out, itrm->altscreen); itrm->mouse_h = handle_mouse(0, (void (*)(void *, unsigned char *, int)) queue_event, itrm);}static voidunblock_itrm_x(void *h){ close_handle(h); if (!ditrm) return; unblock_itrm(0); resize_terminal();}intunblock_itrm(int fd){ struct itrm *itrm = ditrm; if (!itrm) return -1; if (itrm->ctl_in >= 0 && setraw(itrm->ctl_in, NULL)) return -1; itrm->blocked = 0; send_init_sequence(itrm->std_out, itrm->altscreen); set_handlers(itrm->std_in, (void (*)(void *)) in_kbd, NULL, (void (*)(void *)) free_trm, itrm); resume_mouse(itrm->mouse_h); handle_terminal_resize(itrm->ctl_in, resize_terminal); unblock_stdin(); return 0;}voidblock_itrm(int fd){ struct itrm *itrm = ditrm; if (!itrm) return; itrm->blocked = 1; block_stdin(); unhandle_terminal_resize(itrm->ctl_in); send_done_sequence(itrm->std_out, itrm->altscreen); tcsetattr(itrm->ctl_in, TCSANOW, &itrm->t); set_handlers(itrm->std_in, NULL, NULL, (void (*)(void *)) free_trm, itrm); suspend_mouse(itrm->mouse_h);}static voidfree_trm(struct itrm *itrm){ if (!itrm) return; if (itrm->orig_title && *itrm->orig_title) { set_window_title(itrm->orig_title); } else if (itrm->touched_title) { /* Set the window title to the value of $TERM if X11 wasn't * compiled in. Should hopefully make at least half the users * happy. (debian bug #312955) */ unsigned char title[MAX_TERM_LEN]; set_terminal_name(title); if (*title) set_window_title(title); } mem_free_set(&itrm->orig_title, NULL); unhandle_terminal_resize(itrm->ctl_in); unhandle_mouse(itrm->mouse_h); send_done_sequence(itrm->std_out,itrm->altscreen); tcsetattr(itrm->ctl_in, TCSANOW, &itrm->t); clear_handlers(itrm->std_in); clear_handlers(itrm->sock_in); clear_handlers(itrm->std_out); clear_handlers(itrm->sock_out); if (itrm->timer != -1) kill_timer(itrm->timer); if (itrm == ditrm) ditrm = NULL; mem_free_if(itrm->ev_queue); mem_free(itrm);}/* Resize terminal to dimensions specified by @text string. * @text should look like "width,height,old-width,old-height" where width and * height are integers. */static inline voidresize_terminal_from_str(unsigned char *text){ enum { NEW_WIDTH = 0, NEW_HEIGHT, OLD_WIDTH, OLD_HEIGHT, NUMBERS } i; int numbers[NUMBERS]; assert(text && *text); if_assert_failed return; for (i = 0; i < NUMBERS; i++) { unsigned char *p = strchr(text, ','); if (p) { *p++ = '\0'; } else if (i < OLD_HEIGHT) { return; } numbers[i] = atoi(text); if (p) text = p; } resize_window(numbers[NEW_WIDTH], numbers[NEW_HEIGHT], numbers[OLD_WIDTH], numbers[OLD_HEIGHT]); resize_terminal();}voiddispatch_special(unsigned char *text){ switch (text[0]) { case TERM_FN_TITLE: if (ditrm) { if (!ditrm->orig_title) ditrm->orig_title = get_window_title(); ditrm->touched_title = 1; } set_window_title(text + 1); break; case TERM_FN_RESIZE: resize_terminal_from_str(text + 1); break; }}static void inlinesafe_hard_write(int fd, unsigned char *buf, int len){ if (is_blocked()) return; want_draw(); hard_write(fd, buf, len); done_draw();}static voidin_sock(struct itrm *itrm){ struct string path; struct string delete; char ch; int fg; int bytes_read, i, p; unsigned char buf[OUT_BUF_SIZE]; bytes_read = safe_read(itrm->sock_in, buf, OUT_BUF_SIZE); if (bytes_read <= 0) goto free_and_return;qwerty: for (i = 0; i < bytes_read; i++) if (!buf[i]) goto has_nul_byte; safe_hard_write(itrm->std_out, buf, bytes_read); return;has_nul_byte: if (i) safe_hard_write(itrm->std_out, buf, i); i++; assert(OUT_BUF_SIZE - i > 0); memmove(buf, buf + i, OUT_BUF_SIZE - i); bytes_read -= i; p = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -