#include "signals.h"
#include "term.h"
#include "terminfo-core.h"
+#include "utf8.h"
#include <signal.h>
+/* returns number of characters in the beginning of the buffer being a
+ a single character, or -1 if more input is needed. The character will be
+ saved in result */
+typedef int (*TERM_INPUT_FUNC)(const unsigned char *buffer, int size,
+ unichar *result);
+
struct _TERM_WINDOW {
/* Terminal to use for window */
TERM_REC *term;
};
TERM_WINDOW *root_window;
-int term_width, term_height, term_detached;
static char *term_lines_empty; /* 1 if line is entirely empty */
static int vcmove, vcx, vcy, curs_visible;
static int redraw_needed, redraw_tag;
static int freeze_counter;
+static TERM_INPUT_FUNC input_func;
+static unsigned char term_inbuf[256];
+static int term_inbuf_pos;
+
/* SIGCONT handler */
static void sig_cont(int p)
{
int term_init(void)
{
- struct sigaction act;
+ struct sigaction act;
+ int width, height;
last_fg = last_bg = -1;
last_attrs = 0;
if (current_term == NULL)
return FALSE;
+ if (term_get_size(&width, &height)) {
+ current_term->width = width;
+ current_term->height = height;
+ }
+
/* grab CONT signal */
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
term_lines_empty = g_new0(char, term_height);
+ term_set_input_type(TERM_TYPE_8BIT);
term_common_init();
+ g_atexit(term_deinit);
return TRUE;
}
void term_deinit(void)
{
- g_source_remove(redraw_tag);
+ if (current_term != NULL) {
+ signal(SIGCONT, SIG_DFL);
+ g_source_remove(redraw_tag);
- term_common_deinit();
- terminfo_core_deinit(current_term);
+ term_common_deinit();
+ terminfo_core_deinit(current_term);
+ current_term = NULL;
+ }
}
static void term_move_real(void)
/* bold */
if (col & 0x08)
col |= ATTR_BOLD;
- else if (col & ATTR_BOLD)
+ if (col & ATTR_BOLD)
terminfo_set_bold();
/* underline */
void term_move(TERM_WINDOW *window, int x, int y)
{
+ if (x >= 0 && y >= 0) {
vcmove = TRUE;
vcx = x+window->x;
vcy = y+window->y;
vcx = term_width-1;
if (vcy >= term_height)
vcy = term_height-1;
+ }
}
static void term_printed_text(int count)
However, next term_move() really shouldn't try to cache
the move, otherwise terminals would try to combine the
last word in upper line with first word in lower line. */
- cforcemove = TRUE;
vcx += count;
while (vcx >= term_width) {
vcx -= term_width;
- if (vcy < term_height) vcy++;
+ if (vcy < term_height-1) vcy++;
if (vcx > 0) term_lines_empty[vcy] = FALSE;
}
+
+ crealx += count;
+ if (crealx >= term_width)
+ cforcemove = TRUE;
}
void term_addch(TERM_WINDOW *window, int chr)
if (term_detached) return;
if (vcmove) term_move_real();
- term_printed_text(1);
- if (vcy != term_height || vcx != 0)
+
+ if (vcy < term_height-1 || vcx < term_width-1) {
+ /* With UTF-8, move cursor only if this char is either
+ single-byte (8. bit off) or beginning of multibyte
+ (7. bit off) */
+ if (term_type != TERM_TYPE_UTF8 ||
+ (chr & 0x80) == 0 || (chr & 0x40) == 0) {
+ term_printed_text(1);
+ }
+
+ putc(chr, window->term->out);
+ }
+}
+
+static void term_addch_utf8(TERM_WINDOW *window, unichar chr)
+{
+ char buf[10];
+ int i, len;
+
+ len = utf16_char_to_utf8(chr, buf);
+ for (i = 0; i < len; i++)
+ putc(buf[i], window->term->out);
+}
+
+void term_add_unichar(TERM_WINDOW *window, unichar chr)
+{
+ if (term_detached) return;
+
+ if (vcmove) term_move_real();
+ if (vcy == term_height-1 && vcx == term_width-1)
+ return; /* last char in screen */
+
+ switch (term_type) {
+ case TERM_TYPE_UTF8:
+ term_printed_text(utf8_width(chr));
+ term_addch_utf8(window, chr);
+ break;
+ case TERM_TYPE_BIG5:
+ if (chr > 0xff) {
+ term_printed_text(2);
+ putc((chr >> 8) & 0xff, window->term->out);
+ } else {
+ term_printed_text(1);
+ }
+ putc((chr & 0xff), window->term->out);
+ break;
+ default:
+ term_printed_text(1);
putc(chr, window->term->out);
+ break;
+ }
}
void term_addstr(TERM_WINDOW *window, const char *str)
if (term_detached) return;
if (vcmove) term_move_real();
- len = strlen(str);
+ len = strlen(str); /* FIXME utf8 or big5 */
term_printed_text(len);
if (vcy != term_height || vcx != 0)
terminfo_set_cursor_visible(TRUE);
curs_visible = TRUE;
}
+
term_set_color(window, ATTR_RESET);
fflush(window != NULL ? window->term->out : current_term->out);
}
void term_refresh_freeze(void)
{
freeze_counter++;
-
- if (!term_detached && curs_visible) {
- terminfo_set_cursor_visible(FALSE);
- curs_visible = FALSE;
- }
}
void term_refresh_thaw(void)
void term_stop(void)
{
if (term_detached) {
- kill(getpid(), SIGSTOP);
+ kill(getpid(), SIGTSTP);
} else {
terminfo_stop(current_term);
- kill(getpid(), SIGSTOP);
+ kill(getpid(), SIGTSTP);
terminfo_cont(current_term);
irssi_redraw();
}
}
-int term_gets(unsigned char *buffer, int size)
+static int input_utf8(const unsigned char *buffer, int size, unichar *result)
{
- int ret;
+ const unsigned char *end = buffer;
+
+ switch (get_utf8_char(&end, size, result)) {
+ case -2:
+ /* not UTF8 - fallback to 8bit ascii */
+ *result = *buffer;
+ return 1;
+ case -1:
+ /* need more data */
+ return -1;
+ default:
+ return (int) (end-buffer)+1;
+ }
+}
+
+static int input_big5(const unsigned char *buffer, int size, unichar *result)
+{
+ if (is_big5_hi(*buffer)) {
+ /* could be */
+ if (size == 1)
+ return -1;
+
+ if (is_big5_los(buffer[1]) || is_big5_lox(buffer[1])) {
+ *result = buffer[1] + ((int) *buffer << 8);
+ return 2;
+ }
+ }
+
+ *result = *buffer;
+ return 1;
+}
+
+static int input_8bit(const unsigned char *buffer, int size, unichar *result)
+{
+ *result = *buffer;
+ return 1;
+}
+
+void term_set_input_type(int type)
+{
+ switch (type) {
+ case TERM_TYPE_UTF8:
+ input_func = input_utf8;
+ break;
+ case TERM_TYPE_BIG5:
+ input_func = input_big5;
+ break;
+ default:
+ input_func = input_8bit;
+ }
+}
+
+int term_gets(unichar *buffer, int size)
+{
+ int ret, i, char_len;
if (term_detached)
return 0;
/* fread() doesn't work */
- ret = read(fileno(current_term->in), buffer, size);
+ if (size > sizeof(term_inbuf)-term_inbuf_pos)
+ size = sizeof(term_inbuf)-term_inbuf_pos;
+
+ ret = read(fileno(current_term->in),
+ term_inbuf + term_inbuf_pos, size);
if (ret == 0) {
/* EOF - terminal got lost */
if (auto_detach)
} else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
ret = 0;
+ if (ret > 0) {
+ /* convert input to unichars. */
+ term_inbuf_pos += ret;
+ ret = 0;
+ for (i = 0; i < term_inbuf_pos; ) {
+ char_len = input_func(term_inbuf+i, term_inbuf_pos-i,
+ buffer);
+ if (char_len < 0)
+ break;
+
+ i += char_len;
+ buffer++;
+ ret++;
+ }
+
+ if (i >= term_inbuf_pos)
+ term_inbuf_pos = 0;
+ else if (i > 0) {
+ memmove(term_inbuf, term_inbuf+i, term_inbuf_pos-i);
+ term_inbuf_pos -= i;
+ }
+ }
+
return ret;
}