diff options
author | Nalin Dahyabhai <nalin@src.gnome.org> | 2002-04-30 22:06:35 +0000 |
---|---|---|
committer | Nalin Dahyabhai <nalin@src.gnome.org> | 2002-04-30 22:06:35 +0000 |
commit | cdf1940aa5107ae317b8f3696e6bb0808f8c422f (patch) | |
tree | ec002fdd076fc1f7ecb68d02f3f7024b3dfd95c6 | |
parent | 2c418085fcfbb5fbefcdc840b5b5ba516d4af849 (diff) |
Track and free idle task tags properly. Change F11 and F12 capabilitiesvte_0_3
* src/vte.c: Track and free idle task tags properly. Change F11 and F12
capabilities from 'k;' and 'F1' to 'F1' and 'F2'. Send a NUL on
control space. (#80350) Allow setting and checking of word characters,
and change select-by-word behavior to use the word character list. Emit
"contents_changed" signals whenever the visible contents change, and
"cursor_moved" when the cursor moves. Add snapshotting method. Scroll
when auto-margin handling moves the cursor to the next line. Assume
that the locale charset is actually ISO-8859-1 when we're in a UTF-8
locale, so we don't toggle from UTF-8 to UTF-8. Treat GDK_KP_Page_Up
as a GDK_Page_Up, ditto for GDK_KP_Page_Down and GDK_KP_Tab and
GDK_KP_Space. Add vte_terminal_get_font(). Don't bother messing with
ring buffers if we're resizing them to their current sizes.
* src/pty.c, src/vte.c: Return a pid from vte_terminal_fork_command().
* src/vteaccess.c, src/vteaccess.h: Add VteTerminalAccessible object type. It
might even work, mostly.
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/pty.c | 1 | ||||
-rw-r--r-- | src/vte.c | 545 | ||||
-rw-r--r-- | src/vte.h | 47 | ||||
-rw-r--r-- | src/vteaccess.c | 675 | ||||
-rw-r--r-- | src/vteaccess.h | 63 | ||||
-rw-r--r-- | vte.spec | 9 |
10 files changed, 1241 insertions, 126 deletions
@@ -1,5 +1,19 @@ -2002-04-29 14:56 nalin - * src/vte.c: Track and free idle task tags properly. +2002-04-30 18:06 nalin + * src/vte.c: Track and free idle task tags properly. Change F11 and + F12 capabilities from 'k;' and 'F1' to 'F1' and 'F2'. Send a NUL on + control space. (#80350) Allow setting and checking of word characters, + and change select-by-word behavior to use the word character list. + Emit "contents_changed" signals whenever the visible contents change, + and "cursor_moved" when the cursor moves. Add snapshotting method. + Scroll when auto-margin handling moves the cursor to the next line. + Assume that the locale charset is actually ISO-8859-1 when we're in + a UTF-8 locale, so we don't toggle from UTF-8 to UTF-8. Treat + GDK_KP_Page_Up as a GDK_Page_Up, ditto for GDK_KP_Page_Down and + GDK_KP_Tab and GDK_KP_Space. Add vte_terminal_get_font(). Don't bother + messing with ring buffers if we're resizing them to their current sizes. + * src/pty.c, src/vte.c: Return a pid from vte_terminal_fork_command(). + * src/vteaccess.c, src/vteaccess.h: Add VteTerminalAccessible object + type. It might even work, mostly. 2002-04-29 14:25 nalin * src/vte.c: Handle me() by resetting all attributes (including @@ -19,7 +19,7 @@ what the widget actually sees after it filters incoming data. * What's missing? -- Accessibility isn't started yet. +- Accessibility isn't completed yet. - Mouse tracking isn't started yet. - Entries in the termcap file also don't contain the sequences which a terminal is supposed to send to the application when a specific sequence is received diff --git a/configure.in b/configure.in index b64d94d..b2944f5 100644 --- a/configure.in +++ b/configure.in @@ -17,6 +17,7 @@ AC_DEFINE(G_DISABLE_DEPRECATED,1,[Disable deprecated glib features.]) AC_DEFINE(GDK_DISABLE_DEPRECATED,1,[Disable deprecated gdk features.]) AC_DEFINE(GDK_PIXBUF_DISABLE_DEPRECATED,1,[Disable deprecated gdk-pixbuf features.]) AC_DEFINE(GTK_DISABLE_DEPRECATED,1,[Disable deprecated gtk features.]) +AC_DEFINE(VTE_UTF8_BPC,6,[Maximum number of bytes used per UTF-8 character.]) AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE",[Package name.]) AC_CHECK_FUNCS(getpt grantpt unlockpt ptsname ptsname_r) diff --git a/src/Makefile.am b/src/Makefile.am index 4a3ac42..6714333 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ bin_PROGRAMS = vte -pkginclude_HEADERS = caps.h pty.h ring.h termcap.h trie.h vte.h +pkginclude_HEADERS = caps.h pty.h ring.h termcap.h trie.h vte.h vteaccess.h lib_LTLIBRARIES = libvte.la noinst_PROGRAMS = interpret utf8echo utf8mode iso8859mode EXTRA_PROGRAMS = pty ring termcap trie @@ -21,7 +21,9 @@ libvte_la_SOURCES = \ trie.c \ trie.h \ vte.c \ - vte.h + vte.h \ + vteaccess.c \ + vteaccess.h CLEANFILES = marshal.c marshal.h @@ -44,6 +44,7 @@ vte_pty_fork_on_fd(const char *path, const char **env_add, pid = fork(); if (pid == -1) { /* Error fork()ing. Bail. */ + *child = -1; return -1; } if (pid != 0) { @@ -51,6 +51,7 @@ #include "ring.h" #include "trie.h" #include "vte.h" +#include "vteaccess.h" #include <X11/Xlib.h> #ifdef HAVE_XFT #include <X11/extensions/Xrender.h> @@ -59,7 +60,6 @@ #define VTE_TAB_WIDTH 8 #define VTE_LINE_WIDTH 1 -#define VTE_UTF8_BPC 6 #define VTE_DEF_FG 16 #define VTE_DEF_BG 17 #define VTE_SATURATION_MAX 10000 @@ -91,6 +91,12 @@ typedef enum { VTE_KEYPAD_APPLICATION, } VteKeypad; +typedef struct _VteScreen VteScreen; + +typedef struct _VteWordCharRange { + wchar_t start, end; +} VteWordCharRange; + /* Terminal private data. */ struct _VteTerminalPrivate { /* Emulation setup data. */ @@ -185,6 +191,8 @@ struct _VteTerminalPrivate { } selection_start, selection_end; /* Options. */ + GArray *word_chars; + gboolean scroll_on_output; gboolean scroll_on_keystroke; long scrollback_lines; @@ -322,7 +330,7 @@ vte_terminal_find_charcell(VteTerminal *terminal, long row, long col) { GArray *rowdata; struct vte_charcell *ret = NULL; - struct _VteScreen *screen; + VteScreen *screen; g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); screen = terminal->pvt->screen; if (vte_ring_contains(screen->row_data, row)) { @@ -339,7 +347,7 @@ static void vte_invalidate_cursor_once(gpointer data) { VteTerminal *terminal; - struct _VteScreen *screen; + VteScreen *screen; struct vte_charcell *cell; size_t preedit_length; int columns; @@ -417,6 +425,20 @@ vte_terminal_emit_selection_changed(VteTerminal *terminal) g_signal_emit_by_name(terminal, "selection_changed"); } +/* Emit a "contents_changed" signal. */ +static void +vte_terminal_emit_contents_changed(VteTerminal *terminal) +{ + g_signal_emit_by_name(terminal, "contents_changed"); +} + +/* Emit a "cursor_moved" signal. */ +static void +vte_terminal_emit_cursor_moved(VteTerminal *terminal) +{ + g_signal_emit_by_name(terminal, "cursor_moved"); +} + /* Deselect anything which is selected and refresh the screen if needed. */ static void vte_terminal_deselect_all(VteTerminal *terminal) @@ -535,7 +557,7 @@ vte_terminal_scroll_pages(VteTerminal *terminal, gint pages) /* Scroll so that the scroll delta is the insertion delta. */ static void -vte_terminal_scroll_on_something(VteTerminal *terminal) +vte_terminal_scroll_to_bottom(VteTerminal *terminal) { g_return_if_fail(VTE_IS_TERMINAL(terminal)); if (floor(gtk_adjustment_get_value(terminal->adjustment)) != @@ -717,7 +739,7 @@ vte_sequence_handler_al(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; GtkWidget *widget; long start, end; g_return_if_fail(VTE_IS_TERMINAL(terminal)); @@ -832,7 +854,7 @@ vte_sequence_handler_cb(VteTerminal *terminal, { GArray *rowdata; long i; - struct _VteScreen *screen; + VteScreen *screen; struct vte_charcell *pcell; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; @@ -872,7 +894,7 @@ vte_sequence_handler_cd(VteTerminal *terminal, { GArray *rowdata; long i; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* If the cursor is actually on the screen, clear data in the rows @@ -901,7 +923,7 @@ vte_sequence_handler_ce(VteTerminal *terminal, GValueArray *params) { GArray *rowdata; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* If the cursor is actually on the screen, clear data in the row @@ -928,7 +950,7 @@ vte_sequence_handler_ch(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; GValue *value; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; @@ -961,7 +983,7 @@ vte_sequence_handler_cm(VteTerminal *terminal, GValueArray *params) { GValue *row, *col; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* We need at least two parameters. */ @@ -986,7 +1008,7 @@ vte_sequence_handler_clear_current_line(VteTerminal *terminal, GValueArray *params) { GArray *rowdata; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* If the cursor is actually on the screen, clear data in the row @@ -1056,7 +1078,7 @@ vte_sequence_handler_cv(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; GValue *value; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; @@ -1077,7 +1099,7 @@ vte_sequence_handler_dc(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; GArray *rowdata; long col; @@ -1120,7 +1142,7 @@ vte_sequence_handler_dl(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; long end; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; @@ -1157,7 +1179,7 @@ static void vte_terminal_ensure_cursor(VteTerminal *terminal) { GArray *array; - struct _VteScreen *screen; + VteScreen *screen; struct vte_charcell cell; g_return_if_fail(VTE_IS_TERMINAL(terminal)); @@ -1184,11 +1206,36 @@ vte_terminal_ensure_cursor(VteTerminal *terminal) array = g_array_append_val(array, cell); } /* Add one more cell to the end of the line to get - * it into the column, and use it. */ + * one for the column. */ array = g_array_append_val(array, cell); } } +static void +vte_terminal_scroll_insertion(VteTerminal *terminal) +{ + long rows, delta; + VteScreen *screen; + + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + screen = terminal->pvt->screen; + + /* Make sure that the bottom row is visible, and that it's in + * the buffer (even if it's empty). This usually causes the + * top row to become a history-only row. */ + rows = MAX(vte_ring_next(screen->row_data), + screen->cursor_current.row + 1); + delta = MAX(0, rows - terminal->row_count); + + /* Adjust the insert delta and scroll if needed. */ + if (delta != screen->insert_delta) { + vte_terminal_ensure_cursor(terminal); + screen->insert_delta = delta; + /* Update scroll bar adjustments. */ + vte_terminal_adjust_adjustments(terminal); + } +} + /* Scroll forward. */ static void vte_sequence_handler_do(VteTerminal *terminal, @@ -1197,8 +1244,8 @@ vte_sequence_handler_do(VteTerminal *terminal, GValueArray *params) { GtkWidget *widget; - long rows, col, row, start, end, delta; - struct _VteScreen *screen; + long col, row, start, end; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); widget = GTK_WIDGET(terminal); @@ -1248,19 +1295,10 @@ vte_sequence_handler_do(VteTerminal *terminal, /* Move the cursor down. */ screen->cursor_current.row++; - /* Make sure that the bottom row is visible, and that it's in - * the buffer (even if it's empty). This usually causes the - * top row to become a history-only row. */ - rows = MAX(vte_ring_next(screen->row_data), - screen->cursor_current.row + 1); - delta = MAX(0, rows - terminal->row_count); - if (delta != screen->insert_delta) { - vte_terminal_ensure_cursor(terminal); - } - screen->insert_delta = delta; - - /* Update scroll bar adjustments. */ - vte_terminal_adjust_adjustments(terminal); + /* Adjust the insert delta so that the row the cursor is on + * is viewable if the insert delta is equal to the scrolling + * delta. */ + vte_terminal_scroll_insertion(terminal); } } @@ -1283,7 +1321,7 @@ vte_sequence_handler_ec(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; GArray *rowdata; GValue *value; struct vte_charcell *cell; @@ -1346,7 +1384,7 @@ vte_sequence_handler_ho(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; screen->cursor_current.row = screen->insert_delta; @@ -1361,7 +1399,7 @@ vte_sequence_handler_ic(VteTerminal *terminal, GValueArray *params) { long row, col; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; @@ -1405,7 +1443,7 @@ vte_sequence_handler_kb(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; screen->cursor_current.col = MAX(0, screen->cursor_current.col - 1); @@ -1494,7 +1532,7 @@ vte_sequence_handler_nd(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; screen->cursor_current.col++; @@ -1507,7 +1545,7 @@ vte_sequence_handler_rc(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; screen->cursor_current.col = screen->cursor_saved.col; @@ -1533,7 +1571,7 @@ vte_sequence_handler_sc(VteTerminal *terminal, GQuark match_quark, GValueArray *params) { - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; screen->cursor_saved.col = screen->cursor_current.col; @@ -1607,7 +1645,7 @@ vte_sequence_handler_up(VteTerminal *terminal, { GtkWidget *widget; long col, row, start, end; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); widget = GTK_WIDGET(terminal); @@ -1875,7 +1913,7 @@ vte_sequence_handler_clear_above_current(VteTerminal *terminal, { GArray *rowdata; long i; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* If the cursor is actually on the screen, clear data in the row @@ -1905,7 +1943,7 @@ vte_sequence_handler_clear_screen(VteTerminal *terminal, { GArray *rowdata; long i; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(VTE_IS_TERMINAL(terminal)); screen = terminal->pvt->screen; /* If the cursor is actually on the screen, clear data in the row @@ -2315,7 +2353,7 @@ vte_sequence_handler_insert_lines(VteTerminal *terminal, GValueArray *params) { GValue *value; - struct _VteScreen *screen; + VteScreen *screen; long param, end, row; int i; g_return_if_fail(VTE_IS_TERMINAL(terminal)); @@ -2352,7 +2390,7 @@ vte_sequence_handler_delete_lines(VteTerminal *terminal, GValueArray *params) { GValue *value; - struct _VteScreen *screen; + VteScreen *screen; long param, end, row; int i; g_return_if_fail(VTE_IS_TERMINAL(terminal)); @@ -2417,7 +2455,11 @@ vte_sequence_handler_local_charset(VteTerminal *terminal, #ifdef VTE_DEFAULT_ISO_8859_1 vte_terminal_set_encoding(terminal, "ISO-8859-1"); #else - vte_terminal_set_encoding(terminal, nl_langinfo(CODESET)); + if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) { + vte_terminal_set_encoding(terminal, "ISO-8859-1"); + } else { + vte_terminal_set_encoding(terminal, nl_langinfo(CODESET)); + } #endif } @@ -2944,7 +2986,7 @@ vte_terminal_insert_char(GtkWidget *widget, wchar_t c) struct vte_charcell cell, *pcell; int columns, i; long col; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(widget != NULL); g_return_if_fail(VTE_IS_TERMINAL(widget)); @@ -3114,7 +3156,7 @@ vte_terminal_handle_sequence(GtkWidget *widget, { VteTerminal *terminal; VteTerminalSequenceHandler handler; - struct _VteScreen *screen; + VteScreen *screen; g_return_if_fail(widget != NULL); g_return_if_fail(VTE_IS_TERMINAL(widget)); @@ -3152,13 +3194,14 @@ vte_terminal_handle_sequence(GtkWidget *widget, } /* Start up a command in a slave PTY. */ -void +pid_t vte_terminal_fork_command(VteTerminal *terminal, const char *command, const char **argv) { const char **env_add; char *term, *colorterm; int i; + pid_t pid; /* Start up the command and get the PTY of the master. */ env_add = g_malloc0(sizeof(char*) * 3); @@ -3167,7 +3210,7 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command, env_add[0] = term; env_add[1] = colorterm; env_add[2] = NULL; - terminal->pvt->pty_master = vte_pty_open(&terminal->pvt->pty_pid, + terminal->pvt->pty_master = vte_pty_open(&pid, env_add, command ?: terminal->pvt->shell, @@ -3176,21 +3219,30 @@ vte_terminal_fork_command(VteTerminal *terminal, const char *command, g_free(colorterm); g_free((char**)env_add); - /* Set the pty to be non-blocking. */ - i = fcntl(terminal->pvt->pty_master, F_GETFL); - fcntl(terminal->pvt->pty_master, F_SETFL, i | O_NONBLOCK); + /* If we started the process, set up to listen for its output. */ + if (pid != -1) { + /* Set this as the child's pid. */ + terminal->pvt->pty_pid = pid; + + /* Set the pty to be non-blocking. */ + i = fcntl(terminal->pvt->pty_master, F_GETFL); + fcntl(terminal->pvt->pty_master, F_SETFL, i | O_NONBLOCK); + + /* Open a channel to listen for input on. */ + terminal->pvt->pty_input = + g_io_channel_unix_new(terminal->pvt->pty_master); + terminal->pvt->pty_output = NULL; + g_io_add_watch_full(terminal->pvt->pty_input, + G_PRIORITY_LOW, + G_IO_IN | G_IO_HUP, + vte_terminal_io_read, + terminal, + NULL); + g_io_channel_unref(terminal->pvt->pty_input); + } - /* Open a channel to listen for input on. */ - terminal->pvt->pty_input = - g_io_channel_unix_new(terminal->pvt->pty_master); - terminal->pvt->pty_output = NULL; - g_io_add_watch_full(terminal->pvt->pty_input, - G_PRIORITY_LOW, - G_IO_IN | G_IO_HUP, - vte_terminal_io_read, - terminal, - NULL); - g_io_channel_unref(terminal->pvt->pty_input); + /* Return the pid to the caller. */ + return pid; } /* Handle an EOF from the client. */ @@ -3233,6 +3285,8 @@ vte_terminal_process_incoming(gpointer data) { GValueArray *params = NULL; VteTerminal *terminal; + VteScreen *screen; + long cursor_row, cursor_col; GtkWidget *widget; GdkRectangle rect; char *ibuf, *obuf, *obufptr, *ubuf, *ubufptr; @@ -3317,6 +3371,11 @@ vte_terminal_process_incoming(gpointer data) wcount = (obuf - obufptr) / sizeof(wchar_t); wbuf = (wchar_t*) obufptr; + /* Save the current cursor position. */ + screen = terminal->pvt->screen; + cursor_row = screen->cursor_current.row; + cursor_col = screen->cursor_current.col; + /* Try initial substrings. */ start = 0; inserted = leftovers = FALSE; @@ -3476,8 +3535,9 @@ vte_terminal_process_incoming(gpointer data) if (inserted) { /* Keep the cursor on-screen if we scroll on output, or if * we're currently at the bottom of the buffer. */ + vte_terminal_scroll_insertion(terminal); if (terminal->pvt->scroll_on_output || bottom) { - vte_terminal_scroll_on_something(terminal); + vte_terminal_scroll_to_bottom(terminal); } /* The cursor moved, so force it to be redrawn. */ @@ -3487,6 +3547,16 @@ vte_terminal_process_incoming(gpointer data) vte_terminal_deselect_all(terminal); } + if (inserted || (screen != terminal->pvt->screen)) { + /* Signal that the visible contents changed. */ + vte_terminal_emit_contents_changed(terminal); + } + if ((cursor_row != terminal->pvt->screen->cursor_current.row) || + (cursor_col != terminal->pvt->screen->cursor_current.col)) { + /* Signal that the cursor moved. */ + vte_terminal_emit_cursor_moved(terminal); + } + /* Tell the input method where the cursor is. */ rect.x = terminal->pvt->screen->cursor_current.col * terminal->char_width; @@ -3501,12 +3571,14 @@ vte_terminal_process_incoming(gpointer data) terminal->pvt->n_incoming); #endif terminal->pvt->processing = again && (terminal->pvt->n_incoming > 0); + if (terminal->pvt->processing == FALSE) { + terminal->pvt->processing_tag = -1; + } #ifdef VTE_DEBUG if (terminal->pvt->processing) { fprintf(stderr, "Leaving processing handler on.\n"); } else { fprintf(stderr, "Turning processing handler off.\n"); - terminal->pvt->processing_tag = -1; } #endif return terminal->pvt->processing; @@ -3921,6 +3993,11 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) &modifiers) == FALSE) { modifiers = 0; } +#ifdef VTE_DEBUG + fprintf(stderr, "Keypress, modifiers=%d, keyval=%d, " + "string=`%s'.\n", modifiers, event->keyval, + event->string); +#endif /* Map the key to a sequence name if we can. */ switch (event->keyval) { case GDK_BackSpace: @@ -3974,10 +4051,10 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) special = "k0"; break; case GDK_F11: - special = "k;"; + special = "F1"; break; case GDK_F12: - special = "F1"; + special = "F2"; break; /* Cursor keys. */ case GDK_KP_Up: @@ -3996,6 +4073,7 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) case GDK_Right: special = "kr"; break; + case GDK_KP_Page_Up: case GDK_Page_Up: if (modifiers & GDK_SHIFT_MASK) { vte_terminal_scroll_pages(terminal, -1); @@ -4004,6 +4082,7 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) special = "kP"; } break; + case GDK_KP_Page_Down: case GDK_Page_Down: if (modifiers & GDK_SHIFT_MASK) { vte_terminal_scroll_pages(terminal, 1); @@ -4012,6 +4091,7 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) special = "kN"; } break; + case GDK_KP_Tab: case GDK_Tab: if (modifiers & GDK_SHIFT_MASK) { special = "kB"; @@ -4020,6 +4100,17 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) normal_length = 1; } break; + case GDK_KP_Space: + case GDK_space: + if (modifiers & GDK_CONTROL_MASK) { + /* Ctrl-Space sends NUL?!? Madness! */ + normal = g_strdup(""); + normal_length = 1; + } else { + normal = g_strdup(" "); + normal_length = 1; + } + break; /* The default is to just send the string. */ default: if (event->string != NULL) { @@ -4068,46 +4159,56 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event) } /* Keep the cursor on-screen. */ if (!scrolled && terminal->pvt->scroll_on_keystroke) { - vte_terminal_scroll_on_something(terminal); + vte_terminal_scroll_to_bottom(terminal); } return TRUE; } return FALSE; } -/* Classify a wide character with some value, useful only for comparing - * for equality. */ -static guint -vte_charclass(wchar_t c) +/* Check if a particular character is part of a "word" or not. */ +gboolean +vte_terminal_is_word_char(VteTerminal *terminal, gunichar c) { - if (iswalnum(c)) { - return 1; - } - if (iswpunct(c)) { - return 2; + int i; + VteWordCharRange *range; + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); + if (terminal->pvt->word_chars == NULL) { + return FALSE; } - if (iswblank(c)) { - return 3; + /* FIXME: if a gunichar isn't a wchar_t, we're probably screwed, so + * should we convert from UCS-4 to WCHAR_T or something here? (Is a + * gunichar even a UCS-4 character)? Or should we convert to UTF-8 + * and then to WCHAR_T? Aaaargh. */ + for (i = 0; i < terminal->pvt->word_chars->len; i++) { + range = &g_array_index(terminal->pvt->word_chars, + VteWordCharRange, + i); + if ((c >= range->start) && (c <= range->end)) { + return TRUE; + } } - return 0; + return FALSE; } -/* Check if the characters in the given block are in the same class. */ +/* Check if the characters in the given block are in the same class (word vs. + * non-word characters). */ static gboolean vte_uniform_class(VteTerminal *terminal, long row, long scol, long ecol) { struct vte_charcell *pcell = NULL; long col; - guint cclass; + gboolean word_char; g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); if ((pcell = vte_terminal_find_charcell(terminal, row, scol)) != NULL) { - cclass = vte_charclass(pcell->c); - for (col = scol; col <= ecol; col++) { + word_char = vte_terminal_is_word_char(terminal, pcell->c); + for (col = scol + 1; col <= ecol; col++) { pcell = vte_terminal_find_charcell(terminal, row, col); if (pcell == NULL) { return FALSE; } - if (cclass != vte_charclass(pcell->c)) { + if (word_char != vte_terminal_is_word_char(terminal, + pcell->c)) { return FALSE; } } @@ -4370,7 +4471,7 @@ vte_terminal_copy(VteTerminal *terminal, GdkAtom board) GtkClipboard *clipboard; GtkWidget *widget; long x, y; - struct _VteScreen *screen; + VteScreen *screen; struct vte_charcell *pcell; wchar_t *buffer; size_t length; @@ -5015,6 +5116,13 @@ vte_terminal_set_font_from_string(VteTerminal *terminal, const char *name) pango_font_description_free(font_desc); } +const PangoFontDescription * +vte_terminal_get_font(VteTerminal *terminal) +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); + return terminal->pvt->fontdesc; +} + /* A comparison function which helps sort quarks. */ static gint vte_compare_direct(gconstpointer a, gconstpointer b) @@ -5069,7 +5177,7 @@ vte_handle_scroll(VteTerminal *terminal) { long dy, adj; GtkWidget *widget; - struct _VteScreen *screen; + VteScreen *screen; /* Sanity checks. */ g_return_if_fail(GTK_IS_WIDGET(terminal)); widget = GTK_WIDGET(terminal); @@ -5260,7 +5368,7 @@ vte_terminal_reset_rowdata(VteRing **ring, long lines) * We need to create a new psuedo-terminal pair, read in the termcap file, and * set ourselves up to do the interpretation of sequences. */ static void -vte_terminal_init(VteTerminal *terminal) +vte_terminal_init(VteTerminal *terminal, gpointer *klass) { struct _VteTerminalPrivate *pvt; struct passwd *pwd; @@ -5293,6 +5401,8 @@ vte_terminal_init(VteTerminal *terminal) pvt->n_outgoing = 0; pvt->keypad = VTE_KEYPAD_NORMAL; + vte_terminal_set_word_chars(terminal, "-a-zA-Z0-9"); + pvt->scroll_on_output = FALSE; pvt->scroll_on_keystroke = TRUE; pvt->scrollback_lines = 0; @@ -5627,6 +5737,9 @@ vte_terminal_finalize(GObject *object) terminal->pvt->bg_transparent_image = NULL; } + /* Free the word chars array. */ + g_array_free(terminal->pvt->word_chars, FALSE); + /* Call the inherited finalize() method. */ if (G_OBJECT_CLASS(widget_class)->finalize) { (G_OBJECT_CLASS(widget_class))->finalize(object); @@ -5709,10 +5822,41 @@ vte_terminal_realize(GtkWidget *widget) gtk_widget_grab_focus(widget); } +static void +vte_terminal_determine_colors(VteTerminal *terminal, + const struct vte_charcell *cell, gboolean reverse, + int *fore, int *back) +{ + /* Determine what the foreground and background colors for rendering + * text should be. */ + if (reverse ^ (cell && cell->reverse)) { + *fore = cell ? cell->back : VTE_DEF_BG; + *back = cell ? cell->fore : VTE_DEF_FG; + } else { + *fore = cell ? cell->fore : VTE_DEF_FG; + *back = cell ? cell->back : VTE_DEF_BG; + } + + /* Handle invisible, bold, and standout text by adjusting colors. */ + if (cell && cell->invisible) { + *fore = *back; + } + if (cell && cell->bold) { + if ((*fore != VTE_DEF_FG) && (*fore != VTE_DEF_BG)) { + *fore += 8; + } + } + if (cell && cell->standout) { + if ((*back != VTE_DEF_FG) && (*back != VTE_DEF_BG)) { + *back += 8; + } + } +} + /* Draw a particular character on the screen. */ static void vte_terminal_draw_char(VteTerminal *terminal, - struct _VteScreen *screen, + VteScreen *screen, struct vte_charcell *cell, long col, long row, @@ -5759,28 +5903,7 @@ vte_terminal_draw_char(VteTerminal *terminal, reverse = cell && cell->reverse; reverse = reverse ^ vte_cell_is_selected(terminal, row, col); reverse = reverse || cursor; - if (reverse) { - fore = cell ? cell->back : VTE_DEF_BG; - back = cell ? cell->fore : VTE_DEF_FG; - } else { - fore = cell ? cell->fore : VTE_DEF_FG; - back = cell ? cell->back : VTE_DEF_BG; - } - - /* Handle invisible, bold, and standout text by adjusting colors. */ - if (cell && cell->invisible) { - fore = back; - } - if (cell && cell->bold) { - if ((fore != VTE_DEF_FG) && (fore != VTE_DEF_BG)) { - fore += 8; - } - } - if (cell && cell->standout) { - if ((back != VTE_DEF_FG) && (back != VTE_DEF_BG)) { - back += 8; - } - } + vte_terminal_determine_colors(terminal, cell, reverse, &fore, &back); /* Paint the background for the cell. */ if ((back != VTE_DEF_BG) && GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) { @@ -6150,7 +6273,7 @@ vte_terminal_paint(GtkWidget *widget, GdkRectangle *area) VteTerminal *terminal = NULL; GtkSettings *settings = NULL; PangoLayout *layout = NULL; - struct _VteScreen *screen; + VteScreen *screen; Display *display; GdkDrawable *gdrawable; Drawable drawable; @@ -6520,6 +6643,17 @@ vte_terminal_scroll(GtkWidget *widget, GdkEventScroll *event) return TRUE; } +/* Create a new accessible object associated with ourselves, and return + * it to the caller. */ +static AtkObject * +vte_terminal_get_accessible(GtkWidget *widget) +{ + AtkObject *access; + g_return_val_if_fail(VTE_IS_TERMINAL(widget), NULL); + access = vte_terminal_accessible_new(VTE_TERMINAL(widget)); + return access; +} + /* Initialize methods. */ static void vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data) @@ -6544,6 +6678,7 @@ vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data) widget_class->unrealize = vte_terminal_unrealize; widget_class->size_request = vte_terminal_size_request; widget_class->size_allocate = vte_terminal_size_allocate; + widget_class->get_accessible = vte_terminal_get_accessible; klass->eof_signal = g_signal_new("eof", @@ -6590,6 +6725,24 @@ vte_terminal_class_init(VteTerminalClass *klass, gconstpointer data) NULL, _vte_marshal_VOID__VOID, G_TYPE_NONE, 0); + klass->contents_changed_signal = + g_signal_new("contents_changed", + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + _vte_marshal_VOID__VOID, + G_TYPE_NONE, 0); + klass->cursor_moved_signal = + g_signal_new("cursor_moved", + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + _vte_marshal_VOID__VOID, + G_TYPE_NONE, 0); } GtkType @@ -7110,7 +7263,7 @@ void vte_terminal_set_scrollback_lines(VteTerminal *terminal, long lines) { long old_delta = 0, new_delta = 0, delta; - struct _VteScreen *screens[2]; + VteScreen *screens[2]; int i; g_return_if_fail(VTE_IS_TERMINAL(terminal)); @@ -7150,3 +7303,171 @@ vte_terminal_set_scrollback_lines(VteTerminal *terminal, long lines) 0, terminal->column_count, 0, terminal->row_count); } + +/* Get a snapshot of what's in the visible part of the window. */ +VteTerminalSnapshot * +vte_terminal_get_snapshot(VteTerminal *terminal) +{ + VteTerminalSnapshot *ret; + int row, column, x; + struct vte_charcell *cell; + int fore, back; + + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); + + ret = g_malloc0(sizeof(VteTerminalSnapshot)); + + /* Save the cursor position and visibility. */ + ret->cursor.x = terminal->pvt->screen->cursor_current.col; + ret->cursor.y = terminal->pvt->screen->cursor_current.row - + terminal->pvt->screen->insert_delta; + ret->cursor_visible = terminal->pvt->screen->cursor_visible; + + /* Save the window size. */ + ret->rows = terminal->row_count; + ret->columns = terminal->column_count; + + /* Save the window contents. */ + ret->contents = g_malloc(sizeof(struct VteTerminalSnapshotCell*) * + (ret->rows + 1)); + for (row = 0; row < ret->rows; row++) { + ret->contents[row] = g_malloc(sizeof(struct VteTerminalSnapshotCell) * + (ret->columns + 1)); + column = x = 0; + while (column < ret->columns) { + cell = vte_terminal_find_charcell(terminal, + row + terminal->pvt->screen->insert_delta, + x++); + if (cell == NULL) { + break; + } + if (cell->columns == 0) { + continue; + } + + /* Get the text. FIXME: convert from wchar_t to + * gunichar when they're not interchangeable. */ + ret->contents[row][column].c = cell->c; + + /* Get text attributes which aren't represented as + * colors. */ + ret->contents[row][column].attributes.underline = + cell->underline; + ret->contents[row][column].attributes.alternate = + cell->alternate; + + /* Get text colors. */ + vte_terminal_determine_colors(terminal, cell, FALSE, + &fore, &back); + + ret->contents[row][column].attributes.foreground.red = + terminal->pvt->palette[fore].red; + ret->contents[row][column].attributes.foreground.green = + terminal->pvt->palette[fore].green; + ret->contents[row][column].attributes.foreground.blue = + terminal->pvt->palette[fore].blue; + + ret->contents[row][column].attributes.background.red = + terminal->pvt->palette[back].red; + ret->contents[row][column].attributes.background.green = + terminal->pvt->palette[back].green; + ret->contents[row][column].attributes.background.blue = + terminal->pvt->palette[back].blue; + } + } + ret->contents[row] = NULL; + + return ret; +} + +void +vte_terminal_free_snapshot(VteTerminalSnapshot *snapshot) +{ + int row; + g_return_if_fail(snapshot != NULL); + for (row = 0; snapshot->contents[row] != NULL; row++) { + memset(snapshot->contents[row], 0, + sizeof(snapshot->contents[row][0]) * snapshot->columns); + g_free(snapshot->contents[row]); + } + g_free(snapshot->contents); + memset(snapshot, 0, sizeof(*snapshot)); + g_free(snapshot); +} + +/* Set the list of characters we consider to be parts of words. Everything + * else will be a non-word character, and we'll use transitions between the + * two sets when doing selection-by-words. */ +void +vte_terminal_set_word_chars(VteTerminal *terminal, const char *spec) +{ + iconv_t conv; + wchar_t *wbuf; + char *ibuf, *ibufptr, *obuf, *obufptr; + size_t ilen, olen; + VteWordCharRange range; + int i; + + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + /* Allocate a new range array. */ + if (terminal->pvt->word_chars != NULL) { + g_array_free(terminal->pvt->word_chars, FALSE); + } + terminal->pvt->word_chars = g_array_new(FALSE, TRUE, + sizeof(VteWordCharRange)); + /* Convert the spec from UTF-8 to a string of wchar_t. */ + conv = iconv_open("WCHAR_T", "UTF-8"); + if (conv == NULL) { + /* Aaargh. We're screwed. */ + g_warning("iconv_open() failed setting word characters"); + return; + } + ilen = strlen(spec); + ibuf = ibufptr = g_strdup(spec); + olen = (ilen + 1) * sizeof(wchar_t); + obuf = obufptr = g_malloc0(sizeof(wchar_t) * (strlen(spec) + 1)); + wbuf = (wchar_t*) obuf; + wbuf[ilen] = '\0'; + iconv(conv, &ibuf, &ilen, &obuf, &olen); + for (i = 0; i < ((obuf - obufptr) / sizeof(wchar_t)); i++) { + /* The hyphen character. */ + if (wbuf[i] == '-') { + range.start = wbuf[i]; + range.end = wbuf[i]; + g_array_append_val(terminal->pvt->word_chars, range); +#ifdef VTE_DEBUG + fprintf(stderr, "Word charset includes hyphen.\n"); +#endif + continue; + } + /* A single character, not the start of a range. */ + if ((wbuf[i] != '-') && (wbuf[i + 1] != '-')) { + range.start = wbuf[i]; + range.end = wbuf[i]; + g_array_append_val(terminal->pvt->word_chars, range); +#ifdef VTE_DEBUG + fprintf(stderr, "Word charset includes `%lc'.\n", + (wint_t) wbuf[i]); +#endif + continue; + } + /* The start of a range. */ + if ((wbuf[i] != '-') && + (wbuf[i + 1] == '-') && + (wbuf[i + 2] != '-') && + (wbuf[i + 2] != 0)) { + range.start = wbuf[i]; + range.end = wbuf[i + 2]; + g_array_append_val(terminal->pvt->word_chars, range); +#ifdef VTE_DEBUG + fprintf(stderr, "Word charset includes range from " + "`%lc' to `%lc'.\n", (wint_t) wbuf[i], + (wint_t) wbuf[i + 2]); +#endif + i += 2; + continue; + } + } + g_free(ibufptr); + g_free(obufptr); +} @@ -68,8 +68,30 @@ typedef struct _VteTerminalClass { guint window_title_changed_signal; guint icon_title_changed_signal; guint selection_changed_signal; + guint contents_changed_signal; + guint cursor_moved_signal; } VteTerminalClass; +/* A snapshot of the screen contents. */ +typedef struct _VteTerminalSnapshot { + struct { + int x, y; /* Location of the cursor. */ + } cursor; + int rows, columns; /* Size of the screen[shot]. */ + gboolean cursor_visible; + struct VteTerminalSnapshotCell { + gunichar c; /* The character itself. */ + struct { + /* Colors of this character. */ + GdkColor foreground, background; + /* Is it underlined? */ + gboolean underline; + /* Is it a graphic character? */ + gboolean alternate; + } attributes; + } **contents; +} VteTerminalSnapshot; + /* The widget's type. */ GtkType vte_terminal_get_type(void); @@ -88,22 +110,24 @@ GtkType vte_terminal_get_type(void); GtkWidget *vte_terminal_new(void); -void vte_terminal_fork_command(VteTerminal *terminal, - const char *command, - const char **argv); +pid_t vte_terminal_fork_command(VteTerminal *terminal, + const char *command, + const char **argv); void vte_terminal_feed(VteTerminal *terminal, const char *data, size_t length); void vte_terminal_feed_child(VteTerminal *terminal, const char *data, size_t length); + +void vte_terminal_copy_clipboard(VteTerminal *terminal); +void vte_terminal_paste_clipboard(VteTerminal *terminal); + void vte_terminal_set_size(VteTerminal *terminal, long columns, long rows); void vte_terminal_set_audible_bell(VteTerminal *terminal, gboolean audible); void vte_terminal_set_scroll_on_output(VteTerminal *terminal, gboolean scroll); void vte_terminal_set_scroll_on_keystroke(VteTerminal *terminal, gboolean scroll); -void vte_terminal_copy_clipboard(VteTerminal *terminal); -void vte_terminal_paste_clipboard(VteTerminal *terminal); void vte_terminal_set_colors(VteTerminal *terminal, const GdkColor *foreground, const GdkColor *background, @@ -118,14 +142,21 @@ void vte_terminal_set_background_saturation(VteTerminal *terminal, void vte_terminal_set_background_transparent(VteTerminal *terminal, gboolean transparent); void vte_terminal_set_cursor_blinks(VteTerminal *terminal, gboolean blink); -gboolean vte_terminal_get_has_selection(VteTerminal *terminal); -gboolean vte_terminal_get_using_xft(VteTerminal *terminal); +void vte_terminal_set_scrollback_lines(VteTerminal *terminal, long lines); +void vte_terminal_set_word_chars(VteTerminal *terminal, const char *spec); void vte_terminal_im_append_menuitems(VteTerminal *terminal, GtkMenuShell *menushell); void vte_terminal_set_font(VteTerminal *terminal, const PangoFontDescription *font_desc); void vte_terminal_set_font_from_string(VteTerminal *terminal, const char *name); -void vte_terminal_set_scrollback_lines(VteTerminal *terminal, long lines); + +gboolean vte_terminal_get_has_selection(VteTerminal *terminal); +gboolean vte_terminal_get_using_xft(VteTerminal *terminal); +gboolean vte_terminal_is_word_char(VteTerminal *terminal, gunichar c); +const PangoFontDescription *vte_terminal_get_font(VteTerminal *terminal); + +VteTerminalSnapshot *vte_terminal_get_snapshot(VteTerminal *terminal); +void vte_terminal_free_snapshot(VteTerminalSnapshot *snapshot); G_END_DECLS diff --git a/src/vteaccess.c b/src/vteaccess.c new file mode 100644 index 0000000..6d1a7d4 --- /dev/null +++ b/src/vteaccess.c @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* VTE accessibility object. Based heavily on inspection of libzvt's + * accessibility code. */ + +#include "../config.h" +#include <iconv.h> +#include <atk/atk.h> +#include <gtk/gtk.h> +#include "vte.h" +#include "vteaccess.h" + +#define VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA "VteTerminalAccessiblePrivateData" +typedef struct _VteTerminalAccessiblePrivate { + gboolean snapshot_invalid; + VteTerminalSnapshot *snapshot; + GArray *snapshot_cells; + GArray *snapshot_linebreaks; + gint snapshot_caret; +} VteTerminalAccessiblePrivate; + +static VteTerminalAccessiblePrivate * +vte_terminal_accessible_new_private_data(void) +{ + VteTerminalAccessiblePrivate *priv; + priv = g_malloc0(sizeof(*priv)); + priv->snapshot = NULL; + priv->snapshot_cells = NULL; + priv->snapshot_linebreaks = NULL; + priv->snapshot_caret = 0; + priv->snapshot_invalid = TRUE; + return priv; +} + +static void +vte_terminal_accessible_update_private_data_if_needed(AtkObject *text) +{ + VteTerminal *terminal; + VteTerminalAccessiblePrivate *priv; + struct VteTerminalSnapshotCell cell; + int row, col, i, caret; + + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text)); + + /* Retrieve the private data structure. */ + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + g_return_if_fail(priv != NULL); + + /* If nothing's changed, just return immediately. */ + if (priv->snapshot_invalid == FALSE) { + return; + } + + /* Free the possibly-outdated snapshot. */ + if (priv->snapshot != NULL) { + vte_terminal_free_snapshot(priv->snapshot); + priv->snapshot = NULL; + } + if (priv->snapshot_cells != NULL) { + g_array_free(priv->snapshot_cells, FALSE); + priv->snapshot_cells = NULL; + } + if (priv->snapshot_linebreaks != NULL) { + g_array_free(priv->snapshot_linebreaks, FALSE); + priv->snapshot_linebreaks = NULL; + } + priv->snapshot_caret = 0; + + /* Get a new snapshot, and munge it into something that might be + * mistaken for a continuous-text display widget. */ + terminal = VTE_TERMINAL((GTK_ACCESSIBLE(text))->widget); + priv->snapshot = vte_terminal_get_snapshot(terminal); + if (priv->snapshot == NULL) { + /* Aaargh! We're screwed. */ + return; + } + + /* Get the addresses of each of the cells, and add them to a linear + * array of characters, tracking where line breaks occur, and setting + * the caret to point at the location where the cursor is. */ + priv->snapshot_cells = g_array_new(FALSE, TRUE, sizeof(cell)); + priv->snapshot_linebreaks = g_array_new(FALSE, TRUE, sizeof(int)); + caret = -1; + for (row = 0; priv->snapshot->contents[row] != NULL; row++) { + for (col = 0; + priv->snapshot->contents[row][col].c != 0; + col++) { + if ((row == priv->snapshot->cursor.y) && + (col == priv->snapshot->cursor.x)) { + caret = priv->snapshot_cells->len; + } + cell = priv->snapshot->contents[row][col]; + g_array_append_val(priv->snapshot_cells, cell); + + } + if ((row == priv->snapshot->cursor.y) && (caret == -1)) { + caret = priv->snapshot_cells->len; + } + i = row; + g_array_append_val(priv->snapshot_linebreaks, i); + } + if (caret == -1) { + caret = priv->snapshot_cells->len; + } + priv->snapshot_caret = caret; +} + +static void +vte_terminal_accessible_free_private_data(VteTerminalAccessiblePrivate *priv) +{ + g_return_if_fail(priv != NULL); + if (priv->snapshot != NULL) { + vte_terminal_free_snapshot(priv->snapshot); + priv->snapshot = NULL; + } + if (priv->snapshot_cells != NULL) { + g_array_free(priv->snapshot_cells, FALSE); + priv->snapshot_cells = NULL; + } + if (priv->snapshot_linebreaks != NULL) { + g_array_free(priv->snapshot_linebreaks, FALSE); + priv->snapshot_linebreaks = NULL; + } + g_free(priv); +} + +/* A signal handler to catch "contents_changed" and "cursor_moved" signals. */ +static void +vte_terminal_accessible_invalidate(VteTerminal *terminal, gpointer data) +{ + VteTerminalAccessiblePrivate *priv; + + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(data)); + g_return_if_fail(G_IS_OBJECT(data)); + + priv = g_object_get_data(G_OBJECT(data), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + g_return_if_fail(priv != NULL); + + priv->snapshot_invalid = TRUE; +} + +AtkObject * +vte_terminal_accessible_new(VteTerminal *terminal) +{ + GtkAccessible *access; + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); + access = GTK_ACCESSIBLE(g_object_new(VTE_TYPE_TERMINAL_ACCESSIBLE, + NULL)); + atk_object_initialize(ATK_OBJECT(access), G_OBJECT(terminal)); + access->widget = GTK_WIDGET(terminal); + + g_object_set_data(G_OBJECT(access), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA, + vte_terminal_accessible_new_private_data()); + g_signal_connect(G_OBJECT(terminal), "contents_changed", + GTK_SIGNAL_FUNC(vte_terminal_accessible_invalidate), + access); + g_signal_connect(G_OBJECT(terminal), "cursor_moved", + GTK_SIGNAL_FUNC(vte_terminal_accessible_invalidate), + access); + + return ATK_OBJECT(access); +} + +static gchar * +vte_terminal_accessible_get_text(AtkText *text, + gint start_offset, gint end_offset) +{ + VteTerminalAccessiblePrivate *priv; + gchar *buf, *p; + int i; + + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + + /* If the requested area is after all of the text, just return an + * empty string. */ + if (start_offset >= priv->snapshot_cells->len) { + return g_strdup(""); + } + + /* Allocate space to hold as many UTF-8 characters as we have + * unicode characters. */ + p = buf = g_malloc((end_offset - start_offset) * VTE_UTF8_BPC + 1); + for (i = start_offset; i < end_offset; i++) { + p += g_unichar_to_utf8(g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + i).c, + p); + } + *p = '\0'; + return buf; +} + +static gchar * +vte_terminal_accessible_get_text_somewhere(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint direction, + gint *start_offset, + gint *end_offset) +{ + VteTerminalAccessiblePrivate *priv; + gunichar c; + gboolean word, in_word; + int i, line; + + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + + switch (boundary_type) { + case ATK_TEXT_BOUNDARY_CHAR: + /* We're either looking at the character at this + * position, the one before it, or the one after it. */ + offset += direction; + *start_offset = MAX(offset, 0); + *end_offset = MIN(offset + 1, + priv->snapshot_cells->len); + break; + case ATK_TEXT_BOUNDARY_WORD_START: + case ATK_TEXT_BOUNDARY_WORD_END: + /* Find the wordstart before the requested point. */ + c = g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + offset).c; + word = in_word = !g_unichar_isspace(c); + *start_offset = offset; + for (i = offset; i >= 0; i--) { + c = g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + i).c; + if (word && g_unichar_isspace(c)) { + *start_offset = i + 1; + break; + } + if (i == 0) { + *start_offset = 0; + break; + } + word = g_unichar_isspace(c); + } + /* If we started in a word and we're looking for the + * word before this one, keep searching. */ + if (in_word && (direction == -1)) { + word = !g_unichar_isspace(c); + for (i = *start_offset; i >= 0; i--) { + c = g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + i).c; + if (word && g_unichar_isspace(c)) { + *start_offset = i + 1; + break; + } + if (i == 0) { + *start_offset = 0; + break; + } + } + } + /* If we're looking for the word after this one, + * search forward. */ + if (direction == 1) { + word = g_unichar_isspace(c); + for (i = *start_offset; i < priv->snapshot_cells->len; i--) { + c = g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + i).c; + if (!word && !g_unichar_isspace(c)) { + *start_offset = i; + break; + } + if (i == priv->snapshot_cells->len - 1) { + *start_offset = i + 1; + break; + } + } + } + /* Now find the end of this word. */ + word = TRUE; + *end_offset = *start_offset; + for (i = *start_offset; i < priv->snapshot_cells->len; i--) { + c = g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + i).c; + if (!word && !g_unichar_isspace(c)) { + *end_offset = i; + break; + } + if (i == priv->snapshot_cells->len - 1) { + *end_offset = i + 1; + break; + } + } + break; + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + /* Figure out which line we're on. If the end of the + * i'th line is after the offset, then i is the line + * we're looking at. */ + line = 0; + for (i = 0; i < priv->snapshot_cells->len; i++) { + if (g_array_index(priv->snapshot_linebreaks, + int, i) > offset) { + line = i; + break; + } + } + /* Perturb the line number to handle before/at/after. */ + line += direction; + line = MAX(0, line); + line = MIN(line, priv->snapshot_linebreaks->len - 1); + /* Read the offsets for this line. */ + if (line == 0) { + *start_offset = 0; + } else { + *start_offset = g_array_index(priv->snapshot_linebreaks, + int, + line - 1); + } + *end_offset = g_array_index(priv->snapshot_linebreaks, + int, + line); + break; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + case ATK_TEXT_BOUNDARY_SENTENCE_END: + /* This doesn't make sense. Fall through. */ + default: + *start_offset = *end_offset = 0; + break; + } + return vte_terminal_accessible_get_text(text, + *start_offset, + *end_offset); +} + +static gchar * +vte_terminal_accessible_get_text_before_offset(AtkText *text, gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + return vte_terminal_accessible_get_text_somewhere(text, + offset, + boundary_type, + -1, + start_offset, + end_offset); +} + +static gchar * +vte_terminal_accessible_get_text_after_offset(AtkText *text, gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + return vte_terminal_accessible_get_text_somewhere(text, + offset, + boundary_type, + 1, + start_offset, + end_offset); +} + +static gchar * +vte_terminal_accessible_get_text_at_offset(AtkText *text, gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + return vte_terminal_accessible_get_text_somewhere(text, + offset, + boundary_type, + 0, + start_offset, + end_offset); +} + +static gunichar +vte_terminal_accessible_get_character_at_offset(AtkText *text, gint offset) +{ + VteTerminalAccessiblePrivate *priv; + + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + + return g_array_index(priv->snapshot_cells, + struct VteTerminalSnapshotCell, + offset).c; +} + +static gint +vte_terminal_accessible_get_caret_offset(AtkText *text) +{ + VteTerminalAccessiblePrivate *priv; + + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + + return priv->snapshot_caret; +} + +static AtkAttributeSet * +vte_terminal_accessible_get_run_attributes(AtkText *text, gint offset, + gint *start_offset, gint *end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME */ + return NULL; +} + +static AtkAttributeSet * +vte_terminal_accessible_get_default_attributes(AtkText *text) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME */ + return NULL; +} + +static void +vte_terminal_accessible_get_character_extents(AtkText *text, gint offset, + gint *x, gint *y, + gint *width, gint *height, + AtkCoordType coords) +{ + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text)); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME */ +} + +static gint +vte_terminal_accessible_get_character_count(AtkText *text) +{ + VteTerminalAccessiblePrivate *priv; + + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + + priv = g_object_get_data(G_OBJECT(text), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + + return priv->snapshot_cells->len; +} + +static gint +vte_terminal_accessible_get_offset_at_point(AtkText *text, + gint x, gint y, + AtkCoordType coords) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), 0); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME */ + return 0; +} + +static gint +vte_terminal_accessible_get_n_selections(AtkText *text) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), 0); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME? */ + return 0; +} + +static gchar * +vte_terminal_accessible_get_selection(AtkText *text, gint selection_number, + gint *start_offset, gint *end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), NULL); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME? */ + return NULL; +} + +static gboolean +vte_terminal_accessible_add_selection(AtkText *text, + gint start_offset, gint end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), FALSE); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME? */ + return FALSE; +} + +static gboolean +vte_terminal_accessible_remove_selection(AtkText *text, + gint selection_number) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), FALSE); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME? */ + return FALSE; +} + +static gboolean +vte_terminal_accessible_set_selection(AtkText *text, gint selection_number, + gint start_offset, gint end_offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), FALSE); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* FIXME? */ + return FALSE; +} + +static gboolean +vte_terminal_accessible_set_caret_offset(AtkText *text, gint offset) +{ + g_return_val_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text), FALSE); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); + /* Whoa, very not allowed. */ + return FALSE; +} + +static void +vte_terminal_accessible_text_changed(AtkText *text, gint position, gint length) +{ + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text)); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); +} + +static void +vte_terminal_accessible_text_caret_moved(AtkText *text, gint location) +{ + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text)); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); +} + +static void +vte_terminal_accessible_text_selection_changed(AtkText *text) +{ + g_return_if_fail(VTE_IS_TERMINAL_ACCESSIBLE(text)); + vte_terminal_accessible_update_private_data_if_needed(ATK_OBJECT(text)); +} + +static void +vte_terminal_accessible_text_init(gpointer iface, gpointer data) +{ + AtkTextIface* text; + g_return_if_fail(ATK_IS_TEXT(iface)); + text = iface; + text->get_text = vte_terminal_accessible_get_text; + text->get_text_after_offset = vte_terminal_accessible_get_text_after_offset; + text->get_text_at_offset = vte_terminal_accessible_get_text_at_offset; + text->get_character_at_offset = vte_terminal_accessible_get_character_at_offset; + text->get_text_before_offset = vte_terminal_accessible_get_text_before_offset; + text->get_caret_offset = vte_terminal_accessible_get_caret_offset; + text->get_run_attributes = vte_terminal_accessible_get_run_attributes; + text->get_default_attributes = vte_terminal_accessible_get_default_attributes; + text->get_character_extents = vte_terminal_accessible_get_character_extents; + text->get_character_count = vte_terminal_accessible_get_character_count; + text->get_offset_at_point = vte_terminal_accessible_get_offset_at_point; + text->get_n_selections = vte_terminal_accessible_get_n_selections; + text->get_selection = vte_terminal_accessible_get_selection; + text->add_selection = vte_terminal_accessible_add_selection; + text->remove_selection = vte_terminal_accessible_remove_selection; + text->set_selection = vte_terminal_accessible_set_selection; + text->set_caret_offset = vte_terminal_accessible_set_caret_offset; + text->text_changed = vte_terminal_accessible_text_changed; + text->text_caret_moved = vte_terminal_accessible_text_caret_moved; + text->text_selection_changed = vte_terminal_accessible_text_selection_changed; +} + +static void +vte_terminal_accessible_text_finalize(gpointer iface, gpointer data) +{ + GtkAccessibleClass *accessible_class; + VteTerminalAccessiblePrivate *priv; + accessible_class = g_type_class_peek(GTK_TYPE_ACCESSIBLE); + + /* Free the private data. */ + priv = g_object_get_data(G_OBJECT(iface), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA); + if (priv) { + vte_terminal_accessible_free_private_data(priv); + g_object_set_data(G_OBJECT(iface), + VTE_TERMINAL_ACCESSIBLE_PRIVATE_DATA, + NULL); + } + + if ((G_OBJECT_CLASS(accessible_class))->finalize) { + (G_OBJECT_CLASS(accessible_class))->finalize(iface); + } +} + +static void +vte_terminal_accessible_class_init(gpointer *klass) +{ + GObjectClass *gobject_class; + AtkObjectClass *atk_object_class; + GtkAccessibleClass *gtk_accessible_class; + GInterfaceInfo text; + + gobject_class = G_OBJECT_CLASS(klass); + atk_object_class = ATK_OBJECT_CLASS(klass); + gtk_accessible_class = GTK_ACCESSIBLE_CLASS(klass); + + /* Add a text interface to this object class. */ + text.interface_init = vte_terminal_accessible_text_init; + text.interface_finalize = vte_terminal_accessible_text_finalize; + text.interface_data = NULL; + g_type_add_interface_static(VTE_TYPE_TERMINAL_ACCESSIBLE, + ATK_TYPE_TEXT, + &text); +} + +static void +vte_terminal_accessible_init(gpointer *instance, gpointer *klass) +{ + /* Mark the role this object plays. The rest of the work is handled + * by the AtkText interface the object class exports. */ + atk_object_set_role(ATK_OBJECT(instance), ATK_ROLE_TERMINAL); +} + +GtkType +vte_terminal_accessible_get_type(void) +{ + static GtkType terminal_accessible_type = 0; + static const GTypeInfo terminal_accessible_info = { + sizeof(VteTerminalAccessibleClass), + (GBaseInitFunc)NULL, + (GBaseFinalizeFunc)NULL, + + (GClassInitFunc)vte_terminal_accessible_class_init, + (GClassFinalizeFunc)NULL, + (gconstpointer)NULL, + + sizeof(VteTerminal), + 0, + (GInstanceInitFunc)vte_terminal_accessible_init, + + (GTypeValueTable*)NULL, + }; + + if (terminal_accessible_type == 0) { + terminal_accessible_type = g_type_register_static(GTK_TYPE_ACCESSIBLE, + "VteTerminalAccessible", + &terminal_accessible_info, + 0); + } + + return terminal_accessible_type; +} diff --git a/src/vteaccess.h b/src/vteaccess.h new file mode 100644 index 0000000..6ee06f9 --- /dev/null +++ b/src/vteaccess.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef vteaccess_h_included +#define vteaccess_h_included + +#ident "$Id$" + +#include <glib.h> +#include <gtk/gtk.h> +#include "vte.h" + +G_BEGIN_DECLS + +/* The terminal accessibility object itself. */ +typedef struct _VteTerminalAccessible { + AtkObject object; +} VteTerminalAccessible; + +/* The object's class structure. */ +typedef struct _VteTerminalAccessibleClass { + /*< public > */ + /* Inherited parent class. */ + AtkObjectClass parent_class; +} VteTerminalAccessibleClass; + +/* The object's type. */ +GtkType vte_terminal_accessible_get_type(void); + +#define VTE_TYPE_TERMINAL_ACCESSIBLE (vte_terminal_accessible_get_type()) +#define VTE_TERMINAL_ACCESSIBLE(obj) (GTK_CHECK_CAST((obj),\ + VTE_TYPE_TERMINAL_ACCESSIBLE,\ + VteTerminalAccessible)) +#define VTE_TERMINAL_ACCESSIBLE_CLASS(klass) GTK_CHECK_CLASS_CAST((klass),\ + VTE_TYPE_TERMINAL_ACCESSIBLE,\ + VteTerminalAccessibleClass) +#define VTE_IS_TERMINAL_ACCESSIBLE(obj) GTK_CHECK_TYPE((obj),\ + VTE_TYPE_TERMINAL_ACCESSIBLE) +#define VTE_IS_TERMINAL_ACCESSIBLE_CLASS(klass) GTK_CHECK_CLASS_TYPE((klass),\ + VTE_TYPE_TERMINAL_ACCESSIBLE) +#define VTE_TERMINAL_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_TERMINAL_ACCESSIBLE, VteTerminalAccessibleClass)) + + +AtkObject *vte_terminal_accessible_new(VteTerminal *terminal); + +G_END_DECLS + +#endif @@ -1,5 +1,5 @@ Name: vte -Version: 0.2.2 +Version: 0.3 Release: 1 Summary: An experimental terminal emulator. License: LGPL @@ -52,6 +52,13 @@ make install DESTDIR=$RPM_BUILD_ROOT %{_libdir}/pkgconfig/* %changelog +* Tue Apr 30 2002 Nalin Dahyabhai <nalin@redhat.com> 0.3-1 +- add an accessiblity object + +* Mon Apr 29 2002 Nalin Dahyabhai <nalin@redhat.com> 0.2.3-1 +- fix color resetting +- fix idle handlers not being disconnected + * Mon Apr 29 2002 Nalin Dahyabhai <nalin@redhat.com> 0.2.2-1 - bug fixes |