From afeb6fcb7d94157808a9290226adbadd42a824ec Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Fri, 15 Feb 2002 13:38:24 +0000 Subject: [PATCH] /SET term_type 8bit|utf8|big5 - Removed --with-big5 configure option, it's now included the same way as utf8, though i'm not sure if it really works but at least it should partially :) Input line is now internally using 32bit chars but it's converted to 8bit chars for external use. Text buffer supports only 8bit + utf8 for now. git-svn-id: http://svn.irssi.org/repos/irssi/trunk@2448 dbcabf3a-b0e7-0310-adc4-f8d773084564 --- configure.in | 21 ---- src/fe-text/gui-entry.c | 207 +++++++++++++++++++++------------- src/fe-text/gui-entry.h | 11 +- src/fe-text/gui-expandos.c | 3 +- src/fe-text/gui-readline.c | 86 ++++++++------ src/fe-text/gui-windows.c | 2 +- src/fe-text/module.h | 1 + src/fe-text/statusbar-items.c | 9 +- src/fe-text/term-terminfo.c | 139 ++++++++++++++++++++++- src/fe-text/term.c | 18 ++- src/fe-text/term.h | 23 ++-- src/fe-text/textbuffer-view.c | 4 +- src/fe-text/utf8.c | 129 +++++++++++++++++++-- src/fe-text/utf8.h | 17 ++- 14 files changed, 502 insertions(+), 168 deletions(-) diff --git a/configure.in b/configure.in index 80882d65..30ae246c 100644 --- a/configure.in +++ b/configure.in @@ -31,19 +31,6 @@ AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h sys/resource.h) # check posix headers.. AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h) -AC_ARG_WITH(big5, -[ --with-big5 Build with tr-Chinese Big5 support], - if test x$withval = xyes; then - want_big5=yes - else - if test "x$withval" = xno; then - want_big5=no - else - want_big5=yes - fi - fi, - want_big5=no) - AC_ARG_WITH(socks, [ --with-socks Build with socks support], if test x$withval = xyes; then @@ -732,14 +719,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS" AC_SUBST(COMMON_NOUI_LIBS) AC_SUBST(COMMON_LIBS) -dnl ** -dnl ** tr-Chinese Big5 support -dnl ** - -if test "x$want_big5" = "xyes"; then - AC_DEFINE(WANT_BIG5) -fi - dnl ** dnl ** IPv6 support dnl ** diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index f70bf0df..2824d4af 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -19,6 +19,7 @@ */ #include "module.h" +#include "misc.h" #include "utf8.h" #include "formats.h" @@ -26,8 +27,19 @@ #include "gui-printtext.h" #include "term.h" +const unichar empty_str[] = { 0 }; + GUI_ENTRY_REC *active_entry; +static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size) +{ + if (entry->text_len+grow_size < entry->text_alloc) + return; + + entry->text_alloc = nearest_power(entry->text_alloc+grow_size); + entry->text = g_realloc(entry->text, entry->text_alloc); +} + GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8) { GUI_ENTRY_REC *rec; @@ -36,7 +48,9 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8) rec->xpos = xpos; rec->ypos = ypos; rec->width = width; - rec->text = g_string_new(NULL); + rec->text_alloc = 1024; + rec->text = g_new(unichar, rec->text_alloc); + rec->text[0] = '\0'; rec->utf8 = utf8; return rec; } @@ -48,8 +62,8 @@ void gui_entry_destroy(GUI_ENTRY_REC *entry) if (active_entry == entry) gui_entry_set_active(NULL); - g_free_not_null(entry->prompt); - g_string_free(entry->text, TRUE); + g_free(entry->text); + g_free(entry->prompt); g_free(entry); } @@ -76,14 +90,9 @@ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry) static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos) { - const unsigned char *p, *end; + const unichar *p; int xpos, end_xpos; - if (entry->utf8) { - /* FIXME: a stupid kludge to make the chars output correctly */ - pos = 0; - } - xpos = entry->xpos + entry->promptlen + pos; end_xpos = entry->xpos + entry->width; if (xpos > end_xpos) @@ -92,20 +101,14 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos) term_set_color(root_window, ATTR_RESET); term_move(root_window, xpos, entry->ypos); - p = (unsigned char *) (entry->scrstart + pos >= entry->text->len ? "" : - entry->text->str + entry->scrstart + pos); + p = entry->scrstart + pos < entry->text_len ? + entry->text + entry->scrstart + pos : empty_str; for (; *p != '\0' && xpos < end_xpos; p++, xpos++) { - end = p; - if (entry->utf8) - get_utf8_char(&end); - if (entry->hidden) term_addch(root_window, ' '); - else if (*p >= 32 && (end != p || (*p & 127) >= 32)) { - for (; p < end; p++) - term_addch(root_window, *p); - term_addch(root_window, *p); - } else { + else if (*p >= 32 && (entry->utf8 || (*p & 127) >= 32)) + term_add_unichar(root_window, *p); + else { term_set_color(root_window, ATTR_RESET|ATTR_REVERSE); term_addch(root_window, *p+'A'-1); term_set_color(root_window, ATTR_RESET); @@ -230,38 +233,67 @@ void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8) void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str) { - g_return_if_fail(entry != NULL); + g_return_if_fail(entry != NULL); g_return_if_fail(str != NULL); - g_string_assign(entry->text, str); - entry->pos = entry->text->len; + entry->text_len = 0; + entry->pos = 0; + entry->text[0] = '\0'; - gui_entry_redraw_from(entry, 0); - gui_entry_fix_cursor(entry); - gui_entry_draw(entry); + gui_entry_insert_text(entry, str); } char *gui_entry_get_text(GUI_ENTRY_REC *entry) { + char *buf; + int i; + g_return_val_if_fail(entry != NULL, NULL); - return entry->text->str; + buf = g_malloc(entry->text_len*6 + 1); + if (entry->utf8) + utf16_to_utf8(entry->text, buf); + else { + for (i = 0; i <= entry->text_len; i++) + buf[i] = entry->text[i]; + } + return buf; } void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str) { + unichar chr; + int i, len; + g_return_if_fail(entry != NULL); g_return_if_fail(str != NULL); gui_entry_redraw_from(entry, entry->pos); - g_string_insert(entry->text, entry->pos, str); - entry->pos += strlen(str); + + len = !entry->utf8 ? strlen(str) : strlen_utf8(str); + entry_text_grow(entry, len); + + /* make space for the string */ + g_memmove(entry->text + entry->pos + len, entry->text + entry->pos, + (entry->text_len-entry->pos + 1) * sizeof(unichar)); + + if (!entry->utf8) { + for (i = 0; i < len; i++) + entry->text[entry->pos+i] = str[i]; + } else { + chr = entry->text[entry->pos+len]; + utf8_to_utf16(str, entry->text+entry->pos); + entry->text[entry->pos+len] = chr; + } + + entry->text_len += len; + entry->pos += len; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } -void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr) +void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr) { g_return_if_fail(entry != NULL); @@ -269,13 +301,38 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr) return; /* never insert NUL, CR or LF characters */ gui_entry_redraw_from(entry, entry->pos); - g_string_insert_c(entry->text, entry->pos, chr); - entry->pos++; + + entry_text_grow(entry, 1); + + /* make space for the string */ + g_memmove(entry->text + entry->pos + 1, entry->text + entry->pos, + (entry->text_len-entry->pos + 1) * sizeof(unichar)); + + entry->text[entry->pos] = chr; + entry->text_len++; + entry->pos++; gui_entry_fix_cursor(entry); gui_entry_draw(entry); } +char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry) +{ + char *buf; + int i; + + g_return_val_if_fail(entry != NULL, NULL); + + buf = g_malloc(entry->cutbuffer_len*6 + 1); + if (entry->utf8) + utf16_to_utf8(entry->cutbuffer, buf); + else { + for (i = 0; i <= entry->cutbuffer_len; i++) + buf[i] = entry->cutbuffer[i]; + } + return buf; +} + void gui_entry_erase(GUI_ENTRY_REC *entry, int size) { g_return_if_fail(entry != NULL); @@ -283,14 +340,22 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size) if (entry->pos < size) return; -#ifdef WANT_BIG5 - if (is_big5(entry->text->str[entry->pos-2], - entry->text->str[entry->pos-1])) - size++; -#endif + /* put erased text to cutbuffer */ + if (entry->cutbuffer_len < size) { + g_free(entry->cutbuffer); + entry->cutbuffer = g_new(unichar, size+1); + } + + entry->cutbuffer_len = size; + entry->cutbuffer[size] = '\0'; + memcpy(entry->cutbuffer, entry->text + entry->pos - size, + size * sizeof(unichar)); + + g_memmove(entry->text + entry->pos - size, entry->text + entry->pos, + (entry->text_len-entry->pos+1) * sizeof(unichar)); entry->pos -= size; - g_string_erase(entry->text, entry->pos, size); + entry->text_len -= size; gui_entry_redraw_from(entry, entry->pos); gui_entry_fix_cursor(entry); @@ -308,52 +373,45 @@ void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space) to = entry->pos - 1; if (to_space) { - while (entry->text->str[to] == ' ' && to > 0) + while (entry->text[to] == ' ' && to > 0) to--; - while (entry->text->str[to] != ' ' && to > 0) + while (entry->text[to] != ' ' && to > 0) to--; } else { - while (!i_isalnum(entry->text->str[to]) && to > 0) + while (!i_isalnum(entry->text[to]) && to > 0) to--; - while (i_isalnum(entry->text->str[to]) && to > 0) + while (i_isalnum(entry->text[to]) && to > 0) to--; } if (to > 0) to++; - g_string_erase(entry->text, to, entry->pos - to); - entry->pos = to; - - gui_entry_redraw_from(entry, entry->pos); - gui_entry_fix_cursor(entry); - gui_entry_draw(entry); + gui_entry_erase(entry, entry->pos-to); } void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space) { - int to; + int to, size; g_return_if_fail(entry != NULL); - if (entry->pos == entry->text->len) + if (entry->pos == entry->text_len) return; to = entry->pos; if (to_space) { - while (entry->text->str[to] == ' ' && to < entry->text->len) + while (entry->text[to] == ' ' && to < entry->text_len) to++; - while (entry->text->str[to] != ' ' && to < entry->text->len) + while (entry->text[to] != ' ' && to < entry->text_len) to++; } else { - while (!i_isalnum(entry->text->str[to]) && to < entry->text->len) + while (!i_isalnum(entry->text[to]) && to < entry->text_len) to++; - while (i_isalnum(entry->text->str[to]) && to < entry->text->len) + while (i_isalnum(entry->text[to]) && to < entry->text_len) to++; } - g_string_erase(entry->text, entry->pos, to - entry->pos); - - gui_entry_redraw_from(entry, entry->pos); - gui_entry_fix_cursor(entry); - gui_entry_draw(entry); + size = to-entry->pos; + entry->pos = to; + gui_entry_erase(entry, size); } int gui_entry_get_pos(GUI_ENTRY_REC *entry) @@ -367,7 +425,7 @@ void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos) { g_return_if_fail(entry != NULL); - if (pos >= 0 && pos <= entry->text->len) + if (pos >= 0 && pos <= entry->text_len) entry->pos = pos; gui_entry_fix_cursor(entry); @@ -378,16 +436,7 @@ void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos) { g_return_if_fail(entry != NULL); -#ifdef WANT_BIG5 - if (pos > 0 && is_big5(entry->text->str[entry->pos], - entry->text->str[entry->pos+1])) - pos++; - else if (pos < 0 && is_big5(entry->text->str[entry->pos-1], - entry->text->str[entry->pos])) - pos--; -#endif - - if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text->len) + if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text_len) entry->pos += pos; gui_entry_fix_cursor(entry); @@ -401,14 +450,14 @@ static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_sp pos = entry->pos; while (count > 0 && pos > 0) { if (to_space) { - while (pos > 0 && entry->text->str[pos-1] == ' ') + while (pos > 0 && entry->text[pos-1] == ' ') pos--; - while (pos > 0 && entry->text->str[pos-1] != ' ') + while (pos > 0 && entry->text[pos-1] != ' ') pos--; } else { - while (pos > 0 && !i_isalnum(entry->text->str[pos-1])) + while (pos > 0 && !i_isalnum(entry->text[pos-1])) pos--; - while (pos > 0 && i_isalnum(entry->text->str[pos-1])) + while (pos > 0 && i_isalnum(entry->text[pos-1])) pos--; } count--; @@ -422,16 +471,16 @@ static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_s int pos; pos = entry->pos; - while (count > 0 && pos < entry->text->len) { + while (count > 0 && pos < entry->text_len) { if (to_space) { - while (pos < entry->text->len && entry->text->str[pos] == ' ') + while (pos < entry->text_len && entry->text[pos] == ' ') pos++; - while (pos < entry->text->len && entry->text->str[pos] != ' ') + while (pos < entry->text_len && entry->text[pos] != ' ') pos++; } else { - while (pos < entry->text->len && !i_isalnum(entry->text->str[pos])) + while (pos < entry->text_len && !i_isalnum(entry->text[pos])) pos++; - while (pos < entry->text->len && i_isalnum(entry->text->str[pos])) + while (pos < entry->text_len && i_isalnum(entry->text[pos])) pos++; } count--; diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h index 364f92da..347be476 100644 --- a/src/fe-text/gui-entry.h +++ b/src/fe-text/gui-entry.h @@ -2,7 +2,13 @@ #define __GUI_ENTRY_H typedef struct { - GString *text; + int text_len, text_alloc; /* as shorts, not chars */ + unichar *text; + + int cutbuffer_len; + unichar *cutbuffer; + + /* all as shorts, not chars */ int xpos, ypos, width; /* entry position in screen */ int pos, scrstart, scrpos; /* cursor position */ int hidden; /* print the chars as spaces in input line (useful for passwords) */ @@ -30,8 +36,9 @@ void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str); char *gui_entry_get_text(GUI_ENTRY_REC *entry); void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str); -void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr); +void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr); +char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry); void gui_entry_erase(GUI_ENTRY_REC *entry, int size); void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space); void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space); diff --git a/src/fe-text/gui-expandos.c b/src/fe-text/gui-expandos.c index b07bf129..74d6b34d 100644 --- a/src/fe-text/gui-expandos.c +++ b/src/fe-text/gui-expandos.c @@ -43,7 +43,8 @@ static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret) /* value of cutbuffer */ static char *expando_cutbuffer(SERVER_REC *server, void *item, int *free_ret) { - return cutbuffer; + *free_ret = TRUE; + return gui_entry_get_cutbuffer(active_entry); } void gui_expandos_init(void) diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index 8fe17410..2ec9bd2d 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -33,6 +33,7 @@ #include "term.h" #include "gui-entry.h" #include "gui-windows.h" +#include "utf8.h" #include @@ -48,7 +49,6 @@ typedef struct { static KEYBOARD_REC *keyboard; static ENTRY_REDIRECT_REC *redir; -char *cutbuffer; static int readtag; static time_t idle_time; @@ -133,9 +133,9 @@ static void window_next_page(void) gui_window_scroll(active_win, get_scroll_count()); } -void handle_key(int key) +void handle_key(unichar key) { - char str[3]; + char str[20]; idle_time = time(NULL); @@ -147,20 +147,23 @@ void handle_key(int key) if (key >= 0 && key < 32) { /* control key */ str[0] = '^'; - str[1] = key+'@'; + str[1] = (char)key+'@'; str[2] = '\0'; } else if (key == 127) { str[0] = '^'; str[1] = '?'; str[2] = '\0'; - } else { - str[0] = key; + } else if (!active_entry->utf8) { + str[0] = (char)key; str[1] = '\0'; + } else { + /* need to convert to utf8 */ + str[utf16_char_to_utf8(key, str)] = '\0'; } if (!key_pressed(keyboard, str)) { /* key wasn't used for anything, print it */ - gui_entry_insert_char(active_entry, (char) key); + gui_entry_insert_char(active_entry, key); } } @@ -170,7 +173,10 @@ static void key_send_line(void) char *str, *add_history; str = gui_entry_get_text(active_entry); - if (*str == '\0') return; + if (*str == '\0') { + g_free(str); + return; + } /* we can't use gui_entry_get_text() later, since the entry might have been destroyed after we get back */ @@ -199,6 +205,8 @@ static void key_send_line(void) if (active_entry != NULL) gui_entry_set_text(active_entry, ""); command_history_clear_pos(active_win); + + g_free(str); } static void key_combo(void) @@ -208,17 +216,23 @@ static void key_combo(void) static void key_backward_history(void) { const char *text; + char *line; - text = command_history_prev(active_win, gui_entry_get_text(active_entry)); + line = gui_entry_get_text(active_entry); + text = command_history_prev(active_win, line); gui_entry_set_text(active_entry, text); + g_free(line); } static void key_forward_history(void) { const char *text; + char *line; - text = command_history_next(active_win, gui_entry_get_text(active_entry)); + line = gui_entry_get_text(active_entry); + text = command_history_next(active_win, line); gui_entry_set_text(active_entry, text); + g_free(line); } static void key_beginning_of_line(void) @@ -228,7 +242,7 @@ static void key_beginning_of_line(void) static void key_end_of_line(void) { - gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry))); + gui_entry_set_pos(active_entry, active_entry->text_len); } static void key_backward_character(void) @@ -263,10 +277,8 @@ static void key_forward_to_space(void) static void key_erase_line(void) { - g_free_not_null(cutbuffer); - cutbuffer = g_strdup(gui_entry_get_text(active_entry)); - - gui_entry_set_text(active_entry, ""); + gui_entry_set_pos(active_entry, active_entry->text_len); + gui_entry_erase(active_entry, active_entry->text_len); } static void key_erase_to_beg_of_line(void) @@ -274,9 +286,6 @@ static void key_erase_to_beg_of_line(void) int pos; pos = gui_entry_get_pos(active_entry); - g_free_not_null(cutbuffer); - cutbuffer = g_strndup(gui_entry_get_text(active_entry), pos); - gui_entry_erase(active_entry, pos); } @@ -285,21 +294,22 @@ static void key_erase_to_end_of_line(void) int pos; pos = gui_entry_get_pos(active_entry); - g_free_not_null(cutbuffer); - cutbuffer = g_strdup(gui_entry_get_text(active_entry)+pos); - - gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry))); - gui_entry_erase(active_entry, strlen(gui_entry_get_text(active_entry)) - pos); + gui_entry_set_pos(active_entry, active_entry->text_len); + gui_entry_erase(active_entry, active_entry->text_len - pos); } static void key_yank_from_cutbuffer(void) { + char *cutbuffer; + + cutbuffer = gui_entry_get_cutbuffer(active_entry); if (cutbuffer != NULL) gui_entry_insert_text(active_entry, cutbuffer); } static void key_transpose_characters(void) { +#if 0 /* FIXME: !!! */ char *line, c; int pos; @@ -316,11 +326,12 @@ static void key_transpose_characters(void) gui_entry_insert_char(active_entry, c); gui_entry_set_pos(active_entry, pos); gui_entry_move_pos(active_entry, 1); +#endif } static void key_delete_character(void) { - if (gui_entry_get_pos(active_entry) < (int)strlen(gui_entry_get_text(active_entry))) { + if (gui_entry_get_pos(active_entry) < active_entry->text_len) { gui_entry_move_pos(active_entry, 1); gui_entry_erase(active_entry, 1); } @@ -353,7 +364,7 @@ static void key_delete_to_next_space(void) static void sig_input(void) { - unsigned char buffer[128]; + unichar buffer[128]; int ret, i; if (!active_entry) { @@ -361,7 +372,7 @@ static void sig_input(void) return; } - ret = term_gets(buffer, sizeof(buffer)); + ret = term_gets(buffer, sizeof(buffer)/sizeof(buffer[0])); if (ret == -1) { /* lost terminal */ if (!term_detached) @@ -404,13 +415,15 @@ static void key_change_window(const char *data) static void key_completion(int erase) { - char *line; + char *text, *line; int pos; pos = gui_entry_get_pos(active_entry); - line = word_complete(active_win, gui_entry_get_text(active_entry), - &pos, erase); + text = gui_entry_get_text(active_entry); + line = word_complete(active_win, text, &pos, erase); + g_free(text); + if (line != NULL) { gui_entry_set_text(active_entry, line); gui_entry_set_pos(active_entry, pos); @@ -430,12 +443,15 @@ static void key_erase_completion(void) static void key_check_replaces(void) { - char *line; + char *text, *line; int pos; pos = gui_entry_get_pos(active_entry); - line = auto_word_complete(gui_entry_get_text(active_entry), &pos); + text = gui_entry_get_text(active_entry); + line = auto_word_complete(text, &pos); + g_free(text); + if (line != NULL) { gui_entry_set_text(active_entry, line); gui_entry_set_pos(active_entry, pos); @@ -537,11 +553,15 @@ static void key_sig_stop(void) static void sig_window_auto_changed(void) { + char *text; + if (active_entry == NULL) return; - command_history_next(active_win, gui_entry_get_text(active_entry)); + text = gui_entry_get_text(active_entry); + command_history_next(active_win, text); gui_entry_set_text(active_entry, ""); + g_free(text); } static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry, @@ -563,7 +583,6 @@ void gui_readline_init(void) char *key, data[MAX_INT_STRLEN]; int n; - cutbuffer = NULL; redir = NULL; idle_time = time(NULL); input_listen_init(STDIN_FILENO); @@ -693,7 +712,6 @@ void gui_readline_init(void) void gui_readline_deinit(void) { - g_free_not_null(cutbuffer); input_listen_deinit(); key_configure_freeze(); diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c index cd834ee4..1e7316f4 100644 --- a/src/fe-text/gui-windows.c +++ b/src/fe-text/gui-windows.c @@ -44,7 +44,7 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, gui->view = textbuffer_view_create(textbuffer_create(), window->width, window->height, settings_get_bool("scroll"), - settings_get_bool("term_utf8")); + term_type == TERM_TYPE_UTF8); textbuffer_view_set_default_indent(gui->view, settings_get_int("indent"), !settings_get_bool("indent_always"), diff --git a/src/fe-text/module.h b/src/fe-text/module.h index c6643545..0ca9f703 100644 --- a/src/fe-text/module.h +++ b/src/fe-text/module.h @@ -1,4 +1,5 @@ #include "common.h" +#include "term.h" #define MODULE_NAME "fe-text" diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c index 7612510a..d86c1e45 100644 --- a/src/fe-text/statusbar-items.c +++ b/src/fe-text/statusbar-items.c @@ -347,8 +347,7 @@ static void item_input(SBAR_ITEM_REC *item, int get_size_only) rec = g_hash_table_lookup(input_entries, item->bar); if (rec == NULL) { rec = gui_entry_create(item->xpos, item->bar->real_ypos, - item->size, - settings_get_bool("term_utf8")); + item->size, term_type == TERM_TYPE_UTF8); gui_entry_set_active(rec); g_hash_table_insert(input_entries, item->bar, rec); } @@ -377,10 +376,8 @@ static void sig_statusbar_destroyed(STATUSBAR_REC *bar) static void read_settings(void) { - if (active_entry != NULL) { - gui_entry_set_utf8(active_entry, - settings_get_bool("term_utf8")); - } + if (active_entry != NULL) + gui_entry_set_utf8(active_entry, term_type == TERM_TYPE_UTF8); } void statusbar_items_init(void) diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c index a1b106a7..fde313b7 100644 --- a/src/fe-text/term-terminfo.c +++ b/src/fe-text/term-terminfo.c @@ -22,9 +22,16 @@ #include "signals.h" #include "term.h" #include "terminfo-core.h" +#include "utf8.h" #include +/* 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; @@ -48,6 +55,10 @@ static int last_fg, last_bg, last_attrs; 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) { @@ -94,6 +105,7 @@ int term_init(void) term_lines_empty = g_new0(char, term_height); + term_set_input_type(TERM_TYPE_8BIT); term_common_init(); g_atexit(term_deinit); return TRUE; @@ -373,6 +385,39 @@ void term_addch(TERM_WINDOW *window, int chr) putc(chr, window->term->out); } +static void term_addch_utf8(TERM_WINDOW *window, unichar chr) +{ + unsigned 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(); + term_printed_text(1); + if (vcy == term_height && vcx == 0) + return; /* last char in screen */ + + switch (term_type) { + case TERM_TYPE_UTF8: + term_addch_utf8(window, chr); + break; + case TERM_TYPE_BIG5: + putc((chr >> 8) & 0xff, window->term->out); + putc((chr & 0xff), window->term->out); + break; + default: + putc(chr, window->term->out); + break; + } +} + void term_addstr(TERM_WINDOW *window, const char *str) { int len; @@ -487,15 +532,80 @@ void term_stop(void) } } -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; + + *result = get_utf8_char(&end, size); + switch (*result) { + case (unichar) -2: + /* not UTF8 - fallback to 8bit ascii */ + *result = *buffer; + return 1; + case (unichar) -1: + /* need more data */ + return -1; + default: + return (int) (end-buffer)+1; + } +} + +/* XXX I didn't check the encoding range of big5+. This is standard big5. */ +#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */ +#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */ +#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE) +#define is_big5(hi,lo) (is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo))) + +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) @@ -504,5 +614,28 @@ int term_gets(unsigned char *buffer, int size) } 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 { + memmove(term_inbuf+i, term_inbuf, term_inbuf_pos-i); + term_inbuf_pos = i; + } + } + return ret; } diff --git a/src/fe-text/term.c b/src/fe-text/term.c index c32b2491..804157fb 100644 --- a/src/fe-text/term.c +++ b/src/fe-text/term.c @@ -35,6 +35,7 @@ #define MIN_SCREEN_WIDTH 20 int term_use_colors; +int term_type; static int force_colors; static int resize_dirty; @@ -91,10 +92,25 @@ static void cmd_resize(void) static void read_settings(void) { + const char *str; int old_colors = term_use_colors; + int old_type = term_type; term_auto_detach(settings_get_bool("term_auto_detach")); + /* set terminal type */ + str = settings_get_str("term_type"); + if (g_strcasecmp(str, "utf8") == 0) + term_type = TERM_TYPE_UTF8; + else if (g_strcasecmp(str, "big5") == 0) + term_type = TERM_TYPE_BIG5; + else + term_type = TERM_TYPE_8BIT; + + if (old_type != term_type) + term_set_input_type(term_type); + + /* change color stuff */ if (force_colors != settings_get_bool("term_force_colors")) { force_colors = settings_get_bool("term_force_colors"); term_force_colors(force_colors); @@ -115,7 +131,7 @@ void term_common_init(void) settings_add_bool("lookandfeel", "colors", TRUE); settings_add_bool("lookandfeel", "term_force_colors", FALSE); settings_add_bool("lookandfeel", "term_auto_detach", FALSE); - settings_add_bool("lookandfeel", "term_utf8", FALSE); + settings_add_str("lookandfeel", "term_type", "8bit"); force_colors = FALSE; term_use_colors = term_has_colors() && settings_get_bool("colors"); diff --git a/src/fe-text/term.h b/src/fe-text/term.h index 9cd1b153..3607fad5 100644 --- a/src/fe-text/term.h +++ b/src/fe-text/term.h @@ -3,6 +3,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW; +/* text attributes */ #define ATTR_RESETFG 0x0100 #define ATTR_RESETBG 0x0200 #define ATTR_BOLD 0x0400 @@ -14,16 +15,16 @@ typedef struct _TERM_WINDOW TERM_WINDOW; #define ATTR_NOCOLORS (ATTR_UNDERLINE|ATTR_REVERSE) -#ifdef WANT_BIG5 -/* XXX I didn't check the encoding range of big5+. This is standard big5. */ -# define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */ -# define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */ -# define is_big5_hi(hi) (((char)0x81<=hi)&&(hi<=(char)0xFE)) -# define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)) -#endif +/* terminal types */ +#define TERM_TYPE_8BIT 0 /* normal 8bit text */ +#define TERM_TYPE_UTF8 1 +#define TERM_TYPE_BIG5 2 + +typedef guint32 unichar; extern TERM_WINDOW *root_window; -extern int term_width, term_height, term_use_colors, term_detached; +extern int term_width, term_height; +extern int term_use_colors, term_type, term_detached; /* Initialize / deinitialize terminal */ int term_init(void); @@ -63,6 +64,7 @@ void term_set_color(TERM_WINDOW *window, int col); void term_move(TERM_WINDOW *window, int x, int y); void term_addch(TERM_WINDOW *window, int chr); +void term_add_unichar(TERM_WINDOW *window, unichar chr); void term_addstr(TERM_WINDOW *window, const char *str); void term_clrtoeol(TERM_WINDOW *window); @@ -78,7 +80,10 @@ void term_detach(void); void term_attach(FILE *in, FILE *out); void term_stop(void); -int term_gets(unsigned char *buffer, int size); + +/* keyboard input handling */ +void term_set_input_type(int type); +int term_gets(unichar *buffer, int size); /* internal */ void term_common_init(void); diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c index d98fc85c..dc21aae6 100644 --- a/src/fe-text/textbuffer-view.c +++ b/src/fe-text/textbuffer-view.c @@ -231,7 +231,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) } if (view->utf8) - get_utf8_char(&ptr); + get_utf8_char(&ptr, 6); xpos++; if (*ptr++ == ' ') { @@ -398,7 +398,7 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, if (xpos < term_width) { const unsigned char *end = text; if (view->utf8) - get_utf8_char(&end); + get_utf8_char(&end, 6); if (*text >= 32 && (end != text || (*text & 127) >= 32)) { diff --git a/src/fe-text/utf8.c b/src/fe-text/utf8.c index 63a834c8..e58307d2 100644 --- a/src/fe-text/utf8.c +++ b/src/fe-text/utf8.c @@ -1,3 +1,29 @@ +/* utf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 2002 Timo Sirainen + * + * Based on GLib code by + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "module.h" #define UTF8_COMPUTE(Char, Mask, Len) \ if (Char < 128) \ @@ -46,19 +72,106 @@ (Result) |= ((Chars)[(Count)] & 0x3f); \ } -void get_utf8_char(const unsigned char **ptr) +unichar get_utf8_char(const unsigned char **ptr, int len) { - int i, mask = 0, len; - unsigned int result; - unsigned char c = (unsigned char) **ptr; + int i, result, mask, chrlen; - UTF8_COMPUTE(c, mask, len); + mask = 0; + UTF8_COMPUTE(**ptr, mask, chrlen); if (len == -1) - return; + return (unichar) -2; + + if (chrlen > len) + return (unichar) -1; UTF8_GET(result, *ptr, i, mask, len); if (result == -1) - return; + return (unichar) -2; - *ptr += len-1; + *ptr += len-1; + return result; +} + +int strlen_utf8(const char *str) +{ + const unsigned char *p = (const unsigned char *) str; + int len; + + len = 0; + while (*p != '\0' && get_utf8_char(&p, 6) > 0) { + len++; + p++; + } + return len; +} + +int utf16_char_to_utf8(unichar c, unsigned char *outbuf) +{ + int len, i, first; + + len = 0; + if (c < 0x80) { + first = 0; + len = 1; + } else if (c < 0x800) { + first = 0xc0; + len = 2; + } else if (c < 0x10000) { + first = 0xe0; + len = 3; + } else if (c < 0x200000) { + first = 0xf0; + len = 4; + } else if (c < 0x4000000) { + first = 0xf8; + len = 5; + } else { + first = 0xfc; + len = 6; + } + + if (outbuf) { + for (i = len - 1; i > 0; --i) { + outbuf[i] = (c & 0x3f) | 0x80; + c >>= 6; + } + outbuf[0] = c | first; + } + + return len; +} + +void utf8_to_utf16(const char *str, unichar *out) +{ + const unsigned char *p = (const unsigned char *) str; + int i, result, mask, len; + + while (*p != '\0') { + mask = 0; + UTF8_COMPUTE(*p, mask, len); + if (len == -1) + break; + + UTF8_GET(result, p, i, mask, len); + if (result == -1) + break; + + p += len; + *out++ = result; + } + + *out = '\0'; +} + +void utf16_to_utf8(const unichar *str, char *out) +{ + int len; + + while (*str != '\0') { + len = utf16_char_to_utf8(*str, out); + out += len; + + str++; + } + *out = '\0'; } diff --git a/src/fe-text/utf8.h b/src/fe-text/utf8.h index 3d8f3783..a37c0b6a 100644 --- a/src/fe-text/utf8.h +++ b/src/fe-text/utf8.h @@ -1,6 +1,21 @@ #ifndef __UTF8_H #define __UTF8_H -void get_utf8_char(const unsigned char **ptr); +/* Returns -2 = invalid, -1 = need more data, otherwise unichar. */ +unichar get_utf8_char(const unsigned char **ptr, int len); + +/* Returns length of UTF8 string */ +int strlen_utf8(const char *str); + +/* UTF-8 -> unichar string. The NUL is copied as well. */ +void utf8_to_utf16(const char *str, unichar *out); + +/* unichar -> UTF-8 string. outbuf must be at least 6 chars long. + Returns outbuf string length. */ +int utf16_char_to_utf8(unichar c, unsigned char *outbuf); + +/* unichar -> UTF-8 string. The NUL is copied as well. + Make sure out is at least 6 x length of str. */ +void utf16_to_utf8(const unichar *str, char *out); #endif