Use get accessors for adjustments
[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                 v = _vte_ring_delta (screen->row_data);
1997                 current = gtk_adjustment_get_lower(terminal->adjustment);
1998                 if (current != v) {
1999                         _vte_debug_print(VTE_DEBUG_ADJ,
2000                                         "Changing lower bound from %.0f to %ld\n",
2001                                          current, v);
2002                         terminal->adjustment->lower = v;
2003                         changed = TRUE;
2004                 }
2005
2006                 /* The upper value is the number of rows which might be visible.  (Add
2007                  * one to the cursor offset because it's zero-based.) */
2008                 v = MAX(_vte_ring_next(screen->row_data),
2009                                 screen->cursor_current.row + 1);
2010                 current = gtk_adjustment_get_upper(terminal->adjustment);
2011                 if (current != v) {
2012                         _vte_debug_print(VTE_DEBUG_ADJ,
2013                                         "Changing upper bound from %.0f to %ld\n",
2014                                          current, v);
2015                         terminal->adjustment->upper = v;
2016                         changed = TRUE;
2017                 }
2018
2019                 if (changed) {
2020                         _vte_debug_print(VTE_DEBUG_SIGNALS,
2021                                         "Emitting adjustment_changed.\n");
2022                         gtk_adjustment_changed(terminal->adjustment);
2023                 }
2024                 terminal->pvt->adjustment_changed_pending = FALSE;
2025         }
2026         if (terminal->pvt->adjustment_value_changed_pending) {
2027                 glong v;
2028                 _vte_debug_print(VTE_DEBUG_SIGNALS,
2029                                 "Emitting adjustment_value_changed.\n");
2030                 terminal->pvt->adjustment_value_changed_pending = FALSE;
2031                 v = round (gtk_adjustment_get_value(terminal->adjustment));
2032                 if (v != terminal->pvt->screen->scroll_delta) {
2033                         /* this little dance is so that the scroll_delta is
2034                          * updated immediately, but we still handled scrolling
2035                          * via the adjustment - e.g. user interaction with the
2036                          * scrollbar
2037                          */
2038                         terminal->adjustment->value = terminal->pvt->screen->scroll_delta;
2039                         terminal->pvt->screen->scroll_delta = v;
2040                         gtk_adjustment_value_changed(terminal->adjustment);
2041                 }
2042         }
2043 }
2044
2045 /* Queue an adjustment-changed signal to be delivered when convenient. */
2046 static inline void
2047 vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
2048 {
2049         terminal->pvt->adjustment_changed_pending = TRUE;
2050         add_update_timeout (terminal);
2051 }
2052
2053 static void
2054 vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
2055 {
2056         if (v != terminal->pvt->screen->scroll_delta) {
2057                 terminal->pvt->screen->scroll_delta = v;
2058                 terminal->pvt->adjustment_value_changed_pending = TRUE;
2059                 add_update_timeout (terminal);
2060         }
2061 }
2062
2063 static void
2064 vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal *terminal, glong v)
2065 {
2066         gdouble lower, upper;
2067
2068         lower = gtk_adjustment_get_lower(terminal->adjustment);
2069         upper = gtk_adjustment_get_upper(terminal->adjustment);
2070
2071         v = CLAMP(v, lower, MAX (lower, upper - terminal->row_count));
2072
2073         vte_terminal_queue_adjustment_value_changed (terminal, v);
2074 }
2075
2076
2077 void
2078 _vte_terminal_adjust_adjustments(VteTerminal *terminal)
2079 {
2080         VteScreen *screen;
2081         long delta;
2082
2083         g_assert(terminal->pvt->screen != NULL);
2084         g_assert(terminal->pvt->screen->row_data != NULL);
2085
2086         vte_terminal_queue_adjustment_changed(terminal);
2087
2088         /* The lower value should be the first row in the buffer. */
2089         screen = terminal->pvt->screen;
2090         delta = _vte_ring_delta(screen->row_data);
2091         /* Snap the insert delta and the cursor position to be in the visible
2092          * area.  Leave the scrolling delta alone because it will be updated
2093          * when the adjustment changes. */
2094         screen->insert_delta = MAX(screen->insert_delta, delta);
2095         screen->cursor_current.row = MAX(screen->cursor_current.row,
2096                                          screen->insert_delta);
2097
2098         if (screen->scroll_delta > screen->insert_delta) {
2099                 vte_terminal_queue_adjustment_value_changed(terminal,
2100                                 screen->insert_delta);
2101         }
2102 }
2103
2104 /* Update the adjustment field of the widget.  This function should be called
2105  * whenever we add rows to or remove rows from the history or switch screens. */
2106 static void
2107 _vte_terminal_adjust_adjustments_full (VteTerminal *terminal)
2108 {
2109         gboolean changed = FALSE;
2110         gdouble v;
2111
2112         g_assert(terminal->pvt->screen != NULL);
2113         g_assert(terminal->pvt->screen->row_data != NULL);
2114
2115         _vte_terminal_adjust_adjustments(terminal);
2116
2117         /* The step increment should always be one. */
2118         v = gtk_adjustment_get_step_increment(terminal->adjustment);
2119         if (v != 1) {
2120                 _vte_debug_print(VTE_DEBUG_ADJ,
2121                                 "Changing step increment from %.0lf to %ld\n",
2122                                 v, terminal->row_count);
2123                 terminal->adjustment->step_increment = 1;
2124                 changed = TRUE;
2125         }
2126
2127         /* Set the number of rows the user sees to the number of rows the
2128          * user sees. */
2129         v = gtk_adjustment_get_page_size(terminal->adjustment);
2130         if (v != terminal->row_count) {
2131                 _vte_debug_print(VTE_DEBUG_ADJ,
2132                                 "Changing page size from %.0f to %ld\n",
2133                                  v, terminal->row_count);
2134                 terminal->adjustment->page_size = terminal->row_count;
2135                 changed = TRUE;
2136         }
2137
2138         /* Clicking in the empty area should scroll one screen, so set the
2139          * page size to the number of visible rows. */
2140         v = gtk_adjustment_get_page_increment(terminal->adjustment);
2141         if (v != terminal->row_count) {
2142                 _vte_debug_print(VTE_DEBUG_ADJ,
2143                                 "Changing page increment from "
2144                                 "%.0f to %ld\n",
2145                                 v, terminal->row_count);
2146                 terminal->adjustment->page_increment = terminal->row_count;
2147                 changed = TRUE;
2148         }
2149
2150         if (changed) {
2151                 _vte_debug_print(VTE_DEBUG_SIGNALS,
2152                                 "Emitting adjustment_changed.\n");
2153                 gtk_adjustment_changed(terminal->adjustment);
2154         }
2155 }
2156
2157 /* Scroll a fixed number of lines up or down in the current screen. */
2158 static void
2159 vte_terminal_scroll_lines(VteTerminal *terminal, gint lines)
2160 {
2161         glong destination;
2162         _vte_debug_print(VTE_DEBUG_ADJ, "Scrolling %d lines.\n", lines);
2163         /* Calculate the ideal position where we want to be before clamping. */
2164         destination = terminal->pvt->screen->scroll_delta;
2165         destination += lines;
2166         /* Tell the scrollbar to adjust itself. */
2167         vte_terminal_queue_adjustment_value_changed_clamped (terminal, destination);
2168 }
2169
2170 /* Scroll a fixed number of pages up or down, in the current screen. */
2171 static void
2172 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
2173 {
2174         vte_terminal_scroll_lines(terminal, pages * terminal->row_count);
2175 }
2176
2177 /* Scroll so that the scroll delta is the minimum value. */
2178 static void
2179 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
2180 {
2181         vte_terminal_queue_adjustment_value_changed (terminal,
2182                         _vte_ring_delta(terminal->pvt->screen->row_data));
2183 }
2184
2185 static void
2186 vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
2187 {
2188         glong delta;
2189         delta = terminal->pvt->screen->insert_delta;
2190         vte_terminal_queue_adjustment_value_changed (terminal, delta);
2191         _vte_debug_print(VTE_DEBUG_ADJ,
2192                         "Snapping to bottom of screen\n");
2193 }
2194
2195 static void
2196 _vte_terminal_setup_utf8 (VteTerminal *terminal)
2197 {
2198         VteTerminalPrivate *pvt = terminal->pvt;
2199         GError *error = NULL;
2200
2201         if (!vte_pty_set_utf8(pvt->pty,
2202                               strcmp(terminal->pvt->encoding, "UTF-8") == 0,
2203                               &error)) {
2204                 g_warning ("Failed to set UTF8 mode: %s\n", error->message);
2205                 g_error_free (error);
2206         }
2207 }
2208
2209 /**
2210  * vte_terminal_set_encoding:
2211  * @terminal: a #VteTerminal
2212  * @codeset: (allow-none): a valid #GIConv target, or %NULL to use the default encoding
2213  *
2214  * Changes the encoding the terminal will expect data from the child to
2215  * be encoded with.  For certain terminal types, applications executing in the
2216  * terminal can change the encoding.  The default encoding is defined by the
2217  * application's locale settings.
2218  */
2219 void
2220 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
2221 {
2222         VteTerminalPrivate *pvt;
2223         GObject *object;
2224         const char *old_codeset;
2225         VteConv conv;
2226         char *obuf1, *obuf2;
2227         gsize bytes_written;
2228
2229         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2230
2231         object = G_OBJECT(terminal);
2232         pvt = terminal->pvt;
2233
2234         old_codeset = pvt->encoding;
2235         if (codeset == NULL) {
2236                 g_get_charset(&codeset);
2237         }
2238         if ((old_codeset != NULL) && (strcmp(codeset, old_codeset) == 0)) {
2239                 /* Nothing to do! */
2240                 return;
2241         }
2242
2243         g_object_freeze_notify(object);
2244
2245         /* Open new conversions. */
2246         conv = _vte_conv_open(codeset, "UTF-8");
2247         if (conv == VTE_INVALID_CONV) {
2248                 g_warning(_("Unable to convert characters from %s to %s."),
2249                           "UTF-8", codeset);
2250                 /* fallback to no conversion */
2251                 conv = _vte_conv_open(codeset = "UTF-8", "UTF-8");
2252         }
2253         if (terminal->pvt->outgoing_conv != VTE_INVALID_CONV) {
2254                 _vte_conv_close(terminal->pvt->outgoing_conv);
2255         }
2256         terminal->pvt->outgoing_conv = conv;
2257
2258         /* Set the terminal's encoding to the new value. */
2259         terminal->pvt->encoding = g_intern_string(codeset);
2260
2261         /* Convert any buffered output bytes. */
2262         if ((_vte_buffer_length(terminal->pvt->outgoing) > 0) &&
2263             (old_codeset != NULL)) {
2264                 /* Convert back to UTF-8. */
2265                 obuf1 = g_convert((gchar *)terminal->pvt->outgoing->data,
2266                                   _vte_buffer_length(terminal->pvt->outgoing),
2267                                   "UTF-8",
2268                                   old_codeset,
2269                                   NULL,
2270                                   &bytes_written,
2271                                   NULL);
2272                 if (obuf1 != NULL) {
2273                         /* Convert to the new encoding. */
2274                         obuf2 = g_convert(obuf1,
2275                                           bytes_written,
2276                                           codeset,
2277                                           "UTF-8",
2278                                           NULL,
2279                                           &bytes_written,
2280                                           NULL);
2281                         if (obuf2 != NULL) {
2282                                 _vte_buffer_clear(terminal->pvt->outgoing);
2283                                 _vte_buffer_append(terminal->pvt->outgoing,
2284                                                    obuf2, bytes_written);
2285                                 g_free(obuf2);
2286                         }
2287                         g_free(obuf1);
2288                 }
2289         }
2290
2291         /* Set the encoding for incoming text. */
2292         _vte_iso2022_state_set_codeset(terminal->pvt->iso2022,
2293                                        terminal->pvt->encoding);
2294
2295         _vte_debug_print(VTE_DEBUG_IO,
2296                         "Set terminal encoding to `%s'.\n",
2297                         terminal->pvt->encoding);
2298         vte_terminal_emit_encoding_changed(terminal);
2299
2300         g_object_thaw_notify(object);
2301 }
2302
2303 /**
2304  * vte_terminal_get_encoding:
2305  * @terminal: a #VteTerminal
2306  *
2307  * Determines the name of the encoding in which the terminal expects data to be
2308  * encoded.
2309  *
2310  * Returns: (transfer none): the current encoding for the terminal
2311  */
2312 const char *
2313 vte_terminal_get_encoding(VteTerminal *terminal)
2314 {
2315         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
2316         return terminal->pvt->encoding;
2317 }
2318
2319 static inline VteRowData *
2320 vte_terminal_insert_rows (VteTerminal *terminal, guint cnt)
2321 {
2322         VteRowData *row;
2323         do {
2324                 row = _vte_terminal_ring_append (terminal, FALSE);
2325         } while(--cnt);
2326         return row;
2327 }
2328
2329
2330 /* Make sure we have enough rows and columns to hold data at the current
2331  * cursor position. */
2332 VteRowData *
2333 _vte_terminal_ensure_row (VteTerminal *terminal)
2334 {
2335         VteRowData *row;
2336         VteScreen *screen;
2337         gint delta;
2338         glong v;
2339
2340         /* Must make sure we're in a sane area. */
2341         screen = terminal->pvt->screen;
2342         v = screen->cursor_current.row;
2343
2344         /* Figure out how many rows we need to add. */
2345         delta = v - _vte_ring_next(screen->row_data) + 1;
2346         if (delta > 0) {
2347                 row = vte_terminal_insert_rows (terminal, delta);
2348                 _vte_terminal_adjust_adjustments(terminal);
2349         } else {
2350                 /* Find the row the cursor is in. */
2351                 row = _vte_ring_index_writable (screen->row_data, v);
2352         }
2353         g_assert(row != NULL);
2354
2355         return row;
2356 }
2357
2358 static VteRowData *
2359 vte_terminal_ensure_cursor(VteTerminal *terminal)
2360 {
2361         VteRowData *row;
2362
2363         row = _vte_terminal_ensure_row (terminal);
2364         _vte_row_data_fill (row, &basic_cell.cell, terminal->pvt->screen->cursor_current.col);
2365
2366         return row;
2367 }
2368
2369 /* Update the insert delta so that the screen which includes it also
2370  * includes the end of the buffer. */
2371 void
2372 _vte_terminal_update_insert_delta(VteTerminal *terminal)
2373 {
2374         long delta, rows;
2375         VteScreen *screen;
2376
2377         screen = terminal->pvt->screen;
2378
2379         /* The total number of lines.  Add one to the cursor offset
2380          * because it's zero-based. */
2381         rows = _vte_ring_next (screen->row_data);
2382         delta = screen->cursor_current.row - rows + 1;
2383         if (G_UNLIKELY (delta > 0)) {
2384                 vte_terminal_insert_rows (terminal, delta);
2385                 rows = _vte_ring_next (screen->row_data);
2386         }
2387
2388         /* Make sure that the bottom row is visible, and that it's in
2389          * the buffer (even if it's empty).  This usually causes the
2390          * top row to become a history-only row. */
2391         delta = screen->insert_delta;
2392         delta = MIN(delta, rows - terminal->row_count);
2393         delta = MAX(delta,
2394                     screen->cursor_current.row - (terminal->row_count - 1));
2395         delta = MAX(delta, _vte_ring_delta(screen->row_data));
2396
2397         /* Adjust the insert delta and scroll if needed. */
2398         if (delta != screen->insert_delta) {
2399                 screen->insert_delta = delta;
2400                 _vte_terminal_adjust_adjustments(terminal);
2401         }
2402 }
2403
2404 /* Show or hide the pointer. */
2405 void
2406 _vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible)
2407 {
2408         GdkWindow *window;
2409         struct vte_match_regex *regex = NULL;
2410
2411         terminal->pvt->mouse_cursor_visible = visible;
2412
2413         if (! gtk_widget_get_realized (&terminal->widget))
2414                 return;
2415
2416         window = gtk_widget_get_window (&terminal->widget);
2417
2418         if (visible || !terminal->pvt->mouse_autohide) {
2419                 if (terminal->pvt->mouse_tracking_mode) {
2420                         _vte_debug_print(VTE_DEBUG_CURSOR,
2421                                         "Setting mousing cursor.\n");
2422                         gdk_window_set_cursor (window, terminal->pvt->mouse_mousing_cursor);
2423                 } else
2424                 if ( (guint)terminal->pvt->match_tag < terminal->pvt->match_regexes->len) {
2425                         regex = &g_array_index(terminal->pvt->match_regexes,
2426                                                struct vte_match_regex,
2427                                                terminal->pvt->match_tag);
2428                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
2429                 } else {
2430                         _vte_debug_print(VTE_DEBUG_CURSOR,
2431                                         "Setting default mouse cursor.\n");
2432                         gdk_window_set_cursor (window, terminal->pvt->mouse_default_cursor);
2433                 }
2434         } else {
2435                 _vte_debug_print(VTE_DEBUG_CURSOR,
2436                                 "Setting to invisible cursor.\n");
2437                 gdk_window_set_cursor (window, terminal->pvt->mouse_inviso_cursor);
2438         }
2439 }
2440
2441 /**
2442  * vte_terminal_new:
2443  *
2444  * Creates a new terminal widget.
2445  *
2446  * Returns: (transfer full) (type Vte.Terminal): a new #VteTerminal object
2447  */
2448 GtkWidget *
2449 vte_terminal_new(void)
2450 {
2451         return g_object_new(VTE_TYPE_TERMINAL, NULL);
2452 }
2453
2454 /* Set up a palette entry with a more-or-less match for the requested color. */
2455 static void
2456 vte_terminal_set_color_internal(VteTerminal *terminal, int entry,
2457                                 const GdkColor *proposed)
2458 {
2459         PangoColor *color;
2460
2461         color = &terminal->pvt->palette[entry];
2462
2463         if (color->red == proposed->red &&
2464             color->green == proposed->green &&
2465             color->blue == proposed->blue) {
2466                 return;
2467         }
2468
2469         _vte_debug_print(VTE_DEBUG_MISC,
2470                         "Set color[%d] to (%04x,%04x,%04x).\n", entry,
2471                         proposed->red, proposed->green, proposed->blue);
2472
2473         /* Save the requested color. */
2474         color->red = proposed->red;
2475         color->green = proposed->green;
2476         color->blue = proposed->blue;
2477
2478         /* If we're not realized yet, there's nothing else to do. */
2479         if (! gtk_widget_get_realized (&terminal->widget)) {
2480                 return;
2481         }
2482
2483         /* If we're setting the background color, set the background color
2484          * on the widget as well. */
2485         if (entry == VTE_DEF_BG) {
2486                 vte_terminal_queue_background_update(terminal);
2487         }
2488
2489         /* and redraw */
2490         if (entry == VTE_CUR_BG)
2491                 _vte_invalidate_cursor_once(terminal, FALSE);
2492         else
2493                 _vte_invalidate_all (terminal);
2494 }
2495
2496 static void
2497 vte_terminal_generate_bold(const PangoColor *foreground,
2498                            const PangoColor *background,
2499                            double factor,
2500                            GdkColor *bold)
2501 {
2502         double fy, fcb, fcr, by, bcb, bcr, r, g, b;
2503         g_assert(foreground != NULL);
2504         g_assert(background != NULL);
2505         g_assert(bold != NULL);
2506         fy =   0.2990 * foreground->red +
2507                0.5870 * foreground->green +
2508                0.1140 * foreground->blue;
2509         fcb = -0.1687 * foreground->red +
2510               -0.3313 * foreground->green +
2511                0.5000 * foreground->blue;
2512         fcr =  0.5000 * foreground->red +
2513               -0.4187 * foreground->green +
2514               -0.0813 * foreground->blue;
2515         by =   0.2990 * background->red +
2516                0.5870 * background->green +
2517                0.1140 * background->blue;
2518         bcb = -0.1687 * background->red +
2519               -0.3313 * background->green +
2520                0.5000 * background->blue;
2521         bcr =  0.5000 * background->red +
2522               -0.4187 * background->green +
2523               -0.0813 * background->blue;
2524         fy = (factor * fy) + ((1.0 - factor) * by);
2525         fcb = (factor * fcb) + ((1.0 - factor) * bcb);
2526         fcr = (factor * fcr) + ((1.0 - factor) * bcr);
2527         r = fy + 1.402 * fcr;
2528         g = fy + 0.34414 * fcb - 0.71414 * fcr;
2529         b = fy + 1.722 * fcb;
2530         _vte_debug_print(VTE_DEBUG_MISC,
2531                         "Calculated bold (%d, %d, %d) = (%lf,%lf,%lf)",
2532                         foreground->red, foreground->green, foreground->blue,
2533                         r, g, b);
2534         bold->pixel = 0;
2535         bold->red = CLAMP(r, 0, 0xffff);
2536         bold->green = CLAMP(g, 0, 0xffff);
2537         bold->blue = CLAMP(b, 0, 0xffff);
2538         _vte_debug_print(VTE_DEBUG_MISC,
2539                         "= (%04x,%04x,%04x).\n",
2540                         bold->red, bold->green, bold->blue);
2541 }
2542
2543 /**
2544  * vte_terminal_set_color_bold:
2545  * @terminal: a #VteTerminal
2546  * @bold: the new bold color
2547  *
2548  * Sets the color used to draw bold text in the default foreground color.
2549  */
2550 void
2551 vte_terminal_set_color_bold(VteTerminal *terminal, const GdkColor *bold)
2552 {
2553         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2554         g_return_if_fail(bold != NULL);
2555
2556         _vte_debug_print(VTE_DEBUG_MISC,
2557                         "Set bold color to (%04x,%04x,%04x).\n",
2558                         bold->red, bold->green, bold->blue);
2559         vte_terminal_set_color_internal(terminal, VTE_BOLD_FG, bold);
2560 }
2561
2562 /**
2563  * vte_terminal_set_color_dim:
2564  * @terminal: a #VteTerminal
2565  * @dim: the new dim color
2566  *
2567  * Sets the color used to draw dim text in the default foreground color.
2568  */
2569 void
2570 vte_terminal_set_color_dim(VteTerminal *terminal, const GdkColor *dim)
2571 {
2572         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2573         g_return_if_fail(dim != NULL);
2574
2575         _vte_debug_print(VTE_DEBUG_MISC,
2576                         "Set dim color to (%04x,%04x,%04x).\n",
2577                         dim->red, dim->green, dim->blue);
2578         vte_terminal_set_color_internal(terminal, VTE_DIM_FG, dim);
2579 }
2580
2581 /**
2582  * vte_terminal_set_color_foreground:
2583  * @terminal: a #VteTerminal
2584  * @foreground: the new foreground color
2585  *
2586  * Sets the foreground color used to draw normal text
2587  */
2588 void
2589 vte_terminal_set_color_foreground(VteTerminal *terminal,
2590                                   const GdkColor *foreground)
2591 {
2592         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2593         g_return_if_fail(foreground != NULL);
2594
2595         _vte_debug_print(VTE_DEBUG_MISC,
2596                         "Set foreground color to (%04x,%04x,%04x).\n",
2597                         foreground->red, foreground->green, foreground->blue);
2598         vte_terminal_set_color_internal(terminal, VTE_DEF_FG, foreground);
2599 }
2600
2601 /**
2602  * vte_terminal_set_color_background:
2603  * @terminal: a #VteTerminal
2604  * @background: the new background color
2605  *
2606  * Sets the background color for text which does not have a specific background
2607  * color assigned.  Only has effect when no background image is set and when
2608  * the terminal is not transparent.
2609  */
2610 void
2611 vte_terminal_set_color_background(VteTerminal *terminal,
2612                                   const GdkColor *background)
2613 {
2614         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2615         g_return_if_fail(background != NULL);
2616
2617         _vte_debug_print(VTE_DEBUG_MISC,
2618                         "Set background color to (%04x,%04x,%04x).\n",
2619                         background->red, background->green, background->blue);
2620         vte_terminal_set_color_internal(terminal, VTE_DEF_BG, background);
2621 }
2622
2623 /**
2624  * vte_terminal_set_color_cursor:
2625  * @terminal: a #VteTerminal
2626  * @cursor_background: (allow-none): the new color to use for the text cursor, or %NULL
2627  *
2628  * Sets the background color for text which is under the cursor.  If %NULL, text
2629  * under the cursor will be drawn with foreground and background colors
2630  * reversed.
2631  *
2632  * Since: 0.11.11
2633  */
2634 void
2635 vte_terminal_set_color_cursor(VteTerminal *terminal,
2636                               const GdkColor *cursor_background)
2637 {
2638         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2639
2640         if (cursor_background != NULL) {
2641                 _vte_debug_print(VTE_DEBUG_MISC,
2642                                 "Set cursor color to (%04x,%04x,%04x).\n",
2643                                 cursor_background->red,
2644                                 cursor_background->green,
2645                                 cursor_background->blue);
2646                 vte_terminal_set_color_internal(terminal, VTE_CUR_BG,
2647                                                 cursor_background);
2648                 terminal->pvt->cursor_color_set = TRUE;
2649         } else {
2650                 _vte_debug_print(VTE_DEBUG_MISC,
2651                                 "Cleared cursor color.\n");
2652                 terminal->pvt->cursor_color_set = FALSE;
2653         }
2654 }
2655
2656 /**
2657  * vte_terminal_set_color_highlight:
2658  * @terminal: a #VteTerminal
2659  * @highlight_background: (allow-none): the new color to use for highlighted text, or %NULL
2660  *
2661  * Sets the background color for text which is highlighted.  If %NULL,
2662  * highlighted text (which is usually highlighted because it is selected) will
2663  * be drawn with foreground and background colors reversed.
2664  *
2665  * Since: 0.11.11
2666  */
2667 void
2668 vte_terminal_set_color_highlight(VteTerminal *terminal,
2669                                  const GdkColor *highlight_background)
2670 {
2671         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2672
2673         if (highlight_background != NULL) {
2674                 _vte_debug_print(VTE_DEBUG_MISC,
2675                                 "Set highlight color to (%04x,%04x,%04x).\n",
2676                                 highlight_background->red,
2677                                 highlight_background->green,
2678                                 highlight_background->blue);
2679                 vte_terminal_set_color_internal(terminal, VTE_DEF_HL,
2680                                                 highlight_background);
2681                 terminal->pvt->highlight_color_set = TRUE;
2682         } else {
2683                 _vte_debug_print(VTE_DEBUG_MISC,
2684                                 "Cleared highlight color.\n");
2685                 terminal->pvt->highlight_color_set = FALSE;
2686         }
2687 }
2688
2689 /**
2690  * vte_terminal_set_colors:
2691  * @terminal: a #VteTerminal
2692  * @foreground: (allow-none): the new foreground color, or %NULL
2693  * @background: (allow-none): the new background color, or %NULL
2694  * @palette: (array length=palette_size zero-terminated=0) (element-type Gdk.Color): the color palette
2695  * @palette_size: the number of entries in @palette
2696  *
2697  * The terminal widget uses a 28-color model comprised of the default foreground
2698  * and background colors, the bold foreground color, the dim foreground
2699  * color, an eight color palette, bold versions of the eight color palette,
2700  * and a dim version of the the eight color palette.
2701  *
2702  * @palette_size must be either 0, 8, 16, or 24, or between 25 and 255 inclusive.
2703  * If @foreground is %NULL and
2704  * @palette_size is greater than 0, the new foreground color is taken from
2705  * @palette[7].  If @background is %NULL and @palette_size is greater than 0,
2706  * the new background color is taken from @palette[0].  If
2707  * @palette_size is 8 or 16, the third (dim) and possibly the second (bold)
2708  * 8-color palettes are extrapolated from the new background color and the items
2709  * in @palette.
2710  */
2711 void
2712 vte_terminal_set_colors(VteTerminal *terminal,
2713                         const GdkColor *foreground,
2714                         const GdkColor *background,
2715                         const GdkColor *palette,
2716                         glong palette_size)
2717 {
2718         guint i;
2719         GdkColor color;
2720
2721         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2722
2723         g_return_if_fail(palette_size >= 0);
2724         g_return_if_fail((palette_size == 0) ||
2725                          (palette_size == 8) ||
2726                          (palette_size == 16) ||
2727                          (palette_size == 24) ||
2728                          (palette_size > 24 && palette_size < 256));
2729
2730         _vte_debug_print(VTE_DEBUG_MISC,
2731                         "Set color palette [%ld elements].\n",
2732                         palette_size);
2733
2734         /* Accept NULL as the default foreground and background colors if we
2735          * got a palette. */
2736         if ((foreground == NULL) && (palette_size >= 8)) {
2737                 foreground = &palette[7];
2738         }
2739         if ((background == NULL) && (palette_size >= 8)) {
2740                 background = &palette[0];
2741         }
2742
2743         memset(&color, 0, sizeof(color));
2744
2745         /* Initialize each item in the palette if we got any entries to work
2746          * with. */
2747         for (i=0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
2748                 if (i < 16) {
2749                         color.blue = (i & 4) ? 0xc000 : 0;
2750                         color.green = (i & 2) ? 0xc000 : 0;
2751                         color.red = (i & 1) ? 0xc000 : 0;
2752                         if (i > 7) {
2753                                 color.blue += 0x3fff;
2754                                 color.green += 0x3fff;
2755                                 color.red += 0x3fff;
2756                         }
2757                 }
2758                 else if (i < 232) {
2759                         int j = i - 16;
2760                         int r = j / 36, g = (j / 6) % 6, b = j % 6;
2761                         int red =   (r == 0) ? 0 : r * 40 + 55;
2762                         int green = (g == 0) ? 0 : g * 40 + 55;
2763                         int blue =  (b == 0) ? 0 : b * 40 + 55;
2764                         color.red   = red | red << 8  ;
2765                         color.green = green | green << 8;
2766                         color.blue  = blue | blue << 8;
2767                 } else if (i < 256) {
2768                         int shade = 8 + (i - 232) * 10;
2769                         color.red = color.green = color.blue = shade | shade << 8;
2770                 }
2771                 else switch (i) {
2772                         case VTE_DEF_BG:
2773                                 if (background != NULL) {
2774                                         color = *background;
2775                                 } else {
2776                                         color.red = 0;
2777                                         color.blue = 0;
2778                                         color.green = 0;
2779                                 }
2780                                 break;
2781                         case VTE_DEF_FG:
2782                                 if (foreground != NULL) {
2783                                         color = *foreground;
2784                                 } else {
2785                                         color.red = 0xc000;
2786                                         color.blue = 0xc000;
2787                                         color.green = 0xc000;
2788                                 }
2789                                 break;
2790                         case VTE_BOLD_FG:
2791                                 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2792                                                            &terminal->pvt->palette[VTE_DEF_BG],
2793                                                            1.8,
2794                                                            &color);
2795                                 break;
2796                         case VTE_DIM_FG:
2797                                 vte_terminal_generate_bold(&terminal->pvt->palette[VTE_DEF_FG],
2798                                                            &terminal->pvt->palette[VTE_DEF_BG],
2799                                                            0.5,
2800                                                            &color);
2801                                 break;
2802                         case VTE_DEF_HL:
2803                                 color.red = 0xc000;
2804                                 color.blue = 0xc000;
2805                                 color.green = 0xc000;
2806                                 break;
2807                         case VTE_CUR_BG:
2808                                 color.red = 0x0000;
2809                                 color.blue = 0x0000;
2810                                 color.green = 0x0000;
2811                                 break;
2812                         }
2813
2814                 /* Override from the supplied palette if there is one. */
2815                 if ((glong) i < palette_size) {
2816                         color = palette[i];
2817                 }
2818
2819                 /* Set up the color entry. */
2820                 vte_terminal_set_color_internal(terminal, i, &color);
2821         }
2822
2823         /* Track that we had a color palette set. */
2824         terminal->pvt->palette_initialized = TRUE;
2825 }
2826
2827 /**
2828  * vte_terminal_set_opacity:
2829  * @terminal: a #VteTerminal
2830  * @opacity: the new opacity
2831  *
2832  * Sets the opacity of the terminal background, were 0 means completely
2833  * transparent and 65535 means completely opaque.
2834  */
2835 void
2836 vte_terminal_set_opacity(VteTerminal *terminal, guint16 opacity)
2837 {
2838         VteTerminalPrivate *pvt;
2839
2840         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2841
2842         pvt = terminal->pvt;
2843
2844         if (opacity == pvt->bg_opacity)
2845                 return;
2846
2847         pvt->bg_opacity = opacity;
2848
2849         g_object_notify(G_OBJECT(terminal), "background-opacity");
2850 }
2851
2852 /**
2853  * vte_terminal_set_default_colors:
2854  * @terminal: a #VteTerminal
2855  *
2856  * Reset the terminal palette to reasonable compiled-in default color.
2857  */
2858 void
2859 vte_terminal_set_default_colors(VteTerminal *terminal)
2860 {
2861         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2862         vte_terminal_set_colors(terminal, NULL, NULL, NULL, 0);
2863 }
2864
2865
2866 /* Cleanup smart-tabs.  See vte_sequence_handler_ta() */
2867 void
2868 _vte_terminal_cleanup_tab_fragments_at_cursor (VteTerminal *terminal)
2869 {
2870         VteRowData *row = _vte_terminal_ensure_row (terminal);
2871         VteScreen *screen = terminal->pvt->screen;
2872         long col = screen->cursor_current.col;
2873         const VteCell *pcell = _vte_row_data_get (row, col);
2874
2875         if (G_UNLIKELY (pcell != NULL && pcell->c == '\t')) {
2876                 long i, num_columns;
2877                 VteCell *cell = _vte_row_data_get_writable (row, col);
2878                 
2879                 _vte_debug_print(VTE_DEBUG_MISC,
2880                                  "Cleaning tab fragments at %ld",
2881                                  col);
2882
2883                 /* go back to the beginning of the tab */
2884                 while (cell->attr.fragment && col > 0)
2885                         cell = _vte_row_data_get_writable (row, --col);
2886
2887                 num_columns = cell->attr.columns;
2888                 for (i = 0; i < num_columns; i++) {
2889                         cell = _vte_row_data_get_writable (row, col++);
2890                         if (G_UNLIKELY (!cell))
2891                           break;
2892                         *cell = screen->fill_defaults;
2893                 }
2894         }
2895 }
2896
2897 /* Cursor down, with scrolling. */
2898 void
2899 _vte_terminal_cursor_down (VteTerminal *terminal)
2900 {
2901         long start, end;
2902         VteScreen *screen;
2903
2904         screen = terminal->pvt->screen;
2905
2906         if (screen->scrolling_restricted) {
2907                 start = screen->insert_delta + screen->scrolling_region.start;
2908                 end = screen->insert_delta + screen->scrolling_region.end;
2909         } else {
2910                 start = screen->insert_delta;
2911                 end = start + terminal->row_count - 1;
2912         }
2913         if (screen->cursor_current.row == end) {
2914                 /* Match xterm and fill to the end of row when scrolling. */
2915                 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2916                         VteRowData *rowdata;
2917                         rowdata = _vte_terminal_ensure_row (terminal);
2918                         _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2919                 }
2920
2921                 if (screen->scrolling_restricted) {
2922                         if (start == screen->insert_delta) {
2923                                 /* Scroll this line into the scrollback
2924                                  * buffer by inserting a line at the next
2925                                  * line and scrolling the area up. */
2926                                 screen->insert_delta++;
2927                                 screen->scroll_delta++;
2928                                 screen->cursor_current.row++;
2929                                 /* update start and end, as they are relative
2930                                  * to insert_delta. */
2931                                 start++;
2932                                 end++;
2933                                 _vte_terminal_ring_insert (terminal, screen->cursor_current.row, FALSE);
2934                                 /* Force the areas below the region to be
2935                                  * redrawn -- they've moved. */
2936                                 _vte_terminal_scroll_region(terminal, start,
2937                                                             end - start + 1, 1);
2938                                 /* Force scroll. */
2939                                 _vte_terminal_adjust_adjustments(terminal);
2940                         } else {
2941                                 /* If we're at the bottom of the scrolling
2942                                  * region, add a line at the top to scroll the
2943                                  * bottom off. */
2944                                 _vte_terminal_ring_remove (terminal, start);
2945                                 _vte_terminal_ring_insert (terminal, end, TRUE);
2946                                 /* Update the display. */
2947                                 _vte_terminal_scroll_region(terminal, start,
2948                                                            end - start + 1, -1);
2949                                 _vte_invalidate_cells(terminal,
2950                                                       0, terminal->column_count,
2951                                                       end - 2, 2);
2952                         }
2953                 } else {
2954                         /* Scroll up with history. */
2955                         screen->cursor_current.row++;
2956                         _vte_terminal_update_insert_delta(terminal);
2957                 }
2958
2959                 /* Match xterm and fill the new row when scrolling. */
2960                 if (screen->fill_defaults.attr.back != VTE_DEF_BG) {
2961                         VteRowData *rowdata;
2962                         rowdata = _vte_terminal_ensure_row (terminal);
2963                         _vte_row_data_fill (rowdata, &screen->fill_defaults, terminal->column_count);
2964                 }
2965         } else {
2966                 /* Otherwise, just move the cursor down. */
2967                 screen->cursor_current.row++;
2968         }
2969 }
2970
2971 /* Insert a single character into the stored data array. */
2972 gboolean
2973 _vte_terminal_insert_char(VteTerminal *terminal, gunichar c,
2974                          gboolean insert, gboolean invalidate_now)
2975 {
2976         VteCellAttr attr;
2977         VteRowData *row;
2978         long col;
2979         int columns, i;
2980         VteScreen *screen;
2981         gboolean line_wrapped = FALSE; /* cursor moved before char inserted */
2982
2983         screen = terminal->pvt->screen;
2984         insert |= screen->insert_mode;
2985         invalidate_now |= insert;
2986
2987         /* If we've enabled the special drawing set, map the characters to
2988          * Unicode. */
2989         if (G_UNLIKELY (screen->alternate_charset)) {
2990                 _vte_debug_print(VTE_DEBUG_SUBSTITUTION,
2991                                 "Attempting charset substitution"
2992                                 "for U+%04X.\n", c);
2993                 /* See if there's a mapping for it. */
2994                 c = _vte_iso2022_process_single(terminal->pvt->iso2022, c, '0');
2995         }
2996
2997         /* If this character is destined for the status line, save it. */
2998         if (G_UNLIKELY (screen->status_line)) {
2999                 g_string_append_unichar(screen->status_line_contents, c);
3000                 screen->status_line_changed = TRUE;
3001                 return FALSE;
3002         }
3003
3004         /* Figure out how many columns this character should occupy. */
3005         if (G_UNLIKELY (VTE_ISO2022_HAS_ENCODED_WIDTH(c))) {
3006                 columns = _vte_iso2022_get_encoded_width(c);
3007                 c &= ~VTE_ISO2022_ENCODED_WIDTH_MASK;
3008         } else {
3009                 columns = _vte_iso2022_unichar_width(terminal->pvt->iso2022, c);
3010         }
3011
3012
3013         /* If we're autowrapping here, do it. */
3014         col = screen->cursor_current.col;
3015         if (G_UNLIKELY (columns && col + columns > terminal->column_count)) {
3016                 if (terminal->pvt->flags.am) {
3017                         _vte_debug_print(VTE_DEBUG_ADJ,
3018                                         "Autowrapping before character\n");
3019                         /* Wrap. */
3020                         /* XXX clear to the end of line */
3021                         col = screen->cursor_current.col = 0;
3022                         /* Mark this line as soft-wrapped. */
3023                         row = _vte_terminal_ensure_row (terminal);
3024                         row->attr.soft_wrapped = 1;
3025                         _vte_terminal_cursor_down (terminal);
3026                 } else {
3027                         /* Don't wrap, stay at the rightmost column. */
3028                         col = screen->cursor_current.col =
3029                                 terminal->column_count - columns;
3030                 }
3031                 line_wrapped = TRUE;
3032         }
3033
3034         _vte_debug_print(VTE_DEBUG_PARSE,
3035                         "Inserting %ld '%c' (%d/%d) (%ld+%d, %ld), delta = %ld; ",
3036                         (long)c, c < 256 ? c : ' ',
3037                         screen->defaults.attr.fore,
3038                         screen->defaults.attr.back,
3039                         col, columns, (long)screen->cursor_current.row,
3040                         (long)screen->insert_delta);
3041
3042
3043         if (G_UNLIKELY (columns == 0)) {
3044
3045                 /* It's a combining mark */
3046
3047                 long row_num;
3048                 VteCell *cell;
3049
3050                 _vte_debug_print(VTE_DEBUG_PARSE, "combining U+%04X", c);
3051
3052                 row_num = screen->cursor_current.row;
3053                 row = NULL;
3054                 if (G_UNLIKELY (col == 0)) {
3055                         /* We are at first column.  See if the previous line softwrapped.
3056                          * If it did, move there.  Otherwise skip inserting. */
3057
3058                         if (G_LIKELY (row_num > 0)) {
3059                                 row_num--;
3060                                 row = _vte_terminal_find_row_data_writable (terminal, row_num);
3061
3062                                 if (row) {
3063                                         if (!row->attr.soft_wrapped)
3064                                                 row = NULL;
3065                                         else
3066                                                 col = _vte_row_data_length (row);
3067                                 }
3068                         }
3069                 } else {
3070                         row = _vte_terminal_find_row_data_writable (terminal, row_num);
3071                 }
3072
3073                 if (G_UNLIKELY (!row || !col))
3074                         goto not_inserted;
3075
3076                 /* Combine it on the previous cell */
3077
3078                 col--;
3079                 cell = _vte_row_data_get_writable (row, col);
3080
3081                 if (G_UNLIKELY (!cell))
3082                         goto not_inserted;
3083
3084                 /* Find the previous cell */
3085                 while (cell && cell->attr.fragment && col > 0)
3086                         cell = _vte_row_data_get_writable (row, --col);
3087                 if (G_UNLIKELY (!cell || cell->c == '\t'))
3088                         goto not_inserted;
3089
3090                 /* Combine the new character on top of the cell string */
3091                 c = _vte_unistr_append_unichar (cell->c, c);
3092
3093                 /* And set it */
3094                 columns = cell->attr.columns;
3095                 for (i = 0; i < columns; i++) {
3096                         cell = _vte_row_data_get_writable (row, col++);
3097                         cell->c = c;
3098                 }
3099
3100                 /* Always invalidate since we put the mark on the *previous* cell
3101                  * and the higher level code doesn't know this. */
3102                 _vte_invalidate_cells(terminal,
3103                                       col - columns,
3104                                       columns,
3105                                       row_num, 1);
3106
3107                 goto done;
3108         }
3109
3110         /* Make sure we have enough rows to hold this data. */
3111         row = vte_terminal_ensure_cursor (terminal);
3112         g_assert(row != NULL);
3113
3114         _vte_terminal_cleanup_tab_fragments_at_cursor (terminal);
3115
3116         if (insert) {
3117                 for (i = 0; i < columns; i++)
3118                         _vte_row_data_insert (row, col + i, &screen->color_defaults);
3119         } else {
3120                 _vte_row_data_fill (row, &basic_cell.cell, col + columns);
3121         }
3122
3123         /* Convert any wide characters we may have broken into single
3124          * cells. (#514632) */
3125         if (G_LIKELY (col > 0)) {
3126                 glong col2 = col - 1;
3127                 VteCell *cell = _vte_row_data_get_writable (row, col2);
3128                 while (col2 > 0 && cell != NULL && cell->attr.fragment)
3129                         cell = _vte_row_data_get_writable (row, --col2);
3130                 cell->attr.columns = col - col2;
3131         }
3132         {
3133                 glong col2 = col + columns;
3134                 VteCell *cell = _vte_row_data_get_writable (row, col2);
3135                 while (cell != NULL && cell->attr.fragment) {
3136                         cell->attr.columns = 1;
3137                         cell->c = 0;
3138                         cell = _vte_row_data_get_writable (row, ++col2);
3139                 }
3140         }
3141
3142         attr = screen->defaults.attr;
3143         attr.columns = columns;
3144
3145         if (G_UNLIKELY (c == '_' && terminal->pvt->flags.ul)) {
3146                 const VteCell *pcell = _vte_row_data_get (row, col);
3147                 /* Handle overstrike-style underlining. */
3148                 if (pcell->c != 0) {
3149                         /* restore previous contents */
3150                         c = pcell->c;
3151                         attr.columns = pcell->attr.columns;
3152                         attr.fragment = pcell->attr.fragment;
3153
3154                         attr.underline = 1;
3155                 }
3156         }
3157
3158
3159         {
3160                 VteCell *pcell = _vte_row_data_get_writable (row, col);
3161                 pcell->c = c;
3162                 pcell->attr = attr;
3163                 col++;
3164         }
3165
3166         /* insert wide-char fragments */
3167         attr.fragment = 1;
3168         for (i = 1; i < columns; i++) {
3169                 VteCell *pcell = _vte_row_data_get_writable (row, col);
3170                 pcell->c = c;
3171                 pcell->attr = attr;
3172                 col++;
3173         }
3174         _vte_row_data_shrink (row, terminal->column_count);
3175
3176         /* Signal that this part of the window needs drawing. */
3177         if (G_UNLIKELY (invalidate_now)) {
3178                 _vte_invalidate_cells(terminal,
3179                                 col - columns,
3180                                 insert ? terminal->column_count : columns,
3181                                 screen->cursor_current.row, 1);
3182         }
3183
3184
3185         /* If we're autowrapping *here*, do it. */
3186         screen->cursor_current.col = col;
3187         if (G_UNLIKELY (col >= terminal->column_count)) {
3188                 if (terminal->pvt->flags.am && !terminal->pvt->flags.xn) {
3189                         /* Wrap. */
3190                         screen->cursor_current.col = 0;
3191                         /* Mark this line as soft-wrapped. */
3192                         row->attr.soft_wrapped = 1;
3193                         _vte_terminal_cursor_down (terminal);
3194                 }
3195         }
3196
3197 done:
3198         /* We added text, so make a note of it. */
3199         terminal->pvt->text_inserted_flag = TRUE;
3200
3201 not_inserted:
3202         _vte_debug_print(VTE_DEBUG_ADJ|VTE_DEBUG_PARSE,
3203                         "insertion delta => %ld.\n",
3204                         (long)screen->insert_delta);
3205         return line_wrapped;
3206 }
3207
3208 /* Catch a VteReaper child-exited signal, and if it matches the one we're
3209  * looking for, emit one of our own. */
3210 static void
3211 vte_terminal_catch_child_exited(VteReaper *reaper, int pid, int status,
3212                                 VteTerminal *terminal)
3213 {
3214         if (pid == terminal->pvt->pty_pid) {
3215                 GObject *object = G_OBJECT(terminal);
3216
3217                 g_object_ref(object);
3218                 g_object_freeze_notify(object);
3219
3220                 _VTE_DEBUG_IF (VTE_DEBUG_LIFECYCLE) {
3221                         g_printerr ("Child[%d] exited with status %d\n",
3222                                         pid, status);
3223 #ifdef HAVE_SYS_WAIT_H
3224                         if (WIFEXITED (status)) {
3225                                 g_printerr ("Child[%d] exit code %d.\n",
3226                                                 pid, WEXITSTATUS (status));
3227                         }else if (WIFSIGNALED (status)) {
3228                                 g_printerr ("Child[%d] dies with signal %d.\n",
3229                                                 pid, WTERMSIG (status));
3230                         }
3231 #endif
3232                 }
3233                 /* Disconnect from the reaper. */
3234                 if (terminal->pvt->pty_reaper != NULL) {
3235                         g_signal_handlers_disconnect_by_func(terminal->pvt->pty_reaper,
3236                                                              vte_terminal_catch_child_exited,
3237                                                              terminal);
3238                         g_object_unref(terminal->pvt->pty_reaper);
3239                         terminal->pvt->pty_reaper = NULL;
3240                 }
3241                 terminal->pvt->pty_pid = -1;
3242
3243                 /* Close out the PTY. */
3244                 vte_terminal_set_pty_object(terminal, NULL);
3245
3246                 /* Tell observers what's happened. */
3247                 terminal->pvt->child_exit_status = status;
3248                 vte_terminal_emit_child_exited(terminal);
3249
3250                 g_object_thaw_notify(object);
3251                 g_object_unref(object);
3252
3253                 /* Note: terminal may be destroyed at this point */
3254         }
3255 }
3256
3257 static void mark_input_source_invalid(VteTerminal *terminal)
3258 {
3259         _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_read\n");
3260         terminal->pvt->pty_input_source = 0;
3261 }
3262 static void
3263 _vte_terminal_connect_pty_read(VteTerminal *terminal)
3264 {
3265         if (terminal->pvt->pty_channel == NULL) {
3266                 return;
3267         }
3268
3269         if (terminal->pvt->pty_input_source == 0) {
3270                 _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_read\n");
3271                 terminal->pvt->pty_input_source =
3272                         g_io_add_watch_full(terminal->pvt->pty_channel,
3273                                             VTE_CHILD_INPUT_PRIORITY,
3274                                             G_IO_IN | G_IO_HUP,
3275                                             (GIOFunc) vte_terminal_io_read,
3276                                             terminal,
3277                                             (GDestroyNotify) mark_input_source_invalid);
3278         }
3279 }
3280
3281 static void mark_output_source_invalid(VteTerminal *terminal)
3282 {
3283         _vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_write\n");
3284         terminal->pvt->pty_output_source = 0;
3285 }
3286 static void
3287 _vte_terminal_connect_pty_write(VteTerminal *terminal)
3288 {
3289         VteTerminalPrivate *pvt = terminal->pvt;
3290
3291         g_assert(pvt->pty != NULL);
3292         if (terminal->pvt->pty_channel == NULL) {
3293                 pvt->pty_channel =
3294                         g_io_channel_unix_new(vte_pty_get_fd(pvt->pty));
3295         }
3296
3297         if (terminal->pvt->pty_output_source == 0) {
3298                 if (vte_terminal_io_write (terminal->pvt->pty_channel,
3299                                              G_IO_OUT,
3300                                              terminal))
3301                 {
3302                         _vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_write\n");
3303                         terminal->pvt->pty_output_source =
3304                                 g_io_add_watch_full(terminal->pvt->pty_channel,
3305                                                     VTE_CHILD_OUTPUT_PRIORITY,
3306                                                     G_IO_OUT,
3307                                                     (GIOFunc) vte_terminal_io_write,
3308                                                     terminal,
3309                                                     (GDestroyNotify) mark_output_source_invalid);
3310                 }
3311         }
3312 }
3313
3314 static void
3315 _vte_terminal_disconnect_pty_read(VteTerminal *terminal)
3316 {
3317         if (terminal->pvt->pty_input_source != 0) {
3318                 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_read\n");
3319                 g_source_remove(terminal->pvt->pty_input_source);
3320                 terminal->pvt->pty_input_source = 0;
3321         }
3322 }
3323
3324 static void
3325 _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
3326 {
3327         if (terminal->pvt->pty_output_source != 0) {
3328                 _vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_write\n");
3329
3330                 g_source_remove(terminal->pvt->pty_output_source);
3331                 terminal->pvt->pty_output_source = 0;
3332         }
3333 }
3334
3335 /**
3336  * vte_terminal_pty_new:
3337  * @terminal: a #VteTerminal
3338  * @flags: flags from #VtePtyFlags
3339  * @error: (allow-none): return location for a #GError, or %NULL
3340  *
3341  * Creates a new #VtePty, and sets the emulation property
3342  * from #VteTerminal:emulation.
3343  *
3344  * See vte_pty_new() for more information.
3345  *
3346  * Returns: (transfer full): a new #VtePty
3347  * Since: 0.26
3348  */
3349 VtePty *
3350 vte_terminal_pty_new(VteTerminal *terminal,
3351                      VtePtyFlags flags,
3352                      GError **error)
3353 {
3354         VteTerminalPrivate *pvt;
3355         VtePty *pty;
3356
3357         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
3358
3359         pvt = terminal->pvt;
3360
3361         pty = vte_pty_new(flags, error);
3362         if (pty == NULL)
3363                 return NULL;
3364
3365         vte_pty_set_term(pty, vte_terminal_get_emulation(terminal));
3366
3367         return pty;
3368 }
3369
3370 /**
3371  * vte_terminal_watch_child:
3372  * @terminal: a #VteTerminal
3373  * @child_pid: a #GPid
3374  *
3375  * Watches @child_pid. When the process exists, the #VteReaper::child-exited
3376  * signal will be called. Use vte_terminal_get_child_exit_status() to
3377  * retrieve the child's exit status.
3378  *
3379  * Prior to calling this function, a #VtePty must have been set in @terminal
3380  * using vte_terminal_set_pty_object().
3381  * When the child exits, the terminal's #VtePty will be set to %NULL.
3382  *
3383  * Note: g_child_watch_add() or g_child_watch_add_full() must not have
3384  * been called for @child_pid, nor a #GSource for it been created with
3385  * g_child_watch_source_new().
3386  *
3387  * Note: when using the g_spawn_async() family of functions,
3388  * the %G_SPAWN_DO_NOT_REAP_CHILD flag MUST have been passed.
3389  *
3390  * Since: 0.26
3391  */
3392 void
3393 vte_terminal_watch_child (VteTerminal *terminal,
3394                           GPid child_pid)
3395 {
3396         VteTerminalPrivate *pvt;
3397         GObject *object;
3398         VteReaper *reaper;
3399
3400         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3401         g_return_if_fail(child_pid != -1);
3402
3403         pvt = terminal->pvt;
3404         g_return_if_fail(pvt->pty != NULL);
3405
3406         // FIXMEchpe: support passing child_pid = -1 to remove the wathch
3407
3408         object = G_OBJECT(terminal);
3409
3410         g_object_freeze_notify(object);
3411
3412         /* Set this as the child's pid. */
3413         pvt->pty_pid = child_pid;
3414         pvt->child_exit_status = 0;
3415
3416         /* Catch a child-exited signal from the child pid. */
3417         reaper = vte_reaper_get();
3418         vte_reaper_add_child(child_pid);
3419         if (reaper != pvt->pty_reaper) {
3420                 if (terminal->pvt->pty_reaper != NULL) {
3421                         g_signal_handlers_disconnect_by_func(pvt->pty_reaper,
3422                                         vte_terminal_catch_child_exited,
3423                                         terminal);
3424                         g_object_unref(pvt->pty_reaper);
3425                 }
3426                 g_signal_connect(reaper, "child-exited",
3427                                 G_CALLBACK(vte_terminal_catch_child_exited),
3428                                 terminal);
3429                 pvt->pty_reaper = reaper;
3430         } else {
3431                 g_object_unref(reaper);
3432         }
3433
3434         /* FIXMEchpe: call vte_terminal_set_size here? */
3435
3436         g_object_thaw_notify(object);
3437 }
3438
3439 /*
3440  * _vte_terminal_get_user_shell:
3441  *
3442  * Uses getpwd() to determine the user's shell. If that fails, falls back
3443  * to using the SHELL environment variable. As last-ditch fallback, returns
3444  * "/bin/sh".
3445  *
3446  * Returns: a newly allocated string containing the command to run the
3447  *   user's shell
3448  */
3449 static char *
3450 _vte_terminal_get_user_shell (void)
3451 {
3452         struct passwd *pwd;
3453         char *command;
3454
3455         pwd = getpwuid(getuid());
3456         if (pwd != NULL) {
3457                 command = g_strdup (pwd->pw_shell);
3458                 _vte_debug_print(VTE_DEBUG_MISC,
3459                                 "Using user's shell (%s).\n",
3460                                 command ? command : "(null)");
3461         }
3462         if (command == NULL) {
3463                 if (g_getenv ("SHELL")) {
3464                         command = g_strdup (g_getenv ("SHELL"));
3465                         _vte_debug_print(VTE_DEBUG_MISC,
3466                                          "Using $SHELL shell (%s).\n",
3467                                          command);
3468                 } else {
3469                         command = g_strdup ("/bin/sh");
3470                         _vte_debug_print(VTE_DEBUG_MISC,
3471                                          "Using default shell (%s).\n",
3472                                          command);
3473                 }
3474         }
3475