Use accessors for setting adjustment
[vte.git] / src / vte.c
1 /*
2  * Copyright (C) 2001-2004,2009,2010 Red Hat, Inc.
3  * Copyright © 2008, 2009, 2010 Christian Persch
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 /**
21  * SECTION: vte-terminal
22  * @short_description: A terminal widget implementation
23  *
24  * A VteTerminal is a terminal emulator implemented as a GTK2 widget.
25  */
26
27 #include <config.h>
28
29 #include <math.h>
30
31 #include "vte.h"
32 #include "vte-private.h"
33 #include "vte-gtk-compat.h"
34
35 #ifdef HAVE_WCHAR_H
36 #include <wchar.h>
37 #endif
38 #ifdef HAVE_SYS_SYSLIMITS_H
39 #include <sys/syslimits.h>
40 #endif
41 #ifdef HAVE_SYS_WAIT_H
42 #include <sys/wait.h>
43 #endif
44 #include <glib.h>
45 #include <glib/gstdio.h>
46 #include <glib-object.h>
47 #include <gdk/gdk.h>
48 #include <gdk/gdkkeysyms.h>
49 #include <gtk/gtk.h>
50 #include <pango/pango.h>
51 #include "iso2022.h"
52 #include "keymap.h"
53 #include "marshal.h"
54 #include "matcher.h"
55 #include "pty.h"
56 #include "vteaccess.h"
57 #include "vteint.h"
58 #include "vtepty.h"
59 #include "vtepty-private.h"
60 #include "vteregex.h"
61 #include "vtetc.h"
62
63 #ifdef HAVE_LOCALE_H
64 #include <locale.h>
65 #endif
66
67 #ifndef HAVE_WINT_T
68 typedef gunichar wint_t;
69 #endif
70
71 #ifndef howmany
72 #define howmany(x, y) (((x) + ((y) - 1)) / (y))
73 #endif
74
75 #define STATIC_PARAMS (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
76
77
78 static void vte_terminal_set_visibility (VteTerminal *terminal, GdkVisibilityState state);
79 static void vte_terminal_set_termcap(VteTerminal *terminal, const char *path,
80                                      gboolean reset);
81 static void vte_terminal_paste(VteTerminal *terminal, GdkAtom board);
82 static void vte_terminal_real_copy_clipboard(VteTerminal *terminal);
83 static void vte_terminal_real_paste_clipboard(VteTerminal *terminal);
84 static gboolean vte_terminal_io_read(GIOChannel *channel,
85                                      GIOCondition condition,
86                                      VteTerminal *terminal);
87 static gboolean vte_terminal_io_write(GIOChannel *channel,
88                                       GIOCondition condition,
89                                       VteTerminal *terminal);
90 static void vte_terminal_match_hilite_clear(VteTerminal *terminal);
91 static void vte_terminal_match_hilite_hide(VteTerminal *terminal);
92 static void vte_terminal_match_hilite_show(VteTerminal *terminal, long x, long y);
93 static void vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y);
94 static void vte_terminal_match_contents_clear(VteTerminal *terminal);
95 static gboolean vte_terminal_background_update(VteTerminal *data);
96 static void vte_terminal_queue_background_update(VteTerminal *terminal);
97 static void vte_terminal_process_incoming(VteTerminal *terminal);
98 static void vte_terminal_emit_pending_signals(VteTerminal *terminal);
99 static gboolean vte_cell_is_selected(VteTerminal *terminal,
100                                      glong col, glong row, gpointer data);
101 static char *vte_terminal_get_text_range_maybe_wrapped(VteTerminal *terminal,
102                                                        glong start_row,
103                                                        glong start_col,
104                                                        glong end_row,
105                                                        glong end_col,
106                                                        gboolean wrap,
107                                                        VteSelectionFunc is_selected,
108                                                        gpointer data,
109                                                        GArray *attributes,
110                                                        gboolean include_trailing_spaces);
111 static char *vte_terminal_get_text_maybe_wrapped(VteTerminal *terminal,
112                                                  gboolean wrap,
113                                                  VteSelectionFunc is_selected,
114                                                  gpointer data,
115                                                  GArray *attributes,
116                                                  gboolean include_trailing_spaces);
117 static void _vte_terminal_disconnect_pty_read(VteTerminal *terminal);
118 static void _vte_terminal_disconnect_pty_write(VteTerminal *terminal);
119 static void vte_terminal_stop_processing (VteTerminal *terminal);
120
121 static inline gboolean vte_terminal_is_processing (VteTerminal *terminal);
122 static inline void vte_terminal_start_processing (VteTerminal *terminal);
123 static void vte_terminal_add_process_timeout (VteTerminal *terminal);
124 static void add_update_timeout (VteTerminal *terminal);
125 static void remove_update_timeout (VteTerminal *terminal);
126 static void reset_update_regions (VteTerminal *terminal);
127 static void vte_terminal_set_cursor_blinks_internal(VteTerminal *terminal, gboolean blink);
128 static void vte_terminal_set_font_full_internal(VteTerminal *terminal,
129                                                 const PangoFontDescription *font_desc,
130                                                 VteTerminalAntiAlias antialias);
131
132 static gboolean process_timeout (gpointer data);
133 static gboolean update_timeout (gpointer data);
134
135 enum {
136     COPY_CLIPBOARD,
137     PASTE_CLIPBOARD,
138     LAST_SIGNAL
139 };
140 static guint signals[LAST_SIGNAL];
141
142 enum {
143         PROP_0,
144         PROP_ALLOW_BOLD,
145         PROP_AUDIBLE_BELL,
146         PROP_BACKGROUND_IMAGE_FILE,
147         PROP_BACKGROUND_IMAGE_PIXBUF,
148         PROP_BACKGROUND_OPACITY,
149         PROP_BACKGROUND_SATURATION,
150         PROP_BACKGROUND_TINT_COLOR,
151         PROP_BACKGROUND_TRANSPARENT,
152         PROP_BACKSPACE_BINDING,
153         PROP_CURSOR_BLINK_MODE,
154         PROP_CURSOR_SHAPE,
155         PROP_DELETE_BINDING,
156         PROP_EMULATION,
157         PROP_ENCODING,
158         PROP_FONT_DESC,
159         PROP_ICON_TITLE,
160         PROP_MOUSE_POINTER_AUTOHIDE,
161         PROP_PTY,
162         PROP_PTY_OBJECT,
163         PROP_SCROLL_BACKGROUND,
164         PROP_SCROLLBACK_LINES,
165         PROP_SCROLL_ON_KEYSTROKE,
166         PROP_SCROLL_ON_OUTPUT,
167         PROP_WINDOW_TITLE,
168         PROP_WORD_CHARS,
169         PROP_VISIBLE_BELL
170 };
171
172 /* these static variables are guarded by the GDK mutex */
173 static guint process_timeout_tag = 0;
174 static gboolean in_process_timeout;
175 static guint update_timeout_tag = 0;
176 static gboolean in_update_timeout;
177 static GList *active_terminals;
178 static GTimer *process_timer;
179
180 static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
181
182 /* process incoming data without copying */
183 static struct _vte_incoming_chunk *free_chunks;
184 static struct _vte_incoming_chunk *
185 get_chunk (void)
186 {
187         struct _vte_incoming_chunk *chunk = NULL;
188         if (free_chunks) {
189                 chunk = free_chunks;
190                 free_chunks = free_chunks->next;
191         }
192         if (chunk == NULL) {
193                 chunk = g_new (struct _vte_incoming_chunk, 1);
194         }
195         chunk->next = NULL;
196         chunk->len = 0;
197         return chunk;
198 }
199 static void
200 release_chunk (struct _vte_incoming_chunk *chunk)
201 {
202         chunk->next = free_chunks;
203         chunk->len = free_chunks ? free_chunks->len + 1 : 0;
204         free_chunks = chunk;
205 }
206 static void
207 prune_chunks (guint len)
208 {
209         struct _vte_incoming_chunk *chunk = NULL;
210         if (len && free_chunks != NULL) {
211             if (free_chunks->len > len) {
212                 struct _vte_incoming_chunk *last;
213                 chunk = free_chunks;
214                 while (free_chunks->len > len) {
215                     last = free_chunks;
216                     free_chunks = free_chunks->next;
217                 }
218                 last->next = NULL;
219             }
220         } else {
221             chunk = free_chunks;
222             free_chunks = NULL;
223         }
224         while (chunk != NULL) {
225                 struct _vte_incoming_chunk *next = chunk->next;
226                 g_free (chunk);
227                 chunk = next;
228         }
229 }
230 static void
231 _vte_incoming_chunks_release (struct _vte_incoming_chunk *chunk)
232 {
233         while (chunk) {
234                 struct _vte_incoming_chunk *next = chunk->next;
235                 release_chunk (chunk);
236                 chunk = next;
237         }
238 }
239 static gsize
240 _vte_incoming_chunks_length (struct _vte_incoming_chunk *chunk)
241 {
242         gsize len = 0;
243         while (chunk) {
244                 len += chunk->len;
245                 chunk = chunk->next;
246         }
247         return len;
248 }
249 static gsize
250 _vte_incoming_chunks_count (struct _vte_incoming_chunk *chunk)
251 {
252         gsize cnt = 0;
253         while (chunk) {
254                 cnt ++;
255                 chunk = chunk->next;
256         }
257         return cnt;
258 }
259 static struct _vte_incoming_chunk *
260 _vte_incoming_chunks_reverse(struct _vte_incoming_chunk *chunk)
261 {
262         struct _vte_incoming_chunk *prev = NULL;
263         while (chunk) {
264                 struct _vte_incoming_chunk *next = chunk->next;
265                 chunk->next = prev;
266                 prev = chunk;
267                 chunk = next;
268         }
269         return prev;
270 }
271
272
273 #ifdef VTE_DEBUG
274 G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET,
275                 if (_vte_debug_on(VTE_DEBUG_LIFECYCLE)) {
276                         g_printerr("vte_terminal_get_type()\n");
277                 })
278 #else
279 G_DEFINE_TYPE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET)
280 #endif
281
282
283 /* Indexes in the "palette" color array for the dim colors.
284  * Only the first %VTE_LEGACY_COLOR_SET_SIZE colors have dim versions.  */
285 static const guchar corresponding_dim_index[] = {16,88,28,100,18,90,30,102};
286
287 static void
288 vte_g_array_fill(GArray *array, gconstpointer item, guint final_size)
289 {
290         if (array->len >= final_size)
291                 return;
292
293         final_size -= array->len;
294         do {
295                 g_array_append_vals(array, item, 1);
296         } while (--final_size);
297 }
298
299
300 VteRowData *
301 _vte_terminal_ring_insert (VteTerminal *terminal, glong position, gboolean fill)
302 {
303         VteRowData *row;
304         VteRing *ring = terminal->pvt->screen->row_data;
305         while (G_UNLIKELY (_vte_ring_next (ring) < position)) {
306                 row = _vte_ring_append (ring);
307                 _vte_row_data_fill (row, &terminal->pvt->screen->fill_defaults, terminal->column_count);
308         }
309         row = _vte_ring_insert (ring, position);
310         if (fill)
311                 _vte_row_data_fill (row, &terminal->pvt->screen->fill_defaults, terminal->column_count);
312         return row;
313 }
314
315 VteRowData *
316 _vte_terminal_ring_append (VteTerminal *terminal, gboolean fill)
317 {
318         return _vte_terminal_ring_insert (terminal, _vte_ring_next (terminal->pvt->screen->row_data), fill);
319 }
320
321 void
322 _vte_terminal_ring_remove (VteTerminal *terminal, glong position)
323 {
324         _vte_ring_remove (terminal->pvt->screen->row_data, position);
325 }
326
327 /* Reset defaults for character insertion. */
328 void
329 _vte_terminal_set_default_attributes(VteTerminal *terminal)
330 {
331         VteScreen *screen;
332
333         screen = terminal->pvt->screen;
334
335         screen->defaults = basic_cell.cell;
336         screen->color_defaults = screen->defaults;
337         screen->fill_defaults = screen->defaults;
338 }
339
340 /* Cause certain cells to be repainted. */
341 void
342 _vte_invalidate_cells(VteTerminal *terminal,
343                       glong column_start, gint column_count,
344                       glong row_start, gint row_count)
345 {
346         VteRegionRectangle rect;
347         glong i;
348
349         if (!column_count || !row_count) {
350                 return;
351         }
352
353         if (G_UNLIKELY (! gtk_widget_is_drawable (&terminal->widget)
354                                 || terminal->pvt->invalidated_all)) {
355                 return;
356         }
357
358         _vte_debug_print (VTE_DEBUG_UPDATES,
359                         "Invalidating cells at (%ld,%ld+%ld)x(%d,%d).\n",
360                         column_start, row_start,
361                         (long)terminal->pvt->screen->scroll_delta,
362                         column_count, row_count);
363         _vte_debug_print (VTE_DEBUG_WORK, "?");
364
365         /* Subtract the scrolling offset from the row start so that the
366          * resulting rectangle is relative to the visible portion of the
367          * buffer. */
368         row_start -= terminal->pvt->screen->scroll_delta;
369
370         /* Ensure the start of region is on screen */
371         if (column_start > terminal->column_count ||
372                         row_start > terminal->row_count) {
373                 return;
374         }
375
376         /* Clamp the start values to reasonable numbers. */
377         i = row_start + row_count;
378         row_start = MAX (0, row_start);
379         row_count = CLAMP (i - row_start, 0, terminal->row_count);
380
381         i = column_start + column_count;
382         column_start = MAX (0, column_start);
383         column_count = CLAMP (i - column_start, 0 , terminal->column_count);
384
385         if (!column_count || !row_count) {
386                 return;
387         }
388         if (column_count == terminal->column_count &&
389                         row_count == terminal->row_count) {
390                 _vte_invalidate_all (terminal);
391                 return;
392         }
393
394         /* Convert the column and row start and end to pixel values
395          * by multiplying by the size of a character cell.
396          * Always include the extra pixel border and overlap pixel.
397          */
398         rect.x = column_start * terminal->char_width - 1;
399         if (column_start != 0) {
400                 rect.x += terminal->pvt->inner_border.left;
401         }
402         rect.width = (column_start + column_count) * terminal->char_width + 3 + terminal->pvt->inner_border.left;
403         if (column_start + column_count == terminal->column_count) {
404                 rect.width += terminal->pvt->inner_border.right;
405         }
406         rect.width -= rect.x;
407
408         rect.y = row_start * terminal->char_height - 1;
409         if (row_start != 0) {
410                 rect.y += terminal->pvt->inner_border.top;
411         }
412         rect.height = (row_start + row_count) * terminal->char_height + 2 + terminal->pvt->inner_border.top;
413         if (row_start + row_count == terminal->row_count) {
414                 rect.height += terminal->pvt->inner_border.bottom;
415         }
416         rect.height -= rect.y;
417
418         _vte_debug_print (VTE_DEBUG_UPDATES,
419                         "Invalidating pixels at (%d,%d)x(%d,%d).\n",
420                         rect.x, rect.y, rect.width, rect.height);
421
422         if (terminal->pvt->active != NULL) {
423                 terminal->pvt->update_regions = g_slist_prepend (
424                                 terminal->pvt->update_regions,
425                                 gdk_region_rectangle (&rect));
426                 /* Wait a bit before doing any invalidation, just in
427                  * case updates are coming in really soon. */
428                 add_update_timeout (terminal);
429         } else {
430                 gdk_window_invalidate_rect (gtk_widget_get_window (&terminal->widget), &rect, FALSE);
431         }
432
433         _vte_debug_print (VTE_DEBUG_WORK, "!");
434 }
435
436 static void
437 _vte_invalidate_region (VteTerminal *terminal,
438                         glong scolumn, glong ecolumn,
439                         glong srow, glong erow,
440                         gboolean block)
441 {
442         if (block || srow == erow) {
443                 _vte_invalidate_cells(terminal,
444                                 scolumn, ecolumn - scolumn + 1,
445                                 srow, erow - srow + 1);
446         } else {
447                 _vte_invalidate_cells(terminal,
448                                 scolumn,
449                                 terminal->column_count - scolumn,
450                                 srow, 1);
451                 _vte_invalidate_cells(terminal,
452                                 0, terminal->column_count,
453                                 srow + 1, erow - srow - 1);
454                 _vte_invalidate_cells(terminal,
455                                 0, ecolumn + 1,
456                                 erow, 1);
457         }
458 }
459
460
461 /* Redraw the entire visible portion of the window. */
462 void
463 _vte_invalidate_all(VteTerminal *terminal)
464 {
465         VteRegionRectangle rect;
466         GtkAllocation allocation;
467
468         g_assert(VTE_IS_TERMINAL(terminal));
469
470         if (! gtk_widget_is_drawable (&terminal->widget)) {
471                 return;
472         }
473         if (terminal->pvt->invalidated_all) {
474                 return;
475         }
476
477         _vte_debug_print (VTE_DEBUG_WORK, "*");
478         _vte_debug_print (VTE_DEBUG_UPDATES, "Invalidating all.\n");
479
480         gtk_widget_get_allocation (&terminal->widget, &allocation);
481
482         /* replace invalid regions with one covering the whole terminal */
483         reset_update_regions (terminal);
484         rect.x = rect.y = 0;
485         rect.width = allocation.width;
486         rect.height = allocation.height;
487         terminal->pvt->invalidated_all = TRUE;
488
489         if (terminal->pvt->active != NULL) {
490                 terminal->pvt->update_regions = g_slist_prepend (NULL,
491                                 gdk_region_rectangle (&rect));
492                 /* Wait a bit before doing any invalidation, just in
493                  * case updates are coming in really soon. */
494                 add_update_timeout (terminal);
495         } else {
496                 gdk_window_invalidate_rect (gtk_widget_get_window (&terminal->widget), &rect, FALSE);
497         }
498 }
499
500
501 /* Scroll a rectangular region up or down by a fixed number of lines,
502  * negative = up, positive = down. */
503 void
504 _vte_terminal_scroll_region (VteTerminal *terminal,
505                              long row, glong count, glong delta)
506 {
507         if ((delta == 0) || (count == 0)) {
508                 /* Shenanigans! */
509                 return;
510         }
511
512         if (terminal->pvt->scroll_background || count >= terminal->row_count) {
513                 /* We have to repaint the entire window. */
514                 _vte_invalidate_all(terminal);
515         } else {
516                 /* We have to repaint the area which is to be
517                  * scrolled. */
518                 _vte_invalidate_cells(terminal,
519                                      0, terminal->column_count,
520                                      row, count);
521         }
522 }
523
524 /* Find the row in the given position in the backscroll buffer. */
525 static inline const VteRowData *
526 _vte_terminal_find_row_data (VteTerminal *terminal, glong row)
527 {
528         const VteRowData *rowdata = NULL;
529         VteScreen *screen = terminal->pvt->screen;
530         if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
531                 rowdata = _vte_ring_index (screen->row_data, row);
532         }
533         return rowdata;
534 }
535
536 /* Find the row in the given position in the backscroll buffer. */
537 static inline VteRowData *
538 _vte_terminal_find_row_data_writable (VteTerminal *terminal, glong row)
539 {
540         VteRowData *rowdata = NULL;
541         VteScreen *screen = terminal->pvt->screen;
542         if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
543                 rowdata = _vte_ring_index_writable (screen->row_data, row);
544         }
545         return rowdata;
546 }
547
548 /* Find the character an the given position in the backscroll buffer. */
549 static const VteCell *
550 vte_terminal_find_charcell(VteTerminal *terminal, gulong col, glong row)
551 {
552         const VteRowData *rowdata;
553         const VteCell *ret = NULL;
554         VteScreen *screen;
555         screen = terminal->pvt->screen;
556         if (_vte_ring_contains (screen->row_data, row)) {
557                 rowdata = _vte_ring_index (screen->row_data, row);
558                 ret = _vte_row_data_get (rowdata, col);
559         }
560         return ret;
561 }
562
563 static glong
564 find_start_column (VteTerminal *terminal, glong col, glong row)
565 {
566         const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
567         if (G_UNLIKELY (col < 0))
568                 return col;
569         if (row_data != NULL) {
570                 const VteCell *cell = _vte_row_data_get (row_data, col);
571                 while (col > 0 && cell != NULL && cell->attr.fragment) {
572                         cell = _vte_row_data_get (row_data, --col);
573                 }
574         }
575         return MAX(col, 0);
576 }
577 static glong
578 find_end_column (VteTerminal *terminal, glong col, glong row)
579 {
580         const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
581         gint columns = 0;
582         if (G_UNLIKELY (col < 0))
583                 return col;
584         if (row_data != NULL) {
585                 const VteCell *cell = _vte_row_data_get (row_data, col);
586                 while (col > 0 && cell != NULL && cell->attr.fragment) {
587                         cell = _vte_row_data_get (row_data, --col);
588                 }
589                 if (cell) {
590                         columns = cell->attr.columns - 1;
591                 }
592         }
593         return MIN(col + columns, terminal->column_count);
594 }
595
596
597 /* Determine the width of the portion of the preedit string which lies
598  * to the left of the cursor, or the entire string, in columns. */
599 static gssize
600 vte_terminal_preedit_width(VteTerminal *terminal, gboolean left_only)
601 {
602         gunichar c;
603         int i;
604         gssize ret = 0;
605         const char *preedit = NULL;
606
607         if (terminal->pvt->im_preedit != NULL) {
608                 preedit = terminal->pvt->im_preedit;
609                 for (i = 0;
610                      (preedit != NULL) &&
611                      (preedit[0] != '\0') &&
612                      (!left_only || (i < terminal->pvt->im_preedit_cursor));
613                      i++) {
614                         c = g_utf8_get_char(preedit);
615                         ret += _vte_iso2022_unichar_width(terminal->pvt->iso2022, c);
616                         preedit = g_utf8_next_char(preedit);
617                 }
618         }
619
620         return ret;
621 }
622
623 /* Determine the length of the portion of the preedit string which lies
624  * to the left of the cursor, or the entire string, in gunichars. */
625 static gssize
626 vte_terminal_preedit_length(VteTerminal *terminal, gboolean left_only)
627 {
628         int i = 0;
629         const char *preedit = NULL;
630
631         if (terminal->pvt->im_preedit != NULL) {
632                 preedit = terminal->pvt->im_preedit;
633                 for (i = 0;
634                      (preedit != NULL) &&
635                      (preedit[0] != '\0') &&
636                      (!left_only || (i < terminal->pvt->im_preedit_cursor));
637                      i++) {
638                         preedit = g_utf8_next_char(preedit);
639                 }
640         }
641
642         return i;
643 }
644
645 /* Cause the cell to be redrawn. */
646 void
647 _vte_invalidate_cell(VteTerminal *terminal, glong col, glong row)
648 {
649         const VteRowData *row_data;
650         int columns;
651
652         if (G_UNLIKELY (! gtk_widget_is_drawable (&terminal->widget)
653                                 || terminal->pvt->invalidated_all)) {
654                 return;
655         }
656
657         columns = 1;
658         row_data = _vte_terminal_find_row_data(terminal, row);
659         if (row_data != NULL) {
660                 const VteCell *cell;
661                 cell = _vte_row_data_get (row_data, col);
662                 if (cell != NULL) {
663                         while (cell->attr.fragment && col> 0) {
664                                 cell = _vte_row_data_get (row_data, --col);
665                         }
666                         columns = cell->attr.columns;
667                         if (cell->c != 0 &&
668                                         _vte_draw_get_char_width (
669                                                 terminal->pvt->draw,
670                                                 cell->c,
671                                                 columns, cell->attr.bold) >
672                                         terminal->char_width * columns) {
673                                 columns++;
674                         }
675                 }
676         }
677
678         _vte_debug_print(VTE_DEBUG_UPDATES,
679                         "Invalidating cell at (%ld,%ld-%ld).\n",
680                         row, col, col + columns);
681         _vte_invalidate_cells(terminal,
682                         col, columns,
683                         row, 1);
684 }
685
686 /* Cause the cursor to be redrawn. */
687 void
688 _vte_invalidate_cursor_once(VteTerminal *terminal, gboolean periodic)
689 {
690         VteScreen *screen;
691         const VteCell *cell;
692         gssize preedit_width;
693         glong column, row;
694         gint columns;
695
696         if (terminal->pvt->invalidated_all) {
697                 return;
698         }
699
700         if (periodic) {
701                 if (!terminal->pvt->cursor_blinks) {
702                         return;
703                 }
704         }
705
706         if (terminal->pvt->cursor_visible && gtk_widget_is_drawable (&terminal->widget)) {
707                 preedit_width = vte_terminal_preedit_width(terminal, FALSE);
708
709                 screen = terminal->pvt->screen;
710                 row = screen->cursor_current.row;
711                 column = screen->cursor_current.col;
712                 columns = 1;
713                 column = find_start_column (terminal, column, row);
714                 cell = vte_terminal_find_charcell(terminal, column, row);
715                 if (cell != NULL) {
716                         columns = cell->attr.columns;
717                         if (cell->c != 0 &&
718                                         _vte_draw_get_char_width (
719                                                 terminal->pvt->draw,
720                                                 cell->c,
721                                                 columns, cell->attr.bold) >
722                             terminal->char_width * columns) {
723                                 columns++;
724                         }
725                 }
726                 if (preedit_width > 0) {
727                         columns += preedit_width;
728                         columns++; /* one more for the preedit cursor */
729                 }
730
731                 _vte_debug_print(VTE_DEBUG_UPDATES,
732                                 "Invalidating cursor at (%ld,%ld-%ld).\n",
733                                 row, column, column + columns);
734                 _vte_invalidate_cells(terminal,
735                                      column, columns,
736                                      row, 1);
737         }
738 }
739
740 /* Invalidate the cursor repeatedly. */
741 static gboolean
742 vte_invalidate_cursor_periodic (VteTerminal *terminal)
743 {
744         VteTerminalPrivate *pvt = terminal->pvt;
745
746         pvt->cursor_blink_state = !pvt->cursor_blink_state;
747         pvt->cursor_blink_time += pvt->cursor_blink_cycle;
748
749         _vte_invalidate_cursor_once(terminal, TRUE);
750
751         /* only disable the blink if the cursor is currently shown.
752          * else, wait until next time.
753          */
754         if (pvt->cursor_blink_time / 1000 >= pvt->cursor_blink_timeout &&
755             pvt->cursor_blink_state) {
756                 pvt->cursor_blink_tag = 0;
757                 return FALSE;
758         }
759
760         pvt->cursor_blink_tag = g_timeout_add_full(G_PRIORITY_LOW,
761                                                    terminal->pvt->cursor_blink_cycle,
762                                                    (GSourceFunc)vte_invalidate_cursor_periodic,
763                                                    terminal,
764                                                    NULL);
765         return FALSE;
766 }
767
768 /* Emit a "selection_changed" signal. */
769 static void
770 vte_terminal_emit_selection_changed(VteTerminal *terminal)
771 {
772         _vte_debug_print(VTE_DEBUG_SIGNALS,
773                         "Emitting `selection-changed'.\n");
774         g_signal_emit_by_name(terminal, "selection-changed");
775 }
776
777 /* Emit a "commit" signal. */
778 static void
779 vte_terminal_emit_commit(VteTerminal *terminal, const gchar *text, guint length)
780 {
781         const char *result = NULL;
782         char *wrapped = NULL;
783
784         _vte_debug_print(VTE_DEBUG_SIGNALS,
785                         "Emitting `commit' of %d bytes.\n", length);
786
787         if (length == (guint)-1) {
788                 length = strlen(text);
789                 result = text;
790         } else {
791                 result = wrapped = g_slice_alloc(length + 1);
792                 memcpy(wrapped, text, length);
793                 wrapped[length] = '\0';
794         }
795
796         g_signal_emit_by_name(terminal, "commit", result, length);
797
798         if(wrapped)
799                 g_slice_free1(length+1, wrapped);
800 }
801
802 /* Emit an "emulation-changed" signal. */
803 static void
804 vte_terminal_emit_emulation_changed(VteTerminal *terminal)
805 {
806         _vte_debug_print(VTE_DEBUG_SIGNALS,
807                         "Emitting `emulation-changed'.\n");
808         g_signal_emit_by_name(terminal, "emulation-changed");
809         g_object_notify(G_OBJECT(terminal), "emulation");
810
811 }
812
813 /* Emit an "encoding-changed" signal. */
814 static void
815 vte_terminal_emit_encoding_changed(VteTerminal *terminal)
816 {
817         _vte_debug_print(VTE_DEBUG_SIGNALS,
818                         "Emitting `encoding-changed'.\n");
819         g_signal_emit_by_name(terminal, "encoding-changed");
820         g_object_notify(G_OBJECT(terminal), "encoding");
821 }
822
823 /* Emit a "child-exited" signal. */
824 static void
825 vte_terminal_emit_child_exited(VteTerminal *terminal)
826 {
827         _vte_debug_print(VTE_DEBUG_SIGNALS,
828                         "Emitting `child-exited'.\n");
829         g_signal_emit_by_name(terminal, "child-exited");
830 }
831
832 /* Emit a "contents_changed" signal. */
833 static void
834 vte_terminal_emit_contents_changed(VteTerminal *terminal)
835 {
836         if (terminal->pvt->contents_changed_pending) {
837                 /* Update dingus match set. */
838                 vte_terminal_match_contents_clear(terminal);
839                 if (terminal->pvt->mouse_cursor_visible) {
840                         vte_terminal_match_hilite_update(terminal,
841                                         terminal->pvt->mouse_last_x,
842                                         terminal->pvt->mouse_last_y);
843                 }
844
845                 _vte_debug_print(VTE_DEBUG_SIGNALS,
846                                 "Emitting `contents-changed'.\n");
847                 g_signal_emit_by_name(terminal, "contents-changed");
848                 terminal->pvt->contents_changed_pending = FALSE;
849         }
850 }
851 void
852 _vte_terminal_queue_contents_changed(VteTerminal *terminal)
853 {
854         _vte_debug_print(VTE_DEBUG_SIGNALS,
855                         "Queueing `contents-changed'.\n");
856         terminal->pvt->contents_changed_pending = TRUE;
857 }
858
859 /* Emit a "cursor_moved" signal. */
860 static void
861 vte_terminal_emit_cursor_moved(VteTerminal *terminal)
862 {
863         if (terminal->pvt->cursor_moved_pending) {
864                 _vte_debug_print(VTE_DEBUG_SIGNALS,
865                                 "Emitting `cursor-moved'.\n");
866                 g_signal_emit_by_name(terminal, "cursor-moved");
867                 terminal->pvt->cursor_moved_pending = FALSE;
868         }
869 }
870 static void
871 vte_terminal_queue_cursor_moved(VteTerminal *terminal)
872 {
873         _vte_debug_print(VTE_DEBUG_SIGNALS,
874                         "Queueing `cursor-moved'.\n");
875         terminal->pvt->cursor_moved_pending = TRUE;
876 }
877
878 static gboolean
879 vte_terminal_emit_eof(VteTerminal *terminal)
880 {
881         _vte_debug_print(VTE_DEBUG_SIGNALS,
882                         "Emitting `eof'.\n");
883         GDK_THREADS_ENTER ();
884         g_signal_emit_by_name(terminal, "eof");
885         GDK_THREADS_LEAVE ();
886
887         return FALSE;
888 }
889 /* Emit a "eof" signal. */
890 static void
891 vte_terminal_queue_eof(VteTerminal *terminal)
892 {
893         _vte_debug_print(VTE_DEBUG_SIGNALS,
894                         "Queueing `eof'.\n");
895         g_idle_add_full (G_PRIORITY_HIGH,
896                 (GSourceFunc) vte_terminal_emit_eof,
897                 g_object_ref (terminal),
898                 g_object_unref);
899 }
900
901 /* Emit a "char-size-changed" signal. */
902 static void
903 vte_terminal_emit_char_size_changed(VteTerminal *terminal,
904                                     guint width, guint height)
905 {
906         _vte_debug_print(VTE_DEBUG_SIGNALS,
907                         "Emitting `char-size-changed'.\n");
908         g_signal_emit_by_name(terminal, "char-size-changed",
909                               width, height);
910 /*         g_object_notify(G_OBJECT(terminal), "char-size"); */
911 }
912
913 /* Emit a "status-line-changed" signal. */
914 static void
915 _vte_terminal_emit_status_line_changed(VteTerminal *terminal)
916 {
917         _vte_debug_print(VTE_DEBUG_SIGNALS,
918                         "Emitting `status-line-changed'.\n");
919         g_signal_emit_by_name(terminal, "status-line-changed");
920 /*         g_object_notify(G_OBJECT(terminal), "status-line"); */
921 }
922
923 /* Emit an "increase-font-size" signal. */
924 static void
925 vte_terminal_emit_increase_font_size(VteTerminal *terminal)
926 {
927         _vte_debug_print(VTE_DEBUG_SIGNALS,
928                         "Emitting `increase-font-size'.\n");
929         g_signal_emit_by_name(terminal, "increase-font-size");
930         g_object_notify(G_OBJECT(terminal), "font-scale");
931 }
932
933 /* Emit a "decrease-font-size" signal. */
934 static void
935 vte_terminal_emit_decrease_font_size(VteTerminal *terminal)
936 {
937         _vte_debug_print(VTE_DEBUG_SIGNALS,
938                         "Emitting `decrease-font-size'.\n");
939         g_signal_emit_by_name(terminal, "decrease-font-size");
940         g_object_notify(G_OBJECT(terminal), "font-scale");
941 }
942
943 /* Emit a "text-inserted" signal. */
944 void
945 _vte_terminal_emit_text_inserted(VteTerminal *terminal)
946 {
947         if (!terminal->pvt->accessible_emit) {
948                 return;
949         }
950         _vte_debug_print(VTE_DEBUG_SIGNALS,
951                         "Emitting `text-inserted'.\n");
952         g_signal_emit_by_name(terminal, "text-inserted");
953 }
954
955 /* Emit a "text-deleted" signal. */
956 void
957 _vte_terminal_emit_text_deleted(VteTerminal *terminal)
958 {
959         if (!terminal->pvt->accessible_emit) {
960                 return;
961         }
962         _vte_debug_print(VTE_DEBUG_SIGNALS,
963                         "Emitting `text-deleted'.\n");
964         g_signal_emit_by_name(terminal, "text-deleted");
965 }
966
967 /* Emit a "text-modified" signal. */
968 static void
969 vte_terminal_emit_text_modified(VteTerminal *terminal)
970 {
971         if (!terminal->pvt->accessible_emit) {
972                 return;
973         }
974         _vte_debug_print(VTE_DEBUG_SIGNALS,
975                         "Emitting `text-modified'.\n");
976         g_signal_emit_by_name(terminal, "text-modified");
977 }
978
979 /* Emit a "text-scrolled" signal. */
980 static void
981 vte_terminal_emit_text_scrolled(VteTerminal *terminal, gint delta)
982 {
983         if (!terminal->pvt->accessible_emit) {
984                 return;
985         }
986         _vte_debug_print(VTE_DEBUG_SIGNALS,
987                         "Emitting `text-scrolled'(%d).\n", delta);
988         g_signal_emit_by_name(terminal, "text-scrolled", delta);
989 }
990
991 /* Deselect anything which is selected and refresh the screen if needed. */
992 static void
993 vte_terminal_deselect_all(VteTerminal *terminal)
994 {
995         if (terminal->pvt->has_selection) {
996                 gint sx, sy, ex, ey;
997
998                 _vte_debug_print(VTE_DEBUG_SELECTION,
999                                 "Deselecting all text.\n");
1000
1001                 terminal->pvt->has_selection = FALSE;
1002                 /* Don't free the current selection, as we need to keep
1003                  * hold of it for async copying from the clipboard. */
1004
1005                 vte_terminal_emit_selection_changed(terminal);
1006
1007                 sx = terminal->pvt->selection_start.col;
1008                 sy = terminal->pvt->selection_start.row;
1009                 ex = terminal->pvt->selection_end.col;
1010                 ey = terminal->pvt->selection_end.row;
1011                 _vte_invalidate_region(terminal,
1012                                 MIN (sx, ex), MAX (sx, ex),
1013                                 MIN (sy, ey),   MAX (sy, ey),
1014                                 FALSE);
1015         }
1016 }
1017
1018 /* Remove a tabstop. */
1019 void
1020 _vte_terminal_clear_tabstop(VteTerminal *terminal, int column)
1021 {
1022         g_assert(VTE_IS_TERMINAL(terminal));
1023         if (terminal->pvt->tabstops != NULL) {
1024                 /* Remove a tab stop from the hash table. */
1025                 g_hash_table_remove(terminal->pvt->tabstops,
1026                                     GINT_TO_POINTER(2 * column + 1));
1027         }
1028 }
1029
1030 /* Check if we have a tabstop at a given position. */
1031 gboolean
1032 _vte_terminal_get_tabstop(VteTerminal *terminal, int column)
1033 {
1034         gpointer hash;
1035         g_assert(VTE_IS_TERMINAL(terminal));
1036         if (terminal->pvt->tabstops != NULL) {
1037                 hash = g_hash_table_lookup(terminal->pvt->tabstops,
1038                                            GINT_TO_POINTER(2 * column + 1));
1039                 return (hash != NULL);
1040         } else {
1041                 return FALSE;
1042         }
1043 }
1044
1045 /* Reset the set of tab stops to the default. */
1046 void
1047 _vte_terminal_set_tabstop(VteTerminal *terminal, int column)
1048 {
1049         g_assert(VTE_IS_TERMINAL(terminal));
1050         if (terminal->pvt->tabstops != NULL) {
1051                 /* Just set a non-NULL pointer for this column number. */
1052                 g_hash_table_insert(terminal->pvt->tabstops,
1053                                     GINT_TO_POINTER(2 * column + 1),
1054                                     terminal);
1055         }
1056 }
1057
1058 /* Reset the set of tab stops to the default. */
1059 static void
1060 vte_terminal_set_default_tabstops(VteTerminal *terminal)
1061 {
1062         int i, width = 0;
1063         if (terminal->pvt->tabstops != NULL) {
1064                 g_hash_table_destroy(terminal->pvt->tabstops);
1065         }
1066         terminal->pvt->tabstops = g_hash_table_new(NULL, NULL);
1067         if (terminal->pvt->termcap != NULL) {
1068                 width = _vte_termcap_find_numeric(terminal->pvt->termcap,
1069                                                   terminal->pvt->emulation,
1070                                                   "it");
1071         }
1072         if (width == 0) {
1073                 width = VTE_TAB_WIDTH;
1074         }
1075         for (i = 0; i <= VTE_TAB_MAX; i += width) {
1076                 _vte_terminal_set_tabstop(terminal, i);
1077         }
1078 }
1079
1080 /* Clear the cache of the screen contents we keep. */
1081 static void
1082 vte_terminal_match_contents_clear(VteTerminal *terminal)
1083 {
1084         g_assert(VTE_IS_TERMINAL(terminal));
1085         if (terminal->pvt->match_contents != NULL) {
1086                 g_free(terminal->pvt->match_contents);
1087                 terminal->pvt->match_contents = NULL;
1088         }
1089         if (terminal->pvt->match_attributes != NULL) {
1090                 g_array_free(terminal->pvt->match_attributes, TRUE);
1091                 terminal->pvt->match_attributes = NULL;
1092         }
1093         vte_terminal_match_hilite_clear(terminal);
1094 }
1095
1096 /* Refresh the cache of the screen contents we keep. */
1097 static gboolean
1098 always_selected(VteTerminal *terminal, glong column, glong row, gpointer data)
1099 {
1100         return TRUE;
1101 }
1102
1103 static void
1104 vte_terminal_match_contents_refresh(VteTerminal *terminal)
1105 {
1106         GArray *array;
1107         vte_terminal_match_contents_clear(terminal);
1108         array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
1109         terminal->pvt->match_contents = vte_terminal_get_text(terminal,
1110                                                               always_selected,
1111                                                               NULL,
1112                                                               array);
1113         terminal->pvt->match_attributes = array;
1114 }
1115
1116 static void
1117 regex_match_clear_cursor (struct vte_match_regex *regex)
1118 {
1119         switch (regex->cursor_mode) {
1120                 case VTE_REGEX_CURSOR_GDKCURSOR:
1121                         if (regex->cursor.cursor != NULL) {
1122                                 gdk_cursor_unref(regex->cursor.cursor);
1123                                 regex->cursor.cursor = NULL;
1124                         }
1125                         break;
1126                 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1127                         break;
1128                 case VTE_REGEX_CURSOR_NAME:
1129                         g_free (regex->cursor.cursor_name);
1130                         regex->cursor.cursor_name = NULL;
1131                         break;
1132                 default:
1133                         g_assert_not_reached ();
1134                         return;
1135         }
1136 }
1137
1138 static void
1139 regex_match_clear (struct vte_match_regex *regex)
1140 {
1141         regex_match_clear_cursor(regex);
1142
1143         if (regex->mode == VTE_REGEX_GREGEX) {
1144                 g_regex_unref(regex->regex.gregex.regex);
1145                 regex->regex.gregex.regex = NULL;
1146         } else if (regex->mode == VTE_REGEX_VTE) {
1147                 _vte_regex_free(regex->regex.reg);
1148                 regex->regex.reg = NULL;
1149         }
1150
1151         regex->tag = -1;
1152 }
1153
1154 static void
1155 vte_terminal_set_cursor_from_regex_match(VteTerminal *terminal, struct vte_match_regex *regex)
1156 {
1157         GdkCursor *cursor = NULL;
1158
1159         if (! gtk_widget_get_realized (&terminal->widget))
1160                 return;
1161
1162         switch (regex->cursor_mode) {
1163                 case VTE_REGEX_CURSOR_GDKCURSOR:
1164                         if (regex->cursor.cursor != NULL) {
1165                                 cursor = gdk_cursor_ref(regex->cursor.cursor);
1166                         }
1167                         break;
1168                 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1169                         cursor = gdk_cursor_new_for_display(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_type);
1170                         break;
1171                 case VTE_REGEX_CURSOR_NAME:
1172                         cursor = gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_name);
1173                         break;
1174                 default:
1175                         g_assert_not_reached ();
1176                         return;
1177         }
1178
1179         gdk_window_set_cursor (gtk_widget_get_window (&terminal->widget), cursor);
1180
1181         if (cursor)
1182                 gdk_cursor_unref(cursor);
1183 }
1184
1185 /**
1186  * vte_terminal_match_clear_all:
1187  * @terminal: a #VteTerminal
1188  *
1189  * Clears the list of regular expressions the terminal uses to highlight text
1190  * when the user moves the mouse cursor.
1191  */
1192 void
1193 vte_terminal_match_clear_all(VteTerminal *terminal)
1194 {
1195         struct vte_match_regex *regex;
1196         guint i;
1197         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1198         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1199                 regex = &g_array_index(terminal->pvt->match_regexes,
1200                                        struct vte_match_regex,
1201                                        i);
1202                 /* Unless this is a hole, clean it up. */
1203                 if (regex->tag >= 0) {
1204                         regex_match_clear (regex);
1205                 }
1206         }
1207         g_array_set_size(terminal->pvt->match_regexes, 0);
1208         vte_terminal_match_hilite_clear(terminal);
1209 }
1210
1211 /**
1212  * vte_terminal_match_remove:
1213  * @terminal: a #VteTerminal
1214  * @tag: the tag of the regex to remove
1215  *
1216  * Removes the regular expression which is associated with the given @tag from
1217  * the list of expressions which the terminal will highlight when the user
1218  * moves the mouse cursor over matching text.
1219  */
1220 void
1221 vte_terminal_match_remove(VteTerminal *terminal, int tag)
1222 {
1223         struct vte_match_regex *regex;
1224         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1225         if (terminal->pvt->match_regexes->len > (guint)tag) {
1226                 /* The tag is an index, so find the corresponding struct. */
1227                 regex = &g_array_index(terminal->pvt->match_regexes,
1228                                        struct vte_match_regex,
1229                                        tag);
1230                 /* If it's already been removed, return. */
1231                 if (regex->tag < 0) {
1232                         return;
1233                 }
1234                 /* Remove this item and leave a hole in its place. */
1235                 regex_match_clear (regex);
1236         }
1237         vte_terminal_match_hilite_clear(terminal);
1238 }
1239
1240 static GdkCursor *
1241 vte_terminal_cursor_new(VteTerminal *terminal, GdkCursorType cursor_type)
1242 {
1243         GdkDisplay *display;
1244         GdkCursor *cursor;
1245
1246         display = gtk_widget_get_display(&terminal->widget);
1247         cursor = gdk_cursor_new_for_display(display, cursor_type);
1248         return cursor;
1249 }
1250
1251 /**
1252  * vte_terminal_match_add:
1253  * @terminal: a #VteTerminal
1254  * @match: a regular expression
1255  *
1256  * Adds a regular expression to the list of matching expressions.  When the
1257  * user moves the mouse cursor over a section of displayed text which matches
1258  * this expression, the text will be highlighted.
1259  *
1260  * Returns: an integer associated with this expression
1261  *
1262  * Deprecated: 0.17.1: Use vte_terminal_match_add_gregex() instead
1263  */
1264 int
1265 vte_terminal_match_add(VteTerminal *terminal, const char *match)
1266 {
1267         struct vte_match_regex new_regex, *regex;
1268         guint ret;
1269         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1270         g_return_val_if_fail(terminal->pvt->match_regex_mode != VTE_REGEX_GREGEX, -1);
1271         g_return_val_if_fail(match != NULL, -1);
1272         g_return_val_if_fail(strlen(match) > 0, -1);
1273
1274         terminal->pvt->match_regex_mode = VTE_REGEX_VTE;
1275
1276         memset(&new_regex, 0, sizeof(new_regex));
1277         new_regex.mode = VTE_REGEX_VTE;
1278         new_regex.regex.reg = _vte_regex_compile(match);
1279         if (new_regex.regex.reg == NULL) {
1280                 g_warning(_("Error compiling regular expression \"%s\"."),
1281                           match);
1282                 return -1;
1283         }
1284
1285         /* Search for a hole. */
1286         for (ret = 0; ret < terminal->pvt->match_regexes->len; ret++) {
1287                 regex = &g_array_index(terminal->pvt->match_regexes,
1288                                        struct vte_match_regex,
1289                                        ret);
1290                 if (regex->tag == -1) {
1291                         break;
1292                 }
1293         }
1294         /* Set the tag to the insertion point. */
1295         new_regex.tag = ret;
1296         new_regex.cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1297         new_regex.cursor.cursor_type = VTE_DEFAULT_CURSOR;
1298         if (ret < terminal->pvt->match_regexes->len) {
1299                 /* Overwrite. */
1300                 g_array_index(terminal->pvt->match_regexes,
1301                               struct vte_match_regex,
1302                               ret) = new_regex;
1303         } else {
1304                 /* Append. */
1305                 g_array_append_val(terminal->pvt->match_regexes, new_regex);
1306         }
1307         return new_regex.tag;
1308 }
1309
1310 /**
1311  * vte_terminal_match_add_gregex:
1312  * @terminal: a #VteTerminal
1313  * @regex: a #GRegex
1314  * @flags: the #GRegexMatchFlags to use when matching the regex
1315  *
1316  * Adds the regular expression @regex to the list of matching expressions.  When the
1317  * user moves the mouse cursor over a section of displayed text which matches
1318  * this expression, the text will be highlighted.
1319  *
1320  * Returns: an integer associated with this expression
1321  *
1322  * Since: 0.17.1
1323  */
1324 int
1325 vte_terminal_match_add_gregex(VteTerminal *terminal, GRegex *regex, GRegexMatchFlags flags)
1326 {
1327         VteTerminalPrivate *pvt;
1328         struct vte_match_regex new_regex_match, *regex_match;
1329         guint ret, len;
1330
1331         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1332         g_return_val_if_fail(terminal->pvt->match_regex_mode != VTE_REGEX_VTE, -1);
1333         g_return_val_if_fail(regex != NULL, -1);
1334
1335         pvt = terminal->pvt;
1336         pvt->match_regex_mode = VTE_REGEX_GREGEX;
1337
1338         /* Search for a hole. */
1339         len = pvt->match_regexes->len;
1340         for (ret = 0; ret < len; ret++) {
1341                 regex_match = &g_array_index(pvt->match_regexes,
1342                                              struct vte_match_regex,
1343                                              ret);
1344                 if (regex_match->tag == -1) {
1345                         break;
1346                 }
1347         }
1348
1349         /* Set the tag to the insertion point. */
1350         new_regex_match.mode = VTE_REGEX_GREGEX;
1351         new_regex_match.regex.gregex.regex = g_regex_ref(regex);
1352         new_regex_match.regex.gregex.flags = flags;
1353         new_regex_match.tag = ret;
1354         new_regex_match.cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1355         new_regex_match.cursor.cursor_type = VTE_DEFAULT_CURSOR;
1356         if (ret < pvt->match_regexes->len) {
1357                 /* Overwrite. */
1358                 g_array_index(pvt->match_regexes,
1359                               struct vte_match_regex,
1360                               ret) = new_regex_match;
1361         } else {
1362                 /* Append. */
1363                 g_array_append_val(pvt->match_regexes, new_regex_match);
1364         }
1365
1366         return new_regex_match.tag;
1367 }
1368
1369 /**
1370  * vte_terminal_match_set_cursor:
1371  * @terminal: a #VteTerminal
1372  * @tag: the tag of the regex which should use the specified cursor
1373  * @cursor: (allow-none): the #GdkCursor which the terminal should use when the pattern is
1374  *   highlighted, or %NULL to use the standard cursor
1375  *
1376  * Sets which cursor the terminal will use if the pointer is over the pattern
1377  * specified by @tag.  The terminal keeps a reference to @cursor.
1378  *
1379  * Since: 0.11
1380  *
1381  */
1382 void
1383 vte_terminal_match_set_cursor(VteTerminal *terminal, int tag, GdkCursor *cursor)
1384 {
1385         struct vte_match_regex *regex;
1386         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1387         g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1388         regex = &g_array_index(terminal->pvt->match_regexes,
1389                                struct vte_match_regex,
1390                                tag);
1391         regex_match_clear_cursor(regex);
1392         regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSOR;
1393         regex->cursor.cursor = cursor ? gdk_cursor_ref(cursor) : NULL;
1394         vte_terminal_match_hilite_clear(terminal);
1395 }
1396
1397 /**
1398  * vte_terminal_match_set_cursor_type:
1399  * @terminal: a #VteTerminal
1400  * @tag: the tag of the regex which should use the specified cursor
1401  * @cursor_type: a #GdkCursorType
1402  *
1403  * Sets which cursor the terminal will use if the pointer is over the pattern
1404  * specified by @tag.
1405  *
1406  * Since: 0.11.9
1407  *
1408  */
1409 void
1410 vte_terminal_match_set_cursor_type(VteTerminal *terminal,
1411                                    int tag, GdkCursorType cursor_type)
1412 {
1413         struct vte_match_regex *regex;
1414         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1415         g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1416         regex = &g_array_index(terminal->pvt->match_regexes,
1417                                struct vte_match_regex,
1418                                tag);
1419         regex_match_clear_cursor(regex);
1420         regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1421         regex->cursor.cursor_type = cursor_type;
1422         vte_terminal_match_hilite_clear(terminal);
1423 }
1424
1425 /**
1426  * vte_terminal_match_set_cursor_name:
1427  * @terminal: a #VteTerminal
1428  * @tag: the tag of the regex which should use the specified cursor
1429  * @cursor_name: the name of the cursor
1430  *
1431  * Sets which cursor the terminal will use if the pointer is over the pattern
1432  * specified by @tag.
1433  *
1434  * Since: 0.17.1
1435  *
1436  */
1437 void
1438 vte_terminal_match_set_cursor_name(VteTerminal *terminal,
1439                                    int tag, const char *cursor_name)
1440 {
1441         struct vte_match_regex *regex;
1442         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1443         g_return_if_fail(cursor_name != NULL);
1444         g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1445         regex = &g_array_index(terminal->pvt->match_regexes,
1446                                struct vte_match_regex,
1447                                tag);
1448         regex_match_clear_cursor(regex);
1449         regex->cursor_mode = VTE_REGEX_CURSOR_NAME;
1450         regex->cursor.cursor_name = g_strdup (cursor_name);
1451         vte_terminal_match_hilite_clear(terminal);
1452 }
1453
1454 /* Check if a given cell on the screen contains part of a matched string.  If
1455  * it does, return the string, and store the match tag in the optional tag
1456  * argument. */
1457 static char *
1458 vte_terminal_match_check_internal_vte(VteTerminal *terminal,
1459                                       long column, glong row,
1460                                       int *tag, int *start, int *end)
1461 {
1462         struct _vte_regex_match matches[256];
1463         guint i, j;
1464         gint k;
1465         gint start_blank, end_blank;
1466         int ret, offset;
1467         struct vte_match_regex *regex = NULL;
1468         struct _VteCharAttributes *attr = NULL;
1469         gssize sattr, eattr;
1470         gchar *line, eol;
1471
1472         _vte_debug_print(VTE_DEBUG_EVENTS,
1473                         "Checking for match at (%ld,%ld).\n", row, column);
1474         *tag = -1;
1475         if (start != NULL) {
1476                 *start = 0;
1477         }
1478         if (end != NULL) {
1479                 *end = 0;
1480         }
1481         /* Map the pointer position to a portion of the string. */
1482         eattr = terminal->pvt->match_attributes->len;
1483         for (offset = eattr; offset--; ) {
1484                 attr = &g_array_index(terminal->pvt->match_attributes,
1485                                       struct _VteCharAttributes,
1486                                       offset);
1487                 if (row < attr->row) {
1488                         eattr = offset;
1489                 }
1490                 if (row == attr->row &&
1491                     column == attr->column &&
1492                     terminal->pvt->match_contents[offset] != ' ') {
1493                         break;
1494                 }
1495         }
1496
1497         _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1498                 if (offset < 0)
1499                         g_printerr("Cursor is not on a character.\n");
1500                 else
1501                         g_printerr("Cursor is on character '%c' at %d.\n",
1502                                         g_utf8_get_char (terminal->pvt->match_contents + offset),
1503                                         offset);
1504         }
1505
1506         /* If the pointer isn't on a matchable character, bug out. */
1507         if (offset < 0) {
1508                 return NULL;
1509         }
1510
1511         /* If the pointer is on a newline, bug out. */
1512         if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1513             (terminal->pvt->match_contents[offset] == '\0')) {
1514                 _vte_debug_print(VTE_DEBUG_EVENTS,
1515                                 "Cursor is on whitespace.\n");
1516                 return NULL;
1517         }
1518
1519         /* Snip off any final newlines. */
1520         while (terminal->pvt->match_contents[eattr] == '\n' ||
1521                         terminal->pvt->match_contents[eattr] == '\0') {
1522                 eattr--;
1523         }
1524         /* and scan forwards to find the end of this line */
1525         while (!(terminal->pvt->match_contents[eattr] == '\n' ||
1526                         terminal->pvt->match_contents[eattr] == '\0')) {
1527                 eattr++;
1528         }
1529
1530         /* find the start of row */
1531         if (row == 0) {
1532                 sattr = 0;
1533         } else {
1534                 for (sattr = offset; sattr > 0; sattr--) {
1535                         attr = &g_array_index(terminal->pvt->match_attributes,
1536                                               struct _VteCharAttributes,
1537                                               sattr);
1538                         if (row > attr->row) {
1539                                 break;
1540                         }
1541                 }
1542         }
1543         /* Scan backwards to find the start of this line */
1544         while (sattr > 0 &&
1545                 ! (terminal->pvt->match_contents[sattr] == '\n' ||
1546                     terminal->pvt->match_contents[sattr] == '\0')) {
1547                 sattr--;
1548         }
1549         /* and skip any initial newlines. */
1550         while (terminal->pvt->match_contents[sattr] == '\n' ||
1551                 terminal->pvt->match_contents[sattr] == '\0') {
1552                 sattr++;
1553         }
1554         if (eattr <= sattr) { /* blank line */
1555                 return NULL;
1556         }
1557         if (eattr <= offset || sattr > offset) {
1558                 /* nothing to match on this line */
1559                 return NULL;
1560         }
1561         offset -= sattr;
1562         eattr -= sattr;
1563
1564         /* temporarily shorten the contents to this row */
1565         line = terminal->pvt->match_contents + sattr;
1566         eol = line[eattr];
1567         line[eattr] = '\0';
1568
1569         start_blank = 0;
1570         end_blank = eattr;
1571
1572         /* Now iterate over each regex we need to match against. */
1573         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1574                 regex = &g_array_index(terminal->pvt->match_regexes,
1575                                        struct vte_match_regex,
1576                                        i);
1577                 /* Skip holes. */
1578                 if (regex->tag < 0) {
1579                         continue;
1580                 }
1581                 /* We'll only match the first item in the buffer which
1582                  * matches, so we'll have to skip each match until we
1583                  * stop getting matches. */
1584                 k = 0;
1585                 ret = _vte_regex_exec(regex->regex.reg,
1586                                       line + k,
1587                                       G_N_ELEMENTS(matches),
1588                                       matches);
1589                 while (ret == 0) {
1590                         gint ko = offset - k;
1591                         gint sblank=G_MININT, eblank=G_MAXINT;
1592                         for (j = 0;
1593                              j < G_N_ELEMENTS(matches) &&
1594                              matches[j].rm_so != -1;
1595                              j++) {
1596                                 /* The offsets should be "sane". */
1597                                 g_assert(matches[j].rm_so + k < eattr);
1598                                 g_assert(matches[j].rm_eo + k <= eattr);
1599                                 _VTE_DEBUG_IF(VTE_DEBUG_MISC) {
1600                                         gchar *match;
1601                                         struct _VteCharAttributes *_sattr, *_eattr;
1602                                         match = g_strndup(line + matches[j].rm_so + k,
1603                                                         matches[j].rm_eo - matches[j].rm_so);
1604                                         _sattr = &g_array_index(terminal->pvt->match_attributes,
1605                                                         struct _VteCharAttributes,
1606                                                         matches[j].rm_so + k);
1607                                         _eattr = &g_array_index(terminal->pvt->match_attributes,
1608                                                         struct _VteCharAttributes,
1609                                                         matches[j].rm_eo + k - 1);
1610                                         g_printerr("Match %u `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1611                                                         j, match,
1612                                                         matches[j].rm_so + k,
1613                                                         _sattr->column,
1614                                                         _sattr->row,
1615                                                         matches[j].rm_eo + k - 1,
1616                                                         _eattr->column,
1617                                                         _eattr->row,
1618                                                         offset);
1619                                         g_free(match);
1620
1621                                 }
1622                                 /* If the pointer is in this substring,
1623                                  * then we're done. */
1624                                 if (ko >= matches[j].rm_so &&
1625                                     ko < matches[j].rm_eo) {
1626                                         gchar *result;
1627                                         if (tag != NULL) {
1628                                                 *tag = regex->tag;
1629                                         }
1630                                         if (start != NULL) {
1631                                                 *start = sattr + k + matches[j].rm_so;
1632                                         }
1633                                         if (end != NULL) {
1634                                                 *end = sattr + k + matches[j].rm_eo - 1;
1635                                         }
1636                                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
1637                                         result = g_strndup(line + k + matches[j].rm_so,
1638                                                          matches[j].rm_eo - matches[j].rm_so);
1639                                         line[eattr] = eol;
1640                                         return result;
1641                                 }
1642                                 if (ko > matches[j].rm_eo &&
1643                                                 matches[j].rm_eo > sblank) {
1644                                         sblank = matches[j].rm_eo;
1645                                 }
1646                                 if (ko < matches[j].rm_so &&
1647                                                 matches[j].rm_so < eblank) {
1648                                         eblank = matches[j].rm_so;
1649                                 }
1650                         }
1651                         if (k + sblank > start_blank) {
1652                                 start_blank = k + sblank;
1653                         }
1654                         if (k + eblank < end_blank) {
1655                                 end_blank = k + eblank;
1656                         }
1657                         /* Skip past the beginning of this match to
1658                          * look for more. */
1659                         k += matches[0].rm_so + 1;
1660                         if (k > offset) {
1661                                 break;
1662                         }
1663                         ret = _vte_regex_exec(regex->regex.reg,
1664                                               line + k,
1665                                               G_N_ELEMENTS(matches),
1666                                               matches);
1667                 }
1668         }
1669         line[eattr] = eol;
1670         if (start != NULL) {
1671                 *start = sattr + start_blank;
1672         }
1673         if (end != NULL) {
1674                 *end = sattr + end_blank;
1675         }
1676         return NULL;
1677 }
1678
1679 /* Check if a given cell on the screen contains part of a matched string.  If
1680  * it does, return the string, and store the match tag in the optional tag
1681  * argument. */
1682 static char *
1683 vte_terminal_match_check_internal_gregex(VteTerminal *terminal,
1684                                          long column, glong row,
1685                                          int *tag, int *start, int *end)
1686 {
1687         gint start_blank, end_blank;
1688         guint i;
1689         int offset;
1690         struct vte_match_regex *regex = NULL;
1691         struct _VteCharAttributes *attr = NULL;
1692         gssize sattr, eattr;
1693         gchar *line, eol;
1694         GMatchInfo *match_info;
1695
1696         _vte_debug_print(VTE_DEBUG_EVENTS,
1697                         "Checking for gregex match at (%ld,%ld).\n", row, column);
1698         *tag = -1;
1699         if (start != NULL) {
1700                 *start = 0;
1701         }
1702         if (end != NULL) {
1703                 *end = 0;
1704         }
1705         /* Map the pointer position to a portion of the string. */
1706         eattr = terminal->pvt->match_attributes->len;
1707         for (offset = eattr; offset--; ) {
1708                 attr = &g_array_index(terminal->pvt->match_attributes,
1709                                       struct _VteCharAttributes,
1710                                       offset);
1711                 if (row < attr->row) {
1712                         eattr = offset;
1713                 }
1714                 if (row == attr->row &&
1715                     column == attr->column &&
1716                     terminal->pvt->match_contents[offset] != ' ') {
1717                         break;
1718                 }
1719         }
1720
1721         _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1722                 if (offset < 0)
1723                         g_printerr("Cursor is not on a character.\n");
1724                 else
1725                         g_printerr("Cursor is on character '%c' at %d.\n",
1726                                         g_utf8_get_char (terminal->pvt->match_contents + offset),
1727                                         offset);
1728         }
1729
1730         /* If the pointer isn't on a matchable character, bug out. */
1731         if (offset < 0) {
1732                 return NULL;
1733         }
1734
1735         /* If the pointer is on a newline, bug out. */
1736         if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1737             (terminal->pvt->match_contents[offset] == '\0')) {
1738                 _vte_debug_print(VTE_DEBUG_EVENTS,
1739                                 "Cursor is on whitespace.\n");
1740                 return NULL;
1741         }
1742
1743         /* Snip off any final newlines. */
1744         while (terminal->pvt->match_contents[eattr] == '\n' ||
1745                         terminal->pvt->match_contents[eattr] == '\0') {
1746                 eattr--;
1747         }
1748         /* and scan forwards to find the end of this line */
1749         while (!(terminal->pvt->match_contents[eattr] == '\n' ||
1750                         terminal->pvt->match_contents[eattr] == '\0')) {
1751                 eattr++;
1752         }
1753
1754         /* find the start of row */
1755         if (row == 0) {
1756                 sattr = 0;
1757         } else {
1758                 for (sattr = offset; sattr > 0; sattr--) {
1759                         attr = &g_array_index(terminal->pvt->match_attributes,
1760                                               struct _VteCharAttributes,
1761                                               sattr);
1762                         if (row > attr->row) {
1763                                 break;
1764                         }
1765                 }
1766         }
1767         /* Scan backwards to find the start of this line */
1768         while (sattr > 0 &&
1769                 ! (terminal->pvt->match_contents[sattr] == '\n' ||
1770                     terminal->pvt->match_contents[sattr] == '\0')) {
1771                 sattr--;
1772         }
1773         /* and skip any initial newlines. */
1774         while (terminal->pvt->match_contents[sattr] == '\n' ||
1775                 terminal->pvt->match_contents[sattr] == '\0') {
1776                 sattr++;
1777         }
1778         if (eattr <= sattr) { /* blank line */
1779                 return NULL;
1780         }
1781         if (eattr <= offset || sattr > offset) {
1782                 /* nothing to match on this line */
1783                 return NULL;
1784         }
1785         offset -= sattr;
1786         eattr -= sattr;
1787
1788         /* temporarily shorten the contents to this row */
1789         line = terminal->pvt->match_contents + sattr;
1790         eol = line[eattr];
1791         line[eattr] = '\0';
1792
1793         start_blank = 0;
1794         end_blank = eattr;
1795
1796         /* Now iterate over each regex we need to match against. */
1797         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1798                 regex = &g_array_index(terminal->pvt->match_regexes,
1799                                        struct vte_match_regex,
1800                                        i);
1801                 /* Skip holes. */
1802                 if (regex->tag < 0) {
1803                         continue;
1804                 }
1805                 /* We'll only match the first item in the buffer which
1806                  * matches, so we'll have to skip each match until we
1807                  * stop getting matches. */
1808                 if (!g_regex_match_full(regex->regex.gregex.regex,
1809                                         line, -1, 0,
1810                                         regex->regex.gregex.flags,
1811                                         &match_info,
1812                                         NULL)) {
1813                         g_match_info_free(match_info);
1814                         continue;
1815                 }
1816
1817                 while (g_match_info_matches(match_info)) {
1818                         gint ko = offset;
1819                         gint sblank=G_MININT, eblank=G_MAXINT;
1820                         gint rm_so, rm_eo;
1821
1822                         if (g_match_info_fetch_pos (match_info, 0, &rm_so, &rm_eo)) {
1823                                 /* The offsets should be "sane". */
1824                                 g_assert(rm_so < eattr);
1825                                 g_assert(rm_eo <= eattr);
1826                                 _VTE_DEBUG_IF(VTE_DEBUG_MISC) {
1827                                         gchar *match;
1828                                         struct _VteCharAttributes *_sattr, *_eattr;
1829                                         match = g_strndup(line + rm_so, rm_eo - rm_so);
1830                                         _sattr = &g_array_index(terminal->pvt->match_attributes,
1831                                                         struct _VteCharAttributes,
1832                                                         rm_so);
1833                                         _eattr = &g_array_index(terminal->pvt->match_attributes,
1834                                                         struct _VteCharAttributes,
1835                                                         rm_eo - 1);
1836                                         g_printerr("Match `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1837                                                         match,
1838                                                         rm_so,
1839                                                         _sattr->column,
1840                                                         _sattr->row,
1841                                                         rm_eo - 1,
1842                                                         _eattr->column,
1843                                                         _eattr->row,
1844                                                         offset);
1845                                         g_free(match);
1846
1847                                 }
1848                                 /* If the pointer is in this substring,
1849                                  * then we're done. */
1850                                 if (ko >= rm_so &&
1851                                     ko < rm_eo) {
1852                                         gchar *result;
1853                                         if (tag != NULL) {
1854                                                 *tag = regex->tag;
1855                                         }
1856                                         if (start != NULL) {
1857                                                 *start = sattr + rm_so;
1858                                         }
1859                                         if (end != NULL) {
1860                                                 *end = sattr + rm_eo - 1;
1861                                         }
1862                                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
1863                                         result = g_match_info_fetch(match_info, 0);
1864                                         line[eattr] = eol;
1865
1866                                         g_match_info_free(match_info);
1867                                         return result;
1868                                 }
1869                                 if (ko > rm_eo &&
1870                                                 rm_eo > sblank) {
1871                                         sblank = rm_eo;
1872                                 }
1873                                 if (ko < rm_so &&
1874                                                 rm_so < eblank) {
1875                                         eblank = rm_so;
1876                                 }
1877                         }
1878                         if (sblank > start_blank) {
1879                                 start_blank = sblank;
1880                         }
1881                         if (eblank < end_blank) {
1882                                 end_blank = eblank;
1883                         }
1884
1885                         g_match_info_next(match_info, NULL);
1886                 }
1887
1888                 g_match_info_free(match_info);
1889         }
1890         line[eattr] = eol;
1891         if (start != NULL) {
1892                 *start = sattr + start_blank;
1893         }
1894         if (end != NULL) {
1895                 *end = sattr + end_blank;
1896         }
1897         return NULL;
1898 }
1899
1900 static char *
1901 vte_terminal_match_check_internal(VteTerminal *terminal,
1902                                   long column, glong row,
1903                                   int *tag, int *start, int *end)
1904 {
1905         if (terminal->pvt->match_contents == NULL) {
1906                 vte_terminal_match_contents_refresh(terminal);
1907         }
1908
1909         if (terminal->pvt->match_regex_mode == VTE_REGEX_GREGEX)
1910                 return vte_terminal_match_check_internal_gregex(terminal, column, row, tag, start, end);
1911         if (terminal->pvt->match_regex_mode == VTE_REGEX_VTE)
1912                 return vte_terminal_match_check_internal_vte(terminal, column, row, tag, start, end);
1913         return NULL;
1914 }
1915
1916 static gboolean
1917 rowcol_inside_match (VteTerminal *terminal, glong row, glong col)
1918 {
1919         if (terminal->pvt->match_start.row == terminal->pvt->match_end.row) {
1920                 return row == terminal->pvt->match_start.row &&
1921                         col >= terminal->pvt->match_start.col &&
1922                         col <= terminal->pvt->match_end.col;
1923         } else {
1924                 if (row < terminal->pvt->match_start.row ||
1925                                 row > terminal->pvt->match_end.row) {
1926                         return FALSE;
1927                 }
1928                 if (row == terminal->pvt->match_start.row) {
1929                         return col >= terminal->pvt->match_start.col;
1930                 }
1931                 if (row == terminal->pvt->match_end.row) {
1932                         return col <= terminal->pvt->match_end.col;
1933                 }
1934                 return TRUE;
1935         }
1936 }
1937
1938 /**
1939  * vte_terminal_match_check:
1940  * @terminal: a #VteTerminal
1941  * @column: the text column
1942  * @row: the text row
1943  * @tag: (out) (allow-none): a location to store the tag, or %NULL
1944  *
1945  * Checks if the text in and around the specified position matches any of the
1946  * regular expressions previously set using vte_terminal_match_add().  If a
1947  * match exists, the text string is returned and if @tag is not %NULL, the number
1948  * associated with the matched regular expression will be stored in @tag.
1949  *
1950  * If more than one regular expression has been set with
1951  * vte_terminal_match_add(), then expressions are checked in the order in
1952  * which they were added.
1953  *
1954  * Returns: (transfer full): a newly allocated string which matches one of the previously
1955  *   set regular expressions
1956  */
1957 char *
1958 vte_terminal_match_check(VteTerminal *terminal, glong column, glong row,
1959                          int *tag)
1960 {
1961         long delta;
1962         char *ret;
1963         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1964         delta = terminal->pvt->screen->scroll_delta;
1965         _vte_debug_print(VTE_DEBUG_EVENTS,
1966                         "Checking for match at (%ld,%ld).\n",
1967                         row, column);
1968         if (rowcol_inside_match (terminal, row + delta, column)) {
1969                 if (tag) {
1970                         *tag = terminal->pvt->match_tag;
1971                 }
1972                 ret = terminal->pvt->match != NULL ?
1973                         g_strdup (terminal->pvt->match) :
1974                         NULL;
1975         } else {
1976                 ret = vte_terminal_match_check_internal(terminal,
1977                                 column, row + delta,
1978                                 tag, NULL, NULL);
1979         }
1980         _VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1981                 if (ret != NULL) g_printerr("Matched `%s'.\n", ret);
1982         }
1983         return ret;
1984 }
1985
1986 /* Emit an adjustment changed signal on our adjustment object. */
1987 static void
1988 vte_terminal_emit_adjustment_changed(VteTerminal *terminal)
1989 {
1990         if (terminal->pvt->adjustment_changed_pending) {
1991                 VteScreen *screen = terminal->pvt->screen;
1992                 gboolean changed = FALSE;
1993                 glong v;
1994                 gdouble current;
1995
1996                 g_object_freeze_notify (G_OBJECT (terminal->adjustment));
1997
1998                 v = _vte_ring_delta (screen->row_data);
1999                 current = gtk_adjustment_get_lower(terminal->adjustment);
2000                 if (current != v) {
2001                         _vte_debug_print(VTE_DEBUG_ADJ,
2002                                         "Changing lower bound from %.0f to %ld\n",
2003                                          current, v);
2004                         gtk_adjustment_set_lower(terminal->adjustment, v);
2005                         changed = TRUE;
2006                 }
2007
2008                 /* The upper value is the number of rows which might be visible.  (Add
2009                  * one to the cursor offset because it's zero-based.) */
2010                 v = MAX(_vte_ring_next(screen->row_data),
2011                                 screen->cursor_current.row + 1);
2012                 current = gtk_adjustment_get_upper(terminal->adjustment);
2013                 if (current != v) {
2014                         _vte_debug_print(VTE_DEBUG_ADJ,
2015                                         "Changing upper bound from %.0f to %ld\n",
2016                                          current, v);
2017                         gtk_adjustment_set_upper(terminal->adjustment, v);
2018                         changed = TRUE;
2019                 }
2020
2021                 g_object_thaw_notify (G_OBJECT (terminal->adjustment));
2022
2023                 if (changed)
2024                         _vte_debug_print(VTE_DEBUG_SIGNALS,
2025                                         "Emitting adjustment_changed.\n");
2026                 terminal->pvt->adjustment_changed_pending = FALSE;
2027         }
2028         if (terminal->pvt->adjustment_value_changed_pending) {
2029                 glong v, delta;
2030                 _vte_debug_print(VTE_DEBUG_SIGNALS,
2031                                 "Emitting adjustment_value_changed.\n");
2032                 terminal->pvt->adjustment_value_changed_pending = FALSE;
2033                 v = round (gtk_adjustment_get_value(terminal->adjustment));
2034                 if (v != terminal->pvt->screen->scroll_delta) {
2035                         /* this little dance is so that the scroll_delta is
2036                          * updated immediately, but we still handled scrolling
2037                          * via the adjustment - e.g. user interaction with the
2038                          * scrollbar
2039                          */
2040                         delta = terminal->pvt->screen->scroll_delta;
2041                         terminal->pvt->screen->scroll_delta = v;
2042                         gtk_adjustment_set_value(terminal->adjustment, delta);
2043                 }
2044         }
2045 }
2046
2047 /* Queue an adjustment-changed signal to be delivered when convenient. */
2048 static inline void
2049 vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
2050 {
2051         terminal->pvt->adjustment_changed_pending = TRUE;
2052         add_update_timeout (terminal);
2053 }
2054
2055 static void
2056 vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
2057 {
2058         if (v != terminal->pvt->screen->scroll_delta) {
2059                 terminal->pvt->screen->scroll_delta = v;
2060                 terminal->pvt->adjustment_value_changed_pending = TRUE;
2061                 add_update_timeout (terminal);
2062         }
2063 }
2064
2065 static void
2066 vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal *terminal, glong v)
2067 {
2068         gdouble lower, upper;
2069
2070         lower = gtk_adjustment_get_lower(terminal->adjustment);
2071         upper = gtk_adjustment_get_upper(terminal->adjustment);
2072
2073         v = CLAMP(v, lower, MAX (lower, upper - terminal->row_count));
2074
2075         vte_terminal_queue_adjustment_value_changed (terminal, v);
2076 }
2077
2078
2079 void
2080 _vte_terminal_adjust_adjustments(VteTerminal *terminal)
2081 {
2082         VteScreen *screen;
2083         long delta;
2084
2085         g_assert(terminal->pvt->screen != NULL);
2086         g_assert(terminal->pvt->screen->row_data != NULL);
2087
2088         vte_terminal_queue_adjustment_changed(terminal);
2089
2090         /* The lower value should be the first row in the buffer. */
2091         screen = terminal->pvt->screen;
2092         delta = _vte_ring_delta(screen->row_data);
2093         /* Snap the insert delta and the cursor position to be in the visible
2094          * area.  Leave the scrolling delta alone because it will be updated
2095          * when the adjustment changes. */
2096         screen->insert_delta = MAX(screen->insert_delta, delta);
2097         screen->cursor_current.row = MAX(screen->cursor_current.row,
2098                                          screen->insert_delta);
2099
2100         if (screen->scroll_delta > screen->insert_delta) {
2101                 vte_terminal_queue_adjustment_value_changed(terminal,
2102                                 screen->insert_delta);
2103         }
2104 }
2105
2106 /* Update the adjustment field of the widget.  This function should be called
2107  * whenever we add rows to or remove rows from the history or switch screens. */
2108 static void
2109 _vte_terminal_adjust_adjustments_full (VteTerminal *terminal)
2110 {
2111         gboolean changed = FALSE;
2112         gdouble v;
2113
2114         g_assert(terminal->pvt->screen != NULL);
2115         g_assert(terminal->pvt->screen->row_data != NULL);
2116
2117         _vte_terminal_adjust_adjustments(terminal);
2118
2119         g_object_freeze_notify(G_OBJECT(terminal->adjustment));
2120
2121         /* The step increment should always be one. */
2122         v = gtk_adjustment_get_step_increment(terminal->adjustment);
2123         if (v != 1) {
2124                 _vte_debug_print(VTE_DEBUG_ADJ,
2125                                 "Changing step increment from %.0lf to %ld\n",
2126                                 v, terminal->row_count);
2127                 gtk_adjustment_set_step_increment(terminal->adjustment, 1);
2128                 changed = TRUE;
2129         }
2130
2131         /* Set the number of rows the user sees to the number of rows the
2132          * user sees. */
2133         v = gtk_adjustment_get_page_size(terminal->adjustment);
2134         if (v != terminal->row_count) {
2135                 _vte_debug_print(VTE_DEBUG_ADJ,
2136                                 "Changing page size from %.0f to %ld\n",
2137                                  v, terminal->row_count);
2138                 gtk_adjustment_set_page_size(terminal->adjustment,
2139                                              terminal->row_count);
2140                 changed = TRUE;
2141         }
2142
2143         /* Clicking in the empty area should scroll one screen, so set the
2144          * page size to the number of visible rows. */
2145         v = gtk_adjustment_get_page_increment(terminal->adjustment);
2146         if (v != terminal->row_count) {
2147                 _vte_debug_print(VTE_DEBUG_ADJ,
2148                                 "Changing page increment from "
2149                                 "%.0f to %ld\n",
2150                                 v, terminal->row_count);
2151                 gtk_adjustment_set_page_increment(terminal->adjustment,
2152                                                   terminal->row_count);
2153                 changed = TRUE;
2154         }
2155
2156         g_object_thaw_notify(G_OBJECT(terminal->adjustment));
2157
2158         if (changed)
2159                 _vte_debug_print(VTE_DEBUG_SIGNALS,
2160                                 "Emitting adjustment_changed.\n");
2161 }
2162
2163 /* Scroll a fixed number of lines up or down in the current screen. */
2164 static void
2165 vte_terminal_scroll_lines(VteTerminal *terminal, gint lines)
2166 {
2167         glong destination;
2168         _vte_debug_print(VTE_DEBUG_ADJ, "Scrolling %d lines.\n", lines);
2169         /* Calculate the ideal position where we want to be before clamping. */
2170         destination = terminal->pvt->screen->scroll_delta;
2171         destination += lines;
2172         /* Tell the scrollbar to adjust itself. */
2173         vte_terminal_queue_adjustment_value_changed_clamped (terminal, destination);
2174 }
2175
2176 /* Scroll a fixed number of pages up or down, in the current screen. */
2177 static void
2178 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
2179 {
2180         vte_terminal_scroll_lines(terminal, pages * terminal->row_count);
2181 }
2182
2183 /* Scroll so that the scroll delta is the minimum value. */
2184 static void
2185 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
2186 {
2187         vte_terminal_queue_adjustment_value_changed (terminal,
2188                         _vte_ring_delta(terminal->pvt->screen->row_data));
2189 }
2190
2191 static void
2192 vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
2193 {
2194         glong delta;
2195         delta = terminal->pvt->screen->insert_delta;
2196         vte_terminal_queue_adjustment_value_changed (terminal, delta);
2197         _vte_debug_print(VTE_DEBUG_ADJ,
2198                         "Snapping to bottom of screen\n");
2199 }
2200
2201 static void
2202 _vte_terminal_setup_utf8 (VteTerminal *terminal)
2203 {
2204         VteTerminalPrivate *pvt = terminal->pvt;
2205         GError *error = NULL;
2206
2207         if (!vte_pty_set_utf8(pvt->pty,
2208                               strcmp(terminal->pvt->encoding, "UTF-8") == 0,
2209                               &error)) {
2210                 g_warning ("Failed to set UTF8 mode: %s\n", error->message);
2211                 g_error_free (error);
2212         }
2213 }
2214
2215 /**
2216  * vte_terminal_set_encoding:
2217  * @terminal: a #VteTerminal
2218  * @codeset: (allow-none): a valid #GIConv target, or %NULL to use the default encoding
2219  *
2220  * Changes the encoding the terminal will expect data from the child to
2221  * be encoded with.  For certain terminal types, applications executing in the
2222  * terminal can change the encoding.  The default encoding is defined by the
2223  * application's locale settings.
2224  */
2225 void
2226 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
2227 {
2228         VteTerminalPrivate *pvt;
2229         GObject *object;
2230         const char *old_codeset;
2231         VteConv conv;
2232         char *obuf1, *obuf2;
2233         gsize bytes_written;
2234
2235         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2236
2237         object = G_OBJECT(terminal);
2238         pvt = terminal->pvt;
2239
2240         old_codeset = pvt->encoding;
2241         if (codeset == NULL) {
2242                 g_get_charset(&codeset);
2243         }
2244         if ((old_codeset != NULL) && (strcmp(codeset, old_codeset) == 0)) {
2245                 /* Nothing to do! */
2246                 return;
2247         }
2248
2249         g_object_freeze_notify(object);
2250
2251         /* Open new conversions. */
2252         conv = _vte_conv_open(codeset, "UTF-8");
2253         if (conv == VTE_INVALID_CONV) {
2254                 g_warning(_("Unable to convert characters from %s to %s."),
2255                           "UTF-8", codeset);
2256                 /* fallback to no conversion */
2257                 conv = _vte_conv_open(codeset = "UTF-8", "UTF-8");
2258         }
2259         if (terminal->pvt->outgoing_conv != VTE_INVALID_CONV) {
2260                 _vte_conv_close(terminal->pvt->outgoing_conv);
2261         }
2262         terminal->pvt->outgoing_conv = conv;
2263
2264         /* Set the terminal's encoding to the new value. */
2265         terminal->pvt->encoding = g_intern_string(codeset);
2266
2267         /* Convert any buffered output bytes. */
2268         if ((_vte_buffer_length(terminal->pvt->outgoing) > 0) &&
2269             (old_codeset != NULL)) {
2270                 /* Convert back to UTF-8. */
2271                 obuf1 = g_convert((gchar *)terminal->pvt->outgoing->data,
2272                                   _vte_buffer_length(terminal->pvt->outgoing),
2273                                   "UTF-8",
2274                                   old_codeset,
2275                                   NULL,
2276                                   &bytes_written,
2277                                   NULL);
2278                 if (obuf1 != NULL) {
2279                         /* Convert to the new encoding. */
2280                         obuf2 = g_convert(obuf1,
2281                                           bytes_written,
2282                                           codeset,
2283                                           "UTF-8",
2284                                           NULL,
2285                                           &bytes_written,
2286                                           NULL);
2287                         if (obuf2 != NULL) {
2288                                 _vte_buffer_clear(terminal->pvt->outgoing);
2289                                 _vte_buffer_append(terminal->pvt->outgoing,
2290                                                    obuf2, bytes_written);
2291                                 g_free(obuf2);
2292                         }
2293                         g_free(obuf1);
2294                 }
2295         }
2296
2297         /* Set the encoding for incoming text. */
2298         _vte_iso2022_state_set_codeset(terminal->pvt->iso2022,
2299                                        terminal->pvt->encoding);
2300
2301         _vte_debug_print(VTE_DEBUG_IO,
2302                         "Set terminal encoding to `%s'.\n",
2303                         terminal->pvt->encoding);
2304         vte_terminal_emit_encoding_changed(terminal);
2305
2306         g_object_thaw_notify(object);
2307 }
2308
2309 /**
2310  * vte_terminal_get_encoding:
2311  * @terminal: a #VteTerminal
2312  *
2313  * Determines the name of the encoding in which the terminal expects data to be
2314  * encoded.
2315  *
2316  * Returns: (transfer none): the current encoding for the terminal
2317  */
2318 const char *
2319 vte_terminal_get_encoding(VteTerminal *terminal)
2320 {
2321         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
2322         return terminal->pvt->encoding;
2323 }
2324
2325 static inline VteRowData *
2326 vte_terminal_insert_rows (VteTerminal *terminal, guint cnt)
2327 {
2328         VteRowData *row;
2329         do {
2330                 row = _vte_terminal_ring_append (terminal, FALSE);
2331         } while(--cnt);
2332         return row;
2333 }
2334
2335
2336 /* Make sure we have enough rows and columns to hold data at the current
2337  * cursor position. */
2338 VteRowData *
2339 _vte_terminal_ensure_row (VteTerminal *terminal)
2340 {
2341         VteRowData *row;
2342         VteScreen *screen;
2343         gint delta;
2344         glong v;
2345
2346         /* Must make sure we're in a sane area. */
2347         screen = terminal->pvt->screen;
2348         v = screen->cursor_current.row;
2349
2350         /* Figure out how many rows we need to add. */
2351         delta = v - _vte_ring_next(screen->row_data) + 1;
2352         if (delta > 0) {
2353                 row = vte_terminal_insert_rows (terminal, delta);
2354                 _vte_terminal_adjust_adjustments(terminal);
2355         } else {
2356                 /* Find the row the cursor is in. */
2357                 row = _vte_ring_index_writable (screen->row_data, v);
2358         }
2359         g_assert(row != NULL);
2360
2361         return row;
2362 }
2363
2364 static VteRowData *
2365 vte_terminal_ensure_cursor(VteTerminal *terminal)
2366 {
2367         VteRowData *row;
2368
2369         row = _vte_terminal_ensure_row (terminal);
2370         _vte_row_data_fill (row, &basic_cell.cell, terminal->pvt->screen->cursor_current.col);
2371
2372         return row;
2373 }
2374
2375 /* Update the insert delta so that the screen which includes it also
2376  * includes the end of the buffer. */
2377 void
2378 _vte_terminal_update_insert_delta(VteTerminal *terminal)
2379 {
2380         long delta, rows;
2381         VteScreen *screen;
2382
2383         screen = terminal->pvt->screen;
2384
2385         /* The total number of lines.  Add one to the cursor offset
2386          * because it's zero-based. */
2387         rows = _vte_ring_next (screen->row_data);
2388         delta = screen->cursor_current.row - rows + 1;
2389         if (G_UNLIKELY (delta > 0)) {
2390                 vte_terminal_insert_rows (terminal, delta);
2391                 rows = _vte_ring_next (screen->row_data);
2392         }
2393
2394         /* Make sure that the bottom row is visible, and that it's in
2395          * the buffer (even if it's empty).  This usually causes the
2396          * top row to become a history-only row. */
2397         delta = screen->insert_delta;
2398         delta = MIN(delta, rows - terminal->row_count);
2399         delta = MAX(delta,
2400                     screen->cursor_current.row - (terminal->row_count - 1));
2401         delta = MAX(delta, _vte_ring_delta(screen->row_data));
2402
2403         /* Adjust the insert delta and scroll if needed. */
2404         if (delta != screen->insert_delta) {
2405                 screen->insert_delta = delta;
2406                 _vte_terminal_adjust_adjustments(terminal);
2407         }
2408 }
2409
2410 /* Show or hide the pointer. */
2411 void
2412 _vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible)
2413 {
2414         GdkWindow *window;
2415         struct vte_match_regex *regex = NULL;
2416
2417         terminal->pvt->mouse_cursor_visible = visible;
2418
2419         if (! gtk_widget_get_realized (&terminal->widget))
2420                 return;
2421
2422         window = gtk_widget_get_window (&terminal->widget);
2423
2424         if (visible || !terminal->pvt->mouse_autohide) {
2425                 if (terminal->pvt->mouse_tracking_mode) {
2426                         _vte_debug_print(VTE_DEBUG_CURSOR,
2427                                         "Setting mousing cursor.\n");
2428                         gdk_window_set_cursor (window, terminal->pvt->mouse_mousing_cursor);
2429                 } else
2430                 if ( (guint)terminal->pvt->match_tag < terminal->pvt->match_regexes->len) {
2431                         regex = &g_array_index(terminal->pvt->match_regexes,
2432                                                struct vte_match_regex,
2433                                                terminal->pvt->match_tag);
2434                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
2435                 } else {
2436                         _vte_debug_print(VTE_DEBUG_CURSOR,
2437                                         "Setting default mouse cursor.\n");
2438                         gdk_window_set_cursor (window, terminal->pvt->mouse_default_cursor);
2439                 }
2440         } else {
2441                 _vte_debug_print(VTE_DEBUG_CURSOR,
2442                                 "Setting to invisible cursor.\n");
2443                 gdk_window_set_cursor (window, terminal->pvt->mouse_inviso_cursor);
2444         }
2445 }
2446
2447 /**
2448  * vte_terminal_new:
2449  *
2450  * Creates a new terminal widget.
2451  *
2452  * Returns: (transfer full) (type Vte.Terminal): a new #VteTerminal object
2453  */
2454 GtkWidget *
2455 vte_terminal_new(void)
2456 {
2457         return g_object_new(VTE_TYPE_TERMINAL, NULL);
2458 }
2459
2460 /* Set up a palette entry with a more-or-less match for the requested color. */
2461 static void
2462 vte_terminal_set_color_internal(VteTerminal *terminal, int entry,
2463                                 const GdkColor *proposed)
2464 {
2465         PangoColor *color;
2466
2467         color = &terminal->pvt->palette[entry];
2468
2469         if (color->red == proposed->red &&
2470             color->green == proposed->green &&
2471             color->blue == proposed->blue) {
2472                 return;
2473         }
2474
2475         _vte_debug_print(VTE_DEBUG_MISC,
2476                         "Set color[%d] to (%04x,%04x,%04x).\n", entry,
2477                         proposed->red, proposed->green, proposed->blue);
2478
2479         /* Save the requested color. */
2480         color->red = proposed->red;
2481         color->green = proposed->green;
2482         color->blue = proposed->blue;
2483
2484         /* If we're not realized yet, there's nothing else to do. */
2485         if (! gtk_widget_get_realized (&terminal->widget)) {
2486                 return;
2487         }
2488
2489         /* If we're setting the background color, set the background color
2490          * on the widget as well. */
2491         if (entry == VTE_DEF_BG) {
2492                 vte_terminal_queue_background_update(terminal);
2493         }
2494
2495         /* and redraw */
2496         if (entry == VTE_CUR_BG)
2497                 _vte_invalidate_cursor_once(terminal, FALSE);
2498         else
2499                 _vte_invalidate_all (terminal);
2500 }
2501
2502 static void
2503 vte_terminal_generate_bold(const PangoColor *foreground,
2504                            const PangoColor *background,
2505                            double factor,
2506                            GdkColor *bold)
2507 {
2508         double fy, fcb, fcr, by, bcb, bcr, r, g, b;
2509         g_assert(foreground != NULL);
2510         g_assert(background != NULL);
2511         g_assert(bold != NULL);
2512         fy =   0.2990 * foreground->red +
2513                0.5870 * foreground->green +
2514                0.1140 * foreground->blue;
2515         fcb = -0.1687 * foreground->red +
2516               -0.3313 * foreground->green +
2517                0.5000 * foreground->blue;
2518         fcr =  0.5000 * foreground->red +
2519               -0.4187 * foreground->green +
2520               -0.0813 * foreground->blue;
2521         by =   0.2990 * background->red +
2522                0.5870 * background->green +
2523                0.1140 * background->blue;
2524         bcb = -0.1687 * background->red +
2525               -0.3313 * background->green +
2526                0.5000 * background->blue;
2527         bcr =  0.5000 * background->red +
2528               -0.4187 * background->green +
2529               -0.0813 * background->blue;
2530         fy = (factor * fy) + ((1.0 - factor) * by);
2531         fcb = (factor * fcb) + ((1.0 - factor) * bcb);
2532         fcr = (factor * fcr) + ((1.0 - factor) * bcr);
2533         r = fy + 1.402 * fcr;
2534         g = fy + 0.34414 * fcb - 0.71414 * fcr;
2535         b = fy + 1.722 * fcb;
2536         _vte_debug_print(VTE_DEBUG_MISC,
2537                         "Calculated bold (%d, %d, %d) = (%lf,%lf,%lf)",
2538                         foreground->red, foreground->green, foreground->blue,
2539                         r, g, b);
2540         bold->pixel = 0;
2541         bold->red = CLAMP(r, 0, 0xffff);
2542         bold->green = CLAMP(g, 0, 0xffff);
2543         bold->blue = CLAMP(b, 0, 0xffff);
2544         _vte_debug_print(VTE_DEBUG_MISC,
2545                         "= (%04x,%04x,%04x).\n",
2546                         bold->red, bold->green, bold->blue);
2547 }
2548
2549 /**
2550  * vte_terminal_set_color_bold:
2551  * @terminal: a #VteTerminal
2552  * @bold: the new bold color
2553  *
2554  * Sets the color used to draw bold text in the default foreground color.
2555  */
2556 void
2557 vte_terminal_set_color_bold(VteTerminal *terminal, const GdkColor *bold)
2558 {
2559         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2560         g_return_if_fail(bold != NULL);
2561
2562         _vte_debug_print(VTE_DEBUG_MISC,
2563                         "Set bold color to (%04x,%04x,%04x).\n",
2564                         bold->red, bold->green, bold->blue);
2565         vte_terminal_set_color_internal(terminal, VTE_BOLD_FG, bold);
2566 }
2567
2568 /**
2569  * vte_terminal_set_color_dim:
2570  * @terminal: a #VteTerminal
2571  * @dim: the new dim color
2572  *
2573  * Sets the color used to draw dim text in the default foreground color.
2574  */
2575 void
2576 vte_terminal_set_color_dim(VteTerminal *terminal, const GdkColor *dim)
2577 {
2578         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2579         g_return_if_fail(dim != NULL);
2580
2581         _vte_debug_print(VTE_DEBUG_MISC,
2582                         "Set dim color to (%04x,%04x,%04x).\n",
2583                         dim->red, dim->green, dim->blue);
2584         vte_terminal_set_color_internal(terminal, VTE_DIM_FG, dim);
2585 }
2586
2587 /**
2588  * vte_terminal_set_color_foreground:
2589  * @terminal: a #VteTerminal
2590  * @foreground: the new foreground color
2591  *
2592  * Sets the foreground color used to draw normal text
2593  */
2594 void
2595 vte_terminal_set_color_foreground(VteTerminal *terminal,
2596                                   const GdkColor *foreground)
2597 {
2598         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2599         g_return_if_fail(foreground != NULL);
2600
2601         _vte_debug_print(VTE_DEBUG_MISC,
2602                         "Set foreground color to (%04x,%04x,%04x).\n",
2603                         foreground->red, foreground->green, foreground->blue);
2604         vte_terminal_set_color_internal(terminal, VTE_DEF_FG, foreground);
2605 }
2606
2607 /**
2608  * vte_terminal_set_color_background:
2609  * @terminal: a #VteTerminal
2610  * @background: the new background color
2611  *
2612  * Sets the background color for text which does not have a specific background
2613  * color assigned.  Only has effect when no background image is set and when
2614  * the terminal is not transparent.
2615  */
2616 void
2617 vte_terminal_set_color_background(VteTerminal *terminal,
2618                                   const GdkColor *background)
2619 {
2620         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2621         g_return_if_fail(background != NULL);
2622
2623         _vte_debug_print(VTE_DEBUG_MISC,
2624                         "Set background color to (%04x,%04x,%04x).\n",
2625                         background->red, background->green, background->blue);
2626         vte_terminal_set_color_internal(terminal, VTE_DEF_BG, background);
2627 }
2628
2629 /**
2630  * vte_terminal_set_color_cursor:
2631  * @terminal: a #VteTerminal
2632  * @cursor_background: (allow-none): the new color to use for the text cursor, or %NULL
2633  *
2634  * Sets the background color for text which is under the cursor.  If %NULL, text
2635  * under the cursor will be drawn with foreground and background colors
2636  * reversed.
2637  *
2638  * Since: 0.11.11
2639  */
2640 void
2641 vte_terminal_set_color_cursor(VteTerminal *terminal,
2642                               const GdkColor *cursor_background)
2643 {
2644         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2645
2646         if (cursor_background != NULL) {
2647                 _vte_debug_print(VTE_DEBUG_MISC,
2648                                 "Set cursor color to (%04x,%04x,%04x).\n",
2649                                 cursor_background->red,
2650                                 cursor_background->green,
2651                                 cursor_background->blue);
2652                 vte_terminal_set_color_internal(terminal, VTE_CUR_BG,
2653                                                 cursor_background);
2654                 terminal->pvt->cursor_color_set = TRUE;
2655         } else {
2656                 _vte_debug_print(VTE_DEBUG_MISC,
2657                                 "Cleared cursor color.\n");
2658                 terminal->pvt->cursor_color_set = FALSE;
2659         }
2660 }
2661
2662 /**
2663  * vte_terminal_set_color_highlight:
2664  * @terminal: a #VteTerminal
2665  * @highlight_background: (allow-none): the new color to use for highlighted text, or %NULL
2666  *
2667  * Sets the background color for text which is highlighted.  If %NULL,
2668  * highlighted text (which is usually highlighted because it is selected) will
2669  * be drawn with foreground and background colors reversed.
2670  *
2671  * Since: 0.11.11
2672  */
2673 void
2674 vte_terminal_set_color_highlight(VteTerminal *terminal,
2675                                  const GdkColor *highlight_background)
2676 {
2677         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2678
2679         if (highlight_background != NULL) {
2680                 _vte_debug_print(VTE_DEBUG_MISC,
2681                                 "Set highlight color to (%04x,%04x,%04x).\n",
2682                                 highlight_background->red,
2683                                 highlight_background->green,
2684                                 highlight_background->blue);
2685                 vte_terminal_set_color_internal(terminal, VTE_DEF_HL,
2686                                                 highlight_background);
2687                 terminal->pvt->highlight_color_set = TRUE;
2688         } else {
2689                 _vte_debug_print(VTE_DEBUG_MISC,
2690                                 "Cleared highlight color.\n");
2691                 terminal->pvt->highlight_color_set = FALSE;
2692         }
2693 }
2694
2695 /**
2696  * vte_terminal_set_colors:
2697  * @terminal: a #VteTerminal
2698  * @foreground: (allow-none): the new foreground color, or %NULL
2699  * @background: (allow-none): the new background color, or %NULL
2700  * @palette: (array length=palette_size zero-terminated=0) (element-type Gdk.Color): the color palette
2701  * @palette_size: the number of entries in @palette
2702  *
2703  * The terminal widget uses a 28-color model comprised of the default foreground
2704  * and background colors, the bold foreground color, the dim foreground
2705  * color, an eight color palette, bold versions of the eight color palette,
2706  * and a dim version of the the eight color palette.
2707  *
2708  * @palette_size must be either 0, 8, 16, or 24, or between 25 and 255 inclusive.
2709  * If @foreground is %NULL and
2710  * @palette_size is greater than 0, the new foreground color is taken from
2711  * @palette[7].  If @background is %NULL and @palette_size is greater than 0,
2712  * the new background color is taken from @palette[0].  If
2713  * @palette_size is 8 or 16, the third (dim) and possibly the second (bold)
2714  * 8-color palettes are extrapolated from the new background color and the items
2715  * in @palette.
2716  */
2717 void
2718 vte_terminal_set_colors(VteTerminal *terminal,
2719                         const GdkColor *foreground,
2720                         const GdkColor *background,
2721                         const GdkColor *palette,
2722                         glong palette_size)
2723 {
2724         guint i;
2725         GdkColor color;
2726
2727         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2728
2729         g_return_if_fail(palette_size >= 0);
2730         g_return_if_fail((palette_size == 0) ||
2731                          (palette_size == 8) ||
2732                          (palette_size == 16) ||
2733                          (palette_size == 24) ||
2734                          (palette_size > 24 && palette_size < 256));
2735
2736         _vte_debug_print(VTE_DEBUG_MISC,
2737                         "Set color palette [%ld elements].\n",
2738                         palette_size);
2739
2740         /* Accept NULL as the default foreground and background colors if we
2741          * got a palette. */
2742         if ((foreground == NULL) && (palette_size >= 8)) {
2743                 foreground = &palette[7];
2744         }
2745         if ((background == NULL) && (palette_size >= 8)) {
2746                 background = &palette[0];
2747         }
2748
2749         memset(&color, 0, sizeof(color));
2750
2751         /* Initialize each item in the palette if we got any entries to work
2752          * with. */
2753         for (i=0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
2754                 if (i < 16) {
2755                         color.blue = (i & 4) ? 0xc000 : 0;
2756                         color.green = (i & 2) ? 0xc000 : 0;
2757                         color.red = (i & 1) ? 0xc000 : 0;
2758                         if (i > 7) {
2759                                 color.blue += 0x3fff;
2760                                 color.green += 0x3fff;
2761                                 color.red += 0x3fff;
2762                         }
2763                 }
2764                 else if (i < 232) {
2765                         int j = i - 16;
2766                         int r = j / 36, g = (j / 6) % 6, b = j % 6;
2767                         int red =   (r == 0) ? 0 : r * 40 + 55;
2768                         int green = (g == 0) ? 0 : g * 40 + 55;
2769                         int blue =  (b == 0) ? 0 : b * 40 + 55;
2770                         color.red   = red | red << 8  ;
2771                         color.green = green | green << 8;
2772                         color.blue  = blue | blue << 8;
2773                 } else if (i < 256) {
2774                         int shade = 8 + (i - 232) * 10;
2775                         color.red = color.green = color.blue = shade | shade << 8;
2776                 }
2777                 else switch (i) {
2778                         case VTE_DEF_BG:
2779                                 if (background != NULL) {
2780                                         color = *background;
2781                                 } else {
2782                                         color.red = 0;
2783                                         color.blue = 0;
2784                                         color.green = 0;
2785                                 }
2786                                 break;
2787                         case VTE_DEF_FG:
2788                                 if (foreground != NULL) {
2789                                         color = *foreground;
2790                                 } else {
2791                                         color.red = 0xc000;
2792                                         color.blue = 0xc000;
2793                                         color.green = 0xc000;
2794                                 }
2795                                 break;
2796                         case VTE_BOLD_FG:
2797                                 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2798                                                            &terminal->pvt->palette[VTE_DEF_BG],
2799                                                            1.8,
2800                                                            &color);
2801                                 break;
2802                         case VTE_DIM_FG:
2803                                 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2804                                                            &terminal->pvt->palette[VTE_DEF_BG],
2805                                                            0.5,
2806                                                            &color);
2807                                 break;
2808                         case VTE_DEF_HL:
2809                                 color.red = 0xc000;
2810                                 color.blue = 0xc000;
2811                                 color.green = 0xc000;
2812                                 break;
2813                         case VTE_CUR_BG:
2814                                 color.red = 0x0000;
2815                                 color.blue = 0x0000;
2816                                 color.green = 0x0000;
2817                                 break;
2818                         }
2819
2820                 /* Override from the supplied palette if there is one. */
2821                 if ((glong) i < palette_size) {
2822                         color = palette[i];
2823                 }
2824
2825                 /* Set up the color entry. */
2826                 vte_terminal_set_color_internal(terminal, i, &color);
2827         }
2828
2829         /* Track that we had a color palette set. */
2830         terminal->pvt->palette_initialized = TRUE;
2831 }
2832
2833 /**
2834  * vte_terminal_set_opacity:
2835  * @terminal: a #VteTerminal
2836  * @opacity: the new opacity
2837  *
2838  * Sets the opacity of the terminal background, were 0 means completely
2839  * transparent and 65535 means completely opaque.
2840  */
2841 void
2842 vte_terminal_set_opacity(VteTerminal *terminal, guint16 opacity)
2843 {
2844         VteTerminalPrivate *pvt;
2845
2846         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2847
2848         pvt = terminal->pvt;
2849
2850         if (opacity == pvt->bg_opacity)
2851                 return;
2852
2853         pvt->bg_opacity = opacity;
2854
2855         g_object_notify(G_OBJECT(terminal), "background-opacity");
2856 }
2857
2858 /**
2859  * vte_terminal_set_default_colors:
2860  * @terminal: a #VteTerminal
2861  *
2862  * Reset the terminal palette to reasonable compiled-in default color.
2863  */
2864 void
2865 vte_terminal_set_default_colors(VteTerminal *terminal)
2866 {
2867         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2868         vte_terminal_set_colors(terminal, NULL, NULL, NULL, 0);
2869 }
2870
2871
2872 /* Cleanup smart-tabs.  See vte_sequence_handler_ta() */
2873 void
2874 _vte_terminal_cleanup_tab_fragments_at_cursor (VteTerminal *terminal)
2875 {
2876         VteRowData *row = _vte_terminal_ensure_row (terminal);
2877         VteScreen *screen = terminal->pvt->screen;
2878         long col = screen->cursor_current.col;
2879         const VteCell *pcell = _vte_row_data_get (row, col);
2880
2881         if (G_UNLIKELY (pcell != NULL && pcell->c == '\t')) {
2882                 long i, num_columns;
2883                 VteCell *cell = _vte_row_data_get_writable (row, col);
2884                 
2885                 _vte_debug_print(VTE_DEBUG_MISC,
2886                                  "Cleaning tab fragments at %ld",
2887                                  col);
2888
2889                 /* go back to the beginning of the tab */
2890                 while (cell->attr.fragment && col > 0)
2891                         cell = _vte_row_data_get_writable (row, --col);
2892
2893                 num_columns = cell->attr.columns;
2894                 for (i = 0; i < num_columns; i++) {
2895                         cell = _vte_row_data_get_writable (row, col++);
2896                         if (G_UNLIKELY (!cell))
2897                           break;
2898                         *cell = screen->fill_defaults;
2899                 }
2900         }
2901 }
2902
2903 /* Cursor down, with scrolling. */
2904 void
2905 _vte_terminal_cursor_down (VteTerminal *terminal)
2906 {
2907         long start, end;
2908         VteScreen *screen;
2909
2910         screen = terminal->pvt->screen;
2911
2912         if (screen->scrolling_restricted) {
2913                 start = screen->insert_delta + screen->scrolling_region.start;
2914                 end = screen->insert_delta + screen->scrolling_region.end;
2915         } else {
2916                 start = screen->insert_delta;
2917                 end = start + terminal->row_count - 1;
2918         }
2919         if (screen->cursor_current.row == end) {
2920                 /* Match xterm and fill to the end of row when scrolling. */
2921                 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2922                         VteRowData *rowdata;
2923                         rowdata = _vte_terminal_ensure_row (terminal);
2924                         _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2925                 }
2926
2927                 if (screen->scrolling_restricted) {
2928                         if (start == screen->insert_delta) {
2929                                 /* Scroll this line into the scrollback
2930                                  * buffer by inserting a line at the next
2931                                  * line and scrolling the area up. */
2932                                 screen->insert_delta++;
2933                                 screen->scroll_delta++;
2934                                 screen->cursor_current.row++;
2935                                 /* update start and end, as they are relative
2936                                  * to insert_delta. */
2937                                 start++;
2938                                 end++;
2939                                 _vte_terminal_ring_insert (terminal, screen->cursor_current.row, FALSE);
2940                                 /* Force the areas below the region to be
2941                                  * redrawn -- they've moved. */
2942                                 _vte_terminal_scroll_region(terminal, start,
2943                                                             end - start + 1, 1);
2944                                 /* Force scroll. */
2945                                 _vte_terminal_adjust_adjustments(terminal);
2946                         } else {
2947                                 /* If we're at the bottom of the scrolling
2948                                  * region, add a line at the top to scroll the
2949                                  * bottom off. */
2950                                 _vte_terminal_ring_remove (terminal, start);
2951                                 _vte_terminal_ring_insert (terminal, end, TRUE);
2952                                 /* Update the display. */
2953                                 _vte_terminal_scroll_region(terminal, start,
2954                                                            end - start + 1, -1);
2955                                 _vte_invalidate_cells(terminal,
2956                                                       0, terminal->column_count,
2957                                                       end - 2, 2);
2958                         }
2959                 } else {
2960                         /* Scroll up with history. */
2961                         screen->cursor_current.row++;
2962                         _vte_terminal_update_insert_delta(terminal);
2963                 }
2964
2965                 /* Match xterm and fill the new row when scrolling. */
2966                 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2967                         VteRowData *rowdata;
2968                         rowdata = _vte_terminal_ensure_row (terminal);
2969                         _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2970                 }
2971         } else {
2972                 /* Otherwise, just move the cursor down. */
2973                 screen->cursor_current.row++;
2974         }
2975 }
2976
2977 /* Insert a single character into the stored data array. */
2978 gboolean
2979 _vte_terminal_insert_char(VteTerminal *terminal, gunichar c,
2980                          gboolean insert, gboolean invalidate_now)
2981 {
2982         VteCellAttr attr;
2983         VteRowData *row;
2984         long col;
2985         int columns, i;
2986         VteScreen *screen;
2987         gboolean line_wrapped = FALSE; /* cursor moved before char inserted */
2988
2989         screen = terminal->pvt->screen;
2990         insert |= screen->insert_mode;
2991         invalidate_now |= insert;
2992
2993         /* If we've enabled the special drawing set, map the characters to
2994          * Unicode. */
2995         if (G_UNLIKELY (screen->alternate_charset)) {
2996                 _vte_debug_print(VTE_DEBUG_SUBSTITUTION,
2997                                 "Attempting charset substitution"
2998                                 "for U+%04X.\n", c);
2999                 /* See if there's a mapping for it. */
3000                 c = _vte_iso2022_process_single(terminal->pvt->iso2022, c, '0');
3001         }
3002
3003         /* If this character is destined for the status line, save it. */
3004         if (G_UNLIKELY (screen->status_line)) {
3005                 g_string_append_unichar(screen->status_line_contents, c);
3006                 screen->status_line_changed = TRUE;
3007                 return FALSE;
3008         }
3009
3010         /* Figure out how many columns this character should occupy. */
3011         if (G_UNLIKELY (VTE_ISO2022_HAS_ENCODED_WIDTH(c))) {
3012                 columns = _vte_iso2022_get_encoded_width(c);
3013                 c &= ~VTE_ISO2022_ENCODED_WIDTH_MASK;
3014         } else {
3015                 columns = _vte_iso2022_unichar_width(terminal->pvt->iso2022, c);
3016         }
3017
3018
3019         /* If we're autowrapping here, do it. */
3020         col = screen->cursor_current.col;
3021         if (G_UNLIKELY (columns && col + columns > terminal->column_count)) {
3022                 if (terminal->pvt->flags.am) {
3023                         _vte_debug_print(VTE_DEBUG_ADJ,
3024                                         "Autowrapping before character\n");
3025                         /* Wrap. */
3026                         /* XXX clear to the end of line */
3027                         col = screen->cursor_current.col = 0;
3028                         /* Mark this line as soft-wrapped. */
3029                         row = _vte_terminal_ensure_row (terminal);
3030                         row->attr.soft_wrapped = 1;
3031                         _vte_terminal_cursor_down (terminal);
3032                 } else {
3033                         /* Don't wrap, stay at the rightmost column. */
3034                         col = screen->cursor_current.col =
3035                                 terminal->column_count - columns;
3036                 }
3037                 line_wrapped = TRUE;
3038         }
3039
3040         _vte_debug_print(VTE_DEBUG_PARSE,
3041                         "Inserting %ld '%c' (%d/%d) (%ld+%d, %ld), delta = %ld; ",
3042                         (long)c, c < 256 ? c : ' ',
3043                         screen->defaults.attr.fore,
3044                         screen->defaults.attr.back,
3045                         col, columns, (long)screen->cursor_current.row,
3046                         (long)screen->insert_delta);
3047
3048
3049         if (G_UNLIKELY (columns == 0)) {
3050
3051                 /* It's a combining mark */
3052
3053                 long row_num;
3054                 VteCell *cell;
3055
3056                 _vte_debug_print(VTE_DEBUG_PARSE, "combining U+%04X", c);
3057
3058                 row_num = screen->cursor_current.row;
3059                 row = NULL;
3060                 if (G_UNLIKELY (col == 0)) {
3061                         /* We are at first column.  See if the previous line softwrapped.
3062                          * If it did, move there.  Otherwise skip inserting. */
3063
3064                         if (G_LIKELY (row_num > 0)) {
3065                                 row_num--;
3066                                 row = _vte_terminal_find_row_data_writable (terminal, row_num);
3067
3068                                 if (row) {
3069                                         if (!row->attr.soft_wrapped)
3070                                                 row = NULL;
3071                                         else
3072                                                 col = _vte_row_data_length (row);
3073                                 }
3074                         }
3075                 } else {
3076                         row = _vte_terminal_find_row_data_writable (terminal, row_num);
3077                 }
3078
3079                 if (G_UNLIKELY (!row || !col))
3080                         goto not_inserted;
3081
3082                 /* Combine it on the previous cell */
3083
3084                 col--;
3085                 cell = _vte_row_data_get_writable (row, col);
3086
3087                 if (G_UNLIKELY (!cell))
3088                         goto not_inserted;
3089
3090                 /* Find the previous cell */
3091                 while (cell && cell->attr.fragment && col > 0)
3092                         cell = _vte_row_data_get_writable (row, --col);
3093                 if (G_UNLIKELY (!cell || cell->c == '\t'))
3094                         goto not_inserted;
3095
3096                 /* Combine the new character on top of the cell string */
3097                 c = _vte_unistr_append_unichar (cell->c, c);
3098
3099                 /* And set it */
3100                 columns = cell->attr.columns;
3101                 for (i = 0; i < columns; i++) {
3102                         cell = _vte_row_data_get_writable (row, col++);
3103                         cell->c = c;
3104                 }
3105
3106                 /* Always invalidate since we put the mark on the *previous* cell
3107                  * and the higher level code doesn't know this. */
3108                 _vte_invalidate_cells(terminal,
3109                                       col - columns,
3110                                       columns,
3111                                       row_num, 1);
3112
3113                 goto done;
3114         }
3115
3116         /* Make sure we have enough rows to hold this data. */
3117         row = vte_terminal_ensure_cursor (terminal);
3118         g_assert(row != NULL);
3119
3120         _vte_terminal_cleanup_tab_fragments_at_cursor (terminal);
3121
3122         if (insert) {
3123                 for (i = 0; i < columns; i++)
3124                         _vte_row_data_insert (row, col + i, &screen->color_defaults);
3125         } else {
3126                 _vte_row_data_fill (row, &basic_cell.cell, col + columns);
3127         }
3128
3129         /* Convert any wide characters we may have broken into single
3130          * cells. (#514632) */
3131         if (G_LIKELY (col > 0)) {
3132                 glong col2 = col - 1;
3133                 VteCell *cell = _vte_row_data_get_writable (row, col2);
3134                 while (col2 > 0 && cell != NULL && cell->attr.fragment)
3135                         cell = _vte_row_data_get_writable (row, --col2);
3136                 cell->attr.columns = col - col2;
3137         }
3138         {
3139                 glong col2 = col + columns;
3140                 VteCell *cell = _vte_row_data_get_writable (row, col2);
3141                 while (cell != NULL && cell->attr.fragment) {
3142                         cell->attr.columns = 1;
3143                         cell->c = 0;
3144                         cell = _vte_row_data_get_writable (row, ++col2);
3145                 }
3146         }
3147
3148         attr = screen->defaults.attr;
3149         attr.columns = columns;
3150
3151         if (G_UNLIKELY (c == '_' && terminal->pvt->flags.ul)) {
3152                 const VteCell *pcell = _vte_row_data_get (row, col);
3153                 /* Handle overstrike-style underlining. */
3154                 if (pcell->c != 0) {
3155                         /* restore previous contents */
3156                         c = pcell->c;
3157                         attr.columns = pcell->attr.columns;
3158                         attr.fragment = pcell->attr.fragment;
3159
3160                         attr.underline = 1;
3161                 }
3162         }
3163
3164
3165         {
3166                 VteCell *pcell = _vte_row_data_get_writable (row, col);
3167                 pcell->c = c;
3168                 pcell->attr = attr;
3169                 col++;
3170         }
3171
3172         /* insert wide-char fragments */
3173         attr.fragment = 1;
3174         for (i = 1; i < columns; i++) {
3175                 VteCell *pcell = _vte_row_data_get_writable (row, col);
3176                 pcell->c = c;
3177                 pcell->attr = attr;
3178                 col++;
3179         }
3180         _vte_row_data_shrink (row, terminal->column_count);
3181
3182         /* Signal that this part of the window needs drawing. */
3183         if (G_UNLIKELY (invalidate_now)) {
3184                 _vte_invalidate_cells(terminal,
3185                                 col - columns,
3186                                 insert ? terminal->column_count : columns,
3187                                 screen->cursor_current.row, 1);
3188         }
3189
3190
3191         /* If we're autowrapping *here*, do it. */
3192         screen->cursor_current.col = col;
3193         if (G_UNLIKELY (col >= terminal->column_count)) {
3194                 if (terminal->pvt->flags.am && !terminal->pvt->flags.xn) {
3195                         /* Wrap. */
3196                         screen->cursor_current.col = 0;
3197                         /* Mark this line as soft-wrapped. */
3198                         row->attr.soft_wrapped = 1;
3199                         _vte_terminal_cursor_down (terminal);
3200                 }
3201         }
3202
3203 done:
3204         /* We added text, so make a note of it. */
3205         terminal->pvt->text_inserted_flag = TRUE;
3206
3207 not_inserted:
3208         _vte_debug_print(VTE_DEBUG_ADJ|VTE_DEBUG_PARSE,
3209                         "insertion delta => %ld.\n",
3210                         (long)screen->insert_delta);
3211         return line_wrapped;
3212 }
3213
3214 /* Catch a VteReaper child-exited signal, and if it matches the one we're
3215  * looking for, emit one of our own. */
3216 static void
3217 vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status,
3218                                 VteTerminal *terminal)
3219 {
3220         if (pid == terminal->pvt->pty_pid) {
3221                 GObject *object = G_OBJECT(terminal);
3222
3223                 g_object_ref(object);
3224                 g_object_freeze_notify(object);
3225
3226                 _VTE_DEBUG_IF (VTE_DEBUG_LIFECYCLE) {
3227                         g_printerr ("Child[%d] exited with status %d\n",
3228                                         pid, status);
3229 #ifdef HAVE_SYS_WAIT_H
3230                         if (WIFEXITED (status)) {
3231                                 g_printerr ("Child[%d] exit code %d.\n",
3232                                                 pid, WEXITSTATUS (status));
3233                         }else if (WIFSIGNALED (status)) {
3234                                 g_printerr ("Child[%d] dies with signal %d.\n",
3235                                                 pid, WTERMSIG (status));
3236                         }
3237 #endif
3238                 }
3239                 /* Disconnect from the reaper. */
3240                 if (terminal->pvt->pty_reaper != NULL) {
3241                         g_signal_handlers_disconnect_by_func(terminal->pvt->pty_reaper,
3242                                                              vte_terminal_catch_child_exited,
3243                                                              terminal);
3244                         g_object_unref(terminal->pvt->pty_reaper);
3245                         terminal->pvt->pty_reaper = NULL;
3246                 }
3247                 terminal->pvt->pty_pid = -1;
3248
3249                 /* Close out the PTY. */
3250                 vte_terminal_set_pty_object(terminal, NULL);
3251
3252                 /* Tell observers what's happened. */
3253                 terminal->pvt->child_exit_status = status;
3254                 vte_terminal_emit_child_exited(terminal);
3255
3256                 g_object_thaw_notify(object);
3257                 g_object_unref(object);
3258
3259                 /* Note: terminal may be destroyed at this point */
3260         }
3261 }
3262
3263 static void mark_input_source_invalid(VteTerminal *terminal)
3264 {
3265         _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_read\n");
3266         terminal->pvt->pty_input_source = 0;
3267 }
3268 static void
3269 _vte_terminal_connect_pty_read(VteTerminal *terminal)
3270 {
3271         if (terminal->pvt->pty_channel == NULL) {
3272                 return;
3273         }
3274
3275         if (terminal->pvt->pty_input_source == 0) {
3276                 _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_read\n");
3277                 terminal->pvt->pty_input_source =
3278                         g_io_add_watch_full(terminal->pvt->pty_channel,
3279                                             VTE_CHILD_INPUT_PRIORITY,
3280                                             G_IO_IN | G_IO_HUP,
3281                                             (GIOFunc) vte_terminal_io_read,
3282                                             terminal,
3283                                             (GDestroyNotify) mark_input_source_invalid);
3284         }
3285 }
3286
3287 static void mark_output_source_invalid(VteTerminal *terminal)
3288 {
3289         _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_write\n");
3290         terminal->pvt->pty_output_source = 0;
3291 }
3292 static void
3293 _vte_terminal_connect_pty_write(VteTerminal *terminal)
3294 {
3295         VteTerminalPrivate *pvt = terminal->pvt;
3296
3297         g_assert(pvt->pty != NULL);
3298         if (terminal->pvt->pty_channel == NULL) {
3299                 pvt->pty_channel =
3300                         g_io_channel_unix_new(vte_pty_get_fd(pvt->pty));
3301         }
3302
3303         if (terminal->pvt->pty_output_source == 0) {
3304                 if (vte_terminal_io_write (terminal->pvt->pty_channel,
3305                                              G_IO_OUT,
3306                                              terminal))
3307                 {
3308                         _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_write\n");
3309                         terminal->pvt->pty_output_source =
3310                                 g_io_add_watch_full(terminal->pvt->pty_channel,
3311                                                     VTE_CHILD_OUTPUT_PRIORITY,
3312                                                     G_IO_OUT,
3313                                                     (GIOFunc) vte_terminal_io_write,
3314                                                     terminal,
3315                                                     (GDestroyNotify) mark_output_source_invalid);
3316                 }
3317         }
3318 }
3319
3320 static void
3321 _vte_terminal_disconnect_pty_read(VteTerminal *terminal)
3322 {
3323         if (terminal->pvt->pty_input_source != 0) {
3324                 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_read\n");
3325                 g_source_remove(terminal->pvt->pty_input_source);
3326                 terminal->pvt->pty_input_source = 0;
3327         }
3328 }
3329
3330 static void
3331 _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
3332 {
3333         if (terminal->pvt->pty_output_source != 0) {
3334                 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_write\n");
3335
3336                 g_source_remove(terminal->pvt->pty_output_source);
3337                 terminal->pvt->pty_output_source = 0;
3338         }
3339 }
3340
3341 /**
3342  * vte_terminal_pty_new:
3343  * @terminal: a #VteTerminal
3344  * @flags: flags from #VtePtyFlags
3345  * @error: (allow-none): return location for a #GError, or %NULL
3346  *
3347  * Creates a new #VtePty, and sets the emulation property
3348  * from #VteTerminal:emulation.
3349  *
3350  * See vte_pty_new() for more information.
3351  *
3352  * Returns: (transfer full): a new #VtePty
3353  * Since: 0.26
3354  */
3355 VtePty *
3356 vte_terminal_pty_new(VteTerminal *terminal,
3357                      VtePtyFlags flags,
3358                      GError **error)
3359 {
3360         VteTerminalPrivate *pvt;
3361         VtePty *pty;
3362
3363         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
3364
3365         pvt = terminal->pvt;
3366
3367         pty = vte_pty_new(flags, error);
3368         if (pty == NULL)
3369                 return NULL;
3370
3371         vte_pty_set_term(pty, vte_terminal_get_emulation(terminal));
3372
3373         return pty;
3374 }
3375
3376 /**
3377  * vte_terminal_watch_child:
3378  * @terminal: a #VteTerminal
3379  * @child_pid: a #GPid
3380  *
3381  * Watches @child_pid. When the process exists, the #VteReaper::child-exited
3382  * signal will be called. Use vte_terminal_get_child_exit_status() to
3383  * retrieve the child's exit status.
3384  *
3385  * Prior to calling this function, a #VtePty must have been set in @terminal
3386  * using vte_terminal_set_pty_object().
3387  * When the child exits, the terminal's #VtePty will be set to %NULL.
3388  *
3389  * Note: g_child_watch_add() or g_child_watch_add_full() must not have
3390  * been called for @child_pid, nor a #GSource for it been created with
3391  * g_child_watch_source_new().
3392  *
3393  * Note: when using the g_spawn_async() family of functions,
3394  * the %G_SPAWN_DO_NOT_REAP_CHILD flag MUST have been passed.
3395  *
3396  * Since: 0.26
3397  */
3398 void
3399 vte_terminal_watch_child (VteTerminal *terminal,
3400                           GPid child_pid)
3401 {
3402         VteTerminalPrivate *pvt;
3403         GObject *object;
3404         VteReaper *reaper;
3405
3406         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3407         g_return_if_fail(child_pid != -1);
3408
3409         pvt = terminal->pvt;
3410         g_return_if_fail(pvt->pty != NULL);
3411
3412         // FIXMEchpe: support passing child_pid = -1 to remove the wathch
3413
3414         object = G_OBJECT(terminal);
3415
3416         g_object_freeze_notify(object);
3417
3418         /* Set this as the child's pid. */
3419         pvt->pty_pid = child_pid;
3420         pvt->child_exit_status = 0;
3421
3422         /* Catch a child-exited signal from the child pid. */
3423         reaper = vte_reaper_get();
3424         vte_reaper_add_child(child_pid);
3425         if (reaper != pvt->pty_reaper) {
3426                 if (terminal->pvt->pty_reaper != NULL) {
3427                         g_signal_handlers_disconnect_by_func(pvt->pty_reaper,
3428                                         vte_terminal_catch_child_exited,
3429                                         terminal);
3430                         g_object_unref(pvt->pty_reaper);
3431                 }
3432                 g_signal_connect(reaper, "child-exited",
3433                                 G_CALLBACK(vte_terminal_catch_child_exited),
3434                                 terminal);
3435                 pvt->pty_reaper = reaper;
3436         } else {
3437                 g_object_unref(reaper);
3438         }
3439
3440         /* FIXMEchpe: call vte_terminal_set_size here? */
3441
3442         g_object_thaw_notify(object);
3443 }
3444
3445 /*
3446  * _vte_terminal_get_user_shell:
3447  *
3448  * Uses getpwd() to determine the user's shell. If that fails, falls back
3449  * to using the SHELL environment variable. As last-ditch fallback, returns
3450  * "/bin/sh".
3451  *
3452  * Returns: a newly allocated string containing the command to run the
3453  *   user's shell
3454  */
3455 static char *
3456 _vte_terminal_get_user_shell (void)
3457 {
3458         struct passwd *pwd;
3459         char *command;
3460
3461         pwd = getpwuid(getuid());
3462         if (pwd != NULL) {
3463                 command = g_strdup (pwd->pw_shell);
3464                 _vte_debug_print(VTE_DEBUG_MISC,
3465                                 "Using user's shell (%s).\n",
3466                                 command ? command : "(null)");
3467         }
3468         if (command == NULL) {
3469                 if (g_getenv ("SHELL")) {
3470                         command = g_strdup (g_getenv ("SHELL"));
3471                         _vte_debug_print(VTE_DEBUG_MISC,
3472                                          "Using $SHELL shell (%s).\n",
3473                                          command);
3474                 } else {
3475                         command = g_strdup ("/bin/sh");
3476