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