2 * Copyright (C) 2001-2004,2009,2010 Red Hat, Inc.
3 * Copyright © 2008, 2009, 2010 Christian Persch
5 * This is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Library General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * SECTION: vte-terminal
22 * @short_description: A terminal widget implementation
24 * A VteTerminal is a terminal emulator implemented as a GTK2 widget.
32 #include "vte-private.h"
37 #ifdef HAVE_SYS_SYSLIMITS_H
38 #include <sys/syslimits.h>
40 #ifdef HAVE_SYS_WAIT_H
44 #include <glib/gstdio.h>
45 #include <glib-object.h>
47 #include <gdk/gdkkeysyms.h>
49 #include <pango/pango.h>
55 #include "vteaccess.h"
58 #include "vtepty-private.h"
67 typedef gunichar wint_t;
71 #define howmany(x, y) (((x) + ((y) - 1)) / (y))
74 #define STATIC_PARAMS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
77 static void vte_terminal_set_visibility (VteTerminal *terminal, GdkVisibilityState state);
78 static void vte_terminal_set_termcap(VteTerminal *terminal, const char *path,
80 static void vte_terminal_paste(VteTerminal *terminal, GdkAtom board);
81 static void vte_terminal_real_copy_clipboard(VteTerminal *terminal);
82 static void vte_terminal_real_paste_clipboard(VteTerminal *terminal);
83 static gboolean vte_terminal_io_read(GIOChannel *channel,
84 GIOCondition condition,
85 VteTerminal *terminal);
86 static gboolean vte_terminal_io_write(GIOChannel *channel,
87 GIOCondition condition,
88 VteTerminal *terminal);
89 static void vte_terminal_match_hilite_clear(VteTerminal *terminal);
90 static void vte_terminal_match_hilite_hide(VteTerminal *terminal);
91 static void vte_terminal_match_hilite_show(VteTerminal *terminal, long x, long y);
92 static void vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y);
93 static void vte_terminal_match_contents_clear(VteTerminal *terminal);
94 static gboolean vte_terminal_background_update(VteTerminal *data);
95 static void vte_terminal_queue_background_update(VteTerminal *terminal);
96 static void vte_terminal_process_incoming(VteTerminal *terminal);
97 static void vte_terminal_emit_pending_signals(VteTerminal *terminal);
98 static gboolean vte_cell_is_selected(VteTerminal *terminal,
99 glong col, glong row, gpointer data);
100 static char *vte_terminal_get_text_range_maybe_wrapped(VteTerminal *terminal,
106 VteSelectionFunc is_selected,
109 gboolean include_trailing_spaces);
110 static char *vte_terminal_get_text_maybe_wrapped(VteTerminal *terminal,
112 VteSelectionFunc is_selected,
115 gboolean include_trailing_spaces);
116 static void _vte_terminal_disconnect_pty_read(VteTerminal *terminal);
117 static void _vte_terminal_disconnect_pty_write(VteTerminal *terminal);
118 static void vte_terminal_stop_processing (VteTerminal *terminal);
120 static inline gboolean vte_terminal_is_processing (VteTerminal *terminal);
121 static inline void vte_terminal_start_processing (VteTerminal *terminal);
122 static void vte_terminal_add_process_timeout (VteTerminal *terminal);
123 static void add_update_timeout (VteTerminal *terminal);
124 static void remove_update_timeout (VteTerminal *terminal);
125 static void reset_update_regions (VteTerminal *terminal);
126 static void vte_terminal_set_cursor_blinks_internal(VteTerminal *terminal, gboolean blink);
127 static void vte_terminal_set_font_full_internal(VteTerminal *terminal,
128 const PangoFontDescription *font_desc,
129 VteTerminalAntiAlias antialias);
131 static gboolean process_timeout (gpointer data);
132 static gboolean update_timeout (gpointer data);
139 static guint signals[LAST_SIGNAL];
145 PROP_BACKGROUND_IMAGE_FILE,
146 PROP_BACKGROUND_IMAGE_PIXBUF,
147 PROP_BACKGROUND_OPACITY,
148 PROP_BACKGROUND_SATURATION,
149 PROP_BACKGROUND_TINT_COLOR,
150 PROP_BACKGROUND_TRANSPARENT,
151 PROP_BACKSPACE_BINDING,
152 PROP_CURSOR_BLINK_MODE,
159 PROP_MOUSE_POINTER_AUTOHIDE,
162 PROP_SCROLL_BACKGROUND,
163 PROP_SCROLLBACK_LINES,
164 PROP_SCROLL_ON_KEYSTROKE,
165 PROP_SCROLL_ON_OUTPUT,
171 /* these static variables are guarded by the GDK mutex */
172 static guint process_timeout_tag = 0;
173 static gboolean in_process_timeout;
174 static guint update_timeout_tag = 0;
175 static gboolean in_update_timeout;
176 static GList *active_terminals;
177 static GTimer *process_timer;
179 static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
181 /* process incoming data without copying */
182 static struct _vte_incoming_chunk *free_chunks;
183 static struct _vte_incoming_chunk *
186 struct _vte_incoming_chunk *chunk = NULL;
189 free_chunks = free_chunks->next;
192 chunk = g_new (struct _vte_incoming_chunk, 1);
199 release_chunk (struct _vte_incoming_chunk *chunk)
201 chunk->next = free_chunks;
202 chunk->len = free_chunks ? free_chunks->len + 1 : 0;
206 prune_chunks (guint len)
208 struct _vte_incoming_chunk *chunk = NULL;
209 if (len && free_chunks != NULL) {
210 if (free_chunks->len > len) {
211 struct _vte_incoming_chunk *last;
213 while (free_chunks->len > len) {
215 free_chunks = free_chunks->next;
223 while (chunk != NULL) {
224 struct _vte_incoming_chunk *next = chunk->next;
230 _vte_incoming_chunks_release (struct _vte_incoming_chunk *chunk)
233 struct _vte_incoming_chunk *next = chunk->next;
234 release_chunk (chunk);
239 _vte_incoming_chunks_length (struct _vte_incoming_chunk *chunk)
249 _vte_incoming_chunks_count (struct _vte_incoming_chunk *chunk)
258 static struct _vte_incoming_chunk *
259 _vte_incoming_chunks_reverse(struct _vte_incoming_chunk *chunk)
261 struct _vte_incoming_chunk *prev = NULL;
263 struct _vte_incoming_chunk *next = chunk->next;
273 G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET,
274 if (_vte_debug_on(VTE_DEBUG_LIFECYCLE)) {
275 g_printerr("vte_terminal_get_type()\n");
278 G_DEFINE_TYPE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET)
282 /* Indexes in the "palette" color array for the dim colors.
283 * Only the first %VTE_LEGACY_COLOR_SET_SIZE colors have dim versions. */
284 static const guchar corresponding_dim_index[] = {16,88,28,100,18,90,30,102};
287 vte_g_array_fill(GArray *array, gconstpointer item, guint final_size)
289 if (array->len >= final_size)
292 final_size -= array->len;
294 g_array_append_vals(array, item, 1);
295 } while (--final_size);
300 _vte_terminal_ring_insert (VteTerminal *terminal, glong position, gboolean fill)
303 VteRing *ring = terminal->pvt->screen->row_data;
304 while (G_UNLIKELY (_vte_ring_next (ring) < position)) {
305 row = _vte_ring_append (ring);
306 _vte_row_data_fill (row, &terminal->pvt->screen->fill_defaults, terminal->column_count);
308 row = _vte_ring_insert (ring, position);
310 _vte_row_data_fill (row, &terminal->pvt->screen->fill_defaults, terminal->column_count);
315 _vte_terminal_ring_append (VteTerminal *terminal, gboolean fill)
317 return _vte_terminal_ring_insert (terminal, _vte_ring_next (terminal->pvt->screen->row_data), fill);
321 _vte_terminal_ring_remove (VteTerminal *terminal, glong position)
323 _vte_ring_remove (terminal->pvt->screen->row_data, position);
326 /* Reset defaults for character insertion. */
328 _vte_terminal_set_default_attributes(VteTerminal *terminal)
332 screen = terminal->pvt->screen;
334 screen->defaults = basic_cell.cell;
335 screen->color_defaults = screen->defaults;
336 screen->fill_defaults = screen->defaults;
339 /* Cause certain cells to be repainted. */
341 _vte_invalidate_cells(VteTerminal *terminal,
342 glong column_start, gint column_count,
343 glong row_start, gint row_count)
348 if (!column_count || !row_count) {
352 if (G_UNLIKELY (!GTK_WIDGET_DRAWABLE(terminal) ||
353 terminal->pvt->invalidated_all)) {
357 _vte_debug_print (VTE_DEBUG_UPDATES,
358 "Invalidating cells at (%ld,%ld+%ld)x(%d,%d).\n",
359 column_start, row_start,
360 (long)terminal->pvt->screen->scroll_delta,
361 column_count, row_count);
362 _vte_debug_print (VTE_DEBUG_WORK, "?");
364 /* Subtract the scrolling offset from the row start so that the
365 * resulting rectangle is relative to the visible portion of the
367 row_start -= terminal->pvt->screen->scroll_delta;
369 /* Ensure the start of region is on screen */
370 if (column_start > terminal->column_count ||
371 row_start > terminal->row_count) {
375 /* Clamp the start values to reasonable numbers. */
376 i = row_start + row_count;
377 row_start = MAX (0, row_start);
378 row_count = CLAMP (i - row_start, 0, terminal->row_count);
380 i = column_start + column_count;
381 column_start = MAX (0, column_start);
382 column_count = CLAMP (i - column_start, 0 , terminal->column_count);
384 if (!column_count || !row_count) {
387 if (column_count == terminal->column_count &&
388 row_count == terminal->row_count) {
389 _vte_invalidate_all (terminal);
393 /* Convert the column and row start and end to pixel values
394 * by multiplying by the size of a character cell.
395 * Always include the extra pixel border and overlap pixel.
397 rect.x = column_start * terminal->char_width - 1;
398 if (column_start != 0) {
399 rect.x += terminal->pvt->inner_border.left;
401 rect.width = (column_start + column_count) * terminal->char_width + 3 + terminal->pvt->inner_border.left;
402 if (column_start + column_count == terminal->column_count) {
403 rect.width += terminal->pvt->inner_border.right;
405 rect.width -= rect.x;
407 rect.y = row_start * terminal->char_height - 1;
408 if (row_start != 0) {
409 rect.y += terminal->pvt->inner_border.top;
411 rect.height = (row_start + row_count) * terminal->char_height + 2 + terminal->pvt->inner_border.top;
412 if (row_start + row_count == terminal->row_count) {
413 rect.height += terminal->pvt->inner_border.bottom;
415 rect.height -= rect.y;
417 _vte_debug_print (VTE_DEBUG_UPDATES,
418 "Invalidating pixels at (%d,%d)x(%d,%d).\n",
419 rect.x, rect.y, rect.width, rect.height);
421 if (terminal->pvt->active != NULL) {
422 terminal->pvt->update_regions = g_slist_prepend (
423 terminal->pvt->update_regions,
424 gdk_region_rectangle (&rect));
425 /* Wait a bit before doing any invalidation, just in
426 * case updates are coming in really soon. */
427 add_update_timeout (terminal);
429 gdk_window_invalidate_rect (terminal->widget.window,
433 _vte_debug_print (VTE_DEBUG_WORK, "!");
437 _vte_invalidate_region (VteTerminal *terminal,
438 glong scolumn, glong ecolumn,
439 glong srow, glong erow,
442 if (block || srow == erow) {
443 _vte_invalidate_cells(terminal,
444 scolumn, ecolumn - scolumn + 1,
445 srow, erow - srow + 1);
447 _vte_invalidate_cells(terminal,
449 terminal->column_count - scolumn,
451 _vte_invalidate_cells(terminal,
452 0, terminal->column_count,
453 srow + 1, erow - srow - 1);
454 _vte_invalidate_cells(terminal,
461 /* Redraw the entire visible portion of the window. */
463 _vte_invalidate_all(VteTerminal *terminal)
467 g_assert(VTE_IS_TERMINAL(terminal));
469 if (!GTK_WIDGET_DRAWABLE(terminal)) {
472 if (terminal->pvt->invalidated_all) {
476 _vte_debug_print (VTE_DEBUG_WORK, "*");
477 _vte_debug_print (VTE_DEBUG_UPDATES, "Invalidating all.\n");
479 /* replace invalid regions with one covering the whole terminal */
480 reset_update_regions (terminal);
482 rect.width = terminal->widget.allocation.width;
483 rect.height = terminal->widget.allocation.height;
484 terminal->pvt->invalidated_all = TRUE;
486 if (terminal->pvt->active != NULL) {
487 terminal->pvt->update_regions = g_slist_prepend (NULL,
488 gdk_region_rectangle (&rect));
489 /* Wait a bit before doing any invalidation, just in
490 * case updates are coming in really soon. */
491 add_update_timeout (terminal);
493 gdk_window_invalidate_rect (terminal->widget.window,
499 /* Scroll a rectangular region up or down by a fixed number of lines,
500 * negative = up, positive = down. */
502 _vte_terminal_scroll_region (VteTerminal *terminal,
503 long row, glong count, glong delta)
505 if ((delta == 0) || (count == 0)) {
510 if (terminal->pvt->scroll_background || count >= terminal->row_count) {
511 /* We have to repaint the entire window. */
512 _vte_invalidate_all(terminal);
514 /* We have to repaint the area which is to be
516 _vte_invalidate_cells(terminal,
517 0, terminal->column_count,
522 /* Find the row in the given position in the backscroll buffer. */
523 static inline const VteRowData *
524 _vte_terminal_find_row_data (VteTerminal *terminal, glong row)
526 const VteRowData *rowdata = NULL;
527 VteScreen *screen = terminal->pvt->screen;
528 if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
529 rowdata = _vte_ring_index (screen->row_data, row);
534 /* Find the row in the given position in the backscroll buffer. */
535 static inline VteRowData *
536 _vte_terminal_find_row_data_writable (VteTerminal *terminal, glong row)
538 VteRowData *rowdata = NULL;
539 VteScreen *screen = terminal->pvt->screen;
540 if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
541 rowdata = _vte_ring_index_writable (screen->row_data, row);
546 /* Find the character an the given position in the backscroll buffer. */
547 static const VteCell *
548 vte_terminal_find_charcell(VteTerminal *terminal, gulong col, glong row)
550 const VteRowData *rowdata;
551 const VteCell *ret = NULL;
553 screen = terminal->pvt->screen;
554 if (_vte_ring_contains (screen->row_data, row)) {
555 rowdata = _vte_ring_index (screen->row_data, row);
556 ret = _vte_row_data_get (rowdata, col);
562 find_start_column (VteTerminal *terminal, glong col, glong row)
564 const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
565 if (G_UNLIKELY (col < 0))
567 if (row_data != NULL) {
568 const VteCell *cell = _vte_row_data_get (row_data, col);
569 while (col > 0 && cell != NULL && cell->attr.fragment) {
570 cell = _vte_row_data_get (row_data, --col);
576 find_end_column (VteTerminal *terminal, glong col, glong row)
578 const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
580 if (G_UNLIKELY (col < 0))
582 if (row_data != NULL) {
583 const VteCell *cell = _vte_row_data_get (row_data, col);
584 while (col > 0 && cell != NULL && cell->attr.fragment) {
585 cell = _vte_row_data_get (row_data, --col);
588 columns = cell->attr.columns - 1;
591 return MIN(col + columns, terminal->column_count);
595 /* Determine the width of the portion of the preedit string which lies
596 * to the left of the cursor, or the entire string, in columns. */
598 vte_terminal_preedit_width(VteTerminal *terminal, gboolean left_only)
603 const char *preedit = NULL;
605 if (terminal->pvt->im_preedit != NULL) {
606 preedit = terminal->pvt->im_preedit;
609 (preedit[0] != '\0') &&
610 (!left_only || (i < terminal->pvt->im_preedit_cursor));
612 c = g_utf8_get_char(preedit);
613 ret += _vte_iso2022_unichar_width(terminal->pvt->iso2022, c);
614 preedit = g_utf8_next_char(preedit);
621 /* Determine the length of the portion of the preedit string which lies
622 * to the left of the cursor, or the entire string, in gunichars. */
624 vte_terminal_preedit_length(VteTerminal *terminal, gboolean left_only)
627 const char *preedit = NULL;
629 if (terminal->pvt->im_preedit != NULL) {
630 preedit = terminal->pvt->im_preedit;
633 (preedit[0] != '\0') &&
634 (!left_only || (i < terminal->pvt->im_preedit_cursor));
636 preedit = g_utf8_next_char(preedit);
643 /* Cause the cell to be redrawn. */
645 _vte_invalidate_cell(VteTerminal *terminal, glong col, glong row)
647 const VteRowData *row_data;
650 if (G_UNLIKELY (!GTK_WIDGET_DRAWABLE(terminal) || terminal->pvt->invalidated_all)) {
655 row_data = _vte_terminal_find_row_data(terminal, row);
656 if (row_data != NULL) {
658 cell = _vte_row_data_get (row_data, col);
660 while (cell->attr.fragment && col> 0) {
661 cell = _vte_row_data_get (row_data, --col);
663 columns = cell->attr.columns;
665 _vte_draw_get_char_width (
668 columns, cell->attr.bold) >
669 terminal->char_width * columns) {
675 _vte_debug_print(VTE_DEBUG_UPDATES,
676 "Invalidating cell at (%ld,%ld-%ld).\n",
677 row, col, col + columns);
678 _vte_invalidate_cells(terminal,
683 /* Cause the cursor to be redrawn. */
685 _vte_invalidate_cursor_once(VteTerminal *terminal, gboolean periodic)
689 gssize preedit_width;
693 if (terminal->pvt->invalidated_all) {
698 if (!terminal->pvt->cursor_blinks) {
703 if (terminal->pvt->cursor_visible && GTK_WIDGET_DRAWABLE(terminal)) {
704 preedit_width = vte_terminal_preedit_width(terminal, FALSE);
706 screen = terminal->pvt->screen;
707 row = screen->cursor_current.row;
708 column = screen->cursor_current.col;
710 column = find_start_column (terminal, column, row);
711 cell = vte_terminal_find_charcell(terminal, column, row);
713 columns = cell->attr.columns;
715 _vte_draw_get_char_width (
718 columns, cell->attr.bold) >
719 terminal->char_width * columns) {
723 if (preedit_width > 0) {
724 columns += preedit_width;
725 columns++; /* one more for the preedit cursor */
728 _vte_debug_print(VTE_DEBUG_UPDATES,
729 "Invalidating cursor at (%ld,%ld-%ld).\n",
730 row, column, column + columns);
731 _vte_invalidate_cells(terminal,
737 /* Invalidate the cursor repeatedly. */
739 vte_invalidate_cursor_periodic (VteTerminal *terminal)
741 VteTerminalPrivate *pvt = terminal->pvt;
743 pvt->cursor_blink_state = !pvt->cursor_blink_state;
744 pvt->cursor_blink_time += pvt->cursor_blink_cycle;
746 _vte_invalidate_cursor_once(terminal, TRUE);
748 /* only disable the blink if the cursor is currently shown.
749 * else, wait until next time.
751 if (pvt->cursor_blink_time / 1000 >= pvt->cursor_blink_timeout &&
752 pvt->cursor_blink_state) {
753 pvt->cursor_blink_tag = 0;
757 pvt->cursor_blink_tag = g_timeout_add_full(G_PRIORITY_LOW,
758 terminal->pvt->cursor_blink_cycle,
759 (GSourceFunc)vte_invalidate_cursor_periodic,
765 /* Emit a "selection_changed" signal. */
767 vte_terminal_emit_selection_changed(VteTerminal *terminal)
769 _vte_debug_print(VTE_DEBUG_SIGNALS,
770 "Emitting `selection-changed'.\n");
771 g_signal_emit_by_name(terminal, "selection-changed");
774 /* Emit a "commit" signal. */
776 vte_terminal_emit_commit(VteTerminal *terminal, const gchar *text, guint length)
778 const char *result = NULL;
779 char *wrapped = NULL;
781 _vte_debug_print(VTE_DEBUG_SIGNALS,
782 "Emitting `commit' of %d bytes.\n", length);
784 if (length == (guint)-1) {
785 length = strlen(text);
788 result = wrapped = g_slice_alloc(length + 1);
789 memcpy(wrapped, text, length);
790 wrapped[length] = '\0';
793 g_signal_emit_by_name(terminal, "commit", result, length);
796 g_slice_free1(length+1, wrapped);
799 /* Emit an "emulation-changed" signal. */
801 vte_terminal_emit_emulation_changed(VteTerminal *terminal)
803 _vte_debug_print(VTE_DEBUG_SIGNALS,
804 "Emitting `emulation-changed'.\n");
805 g_signal_emit_by_name(terminal, "emulation-changed");
806 g_object_notify(G_OBJECT(terminal), "emulation");
810 /* Emit an "encoding-changed" signal. */
812 vte_terminal_emit_encoding_changed(VteTerminal *terminal)
814 _vte_debug_print(VTE_DEBUG_SIGNALS,
815 "Emitting `encoding-changed'.\n");
816 g_signal_emit_by_name(terminal, "encoding-changed");
817 g_object_notify(G_OBJECT(terminal), "encoding");
820 /* Emit a "child-exited" signal. */
822 vte_terminal_emit_child_exited(VteTerminal *terminal)
824 _vte_debug_print(VTE_DEBUG_SIGNALS,
825 "Emitting `child-exited'.\n");
826 g_signal_emit_by_name(terminal, "child-exited");
829 /* Emit a "contents_changed" signal. */
831 vte_terminal_emit_contents_changed(VteTerminal *terminal)
833 if (terminal->pvt->contents_changed_pending) {
834 /* Update dingus match set. */
835 vte_terminal_match_contents_clear(terminal);
836 if (terminal->pvt->mouse_cursor_visible) {
837 vte_terminal_match_hilite_update(terminal,
838 terminal->pvt->mouse_last_x,
839 terminal->pvt->mouse_last_y);
842 _vte_debug_print(VTE_DEBUG_SIGNALS,
843 "Emitting `contents-changed'.\n");
844 g_signal_emit_by_name(terminal, "contents-changed");
845 terminal->pvt->contents_changed_pending = FALSE;
849 _vte_terminal_queue_contents_changed(VteTerminal *terminal)
851 _vte_debug_print(VTE_DEBUG_SIGNALS,
852 "Queueing `contents-changed'.\n");
853 terminal->pvt->contents_changed_pending = TRUE;
856 /* Emit a "cursor_moved" signal. */
858 vte_terminal_emit_cursor_moved(VteTerminal *terminal)
860 if (terminal->pvt->cursor_moved_pending) {
861 _vte_debug_print(VTE_DEBUG_SIGNALS,
862 "Emitting `cursor-moved'.\n");
863 g_signal_emit_by_name(terminal, "cursor-moved");
864 terminal->pvt->cursor_moved_pending = FALSE;
868 vte_terminal_queue_cursor_moved(VteTerminal *terminal)
870 _vte_debug_print(VTE_DEBUG_SIGNALS,
871 "Queueing `cursor-moved'.\n");
872 terminal->pvt->cursor_moved_pending = TRUE;
876 vte_terminal_emit_eof(VteTerminal *terminal)
878 _vte_debug_print(VTE_DEBUG_SIGNALS,
879 "Emitting `eof'.\n");
880 GDK_THREADS_ENTER ();
881 g_signal_emit_by_name(terminal, "eof");
882 GDK_THREADS_LEAVE ();
886 /* Emit a "eof" signal. */
888 vte_terminal_queue_eof(VteTerminal *terminal)
890 _vte_debug_print(VTE_DEBUG_SIGNALS,
891 "Queueing `eof'.\n");
892 g_idle_add_full (G_PRIORITY_HIGH,
893 (GSourceFunc) vte_terminal_emit_eof,
894 g_object_ref (terminal),
898 /* Emit a "char-size-changed" signal. */
900 vte_terminal_emit_char_size_changed(VteTerminal *terminal,
901 guint width, guint height)
903 _vte_debug_print(VTE_DEBUG_SIGNALS,
904 "Emitting `char-size-changed'.\n");
905 g_signal_emit_by_name(terminal, "char-size-changed",
907 /* g_object_notify(G_OBJECT(terminal), "char-size"); */
910 /* Emit a "status-line-changed" signal. */
912 _vte_terminal_emit_status_line_changed(VteTerminal *terminal)
914 _vte_debug_print(VTE_DEBUG_SIGNALS,
915 "Emitting `status-line-changed'.\n");
916 g_signal_emit_by_name(terminal, "status-line-changed");
917 /* g_object_notify(G_OBJECT(terminal), "status-line"); */
920 /* Emit an "increase-font-size" signal. */
922 vte_terminal_emit_increase_font_size(VteTerminal *terminal)
924 _vte_debug_print(VTE_DEBUG_SIGNALS,
925 "Emitting `increase-font-size'.\n");
926 g_signal_emit_by_name(terminal, "increase-font-size");
927 g_object_notify(G_OBJECT(terminal), "font-scale");
930 /* Emit a "decrease-font-size" signal. */
932 vte_terminal_emit_decrease_font_size(VteTerminal *terminal)
934 _vte_debug_print(VTE_DEBUG_SIGNALS,
935 "Emitting `decrease-font-size'.\n");
936 g_signal_emit_by_name(terminal, "decrease-font-size");
937 g_object_notify(G_OBJECT(terminal), "font-scale");
940 /* Emit a "text-inserted" signal. */
942 _vte_terminal_emit_text_inserted(VteTerminal *terminal)
944 if (!terminal->pvt->accessible_emit) {
947 _vte_debug_print(VTE_DEBUG_SIGNALS,
948 "Emitting `text-inserted'.\n");
949 g_signal_emit_by_name(terminal, "text-inserted");
952 /* Emit a "text-deleted" signal. */
954 _vte_terminal_emit_text_deleted(VteTerminal *terminal)
956 if (!terminal->pvt->accessible_emit) {
959 _vte_debug_print(VTE_DEBUG_SIGNALS,
960 "Emitting `text-deleted'.\n");
961 g_signal_emit_by_name(terminal, "text-deleted");
964 /* Emit a "text-modified" signal. */
966 vte_terminal_emit_text_modified(VteTerminal *terminal)
968 if (!terminal->pvt->accessible_emit) {
971 _vte_debug_print(VTE_DEBUG_SIGNALS,
972 "Emitting `text-modified'.\n");
973 g_signal_emit_by_name(terminal, "text-modified");
976 /* Emit a "text-scrolled" signal. */
978 vte_terminal_emit_text_scrolled(VteTerminal *terminal, gint delta)
980 if (!terminal->pvt->accessible_emit) {
983 _vte_debug_print(VTE_DEBUG_SIGNALS,
984 "Emitting `text-scrolled'(%d).\n", delta);
985 g_signal_emit_by_name(terminal, "text-scrolled", delta);
988 /* Deselect anything which is selected and refresh the screen if needed. */
990 vte_terminal_deselect_all(VteTerminal *terminal)
992 if (terminal->pvt->has_selection) {
995 _vte_debug_print(VTE_DEBUG_SELECTION,
996 "Deselecting all text.\n");
998 terminal->pvt->has_selection = FALSE;
999 /* Don't free the current selection, as we need to keep
1000 * hold of it for async copying from the clipboard. */
1002 vte_terminal_emit_selection_changed(terminal);
1004 sx = terminal->pvt->selection_start.col;
1005 sy = terminal->pvt->selection_start.row;
1006 ex = terminal->pvt->selection_end.col;
1007 ey = terminal->pvt->selection_end.row;
1008 _vte_invalidate_region(terminal,
1009 MIN (sx, ex), MAX (sx, ex),
1010 MIN (sy, ey), MAX (sy, ey),
1015 /* Remove a tabstop. */
1017 _vte_terminal_clear_tabstop(VteTerminal *terminal, int column)
1019 g_assert(VTE_IS_TERMINAL(terminal));
1020 if (terminal->pvt->tabstops != NULL) {
1021 /* Remove a tab stop from the hash table. */
1022 g_hash_table_remove(terminal->pvt->tabstops,
1023 GINT_TO_POINTER(2 * column + 1));
1027 /* Check if we have a tabstop at a given position. */
1029 _vte_terminal_get_tabstop(VteTerminal *terminal, int column)
1032 g_assert(VTE_IS_TERMINAL(terminal));
1033 if (terminal->pvt->tabstops != NULL) {
1034 hash = g_hash_table_lookup(terminal->pvt->tabstops,
1035 GINT_TO_POINTER(2 * column + 1));
1036 return (hash != NULL);
1042 /* Reset the set of tab stops to the default. */
1044 _vte_terminal_set_tabstop(VteTerminal *terminal, int column)
1046 g_assert(VTE_IS_TERMINAL(terminal));
1047 if (terminal->pvt->tabstops != NULL) {
1048 /* Just set a non-NULL pointer for this column number. */
1049 g_hash_table_insert(terminal->pvt->tabstops,
1050 GINT_TO_POINTER(2 * column + 1),
1055 /* Reset the set of tab stops to the default. */
1057 vte_terminal_set_default_tabstops(VteTerminal *terminal)
1060 if (terminal->pvt->tabstops != NULL) {
1061 g_hash_table_destroy(terminal->pvt->tabstops);
1063 terminal->pvt->tabstops = g_hash_table_new(NULL, NULL);
1064 if (terminal->pvt->termcap != NULL) {
1065 width = _vte_termcap_find_numeric(terminal->pvt->termcap,
1066 terminal->pvt->emulation,
1070 width = VTE_TAB_WIDTH;
1072 for (i = 0; i <= VTE_TAB_MAX; i += width) {
1073 _vte_terminal_set_tabstop(terminal, i);
1077 /* Clear the cache of the screen contents we keep. */
1079 vte_terminal_match_contents_clear(VteTerminal *terminal)
1081 g_assert(VTE_IS_TERMINAL(terminal));
1082 if (terminal->pvt->match_contents != NULL) {
1083 g_free(terminal->pvt->match_contents);
1084 terminal->pvt->match_contents = NULL;
1086 if (terminal->pvt->match_attributes != NULL) {
1087 g_array_free(terminal->pvt->match_attributes, TRUE);
1088 terminal->pvt->match_attributes = NULL;
1090 vte_terminal_match_hilite_clear(terminal);
1093 /* Refresh the cache of the screen contents we keep. */
1095 always_selected(VteTerminal *terminal, glong column, glong row, gpointer data)
1101 vte_terminal_match_contents_refresh(VteTerminal *terminal)
1104 vte_terminal_match_contents_clear(terminal);
1105 array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
1106 terminal->pvt->match_contents = vte_terminal_get_text(terminal,
1110 terminal->pvt->match_attributes = array;
1114 regex_match_clear_cursor (struct vte_match_regex *regex)
1116 switch (regex->cursor_mode) {
1117 case VTE_REGEX_CURSOR_GDKCURSOR:
1118 if (regex->cursor.cursor != NULL) {
1119 gdk_cursor_unref(regex->cursor.cursor);
1120 regex->cursor.cursor = NULL;
1123 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1125 case VTE_REGEX_CURSOR_NAME:
1126 g_free (regex->cursor.cursor_name);
1127 regex->cursor.cursor_name = NULL;
1130 g_assert_not_reached ();
1136 regex_match_clear (struct vte_match_regex *regex)
1138 regex_match_clear_cursor(regex);
1140 if (regex->mode == VTE_REGEX_GREGEX) {
1141 g_regex_unref(regex->regex.gregex.regex);
1142 regex->regex.gregex.regex = NULL;
1143 } else if (regex->mode == VTE_REGEX_VTE) {
1144 _vte_regex_free(regex->regex.reg);
1145 regex->regex.reg = NULL;
1152 vte_terminal_set_cursor_from_regex_match(VteTerminal *terminal, struct vte_match_regex *regex)
1154 GdkCursor *cursor = NULL;
1156 if (!GTK_WIDGET_REALIZED(terminal))
1158 switch (regex->cursor_mode) {
1159 case VTE_REGEX_CURSOR_GDKCURSOR:
1160 if (regex->cursor.cursor != NULL) {
1161 cursor = gdk_cursor_ref(regex->cursor.cursor);
1164 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1165 cursor = gdk_cursor_new_for_display(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_type);
1167 case VTE_REGEX_CURSOR_NAME:
1168 cursor = gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_name);
1171 g_assert_not_reached ();
1175 gdk_window_set_cursor(GTK_WIDGET(terminal)->window, cursor);
1177 gdk_cursor_unref(cursor);
1181 * vte_terminal_match_clear_all:
1182 * @terminal: a #VteTerminal
1184 * Clears the list of regular expressions the terminal uses to highlight text
1185 * when the user moves the mouse cursor.
1188 vte_terminal_match_clear_all(VteTerminal *terminal)
1190 struct vte_match_regex *regex;
1192 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1193 for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1194 regex = &g_array_index(terminal->pvt->match_regexes,
1195 struct vte_match_regex,
1197 /* Unless this is a hole, clean it up. */
1198 if (regex->tag >= 0) {
1199 regex_match_clear (regex);
1202 g_array_set_size(terminal->pvt->match_regexes, 0);
1203 vte_terminal_match_hilite_clear(terminal);
1207 * vte_terminal_match_remove:
1208 * @terminal: a #VteTerminal
1209 * @tag: the tag of the regex to remove
1211 * Removes the regular expression which is associated with the given @tag from
1212 * the list of expressions which the terminal will highlight when the user
1213 * moves the mouse cursor over matching text.
1216 vte_terminal_match_remove(VteTerminal *terminal, int tag)
1218 struct vte_match_regex *regex;
1219 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1220 if (terminal->pvt->match_regexes->len > (guint)tag) {
1221 /* The tag is an index, so find the corresponding struct. */
1222 regex = &g_array_index(terminal->pvt->match_regexes,
1223 struct vte_match_regex,
1225 /* If it's already been removed, return. */
1226 if (regex->tag < 0) {
1229 /* Remove this item and leave a hole in its place. */
1230 regex_match_clear (regex);
1232 vte_terminal_match_hilite_clear(terminal);
1236 vte_terminal_cursor_new(VteTerminal *terminal, GdkCursorType cursor_type)
1238 GdkDisplay *display;
1241 display = gtk_widget_get_display(&terminal->widget);
1242 cursor = gdk_cursor_new_for_display(display, cursor_type);
1247 * vte_terminal_match_add:
1248 * @terminal: a #VteTerminal
1249 * @match: a regular expression
1251 * Adds a regular expression to the list of matching expressions. When the
1252 * user moves the mouse cursor over a section of displayed text which matches
1253 * this expression, the text will be highlighted.
1255 * Returns: an integer associated with this expression
1257 * Deprecated: 0.17.1: Use vte_terminal_match_add_gregex() instead
1260 vte_terminal_match_add(VteTerminal *terminal, const char *match)
1262 struct vte_match_regex new_regex, *regex;
1264 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1265 g_return_val_if_fail(terminal->pvt->match_regex_mode != VTE_REGEX_GREGEX, -1);
1266 g_return_val_if_fail(match != NULL, -1);
1267 g_return_val_if_fail(strlen(match) > 0, -1);
1269 terminal->pvt->match_regex_mode = VTE_REGEX_VTE;
1271 memset(&new_regex, 0, sizeof(new_regex));
1272 new_regex.mode = VTE_REGEX_VTE;
1273 new_regex.regex.reg = _vte_regex_compile(match);
1274 if (new_regex.regex.reg == NULL) {
1275 g_warning(_("Error compiling regular expression \"%s\"."),
1280 /* Search for a hole. */
1281 for (ret = 0; ret < terminal->pvt->match_regexes->len; ret++) {
1282 regex = &g_array_index(terminal->pvt->match_regexes,
1283 struct vte_match_regex,
1285 if (regex->tag == -1) {
1289 /* Set the tag to the insertion point. */
1290 new_regex.tag = ret;
1291 new_regex.cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1292 new_regex.cursor.cursor_type = VTE_DEFAULT_CURSOR;
1293 if (ret < terminal->pvt->match_regexes->len) {
1295 g_array_index(terminal->pvt->match_regexes,
1296 struct vte_match_regex,
1300 g_array_append_val(terminal->pvt->match_regexes, new_regex);
1302 return new_regex.tag;
1306 * vte_terminal_match_add_gregex:
1307 * @terminal: a #VteTerminal
1309 * @flags: the #GRegexMatchFlags to use when matching the regex
1311 * Adds the regular expression @regex to the list of matching expressions. When the
1312 * user moves the mouse cursor over a section of displayed text which matches
1313 * this expression, the text will be highlighted.
1315 * Returns: an integer associated with this expression
1320 vte_terminal_match_add_gregex(VteTerminal *terminal, GRegex *regex, GRegexMatchFlags flags)
1322 VteTerminalPrivate *pvt;
1323 struct vte_match_regex new_regex_match, *regex_match;
1326 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1327 g_return_val_if_fail(terminal->pvt->match_regex_mode != VTE_REGEX_VTE, -1);
1328 g_return_val_if_fail(regex != NULL, -1);
1330 pvt = terminal->pvt;
1331 pvt->match_regex_mode = VTE_REGEX_GREGEX;
1333 /* Search for a hole. */
1334 len = pvt->match_regexes->len;
1335 for (ret = 0; ret < len; ret++) {
1336 regex_match = &g_array_index(pvt->match_regexes,
1337 struct vte_match_regex,
1339 if (regex_match->tag == -1) {
1344 /* Set the tag to the insertion point. */
1345 new_regex_match.mode = VTE_REGEX_GREGEX;
1346 new_regex_match.regex.gregex.regex = g_regex_ref(regex);
1347 new_regex_match.regex.gregex.flags = flags;
1348 new_regex_match.tag = ret;
1349 new_regex_match.cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1350 new_regex_match.cursor.cursor_type = VTE_DEFAULT_CURSOR;
1351 if (ret < pvt->match_regexes->len) {
1353 g_array_index(pvt->match_regexes,
1354 struct vte_match_regex,
1355 ret) = new_regex_match;
1358 g_array_append_val(pvt->match_regexes, new_regex_match);
1361 return new_regex_match.tag;
1365 * vte_terminal_match_set_cursor:
1366 * @terminal: a #VteTerminal
1367 * @tag: the tag of the regex which should use the specified cursor
1368 * @cursor: (allow-none): the #GdkCursor which the terminal should use when the pattern is
1369 * highlighted, or %NULL to use the standard cursor
1371 * Sets which cursor the terminal will use if the pointer is over the pattern
1372 * specified by @tag. The terminal keeps a reference to @cursor.
1378 vte_terminal_match_set_cursor(VteTerminal *terminal, int tag, GdkCursor *cursor)
1380 struct vte_match_regex *regex;
1381 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1382 g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1383 regex = &g_array_index(terminal->pvt->match_regexes,
1384 struct vte_match_regex,
1386 regex_match_clear_cursor(regex);
1387 regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSOR;
1388 regex->cursor.cursor = cursor ? gdk_cursor_ref(cursor) : NULL;
1389 vte_terminal_match_hilite_clear(terminal);
1393 * vte_terminal_match_set_cursor_type:
1394 * @terminal: a #VteTerminal
1395 * @tag: the tag of the regex which should use the specified cursor
1396 * @cursor_type: a #GdkCursorType
1398 * Sets which cursor the terminal will use if the pointer is over the pattern
1399 * specified by @tag.
1405 vte_terminal_match_set_cursor_type(VteTerminal *terminal,
1406 int tag, GdkCursorType cursor_type)
1408 struct vte_match_regex *regex;
1409 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1410 g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1411 regex = &g_array_index(terminal->pvt->match_regexes,
1412 struct vte_match_regex,
1414 regex_match_clear_cursor(regex);
1415 regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1416 regex->cursor.cursor_type = cursor_type;
1417 vte_terminal_match_hilite_clear(terminal);
1421 * vte_terminal_match_set_cursor_name:
1422 * @terminal: a #VteTerminal
1423 * @tag: the tag of the regex which should use the specified cursor
1424 * @cursor_name: the name of the cursor
1426 * Sets which cursor the terminal will use if the pointer is over the pattern
1427 * specified by @tag.
1433 vte_terminal_match_set_cursor_name(VteTerminal *terminal,
1434 int tag, const char *cursor_name)
1436 struct vte_match_regex *regex;
1437 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1438 g_return_if_fail(cursor_name != NULL);
1439 g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1440 regex = &g_array_index(terminal->pvt->match_regexes,
1441 struct vte_match_regex,
1443 regex_match_clear_cursor(regex);
1444 regex->cursor_mode = VTE_REGEX_CURSOR_NAME;
1445 regex->cursor.cursor_name = g_strdup (cursor_name);
1446 vte_terminal_match_hilite_clear(terminal);
1449 /* Check if a given cell on the screen contains part of a matched string. If
1450 * it does, return the string, and store the match tag in the optional tag
1453 vte_terminal_match_check_internal_vte(VteTerminal *terminal,
1454 long column, glong row,
1455 int *tag, int *start, int *end)
1457 struct _vte_regex_match matches[256];
1460 gint start_blank, end_blank;
1462 struct vte_match_regex *regex = NULL;
1463 struct _VteCharAttributes *attr = NULL;
1464 gssize sattr, eattr;
1467 _vte_debug_print(VTE_DEBUG_EVENTS,
1468 "Checking for match at (%ld,%ld).\n", row, column);
1470 if (start != NULL) {
1476 /* Map the pointer position to a portion of the string. */
1477 eattr = terminal->pvt->match_attributes->len;
1478 for (offset = eattr; offset--; ) {
1479 attr = &g_array_index(terminal->pvt->match_attributes,
1480 struct _VteCharAttributes,
1482 if (row < attr->row) {
1485 if (row == attr->row &&
1486 column == attr->column &&
1487 terminal->pvt->match_contents[offset] != ' ') {
1492 _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1494 g_printerr("Cursor is not on a character.\n");
1496 g_printerr("Cursor is on character '%c' at %d.\n",
1497 g_utf8_get_char (terminal->pvt->match_contents + offset),
1501 /* If the pointer isn't on a matchable character, bug out. */
1506 /* If the pointer is on a newline, bug out. */
1507 if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1508 (terminal->pvt->match_contents[offset] == '\0')) {
1509 _vte_debug_print(VTE_DEBUG_EVENTS,
1510 "Cursor is on whitespace.\n");
1514 /* Snip off any final newlines. */
1515 while (terminal->pvt->match_contents[eattr] == '\n' ||
1516 terminal->pvt->match_contents[eattr] == '\0') {
1519 /* and scan forwards to find the end of this line */
1520 while (!(terminal->pvt->match_contents[eattr] == '\n' ||
1521 terminal->pvt->match_contents[eattr] == '\0')) {
1525 /* find the start of row */
1529 for (sattr = offset; sattr > 0; sattr--) {
1530 attr = &g_array_index(terminal->pvt->match_attributes,
1531 struct _VteCharAttributes,
1533 if (row > attr->row) {
1538 /* Scan backwards to find the start of this line */
1540 ! (terminal->pvt->match_contents[sattr] == '\n' ||
1541 terminal->pvt->match_contents[sattr] == '\0')) {
1544 /* and skip any initial newlines. */
1545 while (terminal->pvt->match_contents[sattr] == '\n' ||
1546 terminal->pvt->match_contents[sattr] == '\0') {
1549 if (eattr <= sattr) { /* blank line */
1552 if (eattr <= offset || sattr > offset) {
1553 /* nothing to match on this line */
1559 /* temporarily shorten the contents to this row */
1560 line = terminal->pvt->match_contents + sattr;
1567 /* Now iterate over each regex we need to match against. */
1568 for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1569 regex = &g_array_index(terminal->pvt->match_regexes,
1570 struct vte_match_regex,
1573 if (regex->tag < 0) {
1576 /* We'll only match the first item in the buffer which
1577 * matches, so we'll have to skip each match until we
1578 * stop getting matches. */
1580 ret = _vte_regex_exec(regex->regex.reg,
1582 G_N_ELEMENTS(matches),
1585 gint ko = offset - k;
1586 gint sblank=G_MININT, eblank=G_MAXINT;
1588 j < G_N_ELEMENTS(matches) &&
1589 matches[j].rm_so != -1;
1591 /* The offsets should be "sane". */
1592 g_assert(matches[j].rm_so + k < eattr);
1593 g_assert(matches[j].rm_eo + k <= eattr);
1594 _VTE_DEBUG_IF(VTE_DEBUG_MISC) {
1596 struct _VteCharAttributes *_sattr, *_eattr;
1597 match = g_strndup(line + matches[j].rm_so + k,
1598 matches[j].rm_eo - matches[j].rm_so);
1599 _sattr = &g_array_index(terminal->pvt->match_attributes,
1600 struct _VteCharAttributes,
1601 matches[j].rm_so + k);
1602 _eattr = &g_array_index(terminal->pvt->match_attributes,
1603 struct _VteCharAttributes,
1604 matches[j].rm_eo + k - 1);
1605 g_printerr("Match %u `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1607 matches[j].rm_so + k,
1610 matches[j].rm_eo + k - 1,
1617 /* If the pointer is in this substring,
1618 * then we're done. */
1619 if (ko >= matches[j].rm_so &&
1620 ko < matches[j].rm_eo) {
1625 if (start != NULL) {
1626 *start = sattr + k + matches[j].rm_so;
1629 *end = sattr + k + matches[j].rm_eo - 1;
1631 vte_terminal_set_cursor_from_regex_match(terminal, regex);
1632 result = g_strndup(line + k + matches[j].rm_so,
1633 matches[j].rm_eo - matches[j].rm_so);
1637 if (ko > matches[j].rm_eo &&
1638 matches[j].rm_eo > sblank) {
1639 sblank = matches[j].rm_eo;
1641 if (ko < matches[j].rm_so &&
1642 matches[j].rm_so < eblank) {
1643 eblank = matches[j].rm_so;
1646 if (k + sblank > start_blank) {
1647 start_blank = k + sblank;
1649 if (k + eblank < end_blank) {
1650 end_blank = k + eblank;
1652 /* Skip past the beginning of this match to
1654 k += matches[0].rm_so + 1;
1658 ret = _vte_regex_exec(regex->regex.reg,
1660 G_N_ELEMENTS(matches),
1665 if (start != NULL) {
1666 *start = sattr + start_blank;
1669 *end = sattr + end_blank;
1674 /* Check if a given cell on the screen contains part of a matched string. If
1675 * it does, return the string, and store the match tag in the optional tag
1678 vte_terminal_match_check_internal_gregex(VteTerminal *terminal,
1679 long column, glong row,
1680 int *tag, int *start, int *end)
1682 gint start_blank, end_blank;
1685 struct vte_match_regex *regex = NULL;
1686 struct _VteCharAttributes *attr = NULL;
1687 gssize sattr, eattr;
1689 GMatchInfo *match_info;
1691 _vte_debug_print(VTE_DEBUG_EVENTS,
1692 "Checking for gregex match at (%ld,%ld).\n", row, column);
1694 if (start != NULL) {
1700 /* Map the pointer position to a portion of the string. */
1701 eattr = terminal->pvt->match_attributes->len;
1702 for (offset = eattr; offset--; ) {
1703 attr = &g_array_index(terminal->pvt->match_attributes,
1704 struct _VteCharAttributes,
1706 if (row < attr->row) {
1709 if (row == attr->row &&
1710 column == attr->column &&
1711 terminal->pvt->match_contents[offset] != ' ') {
1716 _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1718 g_printerr("Cursor is not on a character.\n");
1720 g_printerr("Cursor is on character '%c' at %d.\n",
1721 g_utf8_get_char (terminal->pvt->match_contents + offset),
1725 /* If the pointer isn't on a matchable character, bug out. */
1730 /* If the pointer is on a newline, bug out. */
1731 if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1732 (terminal->pvt->match_contents[offset] == '\0')) {
1733 _vte_debug_print(VTE_DEBUG_EVENTS,
1734 "Cursor is on whitespace.\n");
1738 /* Snip off any final newlines. */
1739 while (terminal->pvt->match_contents[eattr] == '\n' ||
1740 terminal->pvt->match_contents[eattr] == '\0') {
1743 /* and scan forwards to find the end of this line */
1744 while (!(terminal->pvt->match_contents[eattr] == '\n' ||
1745 terminal->pvt->match_contents[eattr] == '\0')) {
1749 /* find the start of row */
1753 for (sattr = offset; sattr > 0; sattr--) {
1754 attr = &g_array_index(terminal->pvt->match_attributes,
1755 struct _VteCharAttributes,
1757 if (row > attr->row) {
1762 /* Scan backwards to find the start of this line */
1764 ! (terminal->pvt->match_contents[sattr] == '\n' ||
1765 terminal->pvt->match_contents[sattr] == '\0')) {
1768 /* and skip any initial newlines. */
1769 while (terminal->pvt->match_contents[sattr] == '\n' ||
1770 terminal->pvt->match_contents[sattr] == '\0') {
1773 if (eattr <= sattr) { /* blank line */
1776 if (eattr <= offset || sattr > offset) {
1777 /* nothing to match on this line */
1783 /* temporarily shorten the contents to this row */
1784 line = terminal->pvt->match_contents + sattr;
1791 /* Now iterate over each regex we need to match against. */
1792 for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1793 regex = &g_array_index(terminal->pvt->match_regexes,
1794 struct vte_match_regex,
1797 if (regex->tag < 0) {
1800 /* We'll only match the first item in the buffer which
1801 * matches, so we'll have to skip each match until we
1802 * stop getting matches. */
1803 if (!g_regex_match_full(regex->regex.gregex.regex,
1805 regex->regex.gregex.flags,
1808 g_match_info_free(match_info);
1812 while (g_match_info_matches(match_info)) {
1814 gint sblank=G_MININT, eblank=G_MAXINT;
1817 if (g_match_info_fetch_pos (match_info, 0, &rm_so, &rm_eo)) {
1818 /* The offsets should be "sane". */
1819 g_assert(rm_so < eattr);
1820 g_assert(rm_eo <= eattr);
1821 _VTE_DEBUG_IF(VTE_DEBUG_MISC) {
1823 struct _VteCharAttributes *_sattr, *_eattr;
1824 match = g_strndup(line + rm_so, rm_eo - rm_so);
1825 _sattr = &g_array_index(terminal->pvt->match_attributes,
1826 struct _VteCharAttributes,
1828 _eattr = &g_array_index(terminal->pvt->match_attributes,
1829 struct _VteCharAttributes,
1831 g_printerr("Match `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1843 /* If the pointer is in this substring,
1844 * then we're done. */
1851 if (start != NULL) {
1852 *start = sattr + rm_so;
1855 *end = sattr + rm_eo - 1;
1857 vte_terminal_set_cursor_from_regex_match(terminal, regex);
1858 result = g_match_info_fetch(match_info, 0);
1861 g_match_info_free(match_info);
1873 if (sblank > start_blank) {
1874 start_blank = sblank;
1876 if (eblank < end_blank) {
1880 g_match_info_next(match_info, NULL);
1883 g_match_info_free(match_info);
1886 if (start != NULL) {
1887 *start = sattr + start_blank;
1890 *end = sattr + end_blank;
1896 vte_terminal_match_check_internal(VteTerminal *terminal,
1897 long column, glong row,
1898 int *tag, int *start, int *end)
1900 if (terminal->pvt->match_contents == NULL) {
1901 vte_terminal_match_contents_refresh(terminal);
1904 if (terminal->pvt->match_regex_mode == VTE_REGEX_GREGEX)
1905 return vte_terminal_match_check_internal_gregex(terminal, column, row, tag, start, end);
1906 if (terminal->pvt->match_regex_mode == VTE_REGEX_VTE)
1907 return vte_terminal_match_check_internal_vte(terminal, column, row, tag, start, end);
1912 rowcol_inside_match (VteTerminal *terminal, glong row, glong col)
1914 if (terminal->pvt->match_start.row == terminal->pvt->match_end.row) {
1915 return row == terminal->pvt->match_start.row &&
1916 col >= terminal->pvt->match_start.column &&
1917 col <= terminal->pvt->match_end.column;
1919 if (row < terminal->pvt->match_start.row ||
1920 row > terminal->pvt->match_end.row) {
1923 if (row == terminal->pvt->match_start.row) {
1924 return col >= terminal->pvt->match_start.column;
1926 if (row == terminal->pvt->match_end.row) {
1927 return col <= terminal->pvt->match_end.column;
1934 * vte_terminal_match_check:
1935 * @terminal: a #VteTerminal
1936 * @column: the text column
1937 * @row: the text row
1938 * @tag: (out) (allow-none): a location to store the tag, or %NULL
1940 * Checks if the text in and around the specified position matches any of the
1941 * regular expressions previously set using vte_terminal_match_add(). If a
1942 * match exists, the text string is returned and if @tag is not %NULL, the number
1943 * associated with the matched regular expression will be stored in @tag.
1945 * If more than one regular expression has been set with
1946 * vte_terminal_match_add(), then expressions are checked in the order in
1947 * which they were added.
1949 * Returns: (transfer full): a newly allocated string which matches one of the previously
1950 * set regular expressions
1953 vte_terminal_match_check(VteTerminal *terminal, glong column, glong row,
1958 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1959 delta = terminal->pvt->screen->scroll_delta;
1960 _vte_debug_print(VTE_DEBUG_EVENTS,
1961 "Checking for match at (%ld,%ld).\n",
1963 if (rowcol_inside_match (terminal, row + delta, column)) {
1965 *tag = terminal->pvt->match_tag;
1967 ret = terminal->pvt->match != NULL ?
1968 g_strdup (terminal->pvt->match) :
1971 ret = vte_terminal_match_check_internal(terminal,
1972 column, row + delta,
1975 _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1976 if (ret != NULL) g_printerr("Matched `%s'.\n", ret);
1981 /* Emit an adjustment changed signal on our adjustment object. */
1983 vte_terminal_emit_adjustment_changed(VteTerminal *terminal)
1985 if (terminal->pvt->adjustment_changed_pending) {
1986 VteScreen *screen = terminal->pvt->screen;
1987 gboolean changed = FALSE;
1990 v = _vte_ring_delta (screen->row_data);
1991 if (terminal->adjustment->lower != v) {
1992 _vte_debug_print(VTE_DEBUG_ADJ,
1993 "Changing lower bound from %.0f to %ld\n",
1994 terminal->adjustment->lower,
1996 terminal->adjustment->lower = v;
2000 /* The upper value is the number of rows which might be visible. (Add
2001 * one to the cursor offset because it's zero-based.) */
2002 v = MAX(_vte_ring_next(screen->row_data),
2003 screen->cursor_current.row + 1);
2004 if (terminal->adjustment->upper != v) {
2005 _vte_debug_print(VTE_DEBUG_ADJ,
2006 "Changing upper bound from %.0f to %ld\n",
2007 terminal->adjustment->upper,
2009 terminal->adjustment->upper = v;
2014 _vte_debug_print(VTE_DEBUG_SIGNALS,
2015 "Emitting adjustment_changed.\n");
2016 gtk_adjustment_changed(terminal->adjustment);
2018 terminal->pvt->adjustment_changed_pending = FALSE;
2020 if (terminal->pvt->adjustment_value_changed_pending) {
2022 _vte_debug_print(VTE_DEBUG_SIGNALS,
2023 "Emitting adjustment_value_changed.\n");
2024 terminal->pvt->adjustment_value_changed_pending = FALSE;
2025 v = floor (terminal->adjustment->value);
2026 if (v != terminal->pvt->screen->scroll_delta) {
2027 /* this little dance is so that the scroll_delta is
2028 * updated immediately, but we still handled scrolling
2029 * via the adjustment - e.g. user interaction with the
2032 terminal->adjustment->value = terminal->pvt->screen->scroll_delta;
2033 terminal->pvt->screen->scroll_delta = v;
2034 gtk_adjustment_value_changed(terminal->adjustment);
2039 /* Queue an adjustment-changed signal to be delivered when convenient. */
2041 vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
2043 terminal->pvt->adjustment_changed_pending = TRUE;
2044 add_update_timeout (terminal);
2047 vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
2049 if (v != terminal->pvt->screen->scroll_delta) {
2050 terminal->pvt->screen->scroll_delta = v;
2051 terminal->pvt->adjustment_value_changed_pending = TRUE;
2052 add_update_timeout (terminal);
2058 _vte_terminal_adjust_adjustments(VteTerminal *terminal)
2063 g_assert(terminal->pvt->screen != NULL);
2064 g_assert(terminal->pvt->screen->row_data != NULL);
2066 vte_terminal_queue_adjustment_changed(terminal);
2068 /* The lower value should be the first row in the buffer. */
2069 screen = terminal->pvt->screen;
2070 delta = _vte_ring_delta(screen->row_data);
2071 /* Snap the insert delta and the cursor position to be in the visible
2072 * area. Leave the scrolling delta alone because it will be updated
2073 * when the adjustment changes. */
2074 screen->insert_delta = MAX(screen->insert_delta, delta);
2075 screen->cursor_current.row = MAX(screen->cursor_current.row,
2076 screen->insert_delta);
2078 if (screen->scroll_delta > screen->insert_delta) {
2079 vte_terminal_queue_adjustment_value_changed(terminal,
2080 screen->insert_delta);
2084 /* Update the adjustment field of the widget. This function should be called
2085 * whenever we add rows to or remove rows from the history or switch screens. */
2087 _vte_terminal_adjust_adjustments_full (VteTerminal *terminal)
2089 gboolean changed = FALSE;
2091 g_assert(terminal->pvt->screen != NULL);
2092 g_assert(terminal->pvt->screen->row_data != NULL);
2094 _vte_terminal_adjust_adjustments(terminal);
2096 /* The step increment should always be one. */
2097 if (terminal->adjustment->step_increment != 1) {
2098 _vte_debug_print(VTE_DEBUG_ADJ,
2099 "Changing step increment from %.0lf to %ld\n",
2100 terminal->adjustment->step_increment,
2101 terminal->row_count);
2102 terminal->adjustment->step_increment = 1;
2106 /* Set the number of rows the user sees to the number of rows the
2108 if (terminal->adjustment->page_size != terminal->row_count) {
2109 _vte_debug_print(VTE_DEBUG_ADJ,
2110 "Changing page size from %.0f to %ld\n",
2111 terminal->adjustment->page_size,
2112 terminal->row_count);
2113 terminal->adjustment->page_size = terminal->row_count;
2117 /* Clicking in the empty area should scroll one screen, so set the
2118 * page size to the number of visible rows. */
2119 if (terminal->adjustment->page_increment != terminal->row_count) {
2120 _vte_debug_print(VTE_DEBUG_ADJ,
2121 "Changing page increment from "
2123 terminal->adjustment->page_increment,
2124 terminal->row_count);
2125 terminal->adjustment->page_increment = terminal->row_count;
2130 _vte_debug_print(VTE_DEBUG_SIGNALS,
2131 "Emitting adjustment_changed.\n");
2132 gtk_adjustment_changed(terminal->adjustment);
2136 /* Scroll a fixed number of lines up or down in the current screen. */
2138 vte_terminal_scroll_lines(VteTerminal *terminal, gint lines)
2141 _vte_debug_print(VTE_DEBUG_ADJ, "Scrolling %d lines.\n", lines);
2142 /* Calculate the ideal position where we want to be before clamping. */
2143 destination = terminal->pvt->screen->scroll_delta;
2144 destination += lines;
2145 /* Can't scroll past data we have. */
2146 destination = CLAMP(destination,
2147 terminal->adjustment->lower,
2148 MAX (terminal->adjustment->lower, terminal->adjustment->upper - terminal->row_count));
2149 /* Tell the scrollbar to adjust itself. */
2150 vte_terminal_queue_adjustment_value_changed (terminal,
2154 /* Scroll a fixed number of pages up or down, in the current screen. */
2156 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
2158 vte_terminal_scroll_lines(terminal, pages * terminal->row_count);
2161 /* Scroll so that the scroll delta is the minimum value. */
2163 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
2165 vte_terminal_queue_adjustment_value_changed (terminal,
2166 _vte_ring_delta(terminal->pvt->screen->row_data));
2170 vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
2173 delta = terminal->pvt->screen->insert_delta;
2174 vte_terminal_queue_adjustment_value_changed (terminal, delta);
2175 _vte_debug_print(VTE_DEBUG_ADJ,
2176 "Snapping to bottom of screen\n");
2180 _vte_terminal_setup_utf8 (VteTerminal *terminal)
2182 VteTerminalPrivate *pvt = terminal->pvt;
2183 GError *error = NULL;
2185 if (!vte_pty_set_utf8(pvt->pty,
2186 strcmp(terminal->pvt->encoding, "UTF-8") == 0,
2188 g_warning ("Failed to set UTF8 mode: %s\n", error->message);
2189 g_error_free (error);
2194 * vte_terminal_set_encoding:
2195 * @terminal: a #VteTerminal
2196 * @codeset: (allow-none): a valid #GIConv target, or %NULL to use the default encoding
2198 * Changes the encoding the terminal will expect data from the child to
2199 * be encoded with. For certain terminal types, applications executing in the
2200 * terminal can change the encoding. The default encoding is defined by the
2201 * application's locale settings.
2204 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
2206 VteTerminalPrivate *pvt;
2208 const char *old_codeset;
2210 char *obuf1, *obuf2;
2211 gsize bytes_written;
2213 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2215 object = G_OBJECT(terminal);
2216 pvt = terminal->pvt;
2218 old_codeset = pvt->encoding;
2219 if (codeset == NULL) {
2220 g_get_charset(&codeset);
2222 if ((old_codeset != NULL) && (strcmp(codeset, old_codeset) == 0)) {
2223 /* Nothing to do! */
2227 g_object_freeze_notify(object);
2229 /* Open new conversions. */
2230 conv = _vte_conv_open(codeset, "UTF-8");
2231 if (conv == VTE_INVALID_CONV) {
2232 g_warning(_("Unable to convert characters from %s to %s."),
2234 /* fallback to no conversion */
2235 conv = _vte_conv_open(codeset = "UTF-8", "UTF-8");
2237 if (terminal->pvt->outgoing_conv != VTE_INVALID_CONV) {
2238 _vte_conv_close(terminal->pvt->outgoing_conv);
2240 terminal->pvt->outgoing_conv = conv;
2242 /* Set the terminal's encoding to the new value. */
2243 terminal->pvt->encoding = g_intern_string(codeset);
2245 /* Convert any buffered output bytes. */
2246 if ((_vte_buffer_length(terminal->pvt->outgoing) > 0) &&
2247 (old_codeset != NULL)) {
2248 /* Convert back to UTF-8. */
2249 obuf1 = g_convert((gchar *)terminal->pvt->outgoing->data,
2250 _vte_buffer_length(terminal->pvt->outgoing),
2256 if (obuf1 != NULL) {
2257 /* Convert to the new encoding. */
2258 obuf2 = g_convert(obuf1,
2265 if (obuf2 != NULL) {
2266 _vte_buffer_clear(terminal->pvt->outgoing);
2267 _vte_buffer_append(terminal->pvt->outgoing,
2268 obuf2, bytes_written);
2275 /* Set the encoding for incoming text. */
2276 _vte_iso2022_state_set_codeset(terminal->pvt->iso2022,
2277 terminal->pvt->encoding);
2279 _vte_debug_print(VTE_DEBUG_IO,
2280 "Set terminal encoding to `%s'.\n",
2281 terminal->pvt->encoding);
2282 vte_terminal_emit_encoding_changed(terminal);
2284 g_object_thaw_notify(object);
2288 * vte_terminal_get_encoding:
2289 * @terminal: a #VteTerminal
2291 * Determines the name of the encoding in which the terminal expects data to be
2294 * Returns: (transfer none): the current encoding for the terminal
2297 vte_terminal_get_encoding(VteTerminal *terminal)
2299 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
2300 return terminal->pvt->encoding;
2303 static inline VteRowData *
2304 vte_terminal_insert_rows (VteTerminal *terminal, guint cnt)
2308 row = _vte_terminal_ring_append (terminal, FALSE);
2314 /* Make sure we have enough rows and columns to hold data at the current
2315 * cursor position. */
2317 _vte_terminal_ensure_row (VteTerminal *terminal)
2324 /* Must make sure we're in a sane area. */
2325 screen = terminal->pvt->screen;
2326 v = screen->cursor_current.row;
2328 /* Figure out how many rows we need to add. */
2329 delta = v - _vte_ring_next(screen->row_data) + 1;
2331 row = vte_terminal_insert_rows (terminal, delta);
2332 _vte_terminal_adjust_adjustments(terminal);
2334 /* Find the row the cursor is in. */
2335 row = _vte_ring_index_writable (screen->row_data, v);
2337 g_assert(row != NULL);
2343 vte_terminal_ensure_cursor(VteTerminal *terminal)
2347 row = _vte_terminal_ensure_row (terminal);
2348 _vte_row_data_fill (row, &basic_cell.cell, terminal->pvt->screen->cursor_current.col);
2353 /* Update the insert delta so that the screen which includes it also
2354 * includes the end of the buffer. */
2356 _vte_terminal_update_insert_delta(VteTerminal *terminal)
2361 screen = terminal->pvt->screen;
2363 /* The total number of lines. Add one to the cursor offset
2364 * because it's zero-based. */
2365 rows = _vte_ring_next (screen->row_data);
2366 delta = screen->cursor_current.row - rows + 1;
2367 if (G_UNLIKELY (delta > 0)) {
2368 vte_terminal_insert_rows (terminal, delta);
2369 rows = _vte_ring_next (screen->row_data);
2372 /* Make sure that the bottom row is visible, and that it's in
2373 * the buffer (even if it's empty). This usually causes the
2374 * top row to become a history-only row. */
2375 delta = screen->insert_delta;
2376 delta = MIN(delta, rows - terminal->row_count);
2378 screen->cursor_current.row - (terminal->row_count - 1));
2379 delta = MAX(delta, _vte_ring_delta(screen->row_data));
2381 /* Adjust the insert delta and scroll if needed. */
2382 if (delta != screen->insert_delta) {
2383 screen->insert_delta = delta;
2384 _vte_terminal_adjust_adjustments(terminal);
2388 /* Show or hide the pointer. */
2390 _vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible)
2392 struct vte_match_regex *regex = NULL;
2393 terminal->pvt->mouse_cursor_visible = visible;
2395 if (!GTK_WIDGET_REALIZED(terminal))
2398 if (visible || !terminal->pvt->mouse_autohide) {
2399 if (terminal->pvt->mouse_tracking_mode) {
2400 _vte_debug_print(VTE_DEBUG_CURSOR,
2401 "Setting mousing cursor.\n");
2402 gdk_window_set_cursor(terminal->widget.window, terminal->pvt->mouse_mousing_cursor);
2404 if ( (guint)terminal->pvt->match_tag < terminal->pvt->match_regexes->len) {
2405 regex = &g_array_index(terminal->pvt->match_regexes,
2406 struct vte_match_regex,
2407 terminal->pvt->match_tag);
2408 vte_terminal_set_cursor_from_regex_match(terminal, regex);
2410 _vte_debug_print(VTE_DEBUG_CURSOR,
2411 "Setting default mouse cursor.\n");
2412 gdk_window_set_cursor(terminal->widget.window, terminal->pvt->mouse_default_cursor);
2415 _vte_debug_print(VTE_DEBUG_CURSOR,
2416 "Setting to invisible cursor.\n");
2417 gdk_window_set_cursor(terminal->widget.window, terminal->pvt->mouse_inviso_cursor);
2424 * Creates a new terminal widget.
2426 * Returns: (transfer full) (type Vte.Terminal): a new #VteTerminal object
2429 vte_terminal_new(void)
2431 return g_object_new(VTE_TYPE_TERMINAL, NULL);
2434 /* Set up a palette entry with a more-or-less match for the requested color. */
2436 vte_terminal_set_color_internal(VteTerminal *terminal, int entry,
2437 const GdkColor *proposed)
2441 color = &terminal->pvt->palette[entry];
2443 if (color->red == proposed->red &&
2444 color->green == proposed->green &&
2445 color->blue == proposed->blue) {
2449 _vte_debug_print(VTE_DEBUG_MISC,
2450 "Set color[%d] to (%04x,%04x,%04x).\n", entry,
2451 proposed->red, proposed->green, proposed->blue);
2453 /* Save the requested color. */
2454 color->red = proposed->red;
2455 color->green = proposed->green;
2456 color->blue = proposed->blue;
2458 /* If we're not realized yet, there's nothing else to do. */
2459 if (!GTK_WIDGET_REALIZED(terminal)) {
2463 /* If we're setting the background color, set the background color
2464 * on the widget as well. */
2465 if (entry == VTE_DEF_BG) {
2466 vte_terminal_queue_background_update(terminal);
2470 if (entry == VTE_CUR_BG)
2471 _vte_invalidate_cursor_once(terminal, FALSE);
2473 _vte_invalidate_all (terminal);
2477 vte_terminal_generate_bold(const PangoColor *foreground,
2478 const PangoColor *background,
2482 double fy, fcb, fcr, by, bcb, bcr, r, g, b;
2483 g_assert(foreground != NULL);
2484 g_assert(background != NULL);
2485 g_assert(bold != NULL);
2486 fy = 0.2990 * foreground->red +
2487 0.5870 * foreground->green +
2488 0.1140 * foreground->blue;
2489 fcb = -0.1687 * foreground->red +
2490 -0.3313 * foreground->green +
2491 0.5000 * foreground->blue;
2492 fcr = 0.5000 * foreground->red +
2493 -0.4187 * foreground->green +
2494 -0.0813 * foreground->blue;
2495 by = 0.2990 * background->red +
2496 0.5870 * background->green +
2497 0.1140 * background->blue;
2498 bcb = -0.1687 * background->red +
2499 -0.3313 * background->green +
2500 0.5000 * background->blue;
2501 bcr = 0.5000 * background->red +
2502 -0.4187 * background->green +
2503 -0.0813 * background->blue;
2504 fy = (factor * fy) + ((1.0 - factor) * by);
2505 fcb = (factor * fcb) + ((1.0 - factor) * bcb);
2506 fcr = (factor * fcr) + ((1.0 - factor) * bcr);
2507 r = fy + 1.402 * fcr;
2508 g = fy + 0.34414 * fcb - 0.71414 * fcr;
2509 b = fy + 1.722 * fcb;
2510 _vte_debug_print(VTE_DEBUG_MISC,
2511 "Calculated bold (%d, %d, %d) = (%lf,%lf,%lf)",
2512 foreground->red, foreground->green, foreground->blue,
2515 bold->red = CLAMP(r, 0, 0xffff);
2516 bold->green = CLAMP(g, 0, 0xffff);
2517 bold->blue = CLAMP(b, 0, 0xffff);
2518 _vte_debug_print(VTE_DEBUG_MISC,
2519 "= (%04x,%04x,%04x).\n",
2520 bold->red, bold->green, bold->blue);
2524 * vte_terminal_set_color_bold:
2525 * @terminal: a #VteTerminal
2526 * @bold: the new bold color
2528 * Sets the color used to draw bold text in the default foreground color.
2531 vte_terminal_set_color_bold(VteTerminal *terminal, const GdkColor *bold)
2533 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2534 g_return_if_fail(bold != NULL);
2536 _vte_debug_print(VTE_DEBUG_MISC,
2537 "Set bold color to (%04x,%04x,%04x).\n",
2538 bold->red, bold->green, bold->blue);
2539 vte_terminal_set_color_internal(terminal, VTE_BOLD_FG, bold);
2543 * vte_terminal_set_color_dim:
2544 * @terminal: a #VteTerminal
2545 * @dim: the new dim color
2547 * Sets the color used to draw dim text in the default foreground color.
2550 vte_terminal_set_color_dim(VteTerminal *terminal, const GdkColor *dim)
2552 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2553 g_return_if_fail(dim != NULL);
2555 _vte_debug_print(VTE_DEBUG_MISC,
2556 "Set dim color to (%04x,%04x,%04x).\n",
2557 dim->red, dim->green, dim->blue);
2558 vte_terminal_set_color_internal(terminal, VTE_DIM_FG, dim);
2562 * vte_terminal_set_color_foreground:
2563 * @terminal: a #VteTerminal
2564 * @foreground: the new foreground color
2566 * Sets the foreground color used to draw normal text
2569 vte_terminal_set_color_foreground(VteTerminal *terminal,
2570 const GdkColor *foreground)
2572 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2573 g_return_if_fail(foreground != NULL);
2575 _vte_debug_print(VTE_DEBUG_MISC,
2576 "Set foreground color to (%04x,%04x,%04x).\n",
2577 foreground->red, foreground->green, foreground->blue);
2578 vte_terminal_set_color_internal(terminal, VTE_DEF_FG, foreground);
2582 * vte_terminal_set_color_background:
2583 * @terminal: a #VteTerminal
2584 * @background: the new background color
2586 * Sets the background color for text which does not have a specific background
2587 * color assigned. Only has effect when no background image is set and when
2588 * the terminal is not transparent.
2591 vte_terminal_set_color_background(VteTerminal *terminal,
2592 const GdkColor *background)
2594 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2595 g_return_if_fail(background != NULL);
2597 _vte_debug_print(VTE_DEBUG_MISC,
2598 "Set background color to (%04x,%04x,%04x).\n",
2599 background->red, background->green, background->blue);
2600 vte_terminal_set_color_internal(terminal, VTE_DEF_BG, background);
2604 * vte_terminal_set_color_cursor:
2605 * @terminal: a #VteTerminal
2606 * @cursor_background: (allow-none): the new color to use for the text cursor, or %NULL
2608 * Sets the background color for text which is under the cursor. If %NULL, text
2609 * under the cursor will be drawn with foreground and background colors
2615 vte_terminal_set_color_cursor(VteTerminal *terminal,
2616 const GdkColor *cursor_background)
2618 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2620 if (cursor_background != NULL) {
2621 _vte_debug_print(VTE_DEBUG_MISC,
2622 "Set cursor color to (%04x,%04x,%04x).\n",
2623 cursor_background->red,
2624 cursor_background->green,
2625 cursor_background->blue);
2626 vte_terminal_set_color_internal(terminal, VTE_CUR_BG,
2628 terminal->pvt->cursor_color_set = TRUE;
2630 _vte_debug_print(VTE_DEBUG_MISC,
2631 "Cleared cursor color.\n");
2632 terminal->pvt->cursor_color_set = FALSE;
2637 * vte_terminal_set_color_highlight:
2638 * @terminal: a #VteTerminal
2639 * @highlight_background: (allow-none): the new color to use for highlighted text, or %NULL
2641 * Sets the background color for text which is highlighted. If %NULL,
2642 * highlighted text (which is usually highlighted because it is selected) will
2643 * be drawn with foreground and background colors reversed.
2648 vte_terminal_set_color_highlight(VteTerminal *terminal,
2649 const GdkColor *highlight_background)
2651 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2653 if (highlight_background != NULL) {
2654 _vte_debug_print(VTE_DEBUG_MISC,
2655 "Set highlight color to (%04x,%04x,%04x).\n",
2656 highlight_background->red,
2657 highlight_background->green,
2658 highlight_background->blue);
2659 vte_terminal_set_color_internal(terminal, VTE_DEF_HL,
2660 highlight_background);
2661 terminal->pvt->highlight_color_set = TRUE;
2663 _vte_debug_print(VTE_DEBUG_MISC,
2664 "Cleared highlight color.\n");
2665 terminal->pvt->highlight_color_set = FALSE;
2670 * vte_terminal_set_colors:
2671 * @terminal: a #VteTerminal
2672 * @foreground: (allow-none): the new foreground color, or %NULL
2673 * @background: (allow-none): the new background color, or %NULL
2674 * @palette: (array length=palette_size zero-terminated=0) (element-type Gdk.Color): the color palette
2675 * @palette_size: the number of entries in @palette
2677 * The terminal widget uses a 28-color model comprised of the default foreground
2678 * and background colors, the bold foreground color, the dim foreground
2679 * color, an eight color palette, bold versions of the eight color palette,
2680 * and a dim version of the the eight color palette.
2682 * @palette_size must be either 0, 8, 16, or 24, or between 25 and 255 inclusive.
2683 * If @foreground is %NULL and
2684 * @palette_size is greater than 0, the new foreground color is taken from
2685 * @palette[7]. If @background is %NULL and @palette_size is greater than 0,
2686 * the new background color is taken from @palette[0]. If
2687 * @palette_size is 8 or 16, the third (dim) and possibly the second (bold)
2688 * 8-color palettes are extrapolated from the new background color and the items
2692 vte_terminal_set_colors(VteTerminal *terminal,
2693 const GdkColor *foreground,
2694 const GdkColor *background,
2695 const GdkColor *palette,
2701 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2703 g_return_if_fail(palette_size >= 0);
2704 g_return_if_fail((palette_size == 0) ||
2705 (palette_size == 8) ||
2706 (palette_size == 16) ||
2707 (palette_size == 24) ||
2708 (palette_size > 24 && palette_size < 256));
2710 _vte_debug_print(VTE_DEBUG_MISC,
2711 "Set color palette [%ld elements].\n",
2714 /* Accept NULL as the default foreground and background colors if we
2716 if ((foreground == NULL) && (palette_size >= 8)) {
2717 foreground = &palette[7];
2719 if ((background == NULL) && (palette_size >= 8)) {
2720 background = &palette[0];
2723 memset(&color, 0, sizeof(color));
2725 /* Initialize each item in the palette if we got any entries to work
2727 for (i=0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
2729 color.blue = (i & 4) ? 0xc000 : 0;
2730 color.green = (i & 2) ? 0xc000 : 0;
2731 color.red = (i & 1) ? 0xc000 : 0;
2733 color.blue += 0x3fff;
2734 color.green += 0x3fff;
2735 color.red += 0x3fff;
2740 int r = j / 36, g = (j / 6) % 6, b = j % 6;
2741 int red = (r == 0) ? 0 : r * 40 + 55;
2742 int green = (g == 0) ? 0 : g * 40 + 55;
2743 int blue = (b == 0) ? 0 : b * 40 + 55;
2744 color.red = red | red << 8 ;
2745 color.green = green | green << 8;
2746 color.blue = blue | blue << 8;
2747 } else if (i < 256) {
2748 int shade = 8 + (i - 232) * 10;
2749 color.red = color.green = color.blue = shade | shade << 8;
2753 if (background != NULL) {
2754 color = *background;
2762 if (foreground != NULL) {
2763 color = *foreground;
2766 color.blue = 0xc000;
2767 color.green = 0xc000;
2771 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2772 &terminal->pvt->palette[VTE_DEF_BG],
2777 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2778 &terminal->pvt->palette[VTE_DEF_BG],
2784 color.blue = 0xc000;
2785 color.green = 0xc000;
2789 color.blue = 0x0000;
2790 color.green = 0x0000;
2794 /* Override from the supplied palette if there is one. */
2795 if ((glong) i < palette_size) {
2799 /* Set up the color entry. */
2800 vte_terminal_set_color_internal(terminal, i, &color);
2803 /* Track that we had a color palette set. */
2804 terminal->pvt->palette_initialized = TRUE;
2808 * vte_terminal_set_opacity:
2809 * @terminal: a #VteTerminal
2810 * @opacity: the new opacity
2812 * Sets the opacity of the terminal background, were 0 means completely
2813 * transparent and 65535 means completely opaque.
2816 vte_terminal_set_opacity(VteTerminal *terminal, guint16 opacity)
2818 VteTerminalPrivate *pvt;
2820 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2822 pvt = terminal->pvt;
2824 if (opacity == pvt->bg_opacity)
2827 pvt->bg_opacity = opacity;
2829 g_object_notify(G_OBJECT(terminal), "background-opacity");
2833 * vte_terminal_set_default_colors:
2834 * @terminal: a #VteTerminal
2836 * Reset the terminal palette to reasonable compiled-in default color.
2839 vte_terminal_set_default_colors(VteTerminal *terminal)
2841 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2842 vte_terminal_set_colors(terminal, NULL, NULL, NULL, 0);
2846 /* Cleanup smart-tabs. See vte_sequence_handler_ta() */
2848 _vte_terminal_cleanup_tab_fragments_at_cursor (VteTerminal *terminal)
2850 VteRowData *row = _vte_terminal_ensure_row (terminal);
2851 VteScreen *screen = terminal->pvt->screen;
2852 long col = screen->cursor_current.col;
2853 const VteCell *pcell = _vte_row_data_get (row, col);
2855 if (G_UNLIKELY (pcell != NULL && pcell->c == '\t')) {
2856 long i, num_columns;
2857 VteCell *cell = _vte_row_data_get_writable (row, col);
2859 _vte_debug_print(VTE_DEBUG_MISC,
2860 "Cleaning tab fragments at %ld",
2863 /* go back to the beginning of the tab */
2864 while (cell->attr.fragment && col > 0)
2865 cell = _vte_row_data_get_writable (row, --col);
2867 num_columns = cell->attr.columns;
2868 for (i = 0; i < num_columns; i++) {
2869 cell = _vte_row_data_get_writable (row, col++);
2870 if (G_UNLIKELY (!cell))
2872 *cell = screen->fill_defaults;
2877 /* Cursor down, with scrolling. */
2879 _vte_terminal_cursor_down (VteTerminal *terminal)
2884 screen = terminal->pvt->screen;
2886 if (screen->scrolling_restricted) {
2887 start = screen->insert_delta + screen->scrolling_region.start;
2888 end = screen->insert_delta + screen->scrolling_region.end;
2890 start = screen->insert_delta;
2891 end = start + terminal->row_count - 1;
2893 if (screen->cursor_current.row == end) {
2894 /* Match xterm and fill to the end of row when scrolling. */
2895 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2896 VteRowData *rowdata;
2897 rowdata = _vte_terminal_ensure_row (terminal);
2898 _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2901 if (screen->scrolling_restricted) {
2902 if (start == screen->insert_delta) {
2903 /* Scroll this line into the scrollback
2904 * buffer by inserting a line at the next
2905 * line and scrolling the area up. */
2906 screen->insert_delta++;
2907 screen->scroll_delta++;
2908 screen->cursor_current.row++;
2909 /* update start and end, as they are relative
2910 * to insert_delta. */
2913 _vte_terminal_ring_insert (terminal, screen->cursor_current.row, FALSE);
2914 /* Force the areas below the region to be
2915 * redrawn -- they've moved. */
2916 _vte_terminal_scroll_region(terminal, start,
2917 end - start + 1, 1);
2919 _vte_terminal_adjust_adjustments(terminal);
2921 /* If we're at the bottom of the scrolling
2922 * region, add a line at the top to scroll the
2924 _vte_terminal_ring_remove (terminal, start);
2925 _vte_terminal_ring_insert (terminal, end, TRUE);
2926 /* Update the display. */
2927 _vte_terminal_scroll_region(terminal, start,
2928 end - start + 1, -1);
2929 _vte_invalidate_cells(terminal,
2930 0, terminal->column_count,
2934 /* Scroll up with history. */
2935 screen->cursor_current.row++;
2936 _vte_terminal_update_insert_delta(terminal);
2939 /* Match xterm and fill the new row when scrolling. */
2940 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2941 VteRowData *rowdata;
2942 rowdata = _vte_terminal_ensure_row (terminal);
2943 _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2946 /* Otherwise, just move the cursor down. */
2947 screen->cursor_current.row++;
2951 /* Insert a single character into the stored data array. */
2953 _vte_terminal_insert_char(VteTerminal *terminal, gunichar c,
2954 gboolean insert, gboolean invalidate_now)
2961 gboolean line_wrapped = FALSE; /* cursor moved before char inserted */
2963 screen = terminal->pvt->screen;
2964 insert |= screen->insert_mode;
2965 invalidate_now |= insert;
2967 /* If we've enabled the special drawing set, map the characters to
2969 if (G_UNLIKELY (screen->alternate_charset)) {
2970 _vte_debug_print(VTE_DEBUG_SUBSTITUTION,
2971 "Attempting charset substitution"
2972 "for U+%04X.\n", c);
2973 /* See if there's a mapping for it. */
2974 c = _vte_iso2022_process_single(terminal->pvt->iso2022, c, '0');
2977 /* If this character is destined for the status line, save it. */
2978 if (G_UNLIKELY (screen->status_line)) {
2979 g_string_append_unichar(screen->status_line_contents, c);
2980 screen->status_line_changed = TRUE;
2984 /* Figure out how many columns this character should occupy. */
2985 if (G_UNLIKELY (VTE_ISO2022_HAS_ENCODED_WIDTH(c))) {
2986 columns = _vte_iso2022_get_encoded_width(c);
2987 c &= ~VTE_ISO2022_ENCODED_WIDTH_MASK;
2989 columns = _vte_iso2022_unichar_width(terminal->pvt->iso2022, c);
2993 /* If we're autowrapping here, do it. */
2994 col = screen->cursor_current.col;
2995 if (G_UNLIKELY (columns && col + columns > terminal->column_count)) {
2996 if (terminal->pvt->flags.am) {
2997 _vte_debug_print(VTE_DEBUG_ADJ,
2998 "Autowrapping before character\n");
3000 /* XXX clear to the end of line */
3001 col = screen->cursor_current.col = 0;
3002 /* Mark this line as soft-wrapped. */
3003 row = _vte_terminal_ensure_row (terminal);
3004 row->attr.soft_wrapped = 1;
3005 _vte_terminal_cursor_down (terminal);
3007 /* Don't wrap, stay at the rightmost column. */
3008 col = screen->cursor_current.col =
3009 terminal->column_count - columns;
3011 line_wrapped = TRUE;
3014 _vte_debug_print(VTE_DEBUG_PARSE,
3015 "Inserting %ld '%c' (%d/%d) (%ld+%d, %ld), delta = %ld; ",
3016 (long)c, c < 256 ? c : ' ',
3017 screen->defaults.attr.fore,
3018 screen->defaults.attr.back,
3019 col, columns, (long)screen->cursor_current.row,
3020 (long)screen->insert_delta);
3023 if (G_UNLIKELY (columns == 0)) {
3025 /* It's a combining mark */
3030 _vte_debug_print(VTE_DEBUG_PARSE, "combining U+%04X", c);
3032 row_num = screen->cursor_current.row;
3034 if (G_UNLIKELY (col == 0)) {
3035 /* We are at first column. See if the previous line softwrapped.
3036 * If it did, move there. Otherwise skip inserting. */
3038 if (G_LIKELY (row_num > 0)) {
3040 row = _vte_terminal_find_row_data_writable (terminal, row_num);
3043 if (!row->attr.soft_wrapped)
3046 col = _vte_row_data_length (row);
3050 row = _vte_terminal_find_row_data_writable (terminal, row_num);
3053 if (G_UNLIKELY (!row || !col))
3056 /* Combine it on the previous cell */
3059 cell = _vte_row_data_get_writable (row, col);
3061 if (G_UNLIKELY (!cell))
3064 /* Find the previous cell */
3065 while (cell && cell->attr.fragment && col > 0)
3066 cell = _vte_row_data_get_writable (row, --col);
3067 if (G_UNLIKELY (!cell || cell->c == '\t'))
3070 /* Combine the new character on top of the cell string */
3071 c = _vte_unistr_append_unichar (cell->c, c);
3074 columns = cell->attr.columns;
3075 for (i = 0; i < columns; i++) {
3076 cell = _vte_row_data_get_writable (row, col++);
3080 /* Always invalidate since we put the mark on the *previous* cell
3081 * and the higher level code doesn't know this. */
3082 _vte_invalidate_cells(terminal,
3090 /* Make sure we have enough rows to hold this data. */
3091 row = vte_terminal_ensure_cursor (terminal);
3092 g_assert(row != NULL);
3094 _vte_terminal_cleanup_tab_fragments_at_cursor (terminal);
3097 for (i = 0; i < columns; i++)
3098 _vte_row_data_insert (row, col + i, &screen->color_defaults);
3100 _vte_row_data_fill (row, &basic_cell.cell, col + columns);
3103 /* Convert any wide characters we may have broken into single
3104 * cells. (#514632) */
3105 if (G_LIKELY (col > 0)) {
3106 glong col2 = col - 1;
3107 VteCell *cell = _vte_row_data_get_writable (row, col2);
3108 while (col2 > 0 && cell != NULL && cell->attr.fragment)
3109 cell = _vte_row_data_get_writable (row, --col2);
3110 cell->attr.columns = col - col2;
3113 glong col2 = col + columns;
3114 VteCell *cell = _vte_row_data_get_writable (row, col2);
3115 while (cell != NULL && cell->attr.fragment) {
3116 cell->attr.columns = 1;
3118 cell = _vte_row_data_get_writable (row, ++col2);
3122 attr = screen->defaults.attr;
3123 attr.columns = columns;
3125 if (G_UNLIKELY (c == '_' && terminal->pvt->flags.ul)) {
3126 const VteCell *pcell = _vte_row_data_get (row, col);
3127 /* Handle overstrike-style underlining. */
3128 if (pcell->c != 0) {
3129 /* restore previous contents */
3131 attr.columns = pcell->attr.columns;
3132 attr.fragment = pcell->attr.fragment;
3140 VteCell *pcell = _vte_row_data_get_writable (row, col);
3146 /* insert wide-char fragments */
3148 for (i = 1; i < columns; i++) {
3149 VteCell *pcell = _vte_row_data_get_writable (row, col);
3154 _vte_row_data_shrink (row, terminal->column_count);
3156 /* Signal that this part of the window needs drawing. */
3157 if (G_UNLIKELY (invalidate_now)) {
3158 _vte_invalidate_cells(terminal,
3160 insert ? terminal->column_count : columns,
3161 screen->cursor_current.row, 1);
3165 /* If we're autowrapping *here*, do it. */
3166 screen->cursor_current.col = col;
3167 if (G_UNLIKELY (col >= terminal->column_count)) {
3168 if (terminal->pvt->flags.am && !terminal->pvt->flags.xn) {
3170 screen->cursor_current.col = 0;
3171 /* Mark this line as soft-wrapped. */
3172 row->attr.soft_wrapped = 1;
3173 _vte_terminal_cursor_down (terminal);
3178 /* We added text, so make a note of it. */
3179 terminal->pvt->text_inserted_flag = TRUE;
3182 _vte_debug_print(VTE_DEBUG_ADJ|VTE_DEBUG_PARSE,
3183 "insertion delta => %ld.\n",
3184 (long)screen->insert_delta);
3185 return line_wrapped;
3188 /* Catch a VteReaper child-exited signal, and if it matches the one we're
3189 * looking for, emit one of our own. */
3191 vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status,
3192 VteTerminal *terminal)
3194 if (pid == terminal->pvt->pty_pid) {
3195 GObject *object = G_OBJECT(terminal);
3197 g_object_ref(object);
3198 g_object_freeze_notify(object);
3200 _VTE_DEBUG_IF (VTE_DEBUG_LIFECYCLE) {
3201 g_printerr ("Child[%d] exited with status %d\n",
3203 #ifdef HAVE_SYS_WAIT_H
3204 if (WIFEXITED (status)) {
3205 g_printerr ("Child[%d] exit code %d.\n",
3206 pid, WEXITSTATUS (status));
3207 }else if (WIFSIGNALED (status)) {
3208 g_printerr ("Child[%d] dies with signal %d.\n",
3209 pid, WTERMSIG (status));
3213 /* Disconnect from the reaper. */
3214 if (terminal->pvt->pty_reaper != NULL) {
3215 g_signal_handlers_disconnect_by_func(terminal->pvt->pty_reaper,
3216 vte_terminal_catch_child_exited,
3218 g_object_unref(terminal->pvt->pty_reaper);
3219 terminal->pvt->pty_reaper = NULL;
3221 terminal->pvt->pty_pid = -1;
3223 /* Close out the PTY. */
3224 vte_terminal_set_pty_object(terminal, NULL);
3226 /* Tell observers what's happened. */
3227 terminal->pvt->child_exit_status = status;
3228 vte_terminal_emit_child_exited(terminal);
3230 g_object_thaw_notify(object);
3231 g_object_unref(object);
3233 /* Note: terminal may be destroyed at this point */
3237 static void mark_input_source_invalid(VteTerminal *terminal)
3239 _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_read\n");
3240 terminal->pvt->pty_input_source = 0;
3243 _vte_terminal_connect_pty_read(VteTerminal *terminal)
3245 if (terminal->pvt->pty_channel == NULL) {
3249 if (terminal->pvt->pty_input_source == 0) {
3250 _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_read\n");
3251 terminal->pvt->pty_input_source =
3252 g_io_add_watch_full(terminal->pvt->pty_channel,
3253 VTE_CHILD_INPUT_PRIORITY,
3255 (GIOFunc) vte_terminal_io_read,
3257 (GDestroyNotify) mark_input_source_invalid);
3261 static void mark_output_source_invalid(VteTerminal *terminal)
3263 _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_write\n");
3264 terminal->pvt->pty_output_source = 0;
3267 _vte_terminal_connect_pty_write(VteTerminal *terminal)
3269 VteTerminalPrivate *pvt = terminal->pvt;
3271 g_assert(pvt->pty != NULL);
3272 if (terminal->pvt->pty_channel == NULL) {
3274 g_io_channel_unix_new(vte_pty_get_fd(pvt->pty));
3277 if (terminal->pvt->pty_output_source == 0) {
3278 if (vte_terminal_io_write (terminal->pvt->pty_channel,
3282 _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_write\n");
3283 terminal->pvt->pty_output_source =
3284 g_io_add_watch_full(terminal->pvt->pty_channel,
3285 VTE_CHILD_OUTPUT_PRIORITY,
3287 (GIOFunc) vte_terminal_io_write,
3289 (GDestroyNotify) mark_output_source_invalid);
3295 _vte_terminal_disconnect_pty_read(VteTerminal *terminal)
3297 if (terminal->pvt->pty_input_source != 0) {
3298 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_read\n");
3299 g_source_remove(terminal->pvt->pty_input_source);
3300 terminal->pvt->pty_input_source = 0;
3305 _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
3307 if (terminal->pvt->pty_output_source != 0) {
3308 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_write\n");
3310 g_source_remove(terminal->pvt->pty_output_source);
3311 terminal->pvt->pty_output_source = 0;
3316 * vte_terminal_pty_new:
3317 * @terminal: a #VteTerminal
3318 * @flags: flags from #VtePtyFlags
3319 * @error: (allow-none): return location for a #GError, or %NULL
3321 * Creates a new #VtePty, and sets the emulation property
3322 * from #VteTerminal:emulation.
3324 * See vte_pty_new() for more information.
3326 * Returns: (transfer full): a new #VtePty
3330 vte_terminal_pty_new(VteTerminal *terminal,
3334 VteTerminalPrivate *pvt;
3337 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
3339 pvt = terminal->pvt;
3341 pty = vte_pty_new(flags, error);
3345 vte_pty_set_term(pty, vte_terminal_get_emulation(terminal));
3351 * vte_terminal_watch_child:
3352 * @terminal: a #VteTerminal
3353 * @child_pid: a #GPid
3355 * Watches @child_pid. When the process exists, the #VteReaper::child-exited
3356 * signal will be called. Use vte_terminal_get_child_exit_status() to
3357 * retrieve the child's exit status.
3359 * Prior to calling this function, a #VtePty must have been set in @terminal
3360 * using vte_terminal_set_pty_object().
3361 * When the child exits, the terminal's #VtePty will be set to %NULL.
3363 * Note: g_child_watch_add() or g_child_watch_add_full() must not have
3364 * been called for @child_pid, nor a #GSource for it been created with
3365 * g_child_watch_source_new().
3367 * Note: when using the g_spawn_async() family of functions,
3368 * the %G_SPAWN_DO_NOT_REAP_CHILD flag MUST have been passed.
3373 vte_terminal_watch_child (VteTerminal *terminal,
3376 VteTerminalPrivate *pvt;
3380 g_return_if_fail(VTE_IS_TERMINAL(terminal));
3381 g_return_if_fail(child_pid != -1);
3383 pvt = terminal->pvt;
3384 g_return_if_fail(pvt->pty != NULL);
3386 // FIXMEchpe: support passing child_pid = -1 to remove the wathch
3388 object = G_OBJECT(terminal);
3390 g_object_freeze_notify(object);
3392 /* Set this as the child's pid. */
3393 pvt->pty_pid = child_pid;
3394 pvt->child_exit_status = 0;
3396 /* Catch a child-exited signal from the child pid. */
3397 reaper = vte_reaper_get();
3398 vte_reaper_add_child(child_pid);
3399 if (reaper != pvt->pty_reaper) {
3400 if (terminal->pvt->pty_reaper != NULL) {
3401 g_signal_handlers_disconnect_by_func(pvt->pty_reaper,
3402 vte_terminal_catch_child_exited,
3404 g_object_unref(pvt->pty_reaper);
3406 g_signal_connect(reaper, "child-exited",
3407 G_CALLBACK(vte_terminal_catch_child_exited),
3409 pvt->pty_reaper = reaper;
3411 g_object_unref(reaper);
3414 /* FIXMEchpe: call vte_terminal_set_size here? */
3416 g_object_thaw_notify(object);
3420 * _vte_terminal_get_user_shell:
3422 * Uses getpwd() to determine the user's shell. If that fails, falls back
3423 * to using the SHELL environment variable. As last-ditch fallback, returns
3426 * Returns: a newly allocated string containing the command to run the
3430 _vte_terminal_get_user_shell (void)
3435 pwd = getpwuid(getuid());
3437 command = g_strdup (pwd->pw_shell);
3438 _vte_debug_print(VTE_DEBUG_MISC,
3439 "Using user's shell (%s).\n",
3440 command ? command : "(null)");
3442 if (command == NULL) {
3443 if (g_getenv ("SHELL")) {
3444 command = g_strdup (g_getenv ("SHELL"));
3445 _vte_debug_print(VTE_DEBUG_MISC,
3446 "Using $SHELL shell (%s).\n",
3449 command = g_strdup ("/bin/sh");
3450 _vte_debug_print(VTE_DEBUG_MISC,
3451 "Using default shell (%s).\n",
3456 g_assert (command != NULL);
3462 * _vte_terminal_get_argv:
3463 * @command: the command to run
3464 * @argv: the argument vector
3465 * @flags: (inout) flags from #GSpawnFlags
3467 * Creates an argument vector to pass to g_spawn_async() from @command and
3468 * @argv, modifying *@flags if necessary.
3469 * Like __vte_pty_get_argv(), but returns the argument vector to spawn
3470 * the user's shell if @command is %NULL.
3472 * Returns: a newly allocated array of strings. Free with g_strfreev()
3475 _vte_terminal_get_argv (const char *command,
3477 GSpawnFlags *flags /* inout */)
3482 argv2 = __vte_pty_get_argv(command ? command : (shell = _vte_terminal_get_user_shell()),
3490 * vte_terminal_fork_command:
3491 * @terminal: a #VteTerminal
3492 * @command: (allow-none) (type filename): the name of a binary to run, or %NULL to spawn the user's shell
3493 * @argv: (allow-none) (array zero-terminated=1) (element-type filename): the argument list to be passed to @command, or %NULL
3494 * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment variables to be added to the environment before
3495 * starting @command, or %NULL
3496 * @working_directory: (allow-none) (type filename): the name of a directory the command should start in, or %NULL
3497 * @lastlog: %TRUE if the session should be logged to the lastlog
3498 * @utmp: %TRUE if the session should be logged to the utmp/utmpx log
3499 * @wtmp: %TRUE if the session should be logged to the wtmp/wtmpx log
3501 * Starts the specified command under a newly-allocated controlling
3502 * pseudo-terminal. The @argv and @envv lists should be %NULL-terminated, and
3503 * argv[0] is expected to be the name of the file being run, as it would be if
3504 * execve() were being called. TERM is automatically set to reflect the
3505 * terminal widget's emulation setting. If @lastlog, @utmp, or @wtmp are %TRUE,
3506 * logs the session to the specified system log files.
3508 * Note that all file descriptors except stdin/stdout/stderr will be closed
3509 * before calling exec() in the child.
3511 * Returns: the PID of the new process, or <literal>-1</literal> on failure
3513 * Deprecated: 0.26: Use vte_terminal_fork_command_full()
3516 vte_terminal_fork_command(VteTerminal *terminal,
3517 const char *command,
3520 const char *working_directory,
3526 GSpawnFlags spawn_flags;
3530 GError *error = NULL;
3531 GError **err = &error;
3533 GError **err = NULL;
3536 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
3538 spawn_flags = G_SPAWN_CHILD_INHERITS_STDIN |
3539 G_SPAWN_SEARCH_PATH;
3540 real_argv = _vte_terminal_get_argv (command, argv, &spawn_flags);
3542 ret = vte_terminal_fork_command_full(terminal,
3543 __vte_pty_get_pty_flags(lastlog, utmp, wtmp),
3551 g_strfreev (real_argv);
3555 _vte_debug_print(VTE_DEBUG_MISC,
3556 "vte_terminal_fork_command failed: %s\n", error->message);
3557 g_error_free(error);
3564 return (pid_t) child_pid;
3568 * vte_terminal_fork_command_full:
3569 * @terminal: a #VteTerminal
3570 * @pty_flags: flags from #VtePtyFlags
3571 * @argv: (array zero-terminated=1) (element-type filename): child's argument vector
3572 * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment
3573 * variables to be added to the environment before starting the process, or %NULL
3574 * @working_directory: (allow-none) (type filename): the name of a directory the command should start
3575 * in, or %NULL to use the current working directory
3576 * @spawn_flags: flags from #GSpawnFlags
3577 * @child_setup: (allow-none): function to run in the child just before exec(), or %NULL
3578 * @child_setup_data: user data for @child_setup
3579 * @child_pid: (out) (allow-none) (transfer full): a location to store the child PID, or %NULL
3580 * @error: (allow-none): return location for a #GError, or %NULL
3582 * Starts the specified command under a newly-allocated controlling
3583 * pseudo-terminal. The @argv and @envv lists should be %NULL-terminated.
3584 * The "TERM" environment variable is automatically set to reflect the
3585 * terminal widget's emulation setting.
3586 * @pty_flags controls logging the session to the specified system log files.
3588 * Note that %G_SPAWN_DO_NOT_REAP_CHILD will always be added to @spawn_flags.
3590 * Note that unless @spawn_flags contains %G_SPAWN_LEAVE_DESCRIPTORS_OPEN, all file
3591 * descriptors except stdin/stdout/stderr will be closed before calling exec()
3594 * See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
3596 * Returns: %TRUE on success, or %FALSE on error with @error filled in
3601 vte_terminal_fork_command_full(VteTerminal *terminal,
3602 VtePtyFlags pty_flags,
3603 const char *working_directory,
3606 GSpawnFlags spawn_flags,
3607 GSpawnChildSetupFunc child_setup,
3608 gpointer child_setup_data,
3609 GPid *child_pid /* out */,
3615 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
3616 g_return_val_if_fail(argv != NULL, FALSE);
3617 g_return_val_if_fail(child_setup_data == NULL || child_setup, FALSE);
3618 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
3620 pty = vte_pty_new(pty_flags, error);
3624 /* FIXMEchpe: is this flag needed */
3625 spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN;
3627 if (!__vte_pty_spawn(pty,
3632 child_setup, child_setup_data,
3635 g_object_unref(pty);
3639 vte_terminal_set_pty_object(terminal, pty);
3640 vte_terminal_watch_child(terminal, pid);
3649 * vte_terminal_forkpty:
3650 * @terminal: a #VteTerminal
3651 * @envv: a list of environment variables to be added to the environment before
3652 * starting returning in the child process, or %NULL
3653 * @working_directory: the name of a directory the child process should change to, or
3655 * @lastlog: %TRUE if the session should be logged to the lastlog
3656 * @utmp: %TRUE if the session should be logged to the utmp/utmpx log
3657 * @wtmp: %TRUE if the session should be logged to the wtmp/wtmpx log
3659 * Starts a new child process under a newly-allocated controlling
3660 * pseudo-terminal. TERM is automatically set to reflect the terminal widget's
3661 * emulation setting. If @lastlog, @utmp, or @wtmp are %TRUE, logs the session
3662 * to the specified system log files.
3664 * Note that all file descriptors except stdin/stdout/stderr will be closed
3667 * Note that @envv and @working_directory are silently ignored.
3669 * Returns: the ID of the new process in the parent, 0 in the child, and -1 if
3670 * there was an error
3674 * Deprecated: 0.26: Use #VtePty and fork() instead
3677 vte_terminal_forkpty(VteTerminal *terminal,
3678 char **envv, const char *working_directory,
3679 gboolean lastlog, gboolean utmp, gboolean wtmp)
3685 g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
3687 pty = vte_pty_new(__vte_pty_get_pty_flags(lastlog, utmp, wtmp), NULL);
3691 if (!__vte_pty_fork(pty,
3694 g_object_unref(pty);
3698 vte_terminal_set_pty_object(terminal, pty);
3699 // FIXMEchpe is that really right?
3700 vte_terminal_watch_child(terminal, pid);
3708 /* Handle an EOF from the client. */
3710 vte_terminal_eof(GIOChannel *channel, VteTerminal *terminal)
3712 GObject *object = G_OBJECT(terminal);
3714 g_object_freeze_notify(object);
3716 vte_terminal_set_pty_object(terminal, NULL);
3718 /* Emit a signal that we read an EOF. */
3719 vte_terminal_queue_eof(terminal);
3721 g_object_thaw_notify(object);
3724 /* Reset the input method context. */
3726 vte_terminal_im_reset(VteTerminal *terminal)
3728 if (GTK_WIDGET_REALIZED(terminal)) {
3729 gtk_im_context_reset(terminal->pvt->im_context);
3730 if (terminal->pvt->im_preedit != NULL) {
3731 g_free(terminal->pvt->im_preedit);
3732 terminal->pvt->im_preedit = NULL;
3734 if (terminal->pvt->im_preedit_attrs != NULL) {
3735 pango_attr_list_unref(terminal->pvt->im_preedit_attrs);
3736 terminal->pvt->im_preedit_attrs = NULL;
3741 /* Emit whichever signals are called for here. */
3743 vte_terminal_emit_pending_text_signals(VteTerminal *terminal, GQuark quark)
3748 } non_visual_quarks[] = {
3756 {"character-attributes", 0},
3761 for (i = 0; i < G_N_ELEMENTS(non_visual_quarks); i++) {
3762 if (non_visual_quarks[i].quark == 0) {
3763 non_visual_quarks[i].quark =
3764 g_quark_from_static_string(non_visual_quarks[i].name);
3766 if (quark == non_visual_quarks[i].quark) {
3772 if (terminal->pvt->text_modified_flag) {
3773 _vte_debug_print(VTE_DEBUG_SIGNALS,
3774 "Emitting buffered `text-modified'.\n");
3775 vte_terminal_emit_text_modified(terminal);
3776 terminal->pvt->text_modified_flag = FALSE;
3778 if (terminal->pvt->text_inserted_flag) {
3779 _vte_debug_print(VTE_DEBUG_SIGNALS,
3780 "Emitting buffered `text-inserted'\n");
3781 _vte_terminal_emit_text_inserted(terminal);
3782 terminal->pvt->text_inserted_flag = FALSE;
3784 if (terminal->pvt->text_deleted_flag) {
3785 _vte_debug_print(VTE_DEBUG_SIGNALS,
3786 "Emitting buffered `text-deleted'\n");
3787 _vte_terminal_emit_text_deleted(terminal);
3788 terminal->pvt->text_deleted_flag = FALSE;
3792 /* Process incoming data, first converting it to unicode characters, and then
3793 * processing control sequences. */
3795 vte_terminal_process_incoming(VteTerminal *terminal)
3798 struct vte_cursor_position cursor;
3799 gboolean cursor_visible;
3800 GdkPoint bbox_topleft, bbox_bottomright;
3802 long wcount, start, delta;
3803 gboolean leftovers, modified, bottom, again;
3804 gboolean invalidated_text;
3806 struct _vte_incoming_chunk *chunk, *next_chunk, *achunk = NULL;
3808 _vte_debug_print(VTE_DEBUG_IO,
3809 "Handler processing %"G_GSIZE_FORMAT" bytes over %"G_GSIZE_FORMAT" chunks + %d bytes pending.\n",
3810 _vte_incoming_chunks_length(terminal->pvt->incoming),
3811 _vte_incoming_chunks_count(terminal->pvt->incoming),
3812 terminal->pvt->pending->len);
3813 _vte_debug_print (VTE_DEBUG_WORK, "(");
3815 screen = terminal->pvt->screen;
3817 delta = screen->scroll_delta;
3818 bottom = screen->insert_delta == delta;
3820 /* Save the current cursor position. */
3821 cursor = screen->cursor_current;
3822 cursor_visible = terminal->pvt->cursor_visible;
3824 /* We should only be called when there's data to process. */
3825 g_assert(terminal->pvt->incoming ||
3826 (terminal->pvt->pending->len > 0));
3828 /* Convert the data into unicode characters. */
3829 unichars = terminal->pvt->pending;
3830 for (chunk = _vte_incoming_chunks_reverse (terminal->pvt->incoming);
3832 chunk = next_chunk) {
3834 next_chunk = chunk->next;
3835 if (chunk->len == 0) {
3838 processed = _vte_iso2022_process(terminal->pvt->iso2022,
3839 chunk->data, chunk->len,
3841 if (G_UNLIKELY (processed != chunk->len)) {
3842 /* shuffle the data about */
3843 g_memmove (chunk->data, chunk->data + processed,
3844 chunk->len - processed);
3845 chunk->len = chunk->len - processed;
3846 processed = sizeof (chunk->data) - chunk->len;
3847 if (processed != 0 && next_chunk != NULL) {
3848 if (next_chunk->len <= processed) {
3849 /* consume it entirely */
3850 memcpy (chunk->data + chunk->len,
3853 chunk->len += next_chunk->len;
3854 chunk->next = next_chunk->next;
3855 release_chunk (next_chunk);
3857 /* next few bytes */
3858 memcpy (chunk->data + chunk->len,
3861 chunk->len += processed;
3862 g_memmove (next_chunk->data,
3863 next_chunk->data + processed,
3864 next_chunk->len - processed);
3865 next_chunk->len -= processed;
3867 next_chunk = chunk; /* repeat */
3873 /* cache the last chunk */
3875 release_chunk (achunk);
3881 if (chunk != NULL) {
3882 release_chunk (achunk);
3889 terminal->pvt->incoming = chunk;
3891 /* Compute the number of unicode characters we got. */
3892 wbuf = &g_array_index(unichars, gunichar, 0);
3893 wcount = unichars->len;
3895 /* Try initial substrings. */
3897 modified = leftovers = again = FALSE;
3898 invalidated_text = FALSE;
3900 bbox_bottomright.x = bbox_bottomright.y = -G_MAXINT;
3901 bbox_topleft.x = bbox_topleft.y = G_MAXINT;
3903 while (start < wcount && !leftovers) {
3906 const gunichar *next;
3907 GValueArray *params = NULL;
3909 /* Try to match any control sequences. */
3910 _vte_matcher_match(terminal->pvt->matcher,
3917 /* We're in one of three possible situations now.
3918 * First, the match string is a non-empty string and next
3919 * points to the first character which isn't part of this
3921 if ((match != NULL) && (match[0] != '\0')) {
3922 /* Call the right sequence handler for the requested
3924 _vte_terminal_handle_sequence(terminal,
3928 /* Skip over the proper number of unicode chars. */
3929 start = (next - wbuf);
3932 /* if we have moved during the sequence handler, restart the bbox */
3933 if (invalidated_text &&
3934 (screen->cursor_current.col > bbox_bottomright.x + VTE_CELL_BBOX_SLACK ||
3935 screen->cursor_current.col < bbox_topleft.x - VTE_CELL_BBOX_SLACK ||
3936 screen->cursor_current.row > bbox_bottomright.y + VTE_CELL_BBOX_SLACK ||
3937 screen->cursor_current.row < bbox_topleft.y - VTE_CELL_BBOX_SLACK)) {
3938 /* Clip off any part of the box which isn't already on-screen. */
3939 bbox_topleft.x = MAX(bbox_topleft.x, 0);
3940 bbox_topleft.y = MAX(bbox_topleft.y, delta);
3941 bbox_bottomright.x = MIN(bbox_bottomright.x,
3942 terminal->column_count);
3943 /* lazily apply the +1 to the cursor_row */
3944 bbox_bottomright.y = MIN(bbox_bottomright.y + 1,
3945 delta + terminal->row_count);
3947 _vte_invalidate_cells(terminal,
3949 bbox_bottomright.x - bbox_topleft.x,
3951 bbox_bottomright.y - bbox_topleft.y);
3953 invalidated_text = FALSE;
3954 bbox_bottomright.x = bbox_bottomright.y = -G_MAXINT;
3955 bbox_topleft.x = bbox_topleft.y = G_MAXINT;
3958 /* Second, we have a NULL match, and next points to the very
3959 * next character in the buffer. Insert the character which
3960 * we're currently examining into the screen. */
3961 if (match == NULL) {
3963 /* If it's a control character, permute the order, per
3966 ((*next & 0x1f) == *next) &&
3967 (start + 1 < next - wbuf)) {
3968 const gunichar *tnext = NULL;
3969 const char *tmatch = NULL;
3973 /* We don't want to permute it if it's another
3974 * control sequence, so check if it is. */
3975 _vte_matcher_match(terminal->pvt->matcher,
3977 wcount - (next - wbuf),
3982 /* We only do this for non-control-sequence
3983 * characters and random garbage. */
3984 if (tnext == next + 1) {
3985 /* Save the control character. */
3987 /* Move everything before it up a
3989 for (i = next - wbuf; i > start; i--) {
3990 wbuf[i] = wbuf[i - 1];
3992 /* Move the control character to the
3998 _VTE_DEBUG_IF(VTE_DEBUG_PARSE) {
3999 gunichar cc = c & ~VTE_ISO2022_ENCODED_WIDTH_MASK;
4001 g_printerr("U+%04lx\n", (long) cc);
4004 g_printerr("%ld = ",
4008 g_printerr("^%lc\n",