diff options
author | Nalin Dahyabhai <nalin@src.gnome.org> | 2002-10-25 22:07:11 +0000 |
---|---|---|
committer | Nalin Dahyabhai <nalin@src.gnome.org> | 2002-10-25 22:07:11 +0000 |
commit | 25f084882412a537c4a3cc6993027f9707d8cc81 (patch) | |
tree | d87932bf93020b30a2d37807922d9ee53de0f55b | |
parent | e600d5ea308a700166c9a59e9e88e80ac7f91263 (diff) |
snip off final newlines when we're matching, they're usually not wanted.
* src/vte.c (vte_terminal_match_check_internal): snip off final newlines when
we're matching, they're usually not wanted.
* src/pty.c, src/pty.h: replace the simpler pty_open() function with a logging
version, adding the ability to specify a startup directory (Red Hat
#76529).
* src/vte.c, src/vte.h: replace the simpler fork_command() function with a
logging version, adding the ability to specify a startup directory (Red
Hat #76529).
* src/vteapp.c: modify call to handle new fork_command().
* python/vte.defs: update as above.
* python/vte.override: modify call to handle new fork_command(), adding an
optional "directory" argument.
* src/vte.c: refactor the selection code, cleaning up when selection is
cleared/started/extended (#95783). Grab focus whenever we get button
press or release or motion events.
* src/vte.c: rework how wide characters are stored to allow storing tabs
(#95958).
* python/vte.override: wrap vte_terminal_get_text() and
vte_terminal_get_text_range(). Based on patch from ha shao (#96230).
* src/vte.c, src/vte.h: add a user pointer argument to get_text callbacks
(#96230).
* src/Makefile.am: bump shared library version because we changed a public
function's signature. Take the opportunity to replace padding fields
which had previously been used up.
-rw-r--r-- | ChangeLog | 36 | ||||
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | doc/reference/tmpl/pty.sgml | 36 | ||||
-rw-r--r-- | doc/reference/tmpl/vte-unused.sgml | 73 | ||||
-rw-r--r-- | doc/reference/tmpl/vte.sgml | 15 | ||||
-rw-r--r-- | doc/reference/vte-docs.sgml | 2 | ||||
-rw-r--r-- | doc/reference/vte-sections.txt | 8 | ||||
-rwxr-xr-x | python/cat.py | 13 | ||||
-rw-r--r-- | python/vte.defs | 12 | ||||
-rw-r--r-- | python/vte.override | 248 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/debug.c | 3 | ||||
-rw-r--r-- | src/debug.h | 3 | ||||
-rw-r--r-- | src/pty.c | 94 | ||||
-rw-r--r-- | src/pty.h | 22 | ||||
-rw-r--r-- | src/vte.c | 1912 | ||||
-rw-r--r-- | src/vte.h | 25 | ||||
-rw-r--r-- | src/vteaccess.c | 4 | ||||
-rw-r--r-- | src/vteapp.c | 12 | ||||
-rw-r--r-- | vte.spec | 5 |
20 files changed, 1535 insertions, 995 deletions
@@ -1,5 +1,39 @@ -2002-10-21 jacob berkman <jacob@ximian.com> +2002-10-25 nalin + * src/vte.c (vte_terminal_match_check_internal): snip off final + newlines when we're matching, they're usually not wanted. + +2002-10-24 nalin + * src/pty.c, src/pty.h: replace the simpler pty_open() function with + a logging version, adding the ability to specify a startup directory + (Red Hat #76529). + * src/vte.c, src/vte.h: replace the simpler fork_command() function with + a logging version, adding the ability to specify a startup directory + (Red Hat #76529). + * src/vteapp.c: modify call to handle new fork_command(). + * python/vte.defs: update as above. + * python/vte.override: modify call to handle new fork_command(), adding + an optional "directory" argument. + +2002-10-23 nalin + * src/vte.c: refactor the selection code, cleaning up when selection is + cleared/started/extended (#95783). + +2002-10-22 nalin + * src/vte.c: rework how wide characters are stored to allow storing + tabs (#95958). + +2002-10-21 nalin + * python/vte.override: wrap vte_terminal_get_text() and + vte_terminal_get_text_range(). Based on patch from ha shao (#96230). + +2002-10-21 nalin + * src/vte.c, src/vte.h: add a user pointer argument to get_text + callbacks (#96230). + * src/Makefile.am: bump shared library version because we changed + a public function's signature. Take the opportunity to replace padding + fields which had previously been used up. +2002-10-21 jacob berkman <jacob@ximian.com> * src/Makefile.am (EXTRA_DIST): include decset, osc, and window 2002-10-18 nalin diff --git a/Makefile.am b/Makefile.am index e305aaa..a7098fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,9 +6,12 @@ pkgconfig_DATA = vte.pc CVSTAG=$(shell echo $(PACKAGE)-$(VERSION) | sed 's,[\.\-],_,g') -reallytag: +forcetag: cvs tag -cFR $(CVSTAG) . +reallytag: + cvs tag -cR $(CVSTAG) . + tag: $(top_srcdir)/autogen.sh $(MAKE) reallytag diff --git a/doc/reference/tmpl/pty.sgml b/doc/reference/tmpl/pty.sgml index 554308a..a50efe8 100644 --- a/doc/reference/tmpl/pty.sgml +++ b/doc/reference/tmpl/pty.sgml @@ -16,39 +16,3 @@ pseudo-terminals and to resize psuedo-terminals. </para> -<!-- ##### FUNCTION vte_pty_open ##### --> -<para> - -</para> - -@child: -@env_add: -@command: -@argv: -@columns: -@rows: -@Returns: - - -<!-- ##### FUNCTION vte_pty_get_size ##### --> -<para> - -</para> - -@master: -@columns: -@rows: -@Returns: - - -<!-- ##### FUNCTION vte_pty_set_size ##### --> -<para> - -</para> - -@master: -@columns: -@rows: -@Returns: - - diff --git a/doc/reference/tmpl/vte-unused.sgml b/doc/reference/tmpl/vte-unused.sgml index e69de29..9eefbf6 100644 --- a/doc/reference/tmpl/vte-unused.sgml +++ b/doc/reference/tmpl/vte-unused.sgml @@ -0,0 +1,73 @@ +<!-- ##### SECTION ./tmpl/pty.sgml:Long_Description ##### --> +<para> +The terminal widget uses these functions to start commands with new controlling +pseudo-terminals and to resize psuedo-terminals. +</para> + + +<!-- ##### SECTION ./tmpl/pty.sgml:See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION ./tmpl/pty.sgml:Short_Description ##### --> +Functions for starting a new process on a new pseudo-terminal and for +manipulating psuedo-terminals. + + +<!-- ##### SECTION ./tmpl/pty.sgml:Title ##### --> +pty + + +<!-- ##### FUNCTION vte_pty_get_size ##### --> +<para> + +</para> + +@master: +@columns: +@rows: +@Returns: + +<!-- ##### FUNCTION vte_pty_open ##### --> +<para> + +</para> + +@child: +@env_add: +@command: +@argv: +@directory: +@columns: +@rows: +@lastlog: +@utmp: +@wtmp: +@Returns: + +<!-- ##### FUNCTION vte_pty_set_size ##### --> +<para> + +</para> + +@master: +@columns: +@rows: +@Returns: + +<!-- ##### FUNCTION vte_terminal_fork_logged_command ##### --> +<para> + +</para> + +@terminal: +@command: +@argv: +@envv: +@lastlog: +@utmp: +@wtmp: +@Returns: + diff --git a/doc/reference/tmpl/vte.sgml b/doc/reference/tmpl/vte.sgml index bc70bdc..163c586 100644 --- a/doc/reference/tmpl/vte.sgml +++ b/doc/reference/tmpl/vte.sgml @@ -72,18 +72,7 @@ A VteTerminal is a terminal emulator implemented as a GTK2 widget. @command: @argv: @envv: -@Returns: - - -<!-- ##### FUNCTION vte_terminal_fork_logged_command ##### --> -<para> - -</para> - -@terminal: -@command: -@argv: -@envv: +@directory: @lastlog: @utmp: @wtmp: @@ -447,6 +436,7 @@ A VteTerminal is a terminal emulator implemented as a GTK2 widget. @terminal: @is_selected: +@data: @attributes: @Returns: @@ -462,6 +452,7 @@ A VteTerminal is a terminal emulator implemented as a GTK2 widget. @end_row: @end_col: @is_selected: +@data: @attributes: @Returns: diff --git a/doc/reference/vte-docs.sgml b/doc/reference/vte-docs.sgml index 641e1c2..e8fc617 100644 --- a/doc/reference/vte-docs.sgml +++ b/doc/reference/vte-docs.sgml @@ -3,7 +3,6 @@ <!ENTITY vte-vteaccess SYSTEM "sgml/vteaccess.sgml"> <!ENTITY vte-VteReaper SYSTEM "sgml/reaper.sgml"> <!ENTITY vte-caps SYSTEM "sgml/caps.sgml"> -<!ENTITY vte-pty SYSTEM "sgml/pty.sgml"> <!ENTITY vte-termcap SYSTEM "sgml/termcap.sgml"> <!ENTITY vte-trie SYSTEM "sgml/trie.sgml"> <!ENTITY vte-debug SYSTEM "sgml/debug.sgml"> @@ -21,6 +20,5 @@ &vte-VteTerminal; &vte-vteaccess; &vte-VteReaper; - &vte-pty; </chapter> </book> diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt index 689f3a9..32ba78f 100644 --- a/doc/reference/vte-sections.txt +++ b/doc/reference/vte-sections.txt @@ -7,7 +7,6 @@ VteTerminal vte_terminal_new vte_terminal_im_append_menuitems vte_terminal_fork_command -vte_terminal_fork_logged_command vte_terminal_feed vte_terminal_feed_child vte_terminal_copy_clipboard @@ -110,10 +109,3 @@ VTE_REAPER_CLASS VTE_IS_REAPER_CLASS VTE_REAPER_GET_CLASS </SECTION> - -<SECTION> -<FILE>pty</FILE> -vte_pty_open -vte_pty_get_size -vte_pty_set_size -</SECTION> diff --git a/python/cat.py b/python/cat.py index ce8f553..c870299 100755 --- a/python/cat.py +++ b/python/cat.py @@ -10,9 +10,16 @@ def main_quit(object, *args): def commit_cb(object, *args): (text, length) = args - # Echo the text input by the user to stdout. - sys.stdout.write(text) - sys.stdout.flush() + # Echo the text input by the user to stdout. Note that the string's + # length isn't always going to be right. + if (0): + sys.stdout.write(text) + sys.stdout.flush() + else: + # Test the get_text() function. + for line in (string.splitfields(object.get_text(),"\n")): + if (line.__len__() > 0): + print line # Also display it. object.feed(text, length) diff --git a/python/vte.defs b/python/vte.defs index 5d44fb3..4d186e4 100644 --- a/python/vte.defs +++ b/python/vte.defs @@ -48,17 +48,7 @@ '("const-char*" "command") '("char**" "argv") '("char**" "envv") - ) -) - -(define-method fork_logged_command - (of-object "VteTerminal") - (c-name "vte_terminal_fork_logged_command") - (return-type "pid_t") - (parameters - '("const-char*" "command") - '("char**" "argv") - '("char**" "envv") + '("const-char*" "directory") '("gboolean" "lastlog") '("gboolean" "utmp") '("gboolean" "wtmp") diff --git a/python/vte.override b/python/vte.override index 76d3169..8cc2d8f 100644 --- a/python/vte.override +++ b/python/vte.override @@ -3,6 +3,7 @@ headers #include <Python.h> #include <pygtk/pygtk.h> +#include <pygobject.h> #include <gtk/gtk.h> #include "../src/vte.h" %% @@ -10,24 +11,26 @@ import gtk.gdk.Pixbuf as PyGdkPixbuf_Type import gtk.MenuShell as PyGtkMenuShell_Type import gtk.Widget as PyGtkWidget_Type %% -ignore vte_terminal_get_text -ignore vte_terminal_get_text_range -%% override vte_terminal_fork_command kwargs + static PyObject * _wrap_vte_terminal_fork_command(PyGObject * self, PyObject * args, PyObject * kwargs) { gchar **argv = NULL, **envv = NULL; - gchar *command = NULL; - static char *kwlist[] = { "command", "argv", "envv", NULL }; - PyObject *py_argv = NULL, *py_envv = NULL; + gchar *command = NULL, *directory = NULL; + static char *kwlist[] = { "command", "argv", "envv", "directory", + "loglastlog", "logutmp", "logwtmp", + NULL }; + PyObject *py_argv = NULL, *py_envv = NULL, + *loglastlog = NULL, *logutmp = NULL, *logwtmp = NULL; int i, n_args, n_envs; pid_t pid; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sOO:fork_command", - kwlist, &command, &py_argv, - &py_envv)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sOOsOOO:fork_command", + kwlist, &command, &py_argv, &py_envv, + &directory, + &loglastlog, &logutmp, &logwtmp)) { return NULL; } @@ -67,7 +70,13 @@ _wrap_vte_terminal_fork_command(PyGObject * self, PyObject * args, } pid = vte_terminal_fork_command(VTE_TERMINAL(self->obj), - command, argv, envv); + command, argv, envv, directory, + (loglastlog != NULL) && + PyObject_IsTrue(loglastlog), + (logutmp != NULL) && + PyObject_IsTrue(logutmp), + (logwtmp != NULL) && + PyObject_IsTrue(logwtmp)); if (argv) { g_free(argv); @@ -76,3 +85,222 @@ _wrap_vte_terminal_fork_command(PyGObject * self, PyObject * args, return PyInt_FromLong(pid); } %% +override vte_terminal_get_text kwargs + +static gboolean +call_callback(VteTerminal *terminal, glong column, glong row, gpointer data) +{ + PyObject *cb, *self, *args, *result; + gboolean ret; + int i; + if (!PyArg_ParseTuple(data, "|O:call_callback", &args)) { + return FALSE; + } + args = PyList_New(0); + cb = PySequence_GetItem(args, 0); /* INCREFs */ + Py_DECREF(cb); + self = PySequence_GetItem(args, 1); /* INCREFs */ + Py_DECREF(self); + PyList_Append(args, self); + PyList_Append(args, PyInt_FromLong(column)); + PyList_Append(args, PyInt_FromLong(row)); + for (i = 2; i < PySequence_Length(args); i++) { + PyObject *item = PySequence_GetItem(args, i); + Py_DECREF(item); + PyList_Append(args, item); + } + result = PyObject_CallObject(cb, args); + ret = (result && PyObject_IsTrue(result)); + Py_DECREF(args); + Py_DECREF(result); + return ret; +} + +static gboolean +always_true(VteTerminal *terminal, glong row, glong column, gpointer data) +{ + return TRUE; +} +static PyObject * +_wrap_vte_terminal_get_text(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "callback", "attributes", "data", NULL }; + PyObject *callback = NULL, *do_attr = NULL, *data = NULL; + PyObject *callback_and_args = NULL; + GArray *attrs = NULL; + PyObject *text; + PyObject *py_attrs; + int count; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO:terminal_get_text", + kwlist, &callback, &do_attr, &args)) { + return NULL; + } + + if (do_attr != NULL && do_attr != Py_None) { + attrs = g_array_new(FALSE, TRUE, sizeof(struct vte_char_attributes)); + } else { + attrs = NULL; + } + + if ((callback != NULL) && (callback != Py_None)) { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "1st argument not callable."); + if (attrs) { + g_array_free(attrs, TRUE); + } + return NULL; + } + } else { + callback = NULL; + } + + if (callback != NULL) { + callback_and_args = PyList_New(0); + PyList_Append(callback_and_args, callback); + PyList_Append(callback_and_args, (PyObject*) self); + if (PyList_Check(data)) { + for (count = 0; count < PyList_Size(data); count++) { + PyList_Append(callback_and_args, PyList_GetItem(data, count)); + } + } else { + PyList_Append(callback_and_args, data); + } + } + + text = PyString_FromString(vte_terminal_get_text(VTE_TERMINAL(self->obj), + callback ? + call_callback : + always_true, + callback ? + callback_and_args : + NULL, + attrs)); + Py_XDECREF(callback_and_args); + + if (attrs) { + py_attrs = PyTuple_New(attrs->len); + for (count = 0; count < attrs->len; count++) { + struct vte_char_attributes *cht; + PyObject *py_char_attr; + PyObject *py_gdkcolor; + + cht = &g_array_index(attrs, struct vte_char_attributes, count); + py_char_attr = PyDict_New(); + PyDict_SetItemString(py_char_attr, "row", PyInt_FromLong(cht->row)); + PyDict_SetItemString(py_char_attr, "column", PyInt_FromLong(cht->column)); + + py_gdkcolor = pyg_boxed_new(GDK_TYPE_COLOR, &cht->fore, TRUE, TRUE); + PyDict_SetItemString(py_char_attr, "fore", py_gdkcolor); + py_gdkcolor = pyg_boxed_new(GDK_TYPE_COLOR, &cht->back, TRUE, TRUE); + PyDict_SetItemString(py_char_attr, "back", py_gdkcolor); + + PyDict_SetItemString(py_char_attr, "underline", + PyInt_FromLong(cht->underline)); + PyDict_SetItemString(py_char_attr, "alternate", + PyInt_FromLong(cht->alternate)); + + PyTuple_SetItem(py_attrs, count, py_char_attr); + } + g_array_free(attrs, TRUE); + return Py_BuildValue("(OO)", text, py_attrs); + } else { + return Py_BuildValue("O", text); + } +} +%% +override vte_terminal_get_text_range kwargs +static PyObject * +_wrap_vte_terminal_get_text_range(PyGObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "callback", "start_row", "start_col", "end_row", + "end_col", "attributes", "data", NULL }; + PyObject *callback = NULL, *do_attr = NULL, *data = NULL; + glong start_row, start_col, end_row, end_col; + PyObject *callback_and_args = NULL; + GArray *attrs = NULL; + PyObject *text; + PyObject *py_attrs; + int count; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|llllOOO:terminal_get_text", + kwlist, + &start_row, &start_col, &end_row, &end_col, + &callback, &do_attr, &args)) { + return NULL; + } + + if (do_attr != NULL && do_attr != Py_None) { + attrs = g_array_new(FALSE, TRUE, sizeof(struct vte_char_attributes)); + } else { + attrs = NULL; + } + + if ((callback != NULL) && (callback != Py_None)) { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "1st argument not callable."); + if (attrs) { + g_array_free(attrs, TRUE); + } + return NULL; + } + } else { + callback = NULL; + } + + if (callback != NULL) { + callback_and_args = PyList_New(0); + PyList_Append(callback_and_args, callback); + PyList_Append(callback_and_args, (PyObject*) self); + if (PyList_Check(data)) { + for (count = 0; count < PyList_Size(data); count++) { + PyList_Append(callback_and_args, PyList_GetItem(data, count)); + } + } else { + PyList_Append(callback_and_args, data); + } + } + + text = PyString_FromString(vte_terminal_get_text_range(VTE_TERMINAL(self->obj), + start_row, start_col, + end_row, end_col, + callback ? + call_callback : + always_true, + callback ? + callback_and_args : + NULL, + attrs)); + Py_XDECREF(callback_and_args); + + if (attrs) { + py_attrs = PyTuple_New(attrs->len); + for (count = 0; count < attrs->len; count++) { + struct vte_char_attributes *cht; + PyObject *py_char_attr; + PyObject *py_gdkcolor; + + cht = &g_array_index(attrs, struct vte_char_attributes, count); + py_char_attr = PyDict_New(); + PyDict_SetItemString(py_char_attr, "row", PyInt_FromLong(cht->row)); + PyDict_SetItemString(py_char_attr, "column", PyInt_FromLong(cht->column)); + + py_gdkcolor = pyg_boxed_new(GDK_TYPE_COLOR, &cht->fore, TRUE, TRUE); + PyDict_SetItemString(py_char_attr, "fore", py_gdkcolor); + py_gdkcolor = pyg_boxed_new(GDK_TYPE_COLOR, &cht->back, TRUE, TRUE); + PyDict_SetItemString(py_char_attr, "back", py_gdkcolor); + + PyDict_SetItemString(py_char_attr, "underline", + PyInt_FromLong(cht->underline)); + PyDict_SetItemString(py_char_attr, "alternate", + PyInt_FromLong(cht->alternate)); + + PyTuple_SetItem(py_attrs, count, py_char_attr); + } + g_array_free(attrs, TRUE); + return Py_BuildValue("(OO)", text, py_attrs); + } else { + return Py_BuildValue("O", text); + } +} +%% diff --git a/src/Makefile.am b/src/Makefile.am index c45df29..7792c10 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,7 +48,7 @@ libvte_la_SOURCES = \ vteaccess.h libvte_la_LIBADD = @LIBS@ @VTE_LIBS@ @X_LIBS@ -libvte_la_LDFLAGS = -version-info 4:0:1 +libvte_la_LDFLAGS = -version-info 4:0:0 CLEANFILES = marshal.c marshal.h diff --git a/src/debug.c b/src/debug.c index 3f599ae..6c30eda 100644 --- a/src/debug.c +++ b/src/debug.c @@ -64,6 +64,9 @@ _vte_debug_parse_string(const char *string) } else if (g_ascii_strcasecmp(flags[i], "PTY") == 0) { _vte_debug_flags |= VTE_DEBUG_PTY; + } else + if (g_ascii_strcasecmp(flags[i], "CURSOR") == 0) { + _vte_debug_flags |= VTE_DEBUG_CURSOR; } } g_strfreev(flags); diff --git a/src/debug.h b/src/debug.h index de1efd5..a85f304 100644 --- a/src/debug.h +++ b/src/debug.h @@ -35,7 +35,8 @@ typedef enum { VTE_DEBUG_SELECTION = 1 << 6, VTE_DEBUG_SUBSTITUTION = 1 << 7, VTE_DEBUG_RING = 1 << 8, - VTE_DEBUG_PTY = 1 << 9 + VTE_DEBUG_PTY = 1 << 9, + VTE_DEBUG_CURSOR = 1 << 10 } VteDebugFlags; void _vte_debug_parse_string(const char *string); @@ -201,7 +201,8 @@ _vte_pty_pipe_open_bi(int *a, int *b, int *c, int *d) * terminal. */ static int _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer, - char **env_add, const char *command, char **argv) + char **env_add, const char *command, char **argv, + const char *directory) { int i; char c; @@ -261,6 +262,11 @@ _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer, * weird things to them. */ _vte_pty_reset_signal_handlers(); + /* Change to the requested directory. */ + if (directory != NULL) { + chdir(directory); + } + /* Signal to the parent that we've finished setting things up by * sending an arbitrary byte over the status pipe and waiting for * a response. This synchronization step ensures that the pty is @@ -310,6 +316,7 @@ _vte_pty_run_on_pty(int fd, int ready_reader, int ready_writer, static int _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, const char *command, char **argv, + const char *directory, int columns, int rows, pid_t *child) { int fd, i; @@ -353,7 +360,7 @@ _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, fprintf(stderr, "Parent received child-ready.\n"); } #endif - vte_pty_set_size(parent_fd, columns, rows); + _vte_pty_set_size(parent_fd, columns, rows); #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_PTY)) { fprintf(stderr, "Parent sending parent-ready.\n"); @@ -389,7 +396,7 @@ _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, return -1; } return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1], - env_add, command, argv); + env_add, command, argv, directory); } /* Fork off a child (storing its PID in child), and exec the named command @@ -397,6 +404,7 @@ _vte_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, static int _vte_pty_fork_on_pty_fd(int fd, char **env_add, const char *command, char **argv, + const char *directory, int columns, int rows, pid_t *child) { int i; @@ -441,7 +449,7 @@ _vte_pty_fork_on_pty_fd(int fd, char **env_add, fprintf(stderr, "Parent received child-ready.\n"); } #endif - vte_pty_set_size(fd, columns, rows); + _vte_pty_set_size(fd, columns, rows); #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_PTY)) { fprintf(stderr, "Parent sending parent-ready.\n"); @@ -484,7 +492,7 @@ _vte_pty_fork_on_pty_fd(int fd, char **env_add, } return _vte_pty_run_on_pty(fd, ready_b[0], ready_a[1], - env_add, command, argv); + env_add, command, argv, directory); } /** @@ -499,7 +507,7 @@ _vte_pty_fork_on_pty_fd(int fd, char **env_add, * Returns: 0 on success, -1 on failure. */ int -vte_pty_set_size(int master, int columns, int rows) +_vte_pty_set_size(int master, int columns, int rows) { struct winsize size; int ret; @@ -535,7 +543,7 @@ vte_pty_set_size(int master, int columns, int rows) * Returns: 0 on success, -1 on failure. */ int -vte_pty_get_size(int master, int *columns, int *rows) +_vte_pty_get_size(int master, int *columns, int *rows) { struct winsize size; int ret; @@ -638,8 +646,8 @@ _vte_pty_unlockpt(int fd) static int _vte_pty_open_unix98(pid_t *child, char **env_add, - const char *command, char **argv, - int columns, int rows) + const char *command, char **argv, + const char *directory, int columns, int rows) { int fd; char *buf; @@ -666,7 +674,8 @@ _vte_pty_open_unix98(pid_t *child, char **env_add, } else { /* Start up a child process with the given command. */ if (_vte_pty_fork_on_pty_name(buf, fd, env_add, command, - argv, columns, rows, + argv, directory, + columns, rows, child) != 0) { close(fd); fd = -1; @@ -809,6 +818,7 @@ _vte_pty_start_helper(void) static int _vte_pty_open_with_helper(pid_t *child, char **env_add, const char *command, char **argv, + const char *directory, int columns, int rows, int op) { GnomePtyOps ops; @@ -864,7 +874,8 @@ _vte_pty_open_with_helper(pid_t *child, char **env_add, tag); /* Start up a child process with the given command. */ if (_vte_pty_fork_on_pty_fd(childfd, env_add, command, - argv, columns, rows, child) != 0) { + argv, directory, + columns, rows, child) != 0) { close(parentfd); close(childfd); return -1; @@ -881,24 +892,26 @@ _vte_pty_open_with_helper(pid_t *child, char **env_add, * @env_add: a list of environment variables to add to the child's environment * @command: name of the binary to run * @argv: arguments to pass to @command + * @directory: directory to start the new command in, or NULL * @columns: desired window columns * @rows: desired window rows * @lastlog: TRUE if the lastlog should be updated * @utmp: TRUE if the utmp or utmpx log should be updated * @wtmp: TRUE if the wtmp or wtmpx log should be updated * - * Starts a new copy of @command running under a psuedo-terminal, with window - * size set to @rows x @columns and variables in @env_add added to its - * environment. If any combination of @lastlog, @utmp, and @wtmp is set, - * then the session is logged in the appropriate system log. + * Starts a new copy of @command running under a psuedo-terminal, optionally in + * the supplied @directory, with window size set to @rows x @columns + * and variables in @env_add added to its environment. If any combination of + * @lastlog, @utmp, and @wtmp is set, then the session is logged in the + * corresponding system files. * * Returns: an open file descriptor for the pty master, -1 on failure */ int -vte_pty_open_with_logging(pid_t *child, char **env_add, - const char *command, char **argv, - int columns, int rows, - gboolean lastlog, gboolean utmp, gboolean wtmp) +_vte_pty_open(pid_t *child, char **env_add, + const char *command, char **argv, const char *directory, + int columns, int rows, + gboolean lastlog, gboolean utmp, gboolean wtmp) { int ret = -1; int op = 0; @@ -925,11 +938,12 @@ vte_pty_open_with_logging(pid_t *child, char **env_add, g_assert(op < G_N_ELEMENTS(opmap)); if (ret == -1) { ret = _vte_pty_open_with_helper(child, env_add, command, argv, + directory, columns, rows, opmap[op]); } if (ret == -1) { ret = _vte_pty_open_unix98(child, env_add, command, argv, - columns, rows); + directory, columns, rows); } #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_PTY)) { @@ -940,32 +954,6 @@ vte_pty_open_with_logging(pid_t *child, char **env_add, } /** - * vte_pty_open: - * @child: location to store the new process's ID - * @env_add: a list of environment variables to add to the child's environment - * @command: name of the binary to run - * @argv: arguments to pass to @command - * @columns: desired window columns - * @rows: desired window rows - * - * Starts a new copy of @command running under a psuedo-terminal, with window - * size set to @rows x @columns and variables in @env_add added to its - * environment. A convenience wrapper for vte_pty_open_with_logging(). - * - * Returns: an open file descriptor for the pty master, -1 on failure - */ -int -vte_pty_open(pid_t *child, char **env_add, - const char *command, char **argv, - int columns, int rows) -{ - return vte_pty_open_with_logging(child, env_add, command, argv, - columns, rows, - FALSE, FALSE, FALSE); -} - - -/** * vte_pty_close: * @pty: the pty master descriptor. * @@ -973,7 +961,7 @@ vte_pty_open(pid_t *child, char **env_add, * performed for the session. The descriptor itself remains open. */ void -vte_pty_close(int pty) +_vte_pty_close(int pty) { gpointer tag; GnomePtyOps ops; @@ -1001,7 +989,7 @@ static void sigchld_handler(int signum) { /* This is very unsafe. Never do it in production code. */ - vte_pty_close(fd); + _vte_pty_close(fd); } int @@ -1012,10 +1000,12 @@ main(int argc, char **argv) int ret; signal(SIGCHLD, sigchld_handler); _vte_debug_parse_string(getenv("VTE_DEBUG_FLAGS")); - fd = vte_pty_open(&child, NULL, - (argc > 1) ? argv[1] : "/usr/bin/tty", - (argc > 1) ? argv + 1 : NULL, - 0, 0); + fd = _vte_pty_open(&child, NULL, + (argc > 1) ? argv[1] : "/usr/bin/tty", + (argc > 1) ? argv + 1 : NULL, + NULL, + 0, 0, + FALSE, FALSE, FALSE); g_print("Child pid is %d.\n", (int)child); do { ret = read(fd, &c, 1); @@ -27,24 +27,20 @@ G_BEGIN_DECLS /* Start up the given binary (exact path, not interpreted at all) in a * pseudo-terminal of its own, returning the descriptor for the master - * side of the PTY pair, storing the child's PID in the given argument. */ -int vte_pty_open(pid_t *child, char **env_add, - const char *command, char **argv, - int columns, int rows); - -/* As above, but with session logging. */ -int vte_pty_open_with_logging(pid_t *child, char **env_add, - const char *command, char **argv, - int columns, int rows, - gboolean lastlog, gboolean utmp, gboolean wtmp); + * side of the PTY pair, logging the session to the specified files, and + * storing the child's PID in the given argument. */ +int _vte_pty_open(pid_t *child, char **env_add, + const char *command, char **argv, const char *directory, + int columns, int rows, + gboolean lastlog, gboolean utmp, gboolean wtmp); /* Set or read the size of a terminal. Returns 0 on success, -1 on failure, * with errno set to defined return codes from ioctl(). */ -int vte_pty_get_size(int master, int *columns, int *rows); -int vte_pty_set_size(int master, int columns, int rows); +int _vte_pty_get_size(int master, int *columns, int *rows); +int _vte_pty_set_size(int master, int columns, int rows); /* Close a pty. */ -void vte_pty_close(int pty); +void _vte_pty_close(int pty); G_END_DECLS @@ -118,19 +118,22 @@ typedef gunichar wint_t; * includes any supported visible attributes. */ struct vte_charcell { gunichar c; /* The Unicode character. */ - guint16 columns: 2; /* Number of visible columns (as determined - by g_unicode_iswide(c)). */ - guint16 fore: 5; /* Indices in the color palette for the */ - guint16 back: 5; /* foreground and background of the cell. */ - guint16 standout: 1; /* Single-bit attributes. */ - guint16 underline: 1; - guint16 reverse: 1; - guint16 blink: 1; - guint16 half: 1; - guint16 bold: 1; - guint16 invisible: 1; - guint16 protect: 1; - guint16 alternate: 1; + guint32 columns: 12; /* Number of visible columns (as determined + by g_unicode_iswide(c)). Use as many bits + as possible without making this structure + grow any larger. */ + guint32 fragment: 1; /* The nth fragment of a wide character. */ + guint32 fore: 5; /* Indices in the color palette for the */ + guint32 back: 5; /* foreground and background of the cell. */ + guint32 standout: 1; /* Single-bit attributes. */ + guint32 underline: 1; + guint32 reverse: 1; + guint32 blink: 1; + guint32 half: 1; + guint32 bold: 1; + guint32 invisible: 1; + guint32 protect: 1; + guint32 alternate: 1; }; /* A match regex, with a tag. */ @@ -142,6 +145,7 @@ struct vte_match_regex { /* A drawing request record, for simplicity. */ struct vte_draw_item { gunichar c; + guint16 columns; guint16 xpad; }; @@ -244,7 +248,7 @@ struct _VteTerminalPrivate { gboolean has_selection; gboolean restart_selection; char *selection; - enum { + enum vte_selection_type { selection_type_char, selection_type_word, selection_type_line @@ -252,7 +256,7 @@ struct _VteTerminalPrivate { struct selection_event_coords { double x, y; } selection_origin, selection_last, selection_delayed; - struct { + struct selection_cell_coords { long x, y; } selection_start, selection_end; @@ -318,8 +322,10 @@ struct _VteTerminalPrivate { VteRenderXlib = 0, VteRenderPangoX = 1, VteRenderPango = 2, +#ifdef HAVE_XFT VteRenderXft1 = 3, - VteRenderXft2 = 4 + VteRenderXft2 = 4, +#endif } render_method; #ifdef HAVE_XFT XftFont *ftfont; @@ -375,7 +381,8 @@ static void vte_terminal_ensure_cursor(VteTerminal *terminal, gboolean current); static void vte_terminal_paste(VteTerminal *terminal, GdkAtom board); static void vte_terminal_insert_char(VteTerminal *terminal, gunichar c, gboolean force_insert_mode, - gboolean invalidate_cells); + gboolean invalidate_cells, + gint forced_width); static void vte_sequence_handler_clear_screen(VteTerminal *terminal, const char *match, GQuark match_quark, @@ -459,10 +466,10 @@ vte_free_row_data(gpointer freeing, gpointer data) static void vte_g_array_fill(GArray *array, gpointer item, guint final_size) { + g_assert(array != NULL); if (array->len >= final_size) { return; } - g_assert(array != NULL); g_assert(item != NULL); while (array->len < final_size) { @@ -472,7 +479,7 @@ vte_g_array_fill(GArray *array, gpointer item, guint final_size) /* Allocate a new line. */ static GArray * -vte_new_row_data(void) +vte_new_row_data(VteTerminal *terminal) { return g_array_new(FALSE, FALSE, sizeof(struct vte_charcell)); } @@ -496,7 +503,7 @@ vte_new_row_data_sized(VteTerminal *terminal, gboolean fill) static gssize vte_unichar_width(gunichar c) { - return g_unichar_isdefined(c) ? (g_unichar_iswide(c) ? 2 : 1) : -1; + return g_unichar_isdefined(c) ? (g_unichar_iswide(c) ? 2 : 1) : 1; } /* Check how long a string of unichars is. Slow version. */ @@ -590,8 +597,9 @@ static void vte_terminal_set_default_attributes(VteTerminal *terminal) { g_return_if_fail(VTE_IS_TERMINAL(terminal)); - terminal->pvt->screen->defaults.c = 0; + terminal->pvt->screen->defaults.c = ' '; terminal->pvt->screen->defaults.columns = 1; + terminal->pvt->screen->defaults.fragment = 0; terminal->pvt->screen->defaults.fore = VTE_DEF_FG; terminal->pvt->screen->defaults.back = VTE_DEF_BG; terminal->pvt->screen->defaults.reverse = 0; @@ -746,7 +754,7 @@ vte_invalidate_cursor_once(gpointer data) cell = vte_terminal_find_charcell(terminal, column, screen->cursor_current.row); - while ((cell != NULL) && (cell->columns == 0) && (column > 0)) { + while ((cell != NULL) && (cell->fragment) && (column > 0)) { column--; cell = vte_terminal_find_charcell(terminal, column, @@ -756,10 +764,8 @@ vte_invalidate_cursor_once(gpointer data) columns = cell->columns; } vte_invalidate_cells(terminal, - column, - columns + preedit_length, - screen->cursor_current.row, - 1); + column, columns + preedit_length, + screen->cursor_current.row, 1); #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_UPDATES)) { fprintf(stderr, "Invalidating cursor at (%ld,%ld-%ld)." @@ -1095,7 +1101,6 @@ vte_terminal_deselect_all(VteTerminal *terminal) vte_terminal_emit_selection_changed(terminal); vte_invalidate_all(terminal); } - terminal->pvt->restart_selection = FALSE; } /* Reset the set of tab stops to the default. */ @@ -1169,7 +1174,7 @@ vte_terminal_match_contents_clear(VteTerminal *terminal) g_free(terminal->pvt->match_contents); terminal->pvt->match_contents = NULL;; } - while (terminal->pvt->match_attributes != NULL) { + if (terminal->pvt->match_attributes != NULL) { g_array_free(terminal->pvt->match_attributes, TRUE); terminal->pvt->match_attributes = NULL; } @@ -1178,7 +1183,7 @@ vte_terminal_match_contents_clear(VteTerminal *terminal) /* Refresh the cache of the screen contents we keep. */ static gboolean -always_selected(VteTerminal *terminal, glong row, glong column) +always_selected(VteTerminal *terminal, glong row, glong column, gpointer data) { return TRUE; } @@ -1191,6 +1196,7 @@ vte_terminal_match_contents_refresh(VteTerminal *terminal) array = g_array_new(FALSE, TRUE, sizeof(struct vte_char_attributes)); terminal->pvt->match_contents = vte_terminal_get_text(terminal, always_selected, + NULL, array); terminal->pvt->match_attributes = array; } @@ -1263,6 +1269,12 @@ vte_terminal_match_check_internal(VteTerminal *terminal, struct vte_char_attributes *attr = NULL; gssize coffset; regmatch_t matches[256]; +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_EVENTS)) { + fprintf(stderr, "Checking for match at (%ld,%ld).\n", + row, column); + } +#endif if (tag != NULL) { *tag = -1; } @@ -1283,12 +1295,10 @@ vte_terminal_match_check_internal(VteTerminal *terminal, attr = &g_array_index(terminal->pvt->match_attributes, struct vte_char_attributes, offset); - if (attr != NULL) { - if ((row == attr->row) && - (column == attr->column) && - !g_ascii_isspace(terminal->pvt->match_contents[offset])) { - break; - } + if ((row == attr->row) && + (column == attr->column) && + (terminal->pvt->match_contents[offset] != ' ')) { + break; } } #ifdef VTE_DEBUG @@ -1300,10 +1310,12 @@ vte_terminal_match_check_internal(VteTerminal *terminal, } } #endif + /* If the pointer isn't on a matchable character, bug out. */ if (offset < 0) { return NULL; } + /* If the pointer is on a newline, bug out. */ if (g_ascii_isspace(terminal->pvt->match_contents[offset])) { #ifdef VTE_DEBUG @@ -1319,76 +1331,83 @@ vte_terminal_match_check_internal(VteTerminal *terminal, regex = &g_array_index(terminal->pvt->match_regexes, struct vte_match_regex, i); - if (regex != NULL) { - /* We'll only match the first item in the buffer which - * matches, so we'll have to skip each match until we - * stop getting matches. */ - coffset = 0; - ret = regexec(®ex->reg, - terminal->pvt->match_contents + coffset, - G_N_ELEMENTS(matches), - matches, - VTE_REGEXEC_FLAGS); - while (ret == 0) { - for (j = 0; - j < G_N_ELEMENTS(matches) && - (matches[j].rm_so != -1); - j++) { - /* The offsets should be "sane". */ - g_assert(matches[j].rm_so + coffset < terminal->pvt->match_attributes->len); - g_assert(matches[j].rm_eo + coffset <= terminal->pvt->match_attributes->len); + g_assert(regex != NULL); + /* We'll only match the first item in the buffer which + * matches, so we'll have to skip each match until we + * stop getting matches. */ + coffset = 0; + ret = regexec(®ex->reg, + terminal->pvt->match_contents + coffset, + G_N_ELEMENTS(matches), + matches, + VTE_REGEXEC_FLAGS); + while (ret == 0) { + for (j = 0; + (j < G_N_ELEMENTS(matches)) && + (matches[j].rm_so != -1); + j++) { + /* The offsets should be "sane". */ + g_assert(matches[j].rm_so + coffset < + terminal->pvt->match_attributes->len); + g_assert(matches[j].rm_eo + coffset <= + terminal->pvt->match_attributes->len); #ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_MISC)) { - char *match; - struct vte_char_attributes *sattr, *eattr; - match = g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset, - matches[j].rm_eo - matches[j].rm_so); - sattr = &g_array_index(terminal->pvt->match_attributes, - struct vte_char_attributes, - matches[j].rm_so + coffset); - eattr = &g_array_index(terminal->pvt->match_attributes, - struct vte_char_attributes, - matches[j].rm_eo + coffset - 1); - fprintf(stderr, "Match %d `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n", - j, match, - matches[j].rm_so + coffset, - sattr->column, - sattr->row, - matches[j].rm_eo + coffset - 1, - eattr->column, - eattr->row, - offset); - g_free(match); + if (_vte_debug_on(VTE_DEBUG_MISC)) { + char *match; + struct vte_char_attributes *sattr, *eattr; + match = g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset, + matches[j].rm_eo - matches[j].rm_so); + sattr = &g_array_index(terminal->pvt->match_attributes, + struct vte_char_attributes, + matches[j].rm_so + coffset); + eattr = &g_array_index(terminal->pvt->match_attributes, + struct vte_char_attributes, + matches[j].rm_eo + coffset - 1); + fprintf(stderr, "Match %d `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n", + j, match, + matches[j].rm_so + coffset, + sattr->column, + sattr->row, + matches[j].rm_eo + coffset - 1, + eattr->column, + eattr->row, + offset); + g_free(match); - } + } #endif - /* If the pointer is in this substring, - * then we're done. */ - if ((offset >= matches[j].rm_so + coffset) && - (offset < matches[j].rm_eo + coffset)) { - if (tag != NULL) { - *tag = regex->tag; - } - if (start != NULL) { - *start = matches[j].rm_so + coffset; - } - if (end != NULL) { - *end = matches[j].rm_eo + coffset - 1; - } - return g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset, - matches[j].rm_eo - matches[j].rm_so); + /* Snip off any final newlines. */ + while ((matches[j].rm_eo > matches[j].rm_so) && + (terminal->pvt->match_contents[coffset + matches[j].rm_eo - 1] == '\n')) { + matches[j].rm_eo--; + } + /* If the pointer is in this substring, + * then we're done. */ + if ((offset >= (matches[j].rm_so + coffset)) && + (offset < (matches[j].rm_eo + coffset))) { + if (tag != NULL) { + *tag = regex->tag; + } + if (start != NULL) { + *start = coffset + + matches[j].rm_so; + } + if (end != NULL) { + *end = coffset + + matches[j].rm_eo - 1; } + return g_strndup(terminal->pvt->match_contents + coffset + matches[j].rm_so, + matches[j].rm_eo - matches[j].rm_so); } - /* Skip past the beginning of this match to - * look for more. */ - coffset += (matches[0].rm_so + 1); - ret = regexec(®ex->reg, - terminal->pvt->match_contents + - coffset, - G_N_ELEMENTS(matches), - matches, - VTE_REGEXEC_FLAGS); } + /* Skip past the beginning of this match to + * look for more. */ + coffset += (matches[0].rm_so + 1); + ret = regexec(®ex->reg, + terminal->pvt->match_contents + coffset, + G_N_ELEMENTS(matches), + matches, + VTE_REGEXEC_FLAGS); } } return NULL; @@ -1591,6 +1610,8 @@ vte_terminal_scroll_pages(VteTerminal *terminal, gint pages) gtk_adjustment_set_value(terminal->adjustment, destination); /* Clear dingus match set. */ vte_terminal_match_contents_clear(terminal); + /* Notify viewers that the contents have changed. */ + vte_terminal_emit_contents_changed(terminal); } /* Scroll so that the scroll delta is the insertion delta. */ @@ -2444,7 +2465,7 @@ vte_terminal_ensure_cursor(VteTerminal *terminal, gboolean current) if (fill) { array = vte_new_row_data_sized(terminal, TRUE); } else { - array = vte_new_row_data(); + array = vte_new_row_data(terminal); } _vte_ring_append(screen->row_data, array); readjust = TRUE; @@ -2658,7 +2679,7 @@ vte_sequence_handler_ic(VteTerminal *terminal, save = screen->cursor_current; - vte_terminal_insert_char(terminal, ' ', TRUE, TRUE); + vte_terminal_insert_char(terminal, ' ', TRUE, TRUE, -1); screen->cursor_current = save; } @@ -3231,23 +3252,14 @@ vte_sequence_handler_ta(VteTerminal *terminal, newcol = terminal->column_count - 1; } - /* Wrap to the next line if need be. */ - if (newcol >= terminal->column_count) { - if (terminal->pvt->flags.am) { - /* Move to the next line. */ - terminal->pvt->screen->cursor_current.col = 0; - vte_sequence_handler_sf(terminal, match, - match_quark, params); - } else { - /* Stay in the rightmost column. */ - newcol = terminal->column_count - 1; - } - } else { - terminal->pvt->screen->cursor_current.col = newcol; - } + /* Insert a tab character with the right width. */ + vte_terminal_insert_char(terminal, '\t', + FALSE, FALSE, + newcol - + terminal->pvt->screen->cursor_current.col); #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_PARSE)) { - fprintf(stderr, "Moving cursor to column %ld.\n", (long)newcol); + fprintf(stderr, "Inserting tab.\n"); } #endif } @@ -3308,7 +3320,7 @@ vte_sequence_handler_uc(VteTerminal *terminal, cell = vte_terminal_find_charcell(terminal, column, screen->cursor_current.row); - while ((cell != NULL) && (cell->columns == 0) && (column > 0)) { + while ((cell != NULL) && (cell->fragment) && (column > 0)) { column--; cell = vte_terminal_find_charcell(terminal, column, @@ -3836,14 +3848,14 @@ vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible) terminal->pvt->mouse_cell_motion_tracking || terminal->pvt->mouse_all_motion_tracking) { #ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_IO)) { + if (_vte_debug_on(VTE_DEBUG_CURSOR)) { fprintf(stderr, "Setting mousing cursor.\n"); } #endif cursor = terminal->pvt->mouse_mousing_cursor; } else { #ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_IO)) { + if (_vte_debug_on(VTE_DEBUG_CURSOR)) { fprintf(stderr, "Setting default mouse " "cursor.\n"); } @@ -3852,7 +3864,7 @@ vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible) } } else { #ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_IO)) { + if (_vte_debug_on(VTE_DEBUG_CURSOR)) { fprintf(stderr, "Setting to invisible cursor.\n"); } #endif @@ -4144,6 +4156,10 @@ vte_sequence_handler_decset_internal(VteTerminal *terminal, /* Reset scrollbars and repaint everything. */ vte_terminal_adjust_adjustments(terminal, TRUE); vte_invalidate_all(terminal); + /* Clear the matching view. */ + vte_terminal_match_contents_clear(terminal); + /* Notify viewers that the contents have changed. */ + vte_terminal_emit_contents_changed(terminal); break; case 9: case 1000: @@ -4711,7 +4727,7 @@ vte_sequence_handler_screen_alignment_test(VteTerminal *terminal, row++) { /* Find this row. */ while (_vte_ring_next(screen->row_data) <= row) { - rowdata = vte_new_row_data(); + rowdata = vte_new_row_data(terminal); _vte_ring_append(screen->row_data, rowdata); } vte_terminal_adjust_adjustments(terminal, TRUE); @@ -5641,16 +5657,16 @@ vte_terminal_set_color_background(VteTerminal *terminal, * @palette: the color palette * @palette_size: the number of entries in @palette * - * The terminal widget uses a 19-color model comprised of the default foreground - * and background colors, the bold foreground color, an eight color palette, and - * bold versions of the eight color palette. + * The terminal widget uses a 20-color model comprised of the default foreground + * and background colors, the bold foreground color, the cursor foreground + * color,an eight color palette, and bold versions of the eight color palette. * * @palette_size must be either 0, 8, or 16. If @foreground is NULL and * @palette_size is greater than 0, the new foreground color is taken from * @palette[7]. If @background is NULL and @palette_size is greater than 0, - * the new background color is taken from @palette[0]. If @palette_size is - * 8, the second 8-color palette is extrapolated from the new background - * color and the items in @palette. + * the new background color is taken from @palette[0]. If + * @palette_size is 8, the second 8-color palette is extrapolated from the new + * background color and the items in @palette. * */ void @@ -5773,7 +5789,8 @@ vte_terminal_set_default_colors(VteTerminal *terminal) /* Insert a single character into the stored data array. */ static void vte_terminal_insert_char(VteTerminal *terminal, gunichar c, - gboolean force_insert_mode, gboolean invalidate_cells) + gboolean force_insert_mode, gboolean invalidate_cells, + gint forced_width) { GArray *array; struct vte_charcell cell, *pcell; @@ -5805,11 +5822,15 @@ vte_terminal_insert_char(VteTerminal *terminal, gunichar c, } /* Figure out how many columns this character should occupy. */ - columns = vte_unichar_width(c); - if (columns < 0) { - g_warning(_("Character 0x%x is undefined, allocating one " - "column."), c); - columns = 1; + if (forced_width == -1) { + columns = vte_unichar_width(c); + if (columns < 0) { + g_warning(_("Character 0x%x is undefined, allocating " + "one column."), c); + columns = 1; + } + } else { + columns = forced_width; } /* If we're autowrapping here, do it. */ @@ -5862,9 +5883,12 @@ vte_terminal_insert_char(VteTerminal *terminal, gunichar c, *pcell = screen->defaults; pcell->c = cell.c; pcell->columns = cell.columns; + pcell->fragment = cell.fragment; /* Now set the character and column count. */ if (i == 0) { + /* This is an entire character or the first column of + * a multi-column character. */ if ((pcell->c != 0) && (c == '_') && (terminal->pvt->flags.ul)) { @@ -5874,27 +5898,13 @@ vte_terminal_insert_char(VteTerminal *terminal, gunichar c, /* Insert the character. */ pcell->c = c; pcell->columns = columns; + pcell->fragment = 0; } } else { /* This is a continuation cell. */ - pcell->columns = 0; - } - - /* Signal that this part of the window needs drawing. */ - if (invalidate_cells) { - if (insert) { - vte_invalidate_cells(terminal, - col, - terminal->column_count - - col, - screen->cursor_current.row, - 2); - } else { - vte_invalidate_cells(terminal, - col, 1, - screen->cursor_current.row, - 2); - } + pcell->c = c; + pcell->columns = columns; + pcell->fragment = 1; } /* And take a step to the to the right. */ @@ -5908,6 +5918,20 @@ vte_terminal_insert_char(VteTerminal *terminal, gunichar c, } } + /* Signal that this part of the window needs drawing. */ + if (invalidate_cells) { + col = screen->cursor_current.col - columns; + if (insert) { + vte_invalidate_cells(terminal, + col, terminal->column_count - col, + screen->cursor_current.row, 1); + } else { + vte_invalidate_cells(terminal, + col, columns, + screen->cursor_current.row, 1); + } + } + /* Make sure the location the cursor is on exists. */ vte_terminal_ensure_cursor(terminal, FALSE); @@ -6017,12 +6041,13 @@ vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status, } /** - * vte_terminal_fork_logged_command: + * vte_terminal_fork_command: * @terminal: a #VteTerminal * @command: the name of a binary to run * @argv: the argument list to be passed to @command * @envv: a list of environment variables to be added to the environment before * starting @command + * @directory: the name of a directory the command should start in, or NULL * @lastlog: TRUE if the session should be logged to the lastlog * @utmp: TRUE if the session should be logged to the utmp/utmpx log * @wtmp: TRUE if the session should be logged to the wtmp/wtmpx log @@ -6035,9 +6060,9 @@ vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status, * Returns: the ID of the new process */ pid_t -vte_terminal_fork_logged_command(VteTerminal *terminal, const char *command, - char **argv, char **envv, - gboolean lastlog, gboolean utmp, gboolean wtmp) +vte_terminal_fork_command(VteTerminal *terminal, const char *command, + char **argv, char **envv, const char *directory, + gboolean lastlog, gboolean utmp, gboolean wtmp) { char **env_add; int i; @@ -6062,17 +6087,18 @@ vte_terminal_fork_logged_command(VteTerminal *terminal, const char *command, env_add[i + 1] = NULL; if (terminal->pvt->pty_master != -1) { - vte_pty_close(terminal->pvt->pty_master); - } - terminal->pvt->pty_master = vte_pty_open_with_logging(&pid, - env_add, - command, - argv, - terminal->column_count, - terminal->row_count, - lastlog, - utmp, - wtmp); + _vte_pty_close(terminal->pvt->pty_master); + } + terminal->pvt->pty_master = _vte_pty_open(&pid, + env_add, + command, + argv, + directory, + terminal->column_count, + terminal->row_count, + lastlog, + utmp, + wtmp); for (i = 0; env_add[i] != NULL; i++) { g_free(env_add[i]); @@ -6117,28 +6143,6 @@ vte_terminal_fork_logged_command(VteTerminal *terminal, const char *command, return pid; } -/** - * vte_terminal_fork_command: - * @terminal: a #VteTerminal - * @command: the name of a binary to run - * @argv: the argument list to be passed to @command - * @envv: a list of environment variables to be added to the environment before - * starting @command - * - * Starts the specified command under a newly-alllocated control - * pseudo-terminal. TERM is automatically set to reflect the terminal widget's - * emulation setting. - * - * Returns: the ID of the new process - */ -pid_t -vte_terminal_fork_command(VteTerminal *terminal, const char *command, - char **argv, char **envv) -{ - return vte_terminal_fork_logged_command(terminal, command, argv, envv, - FALSE, FALSE, FALSE); -} - /* Handle an EOF from the client. */ static void vte_terminal_eof(GIOChannel *channel, gpointer data) @@ -6386,7 +6390,7 @@ vte_terminal_process_incoming(gpointer data) if (c != 0) { /* Insert the character. */ vte_terminal_insert_char(terminal, c, - FALSE, FALSE); + FALSE, FALSE, -1); } modified = TRUE; start++; @@ -6511,7 +6515,6 @@ vte_terminal_process_incoming(gpointer data) if (terminal->pvt->scroll_on_output || bottom) { vte_terminal_scroll_to_bottom(terminal); } - /* Deselect any existing selection. */ vte_terminal_deselect_all(terminal); } @@ -6519,6 +6522,7 @@ vte_terminal_process_incoming(gpointer data) if (modified || (screen != terminal->pvt->screen)) { /* Signal that the visible contents changed. */ vte_terminal_match_contents_clear(terminal); + /* Notify viewers that the contents have changed. */ vte_terminal_emit_contents_changed(terminal); } @@ -7398,23 +7402,6 @@ vte_cell_is_between(glong col, glong row, glong acol, glong arow, glong bcol, glong brow, gboolean inclusive) { - long t; - /* Sort endpoints on the same row properly. */ - if (arow == brow) { - if (acol > bcol) { - t = acol; - acol = bcol; - bcol = t; - } - } else - if (arow > brow) { - t = arow; - arow = brow; - brow = t; - t = acol; - acol = bcol; - bcol = t; - } /* No zero-length between allowed. */ if ((row == arow) && (row == brow) && (col == acol) && (col == bcol)) { return FALSE; @@ -7424,32 +7411,42 @@ vte_cell_is_between(glong col, glong row, * or any of the lines in between. */ if ((row > arow) && (row < brow)) { return TRUE; - } else + } /* It's also between the two points if they're on the same row - * the cell lies between the start and end columns (which may not - * be in the more obvious of two possible orders). */ + * the cell lies between the start and end columns. */ if ((row == arow) && (row == brow)) { if (col >= acol) { if (col < bcol) { return TRUE; - } else - if ((col == bcol) && inclusive) { - return TRUE; + } else { + if ((col == bcol) && inclusive) { + return TRUE; + } else { + return FALSE; + } } + } else { + return FALSE; } - } else + } /* It's also "between" if it's on the line where the area starts and * at or after the start column, or on the line where the area ends and * before the end column. */ if ((row == arow) && (col >= acol)) { return TRUE; - } else - if (row == brow) { - if (col < bcol) { - return TRUE; - } else - if ((col == bcol) && inclusive) { - return TRUE; + } else { + if (row == brow) { + if (col < bcol) { + return TRUE; + } else { + if ((col == bcol) && inclusive) { + return TRUE; + } else { + return FALSE; + } + } + } else { + return FALSE; } } return FALSE; @@ -7457,9 +7454,13 @@ vte_cell_is_between(glong col, glong row, /* Check if a cell is selected or not. */ static gboolean -vte_cell_is_selected(VteTerminal *terminal, glong col, glong row) +vte_cell_is_selected(VteTerminal *terminal, glong col, glong row, gpointer data) { - long scol, ecol; + long i; + GArray *rowdata; + struct vte_charcell *cell; + struct selection_cell_coords ss, se; + VteScreen *screen; /* If there's nothing selected, it's an easy question to answer. */ if (!terminal->pvt->has_selection) { @@ -7468,21 +7469,87 @@ vte_cell_is_selected(VteTerminal *terminal, glong col, glong row) /* Sort the two columns, for the cases where the selection is * entirely within a single line. */ - scol = MIN(terminal->pvt->selection_start.x, - terminal->pvt->selection_end.x); - ecol = MAX(terminal->pvt->selection_start.x, - terminal->pvt->selection_end.x); + ss = terminal->pvt->selection_start; + se = terminal->pvt->selection_end; + screen = terminal->pvt->screen; + + /* Adjust the endpoint so that it it's an inclusive range. */ + if (se.x > 0) { + se.x--; + } + + /* Retrieve this row's data. */ + if (!_vte_ring_contains(screen->row_data, row)) { + return FALSE; + } + rowdata = _vte_ring_index(screen->row_data, GArray*, row); + + /* Handle end-of-line selection wackiness. */ + if ((row == ss.y) && (row == se.y)) { + /* It's not selected if the both the start and end are after + * the end of the line. */ + if ((ss.x >= rowdata->len) && + (se.x >= rowdata->len) && + (col >= rowdata->len)) { + return FALSE; + } + } + /* Checks for if we're at the start of the selection. */ + if (row == ss.y) { + /* It's selected if the both the start of the selection and the + * current column are after the end. */ + if ((ss.x >= rowdata->len) && (col >= rowdata->len)) { + return TRUE; + } + /* Also selected if it's before the start of the selection and + * there's nothing between here and the end of the line but + * spaces. */ + if ((col < ss.x) && (ss.x < rowdata->len)) { + for (i = col; i < rowdata->len; i++) { + cell = &g_array_index(rowdata, + struct vte_charcell, + i); + if ((cell->c != 0) && (cell->c != ' ')) { + break; + } + } + if (i >= rowdata->len) { + return TRUE; + } + } + } + /* Checks for if we're at the end of the selection. */ + if (row == se.y) { + /* It's selected if the both the end of the selection and the + * current column are after the end. */ + if ((se.x >= rowdata->len) && (col >= rowdata->len)) { + return TRUE; + } + /* Also selected if it's past the end of the selection and + * there's nothing between here and the end of the line but + * spaces. */ + if ((se.x < rowdata->len) && (se.x < col)) { + for (i = se.x; i < rowdata->len; i++) { + cell = &g_array_index(rowdata, + struct vte_charcell, + i); + if ((cell->c != 0) && (cell->c != ' ')) { + break; + } + } + if (i >= rowdata->len) { + return TRUE; + } + } + } switch (terminal->pvt->selection_type) { case selection_type_char: /* A cell is selected if it's between the start and * endpoints of the selection. */ if (vte_cell_is_between(col, row, - terminal->pvt->selection_start.x, - terminal->pvt->selection_start.y, - terminal->pvt->selection_end.x, - terminal->pvt->selection_end.y, - FALSE)) { + ss.x, ss.y, se.x, se.y, + TRUE)) { return TRUE; } break; @@ -7490,35 +7557,33 @@ vte_cell_is_selected(VteTerminal *terminal, glong col, glong row) /* A cell is selected if it's on the line where the * selected area starts, or the line where it ends, * or any of the lines in between. */ - if ((row > terminal->pvt->selection_start.y) && - (row < terminal->pvt->selection_end.y)) { + if ((row > ss.y) && (row < se.y)) { return TRUE; } else /* It's also selected if the selection is confined to * one line and the character lies between the start * and end columns (which may not be in the more obvious * of two possible orders). */ - if ((terminal->pvt->selection_start.y == row) && - (terminal->pvt->selection_end.y == row)) { - if ((col >= scol) && (col <= ecol)) { + if ((ss.y == row) && (se.y == row)) { + if ((col >= ss.x) && (col <= se.x)) { return TRUE; } else /* If the character is before the beginning of * the region, it's also selected if it and * everything else in between belongs to the * same character class. */ - if (col < scol) { + if (col < ss.x) { if (vte_uniform_class(terminal, row, col, - scol)) { + ss.x)) { return TRUE; } } else - if (col > ecol) { + if (col > se.x) { if (vte_uniform_class(terminal, row, - ecol, + se.x, col)) { return TRUE; } @@ -7528,24 +7593,24 @@ vte_cell_is_selected(VteTerminal *terminal, glong col, glong row) * selected area starts and it's after the start column, * or on the line where the selection ends, after the * last selected column. */ - if (row == terminal->pvt->selection_start.y) { - if (col >= terminal->pvt->selection_start.x) { + if (row == ss.y) { + if (col >= ss.x) { return TRUE; } else if (vte_uniform_class(terminal, row, col, - terminal->pvt->selection_start.x)) { + ss.x)) { return TRUE; } } else - if (row == terminal->pvt->selection_end.y) { - if (col < terminal->pvt->selection_end.x) { + if (row == se.y) { + if (col <= se.x) { return TRUE; } else if (vte_uniform_class(terminal, row, - terminal->pvt->selection_end.x, + se.x, col)) { return TRUE; } @@ -7555,8 +7620,7 @@ vte_cell_is_selected(VteTerminal *terminal, glong col, glong row) /* A cell is selected if it's on the line where the * selected area starts, or the line where it ends, * or any of the lines in between. */ - if ((row >= terminal->pvt->selection_start.y) && - (row <= terminal->pvt->selection_end.y)) { + if ((row >= ss.y) && (row <= se.y)) { return TRUE; } break; @@ -7657,7 +7721,8 @@ vte_terminal_send_mouse_button_internal(VteTerminal *terminal, /* Send a mouse button click/release notification. */ static void -vte_terminal_send_mouse_button(VteTerminal *terminal, GdkEventButton *event) +vte_terminal_maybe_send_mouse_button(VteTerminal *terminal, + GdkEventButton *event) { GdkModifierType modifiers; @@ -7666,6 +7731,24 @@ vte_terminal_send_mouse_button(VteTerminal *terminal, GdkEventButton *event) modifiers = 0; } + /* Decide whether or not to do anything. */ + switch (event->type) { + case GDK_BUTTON_PRESS: + if (!terminal->pvt->mouse_send_xy_on_button && + !terminal->pvt->mouse_send_xy_on_click) { + return; + } + break; + case GDK_BUTTON_RELEASE: + if (!terminal->pvt->mouse_send_xy_on_button) { + return; + } + break; + default: + return; + break; + } + /* Encode the parameters and send them to the app. */ vte_terminal_send_mouse_button_internal(terminal, (event->type == GDK_BUTTON_PRESS) ? @@ -7677,7 +7760,7 @@ vte_terminal_send_mouse_button(VteTerminal *terminal, GdkEventButton *event) /* Send a mouse motion notification. */ static void -vte_terminal_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event) +vte_terminal_maybe_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event) { unsigned char cb = 0, cx = 0, cy = 0; char buf[LINE_MAX]; @@ -7686,19 +7769,31 @@ vte_terminal_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event) g_return_if_fail(VTE_IS_TERMINAL(terminal)); /* First determine if we even want to send notification. */ - if (!terminal->pvt->mouse_cell_motion_tracking && - !terminal->pvt->mouse_all_motion_tracking) { - return; - } - if (terminal->pvt->mouse_cell_motion_tracking) { - if (((event->x - VTE_PAD_WIDTH) / terminal->char_width == - terminal->pvt->mouse_last_x / terminal->char_width) && - ((event->y - VTE_PAD_WIDTH) / terminal->char_height == - terminal->pvt->mouse_last_y / terminal->char_height)) { + switch (event->type) { + case GDK_MOTION_NOTIFY: + if (!terminal->pvt->mouse_cell_motion_tracking && + !terminal->pvt->mouse_all_motion_tracking) { return; } + if (terminal->pvt->mouse_cell_motion_tracking) { + if (((event->x - VTE_PAD_WIDTH) / + terminal->char_width == + terminal->pvt->mouse_last_x / + terminal->char_width) && + ((event->y - VTE_PAD_WIDTH) / + terminal->char_height == + terminal->pvt->mouse_last_y / + terminal->char_height)) { + return; + } + } + break; + default: + return; + break; } + /* Read the modifiers. */ if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { modifiers = 0; @@ -7736,7 +7831,8 @@ vte_terminal_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event) cb += 64; /* 32 for normal, 32 for movement */ /* Encode the cursor coordinates. */ - cx = 32 + 1 + ((event->x - VTE_PAD_WIDTH) / terminal->char_width); + cx = 32 + 1 + ((event->x - VTE_PAD_WIDTH + terminal->char_width / 2) / + terminal->char_width); cy = 32 + 1 + ((event->y - VTE_PAD_WIDTH) / terminal->char_height); /* Send the event to the child. */ @@ -7765,10 +7861,8 @@ vte_terminal_match_hilite_clear(VteTerminal *terminal) } #endif vte_invalidate_cells(terminal, - 0, - terminal->column_count, - srow, - erow - srow + 1); + 0, terminal->column_count, + srow, erow - srow + 1); } } @@ -7844,274 +7938,11 @@ vte_terminal_match_hilite(VteTerminal *terminal, double x, double y) #endif /* Repaint what used to be hilited, if anything. */ vte_invalidate_cells(terminal, - 0, - terminal->column_count, + 0, terminal->column_count, rows, rowe - rows + 1); } } -/* Recalculate the start- and endpoints of the selected text, using the - * selection origin and "last" coordinate sets. */ -static void -vte_terminal_selection_recompute(VteTerminal *terminal) -{ - struct { - long x, y; - } origin, last, start, end; - long width, height; - - g_return_if_fail(VTE_IS_TERMINAL(terminal)); - - width = terminal->char_width; - height = terminal->char_height; - origin.x = (terminal->pvt->selection_origin.x + width / 2) / width; - origin.y = (terminal->pvt->selection_origin.y) / height; - last.x = (terminal->pvt->selection_last.x + width / 2) / width; - last.y = (terminal->pvt->selection_last.y) / height; - start.x = start.y = end.x = end.y = 0; - - if (last.y > origin.y) { - start.x = origin.x; - start.y = origin.y; - end.x = last.x; - end.y = last.y; - } else - if (last.y < origin.y) { - start.x = last.x; - start.y = last.y; - end.x = origin.x; - end.y = origin.y; - } else - /* last.y == origin.y */ - if (last.x > origin.x) { - start.x = origin.x; - start.y = origin.y; - end.x = last.x; - end.y = last.y; - } else { - start.x = last.x; - start.y = last.y; - end.x = origin.x; - end.y = origin.y; - } - - terminal->pvt->selection_start.x = start.x; - terminal->pvt->selection_start.y = start.y; - terminal->pvt->selection_end.x = end.x; - terminal->pvt->selection_end.y = end.y; -} - -static int -vte_terminal_autoscroll(gpointer data) -{ - VteTerminal *terminal; - GtkWidget *widget; - double adj; - long row; - - terminal = VTE_TERMINAL(data); - widget = GTK_WIDGET(terminal); - /* Provide immediate autoscroll for mouse wigglers. */ - if (terminal->pvt->mouse_last_y < widget->allocation.y) { - if (terminal->adjustment) { - adj = CLAMP(terminal->adjustment->value - 1, - terminal->adjustment->lower, - terminal->adjustment->upper - - terminal->row_count); - gtk_adjustment_set_value(terminal->adjustment, - adj); - terminal->pvt->selection_last.x = - terminal->pvt->mouse_last_x; - terminal->pvt->selection_last.y = - terminal->pvt->mouse_last_y + - terminal->char_height * adj; - row = terminal->pvt->selection_start.y; - vte_terminal_selection_recompute(terminal); - terminal->pvt->selection_start.x = 0; - terminal->pvt->selection_start.y = adj; - vte_invalidate_cells(terminal, - 0, - terminal->column_count, - terminal->pvt->selection_start.y, - row + 1 - - terminal->pvt->selection_start.y); - } -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Autoscrolling down.\n"); - } -#endif - } else - if (terminal->pvt->mouse_last_y + 2 * VTE_PAD_WIDTH > - widget->allocation.height) { - adj = CLAMP(terminal->adjustment->value + 1, - terminal->adjustment->lower, - terminal->adjustment->upper - - terminal->row_count); - gtk_adjustment_set_value(terminal->adjustment, - adj); - terminal->pvt->selection_last.x = - terminal->pvt->mouse_last_x; - terminal->pvt->selection_last.y = - terminal->pvt->mouse_last_y + - terminal->char_height * adj; - row = terminal->pvt->selection_end.y; - vte_terminal_selection_recompute(terminal); - terminal->pvt->selection_end.x = - terminal->column_count; - terminal->pvt->selection_end.y = - adj + terminal->row_count - 1; - vte_invalidate_cells(terminal, - 0, - terminal->column_count, - row, - terminal->pvt->selection_end.y - - row + 1); -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Autoscrolling down.\n"); - } -#endif - } else { - terminal->pvt->mouse_autoscroll_tag = 0; - } - return (terminal->pvt->mouse_autoscroll_tag != 0); -} - -/* Read and handle a motion event. */ -static gint -vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event) -{ - VteTerminal *terminal; - long delta; - GdkModifierType modifiers; - long top, height; - double adj; - gboolean need_autoscroll; - - g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE); - terminal = VTE_TERMINAL(widget); - delta = terminal->pvt->screen->scroll_delta; - - /* Show the cursor. */ - vte_terminal_set_pointer_visible(terminal, TRUE); - - /* Read the modifiers. */ - if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { - modifiers = 0; - } - /* Handle a drag event if we're running a mouse-aware application. */ - if ((terminal->pvt->mouse_last_button != 0) && - (terminal->pvt->mouse_send_xy_on_click || - terminal->pvt->mouse_send_xy_on_button || - terminal->pvt->mouse_hilite_tracking || - terminal->pvt->mouse_cell_motion_tracking || - terminal->pvt->mouse_all_motion_tracking) && - ((modifiers & GDK_SHIFT_MASK) == 0)) { -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Mousing drag.\n"); - } -#endif - vte_terminal_send_mouse_drag(terminal, event); - } else - if (terminal->pvt->mouse_last_button == 1) { -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Drag.\n"); - } -#endif - /* If we delayed resetting selection, reset here. */ - if (terminal->pvt->restart_selection) { - vte_terminal_deselect_all(terminal); - terminal->pvt->selection_origin.x = - terminal->pvt->selection_delayed.x; - terminal->pvt->selection_origin.y = - terminal->pvt->selection_delayed.y; - terminal->pvt->selection_type = selection_type_char; - } - terminal->pvt->has_selection = TRUE; - - top = MIN(terminal->pvt->selection_last.y, - event->y - VTE_PAD_WIDTH + - (delta * terminal->char_height)) / - terminal->char_height; - height = (MAX(terminal->pvt->selection_last.y, - event->y - VTE_PAD_WIDTH + - (delta * terminal->char_height)) / - terminal->char_height) - top + 1; - - terminal->pvt->selection_last.x = event->x - VTE_PAD_WIDTH; - terminal->pvt->selection_last.y = event->y - VTE_PAD_WIDTH + - terminal->char_height * delta; - vte_terminal_selection_recompute(terminal); - -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "selection is (%ld,%ld) to (%ld,%ld)\n", - terminal->pvt->selection_start.x, - terminal->pvt->selection_start.y, - terminal->pvt->selection_end.x, - terminal->pvt->selection_end.y); - fprintf(stderr, "repainting rows %ld to %ld\n", - top, top + height); - } -#endif - vte_invalidate_cells(terminal, 0, terminal->column_count, - top, height); - - vte_terminal_emit_selection_changed (terminal); - - /* Provide immediate autoscroll for mouse wigglers. */ - need_autoscroll = FALSE; - if (event->y < 0) { - if (terminal->adjustment) { - adj = CLAMP(terminal->adjustment->value - 1, - terminal->adjustment->lower, - terminal->adjustment->upper - - terminal->row_count); - gtk_adjustment_set_value(terminal->adjustment, - adj); - need_autoscroll = TRUE; - } - } else - if (event->y >= widget->allocation.height) { - adj = CLAMP(terminal->adjustment->value + 1, - terminal->adjustment->lower, - terminal->adjustment->upper - - terminal->row_count); - gtk_adjustment_set_value(terminal->adjustment, - adj); - need_autoscroll = TRUE; - } - if (need_autoscroll && - (terminal->pvt->mouse_autoscroll_tag == 0)) { - terminal->pvt->mouse_autoscroll_tag = g_timeout_add_full(G_PRIORITY_LOW, - 50, - vte_terminal_autoscroll, - terminal, - NULL); - } - } else { -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Mouse move (button %d).\n", - terminal->pvt->mouse_last_button); - } -#endif - } - - /* Hilite any matches. */ - vte_terminal_match_hilite(terminal, - event->x - VTE_PAD_WIDTH, - event->y - VTE_PAD_WIDTH); - - /* Save the pointer coordinates for later use. */ - terminal->pvt->mouse_last_x = event->x - VTE_PAD_WIDTH; - terminal->pvt->mouse_last_y = event->y - VTE_PAD_WIDTH; - - return FALSE; -} /* Note that the clipboard has cleared. */ static void @@ -8162,6 +7993,7 @@ vte_terminal_copy_cb(GtkClipboard *clipboard, GtkSelectionData *data, * @end_row: last row to search for data * @end_col: last column to search for data * @is_selected: a callback + * @data: user data to be passed to the callback * @attributes: location for storing text attributes * * Extracts a view of the visible part of the string. If @is_selected is not @@ -8178,15 +8010,17 @@ char * vte_terminal_get_text_range(VteTerminal *terminal, glong start_row, glong start_col, glong end_row, glong end_col, - gboolean(*is_selected)(VteTerminal *, glong, glong), + gboolean(*is_selected)(VteTerminal *, glong, glong, + gpointer), + gpointer data, GArray *attributes) { - long col, row, spaces; + long col, row, last_space, last_nonspace, last_nonspacecol; VteScreen *screen; - struct vte_charcell *pcell; + struct vte_charcell *pcell = NULL; GString *string; struct vte_char_attributes attr; - struct vte_palette_entry fore, back; + struct vte_palette_entry fore, back, *palette; g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); g_return_val_if_fail(is_selected != NULL, NULL); @@ -8195,53 +8029,45 @@ vte_terminal_get_text_range(VteTerminal *terminal, string = g_string_new(""); memset(&attr, 0, sizeof(attr)); + palette = terminal->pvt->palette; for (row = start_row; row <= end_row; row++) { col = (row == start_row) ? start_col : 0; - spaces = 0; + last_space = last_nonspace = last_nonspacecol = -1; attr.row = row; + pcell = NULL; do { + /* If it's not part of a multi-column character, + * and passes the selection criterion, add it to + * the selection. */ + attr.column = col; pcell = vte_terminal_find_charcell(terminal, col, row); - if (is_selected(terminal, col, row)) { - if (pcell == NULL) { - /* If there are no more cells on this - * line, and we haven't hit the right - * margin yet, add a newline. */ - if ((col < terminal->column_count) || - (spaces > 0)) { - string = g_string_append_c(string, '\n'); - spaces = 0; - } - break; - } else - if (pcell->columns == 0) { - /* Ignore this padding cell. */ - } else - if ((pcell->c == 0) || - (g_unichar_isspace(pcell->c))) { - /* Count this in case there's something - * to the right of it. */ - spaces++; + if (pcell == NULL) { + /* No more characters on this line. */ + break; + } + if (!pcell->fragment && + is_selected(terminal, col, row, data)) { + /* Store the attributes of this character. */ + fore = palette[pcell->fore]; + back = palette[pcell->back]; + attr.fore.red = fore.red; + attr.fore.green = fore.green; + attr.fore.blue = fore.blue; + attr.back.red = back.red; + attr.back.green = back.green; + attr.back.blue = back.blue; + attr.underline = pcell->underline; + attr.alternate = pcell->alternate; + /* Store the character. */ + string = g_string_append_unichar(string, + pcell->c); + /* Record whether or not this was + * whitespace. */ + if ((pcell->c == ' ') || (pcell->c == '\0')) { + last_space = string->len - 1; } else { - /* Use the attributes of this - * character. */ - fore = terminal->pvt->palette[pcell->fore]; - back = terminal->pvt->palette[pcell->back]; - attr.fore.red = fore.red; - attr.fore.green = fore.green; - attr.fore.blue = fore.blue; - attr.back.red = back.red; - attr.back.green = back.green; - attr.back.blue = back.blue; - attr.underline = pcell->underline; - attr.alternate = pcell->alternate; - attr.column = col; - /* Stuff any saved spaces in. */ - while (spaces > 0) { - string = g_string_append_c(string, ' '); - spaces--; - } - /* Stuff the character in this cell. */ - string = g_string_append_unichar(string, pcell->c); + last_nonspace = string->len - 1; + last_nonspacecol = col; } } /* If we added a character to the string, record its @@ -8250,29 +8076,51 @@ vte_terminal_get_text_range(VteTerminal *terminal, vte_g_array_fill(attributes, &attr, string->len); } - col++; - if ((col >= terminal->column_count) || - ((row == end_row) && (col > end_col))) { + /* If we're on the last line, and have just looked in + * the last column, stop. */ + if ((row == end_row) && (col == end_col)) { break; } + col++; } while (pcell != NULL); - /* Extra spaces at the end of a line become newlines unless - * this is the last row of the selection. */ - if (spaces > 0) { - if (row != end_row) { + /* If the last thing we saw was a space, trim the + * trailing spaces off of the line. */ + if ((last_space != -1) && + (last_nonspace != -1) && + (last_space > last_nonspace)) { + g_string_truncate(string, last_nonspace + 1); + } + /* Make sure that the attributes array is as long as the + * string. */ + if (attributes) { + g_array_set_size(attributes, string->len); + } + /* If the last visible column on this line was selected and + * it contained whitespace, append a newline. */ + if (is_selected(terminal, terminal->column_count - 1, + row, data)) { + pcell = vte_terminal_find_charcell(terminal, + terminal->column_count - 1, + row); + /* If it's whitespace, we snipped it off, so add a + * newline. */ + if ((pcell == NULL) || + (pcell->c == '\0') || + (pcell->c == ' ')) { string = g_string_append_c(string, '\n'); - } else { - string = g_string_append_c(string, ' '); } - spaces = 0; - attr.column = col; - } - /* If we broke out of the loop, there might be more characters - * with missing attributes. */ - if (attributes) { - vte_g_array_fill(attributes, &attr, string->len); + /* Move this newline to the end of the line. */ + attr.column = MAX(terminal->column_count, + attr.column + 1); + /* If we broke out of the loop, there's at least one + * character with missing attributes. */ + if (attributes) { + vte_g_array_fill(attributes, &attr, + string->len); + } } } + /* Sanity check. */ if (attributes) { g_assert(string->len == attributes->len); } @@ -8283,6 +8131,7 @@ vte_terminal_get_text_range(VteTerminal *terminal, * vte_terminal_get_text: * @terminal: a #VteTerminal * @is_selected: a callback + * @data: user data to be passed to the callback * @attributes: location for storing text attributes * * Extracts a view of the visible part of the string. If @is_selected is not @@ -8295,7 +8144,9 @@ vte_terminal_get_text_range(VteTerminal *terminal, */ char * vte_terminal_get_text(VteTerminal *terminal, - gboolean(*is_selected)(VteTerminal *, glong, glong), + gboolean(*is_selected)(VteTerminal *, glong, glong, + gpointer), + gpointer data, GArray *attributes) { long start_row, start_col, end_row, end_col; @@ -8308,6 +8159,7 @@ vte_terminal_get_text(VteTerminal *terminal, end_row, end_col, is_selected ? is_selected : always_selected, + data, attributes); } @@ -8354,13 +8206,15 @@ vte_terminal_copy(VteTerminal *terminal, GdkAtom board) if (terminal->pvt->selection != NULL) { g_free(terminal->pvt->selection); } - terminal->pvt->selection = vte_terminal_get_text_range(terminal, - terminal->pvt->selection_start.y, - 0, - terminal->pvt->selection_end.y, - terminal->column_count, - vte_cell_is_selected, - NULL); + terminal->pvt->selection = + vte_terminal_get_text_range(terminal, + terminal->pvt->selection_start.y, + 0, + terminal->pvt->selection_end.y, + terminal->column_count, + vte_cell_is_selected, + NULL, + NULL); /* Place the text on the clipboard. */ if (terminal->pvt->selection != NULL) { @@ -8397,6 +8251,400 @@ vte_terminal_paste(VteTerminal *terminal, GdkAtom board) } } +/* Start selection at the location of the event. */ +static void +vte_terminal_start_selection(GtkWidget *widget, GdkEventButton *event, + enum vte_selection_type selection_type) +{ + VteTerminal *terminal; + long cellx, celly, delta; + + terminal = VTE_TERMINAL(widget); + + /* Convert the event coordinates to cell coordinates. */ + delta = terminal->pvt->screen->scroll_delta; + cellx = (event->x - VTE_PAD_WIDTH) / terminal->char_width; + celly = (event->y - VTE_PAD_WIDTH) / terminal->char_height + delta; + + /* Remove our pre-existing selection unless we're performing a + * delayed selection start. . */ + switch (selection_type) { + case selection_type_char: + terminal->pvt->restart_selection = TRUE; + break; + case selection_type_word: + case selection_type_line: + vte_terminal_deselect_all(terminal); + terminal->pvt->restart_selection = FALSE; + break; + } + + /* Record that we have the selection, and where it started. */ + terminal->pvt->has_selection = TRUE; + terminal->pvt->selection_last.x = event->x - VTE_PAD_WIDTH; + terminal->pvt->selection_last.y = event->y - VTE_PAD_WIDTH + + (terminal->char_height * delta); + terminal->pvt->selection_origin = terminal->pvt->selection_last; + terminal->pvt->selection_start.x = cellx; + terminal->pvt->selection_start.y = celly; + terminal->pvt->selection_end = terminal->pvt->selection_start; + + /* Record the selection type. */ + terminal->pvt->selection_type = selection_type; + + /* Draw the row the selection started on. */ + vte_invalidate_cells(terminal, + 0, terminal->column_count, + terminal->pvt->selection_start.y, 1); +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Selection started at (%ld,%ld).\n", + terminal->pvt->selection_start.x, + terminal->pvt->selection_start.y); + } +#endif + vte_terminal_emit_selection_changed(terminal); +} + +/* Extend selection to include the given event coordinates. */ +static void +vte_terminal_extend_selection(GtkWidget *widget, double x, double y, + gboolean always_grow) +{ + VteTerminal *terminal; + long delta, height, width; + struct selection_event_coords *origin, *last, *start, *end; + struct selection_cell_coords old_start, old_end, *sc, *ec, tc; + + terminal = VTE_TERMINAL(widget); + old_start = terminal->pvt->selection_start; + old_end = terminal->pvt->selection_end; + height = terminal->char_height; + width = terminal->char_width; + + /* Convert the event coordinates to cell coordinates. */ + delta = terminal->pvt->screen->scroll_delta; + + /* If we're restarting selection, set the origin to the last recorded + * position for the mouse. */ + if (terminal->pvt->restart_selection) { + vte_terminal_deselect_all(terminal); + terminal->pvt->selection_origin.x = + terminal->pvt->mouse_last_x; + terminal->pvt->selection_origin.y = + terminal->pvt->mouse_last_y + (height * delta); + terminal->pvt->restart_selection = FALSE; + terminal->pvt->has_selection = TRUE; +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Selection restarted at (%lf,%lf).\n", + terminal->pvt->selection_origin.x / width, + terminal->pvt->selection_origin.y / height); + } +#endif + } + + /* Extend the selection by moving the "last" pointer to the event's + * coordinates. */ + last = &terminal->pvt->selection_last; + if (!always_grow) { + last->x = x + width / 2; + last->y = y + height * delta; + } + + /* Recalculate the selected area. */ + origin = &terminal->pvt->selection_origin; + if ((origin->y / height < last->y / height) || + ((origin->y / height == last->y / height) && + (origin->x / width < last->x / width ))) { + /* The origin point is "before" the last point. */ + start = origin; + end = last; + } else { + /* The last point is "before" the origin point. */ + start = last; + end = origin; + } + + /* Extend the selection by moving whichever end of the selection is + * closer to the new point. */ + if (always_grow) { + /* New endpoint is before existing selection. */ + if ((y / height < start->y / height) || + ((y / height == start->y / height) && + (x / width < start->x / width))) { + start->x = x; + start->y = y + height * delta; + } else { + /* New endpoint is after existing selection. */ + end->x = x; + end->y = y + height * delta; + } + } + +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Selection is (%lf,%lf) to (%lf,%lf).\n", + start->x, start->y, end->x, end->y); + } +#endif + + /* Recalculate the selection area in terms of cell positions. */ + terminal->pvt->selection_start.x = start->x / width; + terminal->pvt->selection_start.y = start->y / height; + terminal->pvt->selection_end.x = end->x / width; + terminal->pvt->selection_end.y = end->y / height; + + /* Re-sort. */ + sc = &terminal->pvt->selection_start; + ec = &terminal->pvt->selection_end; + if ((sc->y > ec->y) || ((sc->y == ec->y) && (sc->x > ec->x))) { + tc = *sc; + *sc = *ec; + *ec = tc; + } + + /* Redraw the rows which have contain cells which have changed their + * is-selected status. */ + if ((old_start.x != terminal->pvt->selection_start.x) || + (old_start.y != terminal->pvt->selection_start.y)) { +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Refreshing lines %ld to %ld.\n", + MIN(old_start.y, + terminal->pvt->selection_start.y), + MAX(old_start.y, + terminal->pvt->selection_start.y)); + } +#endif + vte_invalidate_cells(terminal, + 0, + terminal->column_count, + MIN(old_start.y, + terminal->pvt->selection_start.y), + ABS(old_start.y - + terminal->pvt->selection_start.y) + 1); + } + if ((old_end.x != terminal->pvt->selection_end.x) || + (old_end.y != terminal->pvt->selection_end.y)) { +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Refreshing lines %ld to %ld.\n", + MIN(old_end.y, terminal->pvt->selection_end.y), + MAX(old_end.y, terminal->pvt->selection_end.y)); + } +#endif + vte_invalidate_cells(terminal, + 0, + terminal->column_count, + MIN(old_end.y, + terminal->pvt->selection_end.y), + ABS(old_end.y - + terminal->pvt->selection_end.y) + 1); + } + +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_SELECTION)) { + fprintf(stderr, "Selection changed to " + "(%ld,%ld) to (%ld,%ld).\n", + terminal->pvt->selection_start.x, + terminal->pvt->selection_start.y, + terminal->pvt->selection_end.x, + terminal->pvt->selection_end.y); + } +#endif + vte_terminal_emit_selection_changed(terminal); +} + +/* Autoscroll a bit. */ +static int +vte_terminal_autoscroll(gpointer data) +{ + VteTerminal *terminal; + GtkWidget *widget; + gboolean extend = FALSE; + gdouble x, y, xmax, ymax; + double adj; + + terminal = VTE_TERMINAL(data); + widget = GTK_WIDGET(terminal); + + /* Provide an immediate effect for mouse wigglers. */ + if (terminal->pvt->mouse_last_y < 0) { + if (terminal->adjustment) { + /* Try to scroll up by one line. */ + adj = CLAMP(terminal->adjustment->value - 1, + terminal->adjustment->lower, + terminal->adjustment->upper - + terminal->row_count); + gtk_adjustment_set_value(terminal->adjustment, adj); + extend = TRUE; + } +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_EVENTS)) { + fprintf(stderr, "Autoscrolling down.\n"); + } +#endif + } + if (terminal->pvt->mouse_last_y > + terminal->row_count * terminal->char_height) { + if (terminal->adjustment) { + /* Try to scroll up by one line. */ + adj = CLAMP(terminal->adjustment->value + 1, + terminal->adjustment->lower, + terminal->adjustment->upper - + terminal->row_count); + gtk_adjustment_set_value(terminal->adjustment, adj); + extend = TRUE; + } +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_EVENTS)) { + fprintf(stderr, "Autoscrolling up.\n"); + } +#endif + } + if (extend) { + /* Don't select off-screen areas. That just confuses people. */ + xmax = terminal->column_count * terminal->char_width; + ymax = terminal->row_count * terminal->char_height; + + x = CLAMP(terminal->pvt->mouse_last_x, 0, xmax); + y = CLAMP(terminal->pvt->mouse_last_y, 0, ymax); + /* If we clamped the Y, mess with the X to get the entire + * lines. */ + if (terminal->pvt->mouse_last_y < 0) { + x = 0; + } + if (terminal->pvt->mouse_last_y > ymax) { + x = terminal->column_count * terminal->char_width; + } + /* Extend selection to cover the newly-scrolled area. */ + vte_terminal_extend_selection(widget, x, y, FALSE); + } else { + /* Stop autoscrolling. */ + terminal->pvt->mouse_autoscroll_tag = 0; + } + return (terminal->pvt->mouse_autoscroll_tag != 0); +} + +/* Start autoscroll. */ +static void +vte_terminal_start_autoscroll(VteTerminal *terminal) +{ + if (terminal->pvt->mouse_autoscroll_tag == 0) { + terminal->pvt->mouse_autoscroll_tag = + g_timeout_add_full(G_PRIORITY_LOW, + 1000 / terminal->row_count, + vte_terminal_autoscroll, + terminal, + NULL); + } +} + +/* Stop autoscroll. */ +static void +vte_terminal_stop_autoscroll(VteTerminal *terminal) +{ + if (terminal->pvt->mouse_autoscroll_tag != 0) { + g_source_remove(terminal->pvt->mouse_autoscroll_tag); + terminal->pvt->mouse_autoscroll_tag = 0; + } +} + +/* Read and handle a motion event. */ +static gint +vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event) +{ + VteTerminal *terminal; + GdkModifierType modifiers; + + g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE); + terminal = VTE_TERMINAL(widget); + +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_EVENTS)) { + fprintf(stderr, "Motion notify (%lf,%lf).\n", + event->x, event->y); + } +#endif + + /* Show the cursor. */ + vte_terminal_set_pointer_visible(terminal, TRUE); + + /* Grab input focus. */ + if (!GTK_WIDGET_HAS_FOCUS(widget)) { + gtk_widget_grab_focus(widget); + } + + /* Read the modifiers. */ + if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { + modifiers = 0; + } + + switch (event->type) { + case GDK_MOTION_NOTIFY: + switch (terminal->pvt->mouse_last_button) { + case 1: +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_EVENTS)) { + fprintf(stderr, "Mousing drag 1.\n"); + } +#endif + if (((modifiers & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) || + (!terminal->pvt->mouse_send_xy_on_click && + !terminal->pvt->mouse_send_xy_on_button && + !terminal->pvt->mouse_hilite_tracking && + !terminal->pvt->mouse_cell_motion_tracking && + !terminal->pvt->mouse_all_motion_tracking)) { + vte_terminal_extend_selection(widget, + event->x - VTE_PAD_WIDTH, + event->y - VTE_PAD_WIDTH, + FALSE); + } else { + vte_terminal_maybe_send_mouse_drag(terminal, + event); + } + break; + case 2: + case 3: + default: + break; + } + break; + default: + break; + } + + /* Start scrolling if we need to. */ + if ((event->y < VTE_PAD_WIDTH) || + (event->y > (widget->allocation.height - VTE_PAD_WIDTH))) { + switch (terminal->pvt->mouse_last_button) { + case 1: + /* Give mouse wigglers something. */ + vte_terminal_autoscroll(terminal); + /* Start a timed autoscroll if we're not doing it + * already. */ + vte_terminal_start_autoscroll(terminal); + break; + case 2: + case 3: + default: + break; + } + } + + /* Hilite any matches. */ + vte_terminal_match_hilite(terminal, + event->x - VTE_PAD_WIDTH, + event->y - VTE_PAD_WIDTH); + + /* Save the pointer coordinates for later use. */ + terminal->pvt->mouse_last_x = event->x - VTE_PAD_WIDTH; + terminal->pvt->mouse_last_y = event->y - VTE_PAD_WIDTH; + + return FALSE; +} + /* Read and handle a pointing device buttonpress event. */ static gint vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) @@ -8404,9 +8652,9 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) VteTerminal *terminal; long height, width, delta; GdkModifierType modifiers; - gboolean ret = FALSE; + gboolean handled = FALSE; + gboolean start_selecting = FALSE, extend_selecting = FALSE; long cellx, celly; - struct selection_event_coords *start, *end; g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE); terminal = VTE_TERMINAL(widget); @@ -8415,6 +8663,11 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) delta = terminal->pvt->screen->scroll_delta; vte_terminal_set_pointer_visible(terminal, TRUE); + /* Grab input focus. */ + if (!GTK_WIDGET_HAS_FOCUS(widget)) { + gtk_widget_grab_focus(widget); + } + /* Read the modifiers. */ if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { modifiers = 0; @@ -8424,180 +8677,111 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) cellx = (event->x - VTE_PAD_WIDTH) / width; celly = (event->y - VTE_PAD_WIDTH) / height + delta; - if (event->type == GDK_BUTTON_PRESS) { + switch (event->type) { + case GDK_BUTTON_PRESS: #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - char *match; - int match_tag; - fprintf(stderr, "Button %d pressed at (%lf,%lf) -> ", + fprintf(stderr, "Button %d single-click at (%lf,%lf)\n", event->button, event->x - VTE_PAD_WIDTH, - event->y - VTE_PAD_WIDTH); - fprintf(stderr, "character cell (%ld,%ld).\n", - cellx, celly); - match = vte_terminal_match_check(terminal, - cellx, - celly - delta, - &match_tag); - if (match != NULL) { - fprintf(stderr, "Matched string %d = \"%s\".\n", - match_tag, match); - g_free(match); - } + event->y - VTE_PAD_WIDTH + + (terminal->char_height * delta)); } #endif - /* If the child is handling mouse events, send it the event. */ - if ((terminal->pvt->mouse_send_xy_on_button || - terminal->pvt->mouse_send_xy_on_click) && - ((modifiers & GDK_SHIFT_MASK) == 0)) { -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Sending click to child.\n"); - } -#endif - vte_terminal_send_mouse_button(terminal, event); - } else /* Handle this event ourselves. */ - if (event->button == 1) { + switch (event->button) { + case 1: #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_EVENTS)) { fprintf(stderr, "Handling click ourselves.\n"); } #endif - if (!GTK_WIDGET_HAS_FOCUS(widget)) { - gtk_widget_grab_focus(widget); - } - /* If the user didn't hold down shift, or if the click - * was in an already-selected cell, we deselect and - * start over. */ - if (((modifiers & GDK_SHIFT_MASK) == 0) || - vte_cell_is_selected(terminal, cellx, celly)) { - /* Start or restart selection later. */ - terminal->pvt->restart_selection = TRUE; - terminal->pvt->selection_delayed.x = - event->x - VTE_PAD_WIDTH; - terminal->pvt->selection_delayed.y = - event->y - VTE_PAD_WIDTH + - (terminal->char_height * delta); - ret = TRUE; - } else { - /* Don't restart selection later -- we're - * extending it instead. */ - terminal->pvt->restart_selection = FALSE; -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Selection is " - "(%ld,%ld) to (%ld,%ld).\n", - terminal->pvt->selection_start.x, - terminal->pvt->selection_start.y, - terminal->pvt->selection_end.x, - terminal->pvt->selection_end.y); - } -#endif - /* Extend the selection by moving either the - * origin or last pointer to the event's - * coordinates. */ - if ((terminal->pvt->selection_origin.y < - terminal->pvt->selection_last.y) || - ((terminal->pvt->selection_origin.y == - terminal->pvt->selection_last.y) && - (terminal->pvt->selection_origin.x < - terminal->pvt->selection_last.x))) { - /* The origin point is "before" the end - * point. */ - start = &terminal->pvt->selection_origin; - end = &terminal->pvt->selection_last; - } else { - /* The end point is "before" the origin - * point. */ - end = &terminal->pvt->selection_origin; - start = &terminal->pvt->selection_last; - } - /* Move the selection start- or endpoint. */ - if (((event->y - VTE_PAD_WIDTH) < start->y) || - (((event->y - VTE_PAD_WIDTH) == start->y) && - ((event->x - VTE_PAD_WIDTH) < start->x))) { - /* The click was "before" the start - * point. */ - start->x = event->x - VTE_PAD_WIDTH; - start->y = event->y - VTE_PAD_WIDTH + - (terminal->char_height * delta); + + /* If the user hit shift, override event mode. */ + if ((modifiers & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) { + if (terminal->pvt->mouse_send_xy_on_button || + terminal->pvt->mouse_send_xy_on_click) { + /* If shift is pressed in event mode, + * start selecting. */ + start_selecting = TRUE; } else { - /* The click was "after" the start - * point. */ - end->x = event->x - VTE_PAD_WIDTH; - end->y = event->y - VTE_PAD_WIDTH + - (terminal->char_height * delta); - } - /* Recalculate the selection area using the - * new origin and "last" coordinates. */ - vte_terminal_selection_recompute(terminal); - /* Redraw the newly-highlited rows. */ - vte_invalidate_cells(terminal, - 0, - terminal->column_count, - terminal->pvt->selection_start.y, - terminal->pvt->selection_end.y - - terminal->pvt->selection_start.y + 1); - -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Selection changed to " - "(%ld,%ld) to (%ld,%ld).\n", - terminal->pvt->selection_start.x, - terminal->pvt->selection_start.y, - terminal->pvt->selection_end.x, - terminal->pvt->selection_end.y); + /* If shift is pressed in non-event + * mode, extend selection if the cell + * isn't already selected, otherwise + * start selection. */ + if (!vte_cell_is_selected(terminal, + cellx, + celly, + NULL)) { + extend_selecting = TRUE; + } else { + start_selecting = TRUE; + } } -#endif - ret = TRUE; + } else { + /* If shift isn't set, start selecting. */ + start_selecting = TRUE; + } + if (start_selecting) { + vte_terminal_deselect_all(terminal); + vte_terminal_start_selection(widget, + event, + selection_type_char); + handled = TRUE; } - } else - /* Paste. */ - if (event->button == 2) { -#ifdef VTE_DEBUG - if (_vte_debug_on(VTE_DEBUG_EVENTS)) { - fprintf(stderr, "Handling click ourselves.\n"); + if (extend_selecting) { + vte_terminal_extend_selection(widget, + event->x - VTE_PAD_WIDTH, + event->y - VTE_PAD_WIDTH, + TRUE); + handled = TRUE; } -#endif - vte_terminal_paste_primary(terminal); - ret = TRUE; + break; + /* Paste if the user pressed shift or we're not sending events + * to the app. */ + case 2: + if (((modifiers & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) || + (!terminal->pvt->mouse_send_xy_on_button && + !terminal->pvt->mouse_send_xy_on_click)) { + vte_terminal_paste_primary(terminal); + handled = TRUE; + } + break; + case 3: + default: + break; } - } else - if (event->type == GDK_2BUTTON_PRESS) { + /* If we haven't done anything yet, try sending the mouse + * event to the app. */ + if (handled == FALSE) { + vte_terminal_maybe_send_mouse_button(terminal, event); + handled = TRUE; + } + break; + case GDK_2BUTTON_PRESS: #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_EVENTS)) { fprintf(stderr, "Button %d double-click at (%lf,%lf)\n", event->button, event->x - VTE_PAD_WIDTH, - event->y - VTE_PAD_WIDTH); + event->y - VTE_PAD_WIDTH + + (terminal->char_height * delta)); } #endif - if (event->button == 1) { - if (!GTK_WIDGET_HAS_FOCUS(widget)) { - gtk_widget_grab_focus(widget); - } - vte_terminal_deselect_all(terminal); - terminal->pvt->has_selection = TRUE; - terminal->pvt->selection_origin.x = - event->x - VTE_PAD_WIDTH; - terminal->pvt->selection_origin.y = - event->y - VTE_PAD_WIDTH + - (terminal->char_height * delta); - terminal->pvt->selection_start.x = cellx; - terminal->pvt->selection_start.y = celly; - terminal->pvt->selection_end = - terminal->pvt->selection_start; - terminal->pvt->selection_type = selection_type_word; - vte_invalidate_cells(terminal, - 0, - terminal->column_count, - terminal->pvt->selection_start.y, - 1); - ret = TRUE; + switch (event->button) { + case 1: + vte_terminal_start_selection(widget, + event, + selection_type_word); + handled = TRUE; + break; + case 2: + case 3: + default: + break; } - } else - if (event->type == GDK_3BUTTON_PRESS) { + break; + case GDK_3BUTTON_PRESS: #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_EVENTS)) { fprintf(stderr, "Button %d triple-click at (%lf,%lf).\n", @@ -8607,29 +8791,20 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) (terminal->char_height * delta)); } #endif - if (event->button == 1) { - if (!GTK_WIDGET_HAS_FOCUS(widget)) { - gtk_widget_grab_focus(widget); - } - vte_terminal_deselect_all(terminal); - terminal->pvt->has_selection = TRUE; - terminal->pvt->selection_origin.x = - event->x - VTE_PAD_WIDTH; - terminal->pvt->selection_origin.y = - event->y - VTE_PAD_WIDTH + - (terminal->char_height * delta); - terminal->pvt->selection_start.x = cellx; - terminal->pvt->selection_start.y = celly; - terminal->pvt->selection_end = - terminal->pvt->selection_start; - terminal->pvt->selection_type = selection_type_line; - vte_invalidate_cells(terminal, - 0, - terminal->column_count, - terminal->pvt->selection_start.y, - 1); - ret = TRUE; + switch (event->button) { + case 1: + vte_terminal_start_selection(widget, + event, + selection_type_line); + handled = TRUE; + break; + case 2: + case 3: + default: + break; } + default: + break; } /* Hilite any matches. */ @@ -8642,7 +8817,7 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event) terminal->pvt->mouse_last_x = event->x - VTE_PAD_WIDTH; terminal->pvt->mouse_last_y = event->y - VTE_PAD_WIDTH; - return ret; + return FALSE; } /* Read and handle a pointing device buttonrelease event. */ @@ -8651,12 +8826,27 @@ vte_terminal_button_release(GtkWidget *widget, GdkEventButton *event) { VteTerminal *terminal; GdkModifierType modifiers; + gboolean handled = FALSE; g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE); terminal = VTE_TERMINAL(widget); vte_terminal_set_pointer_visible(terminal, TRUE); - if (event->type == GDK_BUTTON_RELEASE) { + /* Grab input focus. */ + if (!GTK_WIDGET_HAS_FOCUS(widget)) { + gtk_widget_grab_focus(widget); + } + + /* Disconnect from autoscroll requests. */ + vte_terminal_stop_autoscroll(terminal); + + /* Read the modifiers. */ + if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { + modifiers = 0; + } + + switch (event->type) { + case GDK_BUTTON_RELEASE: #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_EVENTS)) { fprintf(stderr, "Button %d released at (%lf,%lf).\n", @@ -8665,23 +8855,32 @@ vte_terminal_button_release(GtkWidget *widget, GdkEventButton *event) event->y - VTE_PAD_WIDTH); } #endif - /* Read the modifiers. */ - if (gdk_event_get_state((GdkEvent*)event, &modifiers) == FALSE) { - modifiers = 0; - } - if ((terminal->pvt->mouse_send_xy_on_button) && - ((modifiers & GDK_SHIFT_MASK) == 0)) { - vte_terminal_send_mouse_button(terminal, event); - } else - if (event->button == 1) { - vte_terminal_copy(terminal, GDK_SELECTION_PRIMARY); + switch (event->button) { + case 1: + /* If Shift is held down, or we're not in events mode, + * copy the selected text. */ + if (((modifiers & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) || + (!terminal->pvt->mouse_send_xy_on_button)) { + /* Copy only if something was selected. */ + if (!terminal->pvt->restart_selection) { + vte_terminal_copy(terminal, + GDK_SELECTION_PRIMARY); + } + handled = TRUE; + } + break; + case 2: + case 3: + default: + break; } - - /* Disconnect from autoscroll requests. */ - if (terminal->pvt->mouse_autoscroll_tag) { - g_source_remove(terminal->pvt->mouse_autoscroll_tag); - terminal->pvt->mouse_autoscroll_tag = 0; + if (handled == FALSE) { + vte_terminal_maybe_send_mouse_button(terminal, event); + handled = TRUE; } + break; + default: + break; } /* Hilite any matches. */ @@ -9662,7 +9861,7 @@ vte_terminal_refresh_size(VteTerminal *terminal) g_return_if_fail(VTE_IS_TERMINAL(terminal)); if (terminal->pvt->pty_master != -1) { /* Use an ioctl to read the size of the terminal. */ - if (vte_pty_get_size(terminal->pvt->pty_master, &columns, &rows) != 0) { + if (_vte_pty_get_size(terminal->pvt->pty_master, &columns, &rows) != 0) { g_warning(_("Error reading PTY size, using defaults: " "%s."), strerror(errno)); } else { @@ -9698,7 +9897,7 @@ vte_terminal_set_size(VteTerminal *terminal, glong columns, glong rows) size.ws_row = rows; size.ws_col = columns; /* Try to set the terminal size. */ - if (vte_pty_set_size(terminal->pvt->pty_master, columns, rows) != 0) { + if (_vte_pty_set_size(terminal->pvt->pty_master, columns, rows) != 0) { g_warning(_("Error setting PTY size: %s."), strerror(errno)); } @@ -10219,7 +10418,6 @@ vte_terminal_init(VteTerminal *terminal, gpointer *klass) * devel version with fontconfig support or 2.2 or later. */ render_max = VteRenderXft2; #endif -#endif /* Let debugging users have some influence on how we render text. */ if ((render_max == VteRenderXft2) && (getenv("VTE_USE_XFT2") != NULL)) { @@ -10239,6 +10437,7 @@ vte_terminal_init(VteTerminal *terminal, gpointer *klass) render_max = VteRenderPango; } } +#endif if ((render_max == VteRenderPango) && (getenv("VTE_USE_PANGO") != NULL)) { if (atol(getenv("VTE_USE_PANGO")) == 0) { @@ -10256,14 +10455,16 @@ vte_terminal_init(VteTerminal *terminal, gpointer *klass) #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_MISC)) { switch (pvt->render_method) { - case VteRenderXft2: #ifdef HAVE_XFT2 + case VteRenderXft2: fprintf(stderr, "Using Xft2.\n"); break; #endif +#ifdef HAVE_XFT case VteRenderXft1: fprintf(stderr, "Using Xft1.\n"); break; +#endif case VteRenderPango: fprintf(stderr, "Using Pango.\n"); break; @@ -10620,10 +10821,7 @@ vte_terminal_finalize(GObject *object) } /* Disconnect from autoscroll requests. */ - if (terminal->pvt->mouse_autoscroll_tag) { - g_source_remove(terminal->pvt->mouse_autoscroll_tag); - terminal->pvt->mouse_autoscroll_tag = 0; - } + vte_terminal_stop_autoscroll(terminal); /* Cancel pending adjustment change notifications. */ if (terminal->pvt->adjustment_changed_tag) { @@ -10726,7 +10924,7 @@ vte_terminal_finalize(GObject *object) terminal->pvt->pty_output_source = -1; } if (terminal->pvt->pty_master != -1) { - vte_pty_close(terminal->pvt->pty_master); + _vte_pty_close(terminal->pvt->pty_master); terminal->pvt->pty_master = -1; } @@ -10897,14 +11095,19 @@ vte_terminal_realize(GtkWidget *widget) g_object_unref(G_OBJECT(mask)); /* Grab input focus. */ - gtk_widget_grab_focus(widget); + if (!GTK_WIDGET_HAS_FOCUS(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) + const struct vte_charcell *cell, + gboolean reverse, int *fore, int *back) { + g_assert(fore != NULL); + g_assert(back != NULL); + /* Determine what the foreground and background colors for rendering * text should be. */ if (reverse ^ ((cell != NULL) && (cell->reverse))) { @@ -11791,9 +11994,22 @@ vte_terminal_draw_cells(VteTerminal *terminal, x += VTE_PAD_WIDTH; y += VTE_PAD_WIDTH; +#ifdef VTE_DEBUG + if (_vte_debug_on(VTE_DEBUG_MISC) && 0) { + fprintf(stderr, "Rendering"); + for (i = 0; i < n; i++) { + fprintf(stderr, " (%ld,%ld)", + (long) items[i].c, + (long) items[i].columns); + g_assert(items[i].columns > 0); + } + fprintf(stderr, ".\n"); + } +#endif + switch (terminal->pvt->render_method) { - case VteRenderXft2: #ifdef HAVE_XFT + case VteRenderXft2: #ifdef HAVE_XFT2 #ifdef VTE_DEBUG if (_vte_debug_on(VTE_DEBUG_MISC) && 0) { @@ -11802,7 +12018,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, #endif /* Set up the draw request. */ ftcharspecs = g_malloc(sizeof(XftCharSpec) * n); - for (i = columns = 0; i < n; i++) { + for (i = columns = 0; i < n; i++) { c = items[i].c ? items[i].c : ' '; ftcharspecs[i].ucs4 = vte_terminal_xft_remap_char(display, terminal->pvt->ftfont, @@ -11810,7 +12026,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, ftcharspecs[i].x = x + (columns * column_width) + items[i].xpad; ftcharspecs[i].y = y + ascent; - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } if (ftdraw != NULL) { /* Draw the background rectangle. */ @@ -11842,10 +12058,10 @@ vte_terminal_draw_cells(VteTerminal *terminal, fprintf(stderr, "Rendering with Xft1.\n"); } #endif - /* Set up the draw request. */ + /* Set up the draw request -- calculate the total width. */ for (i = columns = 0; i < n; i++) { c = items[i].c ? items[i].c : ' '; - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } /* Draw the background rectangle. */ if ((back != VTE_DEF_BG) || draw_default_bg) { @@ -11872,7 +12088,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, y + ascent, &ftchar, 1); } - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } break; #endif @@ -11886,7 +12102,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, if ((back != VTE_DEF_BG) || draw_default_bg) { for (i = columns = 0; i < n; i++) { c = items[i].c ? items[i].c : ' '; - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } color.red = terminal->pvt->palette[back].red; color.blue = terminal->pvt->palette[back].blue; @@ -11921,7 +12137,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, y + y_offs, layout); } - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } break; case VteRenderPangoX: @@ -11934,7 +12150,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, if ((back != VTE_DEF_BG) || draw_default_bg) { for (i = columns = 0; i < n; i++) { c = items[i].c ? items[i].c : ' '; - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } XSetForeground(display, gc, bg->pixel); XFillRectangle(display, drawable, gc, @@ -11969,7 +12185,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, items[i].xpad + 1, y); } - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } break; case VteRenderXlib: @@ -11982,7 +12198,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, if ((back != VTE_DEF_BG) || draw_default_bg) { for (i = columns = 0; i < n; i++) { c = items[i].c ? items[i].c : ' '; - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } XSetForeground(display, gc, bg->pixel); XFillRectangle(display, drawable, gc, @@ -12008,7 +12224,7 @@ vte_terminal_draw_cells(VteTerminal *terminal, items[i].xpad + 1, y + ascent, &textitem, 1); } - columns += g_unichar_iswide(c) ? 2 : 1; + columns += items[i].columns; } } @@ -12071,11 +12287,12 @@ vte_terminal_get_blink_state(VteTerminal *terminal) terminal->pvt->cursor_force_fg--; blink = TRUE; } - terminal->pvt->cursor_force_fg = FALSE; return blink; } -/* Paint the contents of a given row at the given location. */ +/* Paint the contents of a given row at the given location. Take advantage + * of multiple-draw APIs by finding runs of characters with identical + * attributes and bundling them together. */ static void vte_terminal_draw_row(VteTerminal *terminal, VteScreen *screen, @@ -12106,23 +12323,22 @@ vte_terminal_draw_row(VteTerminal *terminal, /* Allocate an array to hold draw requests. */ items = g_array_new(FALSE, FALSE, sizeof(struct vte_draw_item)); - /* Find groups of characters with identical attributes and bundle - * them together. */ + /* Back up in case this is a multicolumn character, making the drawing + * area a little wider. */ i = column; - - /* Back up in case this is a multicolumn character. */ cell = vte_terminal_find_charcell(terminal, i, row); - if ((cell != NULL) && (cell->columns == 0) && (i > 0)) { + if ((cell != NULL) && (cell->fragment) && (i > 0)) { cell = vte_terminal_find_charcell(terminal, i - 1, row); column--; column_count++; } + /* Walk the line. */ while (i < column + column_count) { /* Get the character cell's contents. */ cell = vte_terminal_find_charcell(terminal, i, row); /* Find the colors for this cell. */ - reverse = vte_cell_is_selected(terminal, i, row) ^ + reverse = vte_cell_is_selected(terminal, i, row, NULL) ^ terminal->pvt->screen->reverse_mode; vte_terminal_determine_colors(terminal, cell, reverse, &fore, &back); @@ -12143,6 +12359,7 @@ vte_terminal_draw_row(VteTerminal *terminal, if ((cell != NULL) && (cell->alternate || vte_unichar_isgraphic(cell->c))) { item.c = cell ? cell->c : ' '; + item.columns = cell ? cell->columns : 1; vte_terminal_draw_graphic(terminal, cell->c, fore, back, FALSE, x + @@ -12155,12 +12372,25 @@ vte_terminal_draw_row(VteTerminal *terminal, drawable, ggc, gc); - i++; + i += item.columns; continue; } /* Add this cell to the draw list. */ item.c = cell ? cell->c : ' '; + item.columns = cell ? cell->columns : 1; + /* Special case certain whitespace characters which the font + * probably can't render correctly, if at all. */ + switch (item.c) { + case '\0': + case '\t': + item.c = ' '; + break; + default: + break; + } + /* Compute the left padding necessary to draw this character + * in the approximate middle of its columns. */ if (monospaced) { item.xpad = 0; } else { @@ -12177,19 +12407,24 @@ vte_terminal_draw_row(VteTerminal *terminal, j++) { /* Don't render fragments of multicolumn characters. */ cell = vte_terminal_find_charcell(terminal, j, row); - if ((cell != NULL) && (cell->columns == 0)) { + if ((cell != NULL) && (cell->fragment)) { continue; } + /* Graphic characters must be drawn individually. */ if ((cell != NULL) && (cell->alternate)) { break; } if ((cell != NULL) && vte_unichar_isgraphic(cell->c)) { break; } + /* Special case certain non-printing characters. */ + if ((cell != NULL) && (cell->c == '\t')) { + break; + } /* Resolve attributes to colors where possible and * compare visual attributes to the first character * in this chunk. */ - reverse = vte_cell_is_selected(terminal, j, row) ^ + reverse = vte_cell_is_selected(terminal, j, row, NULL) ^ terminal->pvt->screen->reverse_mode; vte_terminal_determine_colors(terminal, cell, reverse, &nfore, &nback); @@ -12223,7 +12458,8 @@ vte_terminal_draw_row(VteTerminal *terminal, break; } /* Add this cell to the draw list. */ - item.c = cell ? cell->c : ' '; + item.c = cell ? (cell->c ? cell->c : ' ') : ' '; + item.columns = cell ? cell->columns : 1; if (monospaced) { item.xpad = 0; } else { @@ -12404,7 +12640,7 @@ vte_terminal_paint(GtkWidget *widget, GdkRectangle *area) /* Find the character "under" the cursor. */ cell = vte_terminal_find_charcell(terminal, col, drow); - while ((cell != NULL) && (cell->columns == 0) && (col >= 0)) { + while ((cell != NULL) && (cell->fragment) && (col >= 0)) { col--; cell = vte_terminal_find_charcell(terminal, col, drow); } @@ -12431,6 +12667,7 @@ vte_terminal_paint(GtkWidget *widget, GdkRectangle *area) gc); } else { item.c = cell ? cell->c : ' '; + item.columns = cell ? cell->columns : 1; item.xpad = vte_terminal_get_char_padding(terminal, display, item.c); @@ -12497,7 +12734,8 @@ vte_terminal_paint(GtkWidget *widget, GdkRectangle *area) items[i].xpad = vte_terminal_get_char_padding(terminal, display, item.c); - columns += g_unichar_iswide(items[i].c) ? 2 : 1; + items[i].columns = g_unichar_iswide(items[i].c) ? 2 : 1; + columns += items[i].columns; preedit = g_utf8_next_char(preedit); } fore = screen->defaults.fore; @@ -13706,8 +13944,12 @@ gboolean vte_terminal_get_using_xft(VteTerminal *terminal) { g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); +#ifdef HAVE_XFT return (terminal->pvt->render_method == VteRenderXft2) || (terminal->pvt->render_method == VteRenderXft1); +#else + return FALSE; +#endif } /** @@ -13782,6 +14024,10 @@ vte_terminal_set_scrollback_lines(VteTerminal *terminal, glong lines) low, highd); screens[i]->cursor_current.row = CLAMP(screens[i]->cursor_current.row, low, high); + /* Clear the matching view. */ + vte_terminal_match_contents_clear(terminal); + /* Notify viewers that the contents have changed. */ + vte_terminal_emit_contents_changed(terminal); } terminal->pvt->scrollback_lines = lines; @@ -14083,6 +14329,20 @@ vte_terminal_reset(VteTerminal *terminal, gboolean full, gboolean clear_history) g_assert(terminal->pvt->encoding != NULL); /* Reset selection. */ vte_terminal_deselect_all(terminal); + terminal->pvt->has_selection = FALSE; + terminal->pvt->restart_selection = FALSE; + if (terminal->pvt->selection != NULL) { + g_free(terminal->pvt->selection); + terminal->pvt->selection = NULL; + memset(&terminal->pvt->selection_origin, 0, + sizeof(&terminal->pvt->selection_origin)); + memset(&terminal->pvt->selection_last, 0, + sizeof(&terminal->pvt->selection_last)); + memset(&terminal->pvt->selection_start, 0, + sizeof(&terminal->pvt->selection_start)); + memset(&terminal->pvt->selection_end, 0, + sizeof(&terminal->pvt->selection_end)); + } /* Reset mouse motion events. */ terminal->pvt->mouse_send_xy_on_click = FALSE; terminal->pvt->mouse_send_xy_on_button = FALSE; @@ -86,8 +86,9 @@ struct _VteTerminalClass { guint resize_window_signal; guint move_window_signal; guint status_line_changed_signal; - guint commit_signal; + + gpointer reserved1; gpointer reserved2; gpointer reserved3; gpointer reserved4; @@ -133,15 +134,11 @@ GtkType vte_terminal_erase_binding_get_type(void); /* You can get by with just these two functions. */ GtkWidget *vte_terminal_new(void); pid_t vte_terminal_fork_command(VteTerminal *terminal, - const char *command, char **argv, char **envv); - -/* If you need the session logged, try this instead. */ -pid_t vte_terminal_fork_logged_command(VteTerminal *terminal, - const char *command, char **argv, - char **envv, - gboolean lastlog, - gboolean utmp, - gboolean wtmp); + const char *command, char **argv, + char **envv, const char *directory, + gboolean lastlog, + gboolean utmp, + gboolean wtmp); /* Send data to the terminal to display, or to the terminal's forked command * to handle in some way. If it's 'cat', they should be the same. */ @@ -243,14 +240,18 @@ void vte_terminal_reset(VteTerminal *terminal, gboolean full, char *vte_terminal_get_text(VteTerminal *terminal, gboolean(*is_selected)(VteTerminal *terminal, glong column, - glong row), + glong row, + gpointer data), + gpointer data, GArray *attributes); char *vte_terminal_get_text_range(VteTerminal *terminal, glong start_row, glong start_col, glong end_row, glong end_col, gboolean(*is_selected)(VteTerminal *terminal, glong column, - glong row), + glong row, + gpointer data), + gpointer data, GArray *attributes); void vte_terminal_get_cursor_position(VteTerminal *terminal, glong *column, glong *row); diff --git a/src/vteaccess.c b/src/vteaccess.c index 5633b0a..f7f6e96 100644 --- a/src/vteaccess.c +++ b/src/vteaccess.c @@ -139,7 +139,9 @@ vte_terminal_accessible_update_private_data_if_needed(AtkObject *text) priv->snapshot_linebreaks = g_array_new(FALSE, TRUE, sizeof(int)); /* Get a new view of the uber-label. */ - priv->snapshot_text = vte_terminal_get_text(terminal, all_selected, + priv->snapshot_text = vte_terminal_get_text(terminal, + all_selected, + NULL, priv->snapshot_attributes); if (priv->snapshot_text == NULL) { /* Aaargh! We're screwed. */ diff --git a/src/vteapp.c b/src/vteapp.c index b06ffa8..6d4aa16 100644 --- a/src/vteapp.c +++ b/src/vteapp.c @@ -106,6 +106,7 @@ main(int argc, char **argv) const char *font = NULL; const char *terminal = NULL; const char *command = NULL; + const char *working_directory = NULL; char **argv2; int opt; int i, j; @@ -152,7 +153,7 @@ main(int argc, char **argv) argv2[i] = NULL; g_assert(i < (g_list_length(args) + 2)); /* Parse some command-line options. */ - while ((opt = getopt(argc, argv, "B:DTabc:df:ghn:t:")) != -1) { + while ((opt = getopt(argc, argv, "B:DTabc:df:ghn:t:w:")) != -1) { switch (opt) { case 'B': background = optarg; @@ -190,6 +191,9 @@ main(int argc, char **argv) case 't': terminal = optarg; break; + case 'w': + working_directory = optarg; + break; case 'h': default: g_print(usage, argv[0]); @@ -283,9 +287,9 @@ main(int argc, char **argv) strlen(message)); } #endif - vte_terminal_fork_logged_command(VTE_TERMINAL(widget), - command, NULL, env_add, - TRUE, TRUE, TRUE); + vte_terminal_fork_command(VTE_TERMINAL(widget), + command, NULL, env_add, working_directory, + TRUE, TRUE, TRUE); if (command == NULL) { vte_terminal_feed_child(VTE_TERMINAL(widget), "pwd\n", -1); } @@ -1,5 +1,5 @@ Name: vte -Version: 0.9.2 +Version: 0.10 Release: 1 Summary: An experimental terminal emulator. License: LGPL @@ -64,6 +64,9 @@ rm $RPM_BUILD_ROOT/%{_libdir}/lib%{name}.la %{_libdir}/pkgconfig/* %changelog +* Thu Oct 24 2002 Nalin Dahyabhai <nalin@redhat.com> 0.10-1 +- allow setting the working directory (#76529) + * Thu Oct 17 2002 Nalin Dahyabhai <nalin@redhat.com> 0.9.2-1 - fix the crash-on-resize bug (#75871) - add bold |