1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Copyright (C) 2001-2004,2009,2010 Red Hat, Inc.
4  * Copyright © 2008, 2009, 2010 Christian Persch
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 /**
22  * SECTION: vte-terminal
23  * @short_description: A terminal widget implementation
24  *
25  * A VteTerminal is a terminal emulator implemented as a GTK3 widget.
26  */
27 
28 #include <config.h>
29 
30 #include <math.h>
31 
32 #include "vte.h"
33 #include "vte-private.h"
34 
35 #ifdef HAVE_WCHAR_H
36 #include <wchar.h>
37 #endif
38 #ifdef HAVE_SYS_SYSLIMITS_H
39 #include <sys/syslimits.h>
40 #endif
41 #ifdef HAVE_SYS_WAIT_H
42 #include <sys/wait.h>
43 #endif
44 #include <glib.h>
45 #include <glib/gstdio.h>
46 #include <glib-object.h>
47 #include <gdk/gdk.h>
48 #include <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 "vteaccess.h"
55 #include "vteint.h"
56 #include "vtepty.h"
57 #include "vtepty-private.h"
58 
59 #ifdef HAVE_LOCALE_H
60 #include <locale.h>
61 #endif
62 
63 #ifndef HAVE_ROUND
round(double x)64 static inline double round(double x) {
65 	if(x - floor(x) < 0.5) {
66 		return floor(x);
67 	} else {
68 		return ceil(x);
69 	}
70 }
71 #endif
72 
73 #ifndef HAVE_WINT_T
74 typedef gunichar wint_t;
75 #endif
76 
77 #if !GLIB_CHECK_VERSION(2, 41, 2)
78 #define G_PARAM_EXPLICIT_NOTIFY 0
79 #endif
80 
81 #ifndef howmany
82 #define howmany(x, y) (((x) + ((y) - 1)) / (y))
83 #endif
84 
85 #define WORD_CHAR_EXCEPTIONS_DEFAULT "-#%&+,./=?@\\_~\302\267"
86 
87 static int _vte_unichar_width(gunichar c, int utf8_ambiguous_width);
88 static void vte_terminal_set_visibility (VteTerminal *terminal, GdkVisibilityState state);
89 static void vte_terminal_paste(VteTerminal *terminal, GdkAtom board);
90 static void vte_terminal_real_copy_clipboard(VteTerminal *terminal);
91 static void vte_terminal_real_paste_clipboard(VteTerminal *terminal);
92 static gboolean vte_terminal_io_read(GIOChannel *channel,
93 				     GIOCondition condition,
94 				     VteTerminal *terminal);
95 static gboolean vte_terminal_io_write(GIOChannel *channel,
96 				      GIOCondition condition,
97 				      VteTerminal *terminal);
98 static void vte_terminal_match_hilite_clear(VteTerminal *terminal);
99 static void vte_terminal_match_hilite_hide(VteTerminal *terminal);
100 static void vte_terminal_match_hilite_show(VteTerminal *terminal, long x, long y);
101 static void vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y);
102 static void vte_terminal_match_contents_clear(VteTerminal *terminal);
103 static void vte_terminal_background_update(VteTerminal *data);
104 static void vte_terminal_process_incoming(VteTerminal *terminal);
105 static void vte_terminal_emit_pending_signals(VteTerminal *terminal);
106 static gboolean vte_cell_is_selected(VteTerminal *terminal,
107 				     glong col, glong row, gpointer data);
108 static void vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
109                                           gboolean always_grow, gboolean force);
110 static char *vte_terminal_get_text_range_maybe_wrapped(VteTerminal *terminal,
111 						       glong start_row,
112 						       glong start_col,
113 						       glong end_row,
114 						       glong end_col,
115 						       gboolean wrap,
116 						       VteSelectionFunc is_selected,
117 						       gpointer data,
118 						       GArray *attributes,
119 						       gboolean include_trailing_spaces);
120 static char *vte_terminal_get_text_maybe_wrapped(VteTerminal *terminal,
121 						 gboolean wrap,
122 						 VteSelectionFunc is_selected,
123 						 gpointer data,
124 						 GArray *attributes,
125 						 gboolean include_trailing_spaces);
126 static void _vte_terminal_disconnect_pty_read(VteTerminal *terminal);
127 static void _vte_terminal_disconnect_pty_write(VteTerminal *terminal);
128 static void vte_terminal_stop_processing (VteTerminal *terminal);
129 
130 static inline gboolean vte_terminal_is_processing (VteTerminal *terminal);
131 static inline void vte_terminal_start_processing (VteTerminal *terminal);
132 static void vte_terminal_add_process_timeout (VteTerminal *terminal);
133 static void add_update_timeout (VteTerminal *terminal);
134 static void remove_update_timeout (VteTerminal *terminal);
135 static void reset_update_regions (VteTerminal *terminal);
136 static void vte_terminal_update_cursor_blinks_internal(VteTerminal *terminal);
137 static void _vte_check_cursor_blink(VteTerminal *terminal);
138 static VteCursorShape _vte_terminal_decscusr_cursor_shape(VteTerminal *terminal);
139 static VteCursorBlinkMode _vte_terminal_decscusr_cursor_blink(VteTerminal *terminal);
140 
141 static gboolean process_timeout (gpointer data);
142 static gboolean update_timeout (gpointer data);
143 static cairo_region_t *vte_cairo_get_clip_region (cairo_t *cr);
144 
145 enum {
146     COPY_CLIPBOARD,
147     PASTE_CLIPBOARD,
148     LAST_SIGNAL
149 };
150 static guint signals[LAST_SIGNAL];
151 
152 enum {
153         PROP_0,
154         PROP_HADJUSTMENT,
155         PROP_VADJUSTMENT,
156         PROP_HSCROLL_POLICY,
157         PROP_VSCROLL_POLICY,
158         PROP_ALLOW_BOLD,
159         PROP_AUDIBLE_BELL,
160         PROP_BACKSPACE_BINDING,
161         PROP_CJK_AMBIGUOUS_WIDTH,
162         PROP_CURSOR_BLINK_MODE,
163         PROP_CURSOR_SHAPE,
164         PROP_CURRENT_DIRECTORY_URI,
165         PROP_CURRENT_FILE_URI,
166         PROP_DELETE_BINDING,
167         PROP_ENCODING,
168         PROP_FONT_DESC,
169         PROP_FONT_SCALE,
170         PROP_ICON_TITLE,
171         PROP_INPUT_ENABLED,
172         PROP_MOUSE_POINTER_AUTOHIDE,
173         PROP_PTY,
174         PROP_REWRAP_ON_RESIZE,
175         PROP_SCROLLBACK_LINES,
176         PROP_SCROLL_ON_KEYSTROKE,
177         PROP_SCROLL_ON_OUTPUT,
178         PROP_WINDOW_TITLE,
179         PROP_WORD_CHAR_EXCEPTIONS
180 };
181 
182 /* these static variables are guarded by the GDK mutex */
183 static guint process_timeout_tag = 0;
184 static gboolean in_process_timeout;
185 static guint update_timeout_tag = 0;
186 static gboolean in_update_timeout;
187 static GList *active_terminals;
188 static GTimer *process_timer;
189 
190 static const GtkBorder default_padding = { 1, 1, 1, 1 };
191 
192 static int
_vte_unichar_width(gunichar c,int utf8_ambiguous_width)193 _vte_unichar_width(gunichar c, int utf8_ambiguous_width)
194 {
195         if (G_LIKELY (c < 0x80))
196                 return 1;
197         if (G_UNLIKELY (g_unichar_iszerowidth (c)))
198                 return 0;
199         if (G_UNLIKELY (g_unichar_iswide (c)))
200                 return 2;
201         if (G_LIKELY (utf8_ambiguous_width == 1))
202                 return 1;
203         if (G_UNLIKELY (g_unichar_iswide_cjk (c)))
204                 return 2;
205         return 1;
206 }
207 
208 /* process incoming data without copying */
209 static struct _vte_incoming_chunk *free_chunks;
210 static struct _vte_incoming_chunk *
get_chunk(void)211 get_chunk (void)
212 {
213 	struct _vte_incoming_chunk *chunk = NULL;
214 	if (free_chunks) {
215 		chunk = free_chunks;
216 		free_chunks = free_chunks->next;
217 	}
218 	if (chunk == NULL) {
219 		chunk = g_new (struct _vte_incoming_chunk, 1);
220 	}
221 	chunk->next = NULL;
222 	chunk->len = 0;
223 	return chunk;
224 }
225 static void
release_chunk(struct _vte_incoming_chunk * chunk)226 release_chunk (struct _vte_incoming_chunk *chunk)
227 {
228 	chunk->next = free_chunks;
229 	chunk->len = free_chunks ? free_chunks->len + 1 : 0;
230 	free_chunks = chunk;
231 }
232 static void
prune_chunks(guint len)233 prune_chunks (guint len)
234 {
235 	struct _vte_incoming_chunk *chunk = NULL;
236 	if (len && free_chunks != NULL) {
237 	    if (free_chunks->len > len) {
238 		struct _vte_incoming_chunk *last;
239 		chunk = free_chunks;
240 		while (free_chunks->len > len) {
241 		    last = free_chunks;
242 		    free_chunks = free_chunks->next;
243 		}
244 		last->next = NULL;
245 	    }
246 	} else {
247 	    chunk = free_chunks;
248 	    free_chunks = NULL;
249 	}
250 	while (chunk != NULL) {
251 		struct _vte_incoming_chunk *next = chunk->next;
252 		g_free (chunk);
253 		chunk = next;
254 	}
255 }
256 static void
_vte_incoming_chunks_release(struct _vte_incoming_chunk * chunk)257 _vte_incoming_chunks_release (struct _vte_incoming_chunk *chunk)
258 {
259 	while (chunk) {
260 		struct _vte_incoming_chunk *next = chunk->next;
261 		release_chunk (chunk);
262 		chunk = next;
263 	}
264 }
265 static gsize
_vte_incoming_chunks_length(struct _vte_incoming_chunk * chunk)266 _vte_incoming_chunks_length (struct _vte_incoming_chunk *chunk)
267 {
268 	gsize len = 0;
269 	while (chunk) {
270 		len += chunk->len;
271 		chunk = chunk->next;
272 	}
273 	return len;
274 }
275 static gsize
_vte_incoming_chunks_count(struct _vte_incoming_chunk * chunk)276 _vte_incoming_chunks_count (struct _vte_incoming_chunk *chunk)
277 {
278 	gsize cnt = 0;
279 	while (chunk) {
280 		cnt ++;
281 		chunk = chunk->next;
282 	}
283 	return cnt;
284 }
285 static struct _vte_incoming_chunk *
_vte_incoming_chunks_reverse(struct _vte_incoming_chunk * chunk)286 _vte_incoming_chunks_reverse(struct _vte_incoming_chunk *chunk)
287 {
288 	struct _vte_incoming_chunk *prev = NULL;
289 	while (chunk) {
290 		struct _vte_incoming_chunk *next = chunk->next;
291 		chunk->next = prev;
292 		prev = chunk;
293 		chunk = next;
294 	}
295 	return prev;
296 }
297 
298 #ifdef VTE_DEBUG
G_DEFINE_TYPE_WITH_CODE(VteTerminal,vte_terminal,GTK_TYPE_WIDGET,g_type_add_class_private (g_define_type_id,sizeof (VteTerminalClassPrivate));G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL)if (_vte_debug_on (VTE_DEBUG_LIFECYCLE)){ g_printerr("vte_terminal_get_type()\\n"); })299 G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET,
300                         g_type_add_class_private (g_define_type_id, sizeof (VteTerminalClassPrivate));
301                         G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL)
302                         if (_vte_debug_on(VTE_DEBUG_LIFECYCLE)) {
303                                 g_printerr("vte_terminal_get_type()\n");
304                         })
305 #else
306 G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET,
307                         g_type_add_class_private (g_define_type_id, sizeof (VteTerminalClassPrivate));
308                         G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL))
309 #endif
310 
311 static void
312 vte_g_array_fill(GArray *array, gconstpointer item, guint final_size)
313 {
314 	if (array->len >= final_size)
315 		return;
316 
317 	final_size -= array->len;
318 	do {
319 		g_array_append_vals(array, item, 1);
320 	} while (--final_size);
321 }
322 
323 
324 VteRowData *
_vte_terminal_ring_insert(VteTerminal * terminal,glong position,gboolean fill)325 _vte_terminal_ring_insert (VteTerminal *terminal, glong position, gboolean fill)
326 {
327 	VteRowData *row;
328 	VteRing *ring = terminal->pvt->screen->row_data;
329 	while (G_UNLIKELY (_vte_ring_next (ring) < position)) {
330 		row = _vte_ring_append (ring);
331                 if (terminal->pvt->fill_defaults.attr.back != VTE_DEFAULT_BG)
332                         _vte_row_data_fill (row, &terminal->pvt->fill_defaults, terminal->pvt->column_count);
333 	}
334 	row = _vte_ring_insert (ring, position);
335         if (fill && terminal->pvt->fill_defaults.attr.back != VTE_DEFAULT_BG)
336                 _vte_row_data_fill (row, &terminal->pvt->fill_defaults, terminal->pvt->column_count);
337 	return row;
338 }
339 
340 VteRowData *
_vte_terminal_ring_append(VteTerminal * terminal,gboolean fill)341 _vte_terminal_ring_append (VteTerminal *terminal, gboolean fill)
342 {
343 	return _vte_terminal_ring_insert (terminal, _vte_ring_next (terminal->pvt->screen->row_data), fill);
344 }
345 
346 void
_vte_terminal_ring_remove(VteTerminal * terminal,glong position)347 _vte_terminal_ring_remove (VteTerminal *terminal, glong position)
348 {
349 	_vte_ring_remove (terminal->pvt->screen->row_data, position);
350 }
351 
352 /* Reset defaults for character insertion. */
353 void
_vte_terminal_set_default_attributes(VteTerminal * terminal)354 _vte_terminal_set_default_attributes(VteTerminal *terminal)
355 {
356         terminal->pvt->defaults = basic_cell.cell;
357         terminal->pvt->color_defaults = terminal->pvt->defaults;
358         terminal->pvt->fill_defaults = terminal->pvt->defaults;
359 }
360 
361 /* Cause certain cells to be repainted. */
362 void
_vte_invalidate_cells(VteTerminal * terminal,glong column_start,gint column_count,glong row_start,gint row_count)363 _vte_invalidate_cells(VteTerminal *terminal,
364 		      glong column_start, gint column_count,
365 		      glong row_start, gint row_count)
366 {
367 	cairo_rectangle_int_t rect;
368 	glong i;
369 
370 	if (G_UNLIKELY (!gtk_widget_get_realized(&terminal->widget)))
371                 return;
372 
373 	if (!column_count || !row_count) {
374 		return;
375 	}
376 
377 	if (terminal->pvt->invalidated_all) {
378 		return;
379 	}
380 
381 	_vte_debug_print (VTE_DEBUG_UPDATES,
382 			"Invalidating cells at (%ld,%ld+%ld)x(%d,%d).\n",
383 			column_start, row_start,
384 			(long)terminal->pvt->screen->scroll_delta,
385 			column_count, row_count);
386 	_vte_debug_print (VTE_DEBUG_WORK, "?");
387 
388 	/* Subtract the scrolling offset from the row start so that the
389 	 * resulting rectangle is relative to the visible portion of the
390 	 * buffer. */
391 	row_start -= terminal->pvt->screen->scroll_delta;
392 
393 	/* Ensure the start of region is on screen */
394 	if (column_start > terminal->pvt->column_count ||
395 			row_start > terminal->pvt->row_count) {
396 		return;
397 	}
398 
399 	/* Clamp the start values to reasonable numbers. */
400 	i = row_start + row_count;
401 	row_start = MAX (0, row_start);
402 	row_count = CLAMP (i - row_start, 0, terminal->pvt->row_count);
403 
404 	i = column_start + column_count;
405 	column_start = MAX (0, column_start);
406 	column_count = CLAMP (i - column_start, 0 , terminal->pvt->column_count);
407 
408 	if (!column_count || !row_count) {
409 		return;
410 	}
411 	if (column_count == terminal->pvt->column_count &&
412 			row_count == terminal->pvt->row_count) {
413 		_vte_invalidate_all (terminal);
414 		return;
415 	}
416 
417 	/* Convert the column and row start and end to pixel values
418 	 * by multiplying by the size of a character cell.
419 	 * Always include the extra pixel border and overlap pixel.
420 	 */
421 	rect.x = column_start * terminal->pvt->char_width - 1;
422 	if (column_start != 0) {
423 		rect.x += terminal->pvt->padding.left;
424 	}
425 	rect.width = (column_start + column_count) * terminal->pvt->char_width + 3 + terminal->pvt->padding.left;
426 	if (column_start + column_count == terminal->pvt->column_count) {
427 		rect.width += terminal->pvt->padding.right;
428 	}
429 	rect.width -= rect.x;
430 
431 	rect.y = row_start * terminal->pvt->char_height - 1;
432 	if (row_start != 0) {
433 		rect.y += terminal->pvt->padding.top;
434 	}
435 	rect.height = (row_start + row_count) * terminal->pvt->char_height + 2 + terminal->pvt->padding.top;
436 	if (row_start + row_count == terminal->pvt->row_count) {
437 		rect.height += terminal->pvt->padding.bottom;
438 	}
439 	rect.height -= rect.y;
440 
441 	_vte_debug_print (VTE_DEBUG_UPDATES,
442 			"Invalidating pixels at (%d,%d)x(%d,%d).\n",
443 			rect.x, rect.y, rect.width, rect.height);
444 
445 	if (terminal->pvt->active != NULL) {
446 		terminal->pvt->update_regions = g_slist_prepend (
447 				terminal->pvt->update_regions,
448 				cairo_region_create_rectangle (&rect));
449 		/* Wait a bit before doing any invalidation, just in
450 		 * case updates are coming in really soon. */
451 		add_update_timeout (terminal);
452 	} else {
453 		gdk_window_invalidate_rect (gtk_widget_get_window (&terminal->widget), &rect, FALSE);
454 	}
455 
456 	_vte_debug_print (VTE_DEBUG_WORK, "!");
457 }
458 
459 static void
_vte_invalidate_region(VteTerminal * terminal,glong scolumn,glong ecolumn,glong srow,glong erow,gboolean block)460 _vte_invalidate_region (VteTerminal *terminal,
461 			glong scolumn, glong ecolumn,
462 			glong srow, glong erow,
463 			gboolean block)
464 {
465 	if (block || srow == erow) {
466 		_vte_invalidate_cells(terminal,
467 				scolumn, ecolumn - scolumn + 1,
468 				srow, erow - srow + 1);
469 	} else {
470 		_vte_invalidate_cells(terminal,
471 				scolumn,
472 				terminal->pvt->column_count - scolumn,
473 				srow, 1);
474 		_vte_invalidate_cells(terminal,
475 				0, terminal->pvt->column_count,
476 				srow + 1, erow - srow - 1);
477 		_vte_invalidate_cells(terminal,
478 				0, ecolumn + 1,
479 				erow, 1);
480 	}
481 }
482 
483 
484 /* Redraw the entire visible portion of the window. */
485 void
_vte_invalidate_all(VteTerminal * terminal)486 _vte_invalidate_all(VteTerminal *terminal)
487 {
488 	cairo_rectangle_int_t rect;
489 	GtkAllocation allocation;
490 
491 	g_assert(VTE_IS_TERMINAL(terminal));
492 
493 	if (G_UNLIKELY (!gtk_widget_get_realized(&terminal->widget)))
494                 return;
495 
496 	if (terminal->pvt->invalidated_all) {
497 		return;
498 	}
499 
500 	_vte_debug_print (VTE_DEBUG_WORK, "*");
501 	_vte_debug_print (VTE_DEBUG_UPDATES, "Invalidating all.\n");
502 
503 	gtk_widget_get_allocation (&terminal->widget, &allocation);
504 
505 	/* replace invalid regions with one covering the whole terminal */
506 	reset_update_regions (terminal);
507 	rect.x = rect.y = 0;
508 	rect.width = allocation.width;
509 	rect.height = allocation.height;
510 	terminal->pvt->invalidated_all = TRUE;
511 
512 	if (terminal->pvt->active != NULL) {
513 		terminal->pvt->update_regions = g_slist_prepend (NULL,
514 				cairo_region_create_rectangle (&rect));
515 		/* Wait a bit before doing any invalidation, just in
516 		 * case updates are coming in really soon. */
517 		add_update_timeout (terminal);
518 	} else {
519 		gdk_window_invalidate_rect (gtk_widget_get_window (&terminal->widget), &rect, FALSE);
520 	}
521 }
522 
523 /* Scroll a rectangular region up or down by a fixed number of lines,
524  * negative = up, positive = down. */
525 void
_vte_terminal_scroll_region(VteTerminal * terminal,long row,glong count,glong delta)526 _vte_terminal_scroll_region (VteTerminal *terminal,
527 			     long row, glong count, glong delta)
528 {
529 	if ((delta == 0) || (count == 0)) {
530 		/* Shenanigans! */
531 		return;
532 	}
533 
534 	if (count >= terminal->pvt->row_count) {
535 		/* We have to repaint the entire window. */
536 		_vte_invalidate_all(terminal);
537 	} else {
538 		/* We have to repaint the area which is to be
539 		 * scrolled. */
540 		_vte_invalidate_cells(terminal,
541 				     0, terminal->pvt->column_count,
542 				     row, count);
543 	}
544 }
545 
546 /* Find the row in the given position in the backscroll buffer. */
547 static inline const VteRowData *
_vte_terminal_find_row_data(VteTerminal * terminal,glong row)548 _vte_terminal_find_row_data (VteTerminal *terminal, glong row)
549 {
550 	const VteRowData *rowdata = NULL;
551 	VteScreen *screen = terminal->pvt->screen;
552 	if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
553 		rowdata = _vte_ring_index (screen->row_data, row);
554 	}
555 	return rowdata;
556 }
557 
558 /* Find the row in the given position in the backscroll buffer. */
559 static inline VteRowData *
_vte_terminal_find_row_data_writable(VteTerminal * terminal,glong row)560 _vte_terminal_find_row_data_writable (VteTerminal *terminal, glong row)
561 {
562 	VteRowData *rowdata = NULL;
563 	VteScreen *screen = terminal->pvt->screen;
564 	if (G_LIKELY (_vte_ring_contains (screen->row_data, row))) {
565 		rowdata = _vte_ring_index_writable (screen->row_data, row);
566 	}
567 	return rowdata;
568 }
569 
570 /* Find the character an the given position in the backscroll buffer. */
571 static const VteCell *
vte_terminal_find_charcell(VteTerminal * terminal,gulong col,glong row)572 vte_terminal_find_charcell(VteTerminal *terminal, gulong col, glong row)
573 {
574 	const VteRowData *rowdata;
575 	const VteCell *ret = NULL;
576 	VteScreen *screen;
577 	screen = terminal->pvt->screen;
578 	if (_vte_ring_contains (screen->row_data, row)) {
579 		rowdata = _vte_ring_index (screen->row_data, row);
580 		ret = _vte_row_data_get (rowdata, col);
581 	}
582 	return ret;
583 }
584 
585 static glong
find_start_column(VteTerminal * terminal,glong col,glong row)586 find_start_column (VteTerminal *terminal, glong col, glong row)
587 {
588 	const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
589 	if (G_UNLIKELY (col < 0))
590 		return col;
591 	if (row_data != NULL) {
592 		const VteCell *cell = _vte_row_data_get (row_data, col);
593 		while (col > 0 && cell != NULL && cell->attr.fragment) {
594 			cell = _vte_row_data_get (row_data, --col);
595 		}
596 	}
597 	return MAX(col, 0);
598 }
599 static glong
find_end_column(VteTerminal * terminal,glong col,glong row)600 find_end_column (VteTerminal *terminal, glong col, glong row)
601 {
602 	const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
603 	gint columns = 0;
604 	if (G_UNLIKELY (col < 0))
605 		return col;
606 	if (row_data != NULL) {
607 		const VteCell *cell = _vte_row_data_get (row_data, col);
608 		while (col > 0 && cell != NULL && cell->attr.fragment) {
609 			cell = _vte_row_data_get (row_data, --col);
610 		}
611 		if (cell) {
612 			columns = cell->attr.columns - 1;
613 		}
614 	}
615 	return MIN(col + columns, terminal->pvt->column_count);
616 }
617 
618 
619 /* Determine the width of the portion of the preedit string which lies
620  * to the left of the cursor, or the entire string, in columns. */
621 static gssize
vte_terminal_preedit_width(VteTerminal * terminal,gboolean left_only)622 vte_terminal_preedit_width(VteTerminal *terminal, gboolean left_only)
623 {
624 	gunichar c;
625 	int i;
626 	gssize ret = 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 			c = g_utf8_get_char(preedit);
637                         ret += _vte_unichar_width(c, terminal->pvt->utf8_ambiguous_width);
638 			preedit = g_utf8_next_char(preedit);
639 		}
640 	}
641 
642 	return ret;
643 }
644 
645 /* Determine the length of the portion of the preedit string which lies
646  * to the left of the cursor, or the entire string, in gunichars. */
647 static gssize
vte_terminal_preedit_length(VteTerminal * terminal,gboolean left_only)648 vte_terminal_preedit_length(VteTerminal *terminal, gboolean left_only)
649 {
650 	int i = 0;
651 	const char *preedit = NULL;
652 
653 	if (terminal->pvt->im_preedit != NULL) {
654 		preedit = terminal->pvt->im_preedit;
655 		for (i = 0;
656 		     (preedit != NULL) &&
657 		     (preedit[0] != '\0') &&
658 		     (!left_only || (i < terminal->pvt->im_preedit_cursor));
659 		     i++) {
660 			preedit = g_utf8_next_char(preedit);
661 		}
662 	}
663 
664 	return i;
665 }
666 
667 /* Cause the cell to be redrawn. */
668 void
_vte_invalidate_cell(VteTerminal * terminal,glong col,glong row)669 _vte_invalidate_cell(VteTerminal *terminal, glong col, glong row)
670 {
671 	const VteRowData *row_data;
672 	int columns;
673 	guint style;
674 
675 	if (G_UNLIKELY (!gtk_widget_get_realized(&terminal->widget)))
676                 return;
677 
678 	if (terminal->pvt->invalidated_all) {
679 		return;
680 	}
681 
682 	columns = 1;
683 	row_data = _vte_terminal_find_row_data(terminal, row);
684 	if (row_data != NULL) {
685 		const VteCell *cell;
686 		cell = _vte_row_data_get (row_data, col);
687 		if (cell != NULL) {
688 			while (cell->attr.fragment && col> 0) {
689 				cell = _vte_row_data_get (row_data, --col);
690 			}
691 			columns = cell->attr.columns;
692 			style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
693 			if (cell->c != 0 &&
694 					_vte_draw_get_char_width (
695 						terminal->pvt->draw,
696 						cell->c, columns, style) >
697 					terminal->pvt->char_width * columns) {
698 				columns++;
699 			}
700 		}
701 	}
702 
703 	_vte_debug_print(VTE_DEBUG_UPDATES,
704 			"Invalidating cell at (%ld,%ld-%ld).\n",
705 			row, col, col + columns);
706 	_vte_invalidate_cells(terminal,
707 			col, columns,
708 			row, 1);
709 }
710 
711 /* Cause the cursor to be redrawn. */
712 void
_vte_invalidate_cursor_once(VteTerminal * terminal,gboolean periodic)713 _vte_invalidate_cursor_once(VteTerminal *terminal, gboolean periodic)
714 {
715 	const VteCell *cell;
716 	gssize preedit_width;
717 	glong column, row;
718 	gint columns;
719 	guint style;
720 
721         if (G_UNLIKELY(!gtk_widget_get_realized(&terminal->widget)))
722                 return;
723 
724 	if (terminal->pvt->invalidated_all) {
725 		return;
726 	}
727 
728 	if (periodic) {
729 		if (!terminal->pvt->cursor_blinks) {
730 			return;
731 		}
732 	}
733 
734 	if (terminal->pvt->cursor_visible) {
735 		preedit_width = vte_terminal_preedit_width(terminal, FALSE);
736 
737                 row = terminal->pvt->cursor.row;
738                 column = terminal->pvt->cursor.col;
739 		columns = 1;
740 		column = find_start_column (terminal, column, row);
741 		cell = vte_terminal_find_charcell(terminal, column, row);
742 		if (cell != NULL) {
743 			columns = cell->attr.columns;
744 			style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
745 			if (cell->c != 0 &&
746 					_vte_draw_get_char_width (
747 						terminal->pvt->draw,
748 						cell->c,
749 						columns, style) >
750 			    terminal->pvt->char_width * columns) {
751 				columns++;
752 			}
753 		}
754 		if (preedit_width > 0) {
755 			columns += preedit_width;
756 			columns++; /* one more for the preedit cursor */
757 		}
758 
759 		_vte_debug_print(VTE_DEBUG_UPDATES,
760 				"Invalidating cursor at (%ld,%ld-%ld).\n",
761 				row, column, column + columns);
762 		_vte_invalidate_cells(terminal,
763 				     column, columns,
764 				     row, 1);
765 	}
766 }
767 
768 /* Invalidate the cursor repeatedly. */
769 static gboolean
vte_invalidate_cursor_periodic(VteTerminal * terminal)770 vte_invalidate_cursor_periodic (VteTerminal *terminal)
771 {
772         VteTerminalPrivate *pvt = terminal->pvt;
773 
774 	pvt->cursor_blink_state = !pvt->cursor_blink_state;
775 	pvt->cursor_blink_time += pvt->cursor_blink_cycle;
776 
777 	_vte_invalidate_cursor_once(terminal, TRUE);
778 
779 	/* only disable the blink if the cursor is currently shown.
780 	 * else, wait until next time.
781 	 */
782 	if (pvt->cursor_blink_time / 1000 >= pvt->cursor_blink_timeout &&
783 	    pvt->cursor_blink_state) {
784                 pvt->cursor_blink_tag = 0;
785 		return FALSE;
786         }
787 
788 	pvt->cursor_blink_tag = g_timeout_add_full(G_PRIORITY_LOW,
789 						   terminal->pvt->cursor_blink_cycle,
790 						   (GSourceFunc)vte_invalidate_cursor_periodic,
791 						   terminal,
792 						   NULL);
793 	return FALSE;
794 }
795 
796 /* Emit a "selection_changed" signal. */
797 static void
vte_terminal_emit_selection_changed(VteTerminal * terminal)798 vte_terminal_emit_selection_changed(VteTerminal *terminal)
799 {
800 	_vte_debug_print(VTE_DEBUG_SIGNALS,
801 			"Emitting `selection-changed'.\n");
802 	g_signal_emit_by_name(terminal, "selection-changed");
803 }
804 
805 /* Emit a "commit" signal. */
806 static void
vte_terminal_emit_commit(VteTerminal * terminal,const gchar * text,gssize length)807 vte_terminal_emit_commit(VteTerminal *terminal, const gchar *text, gssize length)
808 {
809 	const char *result = NULL;
810 	char *wrapped = NULL;
811 
812 	_vte_debug_print(VTE_DEBUG_SIGNALS,
813 			"Emitting `commit' of %" G_GSSIZE_FORMAT" bytes.\n", length);
814 
815 	if (length == -1) {
816 		length = strlen(text);
817 		result = text;
818 	} else {
819 		result = wrapped = g_slice_alloc(length + 1);
820 		memcpy(wrapped, text, length);
821 		wrapped[length] = '\0';
822 	}
823 
824 	g_signal_emit_by_name(terminal, "commit", result, length);
825 
826 	if(wrapped)
827 		g_slice_free1(length+1, wrapped);
828 }
829 
830 /* Emit an "encoding-changed" signal. */
831 static void
vte_terminal_emit_encoding_changed(VteTerminal * terminal)832 vte_terminal_emit_encoding_changed(VteTerminal *terminal)
833 {
834 	_vte_debug_print(VTE_DEBUG_SIGNALS,
835 			"Emitting `encoding-changed'.\n");
836 	g_signal_emit_by_name(terminal, "encoding-changed");
837         g_object_notify(G_OBJECT(terminal), "encoding");
838 }
839 
840 /* Emit a "child-exited" signal. */
841 static void
vte_terminal_emit_child_exited(VteTerminal * terminal,int status)842 vte_terminal_emit_child_exited(VteTerminal *terminal,
843                                int status)
844 {
845 	_vte_debug_print(VTE_DEBUG_SIGNALS,
846 			"Emitting `child-exited'.\n");
847 	g_signal_emit_by_name(terminal, "child-exited", status);
848 }
849 
850 /* Emit a "contents_changed" signal. */
851 static void
vte_terminal_emit_contents_changed(VteTerminal * terminal)852 vte_terminal_emit_contents_changed(VteTerminal *terminal)
853 {
854 	if (terminal->pvt->contents_changed_pending) {
855 		/* Update dingus match set. */
856 		vte_terminal_match_contents_clear(terminal);
857 		if (terminal->pvt->mouse_cursor_visible) {
858 			vte_terminal_match_hilite_update(terminal,
859 					terminal->pvt->mouse_last_x,
860 					terminal->pvt->mouse_last_y);
861 		}
862 
863 		_vte_debug_print(VTE_DEBUG_SIGNALS,
864 				"Emitting `contents-changed'.\n");
865 		g_signal_emit_by_name(terminal, "contents-changed");
866 		terminal->pvt->contents_changed_pending = FALSE;
867 	}
868 }
869 void
_vte_terminal_queue_contents_changed(VteTerminal * terminal)870 _vte_terminal_queue_contents_changed(VteTerminal *terminal)
871 {
872 	_vte_debug_print(VTE_DEBUG_SIGNALS,
873 			"Queueing `contents-changed'.\n");
874 	terminal->pvt->contents_changed_pending = TRUE;
875 }
876 
877 /* Emit a "cursor_moved" signal. */
878 static void
vte_terminal_emit_cursor_moved(VteTerminal * terminal)879 vte_terminal_emit_cursor_moved(VteTerminal *terminal)
880 {
881 	if (terminal->pvt->cursor_moved_pending) {
882 		_vte_debug_print(VTE_DEBUG_SIGNALS,
883 				"Emitting `cursor-moved'.\n");
884 		g_signal_emit_by_name(terminal, "cursor-moved");
885 		terminal->pvt->cursor_moved_pending = FALSE;
886 	}
887 }
888 static void
vte_terminal_queue_cursor_moved(VteTerminal * terminal)889 vte_terminal_queue_cursor_moved(VteTerminal *terminal)
890 {
891 	_vte_debug_print(VTE_DEBUG_SIGNALS,
892 			"Queueing `cursor-moved'.\n");
893 	terminal->pvt->cursor_moved_pending = TRUE;
894 }
895 
896 static gboolean
vte_terminal_emit_eof(VteTerminal * terminal)897 vte_terminal_emit_eof(VteTerminal *terminal)
898 {
899 	_vte_debug_print(VTE_DEBUG_SIGNALS,
900 			"Emitting `eof'.\n");
901         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
902 	gdk_threads_enter ();
903         G_GNUC_END_IGNORE_DEPRECATIONS;
904 
905 	g_signal_emit_by_name(terminal, "eof");
906 
907         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
908 	gdk_threads_leave ();
909         G_GNUC_END_IGNORE_DEPRECATIONS;
910 
911 	return FALSE;
912 }
913 /* Emit a "eof" signal. */
914 static void
vte_terminal_queue_eof(VteTerminal * terminal)915 vte_terminal_queue_eof(VteTerminal *terminal)
916 {
917 	_vte_debug_print(VTE_DEBUG_SIGNALS,
918 			"Queueing `eof'.\n");
919 	g_idle_add_full (G_PRIORITY_HIGH,
920 		(GSourceFunc) vte_terminal_emit_eof,
921 		g_object_ref (terminal),
922 		g_object_unref);
923 }
924 
925 /* Emit a "char-size-changed" signal. */
926 static void
vte_terminal_emit_char_size_changed(VteTerminal * terminal,guint width,guint height)927 vte_terminal_emit_char_size_changed(VteTerminal *terminal,
928 				    guint width, guint height)
929 {
930 	_vte_debug_print(VTE_DEBUG_SIGNALS,
931 			"Emitting `char-size-changed'.\n");
932 	g_signal_emit_by_name(terminal, "char-size-changed",
933 			      width, height);
934 /*         g_object_notify(G_OBJECT(terminal), "char-size"); */
935 }
936 
937 /* Emit an "increase-font-size" signal. */
938 static void
vte_terminal_emit_increase_font_size(VteTerminal * terminal)939 vte_terminal_emit_increase_font_size(VteTerminal *terminal)
940 {
941 	_vte_debug_print(VTE_DEBUG_SIGNALS,
942 			"Emitting `increase-font-size'.\n");
943 	g_signal_emit_by_name(terminal, "increase-font-size");
944 }
945 
946 /* Emit a "decrease-font-size" signal. */
947 static void
vte_terminal_emit_decrease_font_size(VteTerminal * terminal)948 vte_terminal_emit_decrease_font_size(VteTerminal *terminal)
949 {
950 	_vte_debug_print(VTE_DEBUG_SIGNALS,
951 			"Emitting `decrease-font-size'.\n");
952 	g_signal_emit_by_name(terminal, "decrease-font-size");
953 }
954 
955 /* Emit a "text-inserted" signal. */
956 void
_vte_terminal_emit_text_inserted(VteTerminal * terminal)957 _vte_terminal_emit_text_inserted(VteTerminal *terminal)
958 {
959 	if (!terminal->pvt->accessible_emit) {
960 		return;
961 	}
962 	_vte_debug_print(VTE_DEBUG_SIGNALS,
963 			"Emitting `text-inserted'.\n");
964 	g_signal_emit_by_name(terminal, "text-inserted");
965 }
966 
967 /* Emit a "text-deleted" signal. */
968 void
_vte_terminal_emit_text_deleted(VteTerminal * terminal)969 _vte_terminal_emit_text_deleted(VteTerminal *terminal)
970 {
971 	if (!terminal->pvt->accessible_emit) {
972 		return;
973 	}
974 	_vte_debug_print(VTE_DEBUG_SIGNALS,
975 			"Emitting `text-deleted'.\n");
976 	g_signal_emit_by_name(terminal, "text-deleted");
977 }
978 
979 /* Emit a "text-modified" signal. */
980 static void
vte_terminal_emit_text_modified(VteTerminal * terminal)981 vte_terminal_emit_text_modified(VteTerminal *terminal)
982 {
983 	if (!terminal->pvt->accessible_emit) {
984 		return;
985 	}
986 	_vte_debug_print(VTE_DEBUG_SIGNALS,
987 			"Emitting `text-modified'.\n");
988 	g_signal_emit_by_name(terminal, "text-modified");
989 }
990 
991 /* Emit a "text-scrolled" signal. */
992 static void
vte_terminal_emit_text_scrolled(VteTerminal * terminal,gint delta)993 vte_terminal_emit_text_scrolled(VteTerminal *terminal, gint delta)
994 {
995 	if (!terminal->pvt->accessible_emit) {
996 		return;
997 	}
998 	_vte_debug_print(VTE_DEBUG_SIGNALS,
999 			"Emitting `text-scrolled'(%d).\n", delta);
1000 	g_signal_emit_by_name(terminal, "text-scrolled", delta);
1001 }
1002 
1003 /* Deselect anything which is selected and refresh the screen if needed. */
1004 static void
vte_terminal_deselect_all(VteTerminal * terminal)1005 vte_terminal_deselect_all(VteTerminal *terminal)
1006 {
1007 	if (terminal->pvt->has_selection) {
1008 		gint sx, sy, ex, ey, extra;
1009 
1010 		_vte_debug_print(VTE_DEBUG_SELECTION,
1011 				"Deselecting all text.\n");
1012 
1013 		terminal->pvt->has_selection = FALSE;
1014 		/* Don't free the current selection, as we need to keep
1015 		 * hold of it for async copying from the clipboard. */
1016 
1017 		vte_terminal_emit_selection_changed(terminal);
1018 
1019 		sx = terminal->pvt->selection_start.col;
1020 		sy = terminal->pvt->selection_start.row;
1021 		ex = terminal->pvt->selection_end.col;
1022 		ey = terminal->pvt->selection_end.row;
1023                 extra = terminal->pvt->selection_block_mode ? (VTE_TAB_WIDTH_MAX - 1) : 0;
1024 		_vte_invalidate_region(terminal,
1025 				MIN (sx, ex), MAX (sx, ex) + extra,
1026 				MIN (sy, ey),   MAX (sy, ey),
1027 				FALSE);
1028 	}
1029 }
1030 
1031 /* Remove a tabstop. */
1032 void
_vte_terminal_clear_tabstop(VteTerminal * terminal,int column)1033 _vte_terminal_clear_tabstop(VteTerminal *terminal, int column)
1034 {
1035 	g_assert(VTE_IS_TERMINAL(terminal));
1036 	if (terminal->pvt->tabstops != NULL) {
1037 		/* Remove a tab stop from the hash table. */
1038 		g_hash_table_remove(terminal->pvt->tabstops,
1039 				    GINT_TO_POINTER(2 * column + 1));
1040 	}
1041 }
1042 
1043 /* Check if we have a tabstop at a given position. */
1044 gboolean
_vte_terminal_get_tabstop(VteTerminal * terminal,int column)1045 _vte_terminal_get_tabstop(VteTerminal *terminal, int column)
1046 {
1047 	gpointer hash;
1048 	g_assert(VTE_IS_TERMINAL(terminal));
1049 	if (terminal->pvt->tabstops != NULL) {
1050 		hash = g_hash_table_lookup(terminal->pvt->tabstops,
1051 					   GINT_TO_POINTER(2 * column + 1));
1052 		return (hash != NULL);
1053 	} else {
1054 		return FALSE;
1055 	}
1056 }
1057 
1058 /* Reset the set of tab stops to the default. */
1059 void
_vte_terminal_set_tabstop(VteTerminal * terminal,int column)1060 _vte_terminal_set_tabstop(VteTerminal *terminal, int column)
1061 {
1062 	g_assert(VTE_IS_TERMINAL(terminal));
1063 	if (terminal->pvt->tabstops != NULL) {
1064 		/* Just set a non-NULL pointer for this column number. */
1065 		g_hash_table_insert(terminal->pvt->tabstops,
1066 				    GINT_TO_POINTER(2 * column + 1),
1067 				    terminal);
1068 	}
1069 }
1070 
1071 /* Reset the set of tab stops to the default. */
1072 static void
vte_terminal_set_default_tabstops(VteTerminal * terminal)1073 vte_terminal_set_default_tabstops(VteTerminal *terminal)
1074 {
1075         int i;
1076 	if (terminal->pvt->tabstops != NULL) {
1077 		g_hash_table_destroy(terminal->pvt->tabstops);
1078 	}
1079 	terminal->pvt->tabstops = g_hash_table_new(NULL, NULL);
1080         for (i = 0; i <= VTE_TAB_MAX; i += VTE_TAB_WIDTH) {
1081 		_vte_terminal_set_tabstop(terminal, i);
1082 	}
1083 }
1084 
1085 /* Clear the cache of the screen contents we keep. */
1086 static void
vte_terminal_match_contents_clear(VteTerminal * terminal)1087 vte_terminal_match_contents_clear(VteTerminal *terminal)
1088 {
1089 	g_assert(VTE_IS_TERMINAL(terminal));
1090 	if (terminal->pvt->match_contents != NULL) {
1091 		g_free(terminal->pvt->match_contents);
1092 		terminal->pvt->match_contents = NULL;
1093 	}
1094 	if (terminal->pvt->match_attributes != NULL) {
1095 		g_array_free(terminal->pvt->match_attributes, TRUE);
1096 		terminal->pvt->match_attributes = NULL;
1097 	}
1098 	vte_terminal_match_hilite_clear(terminal);
1099 }
1100 
1101 /* Refresh the cache of the screen contents we keep. */
1102 static gboolean
always_selected(VteTerminal * terminal,glong column,glong row,gpointer data)1103 always_selected(VteTerminal *terminal, glong column, glong row, gpointer data)
1104 {
1105 	return TRUE;
1106 }
1107 
1108 static void
vte_terminal_match_contents_refresh(VteTerminal * terminal)1109 vte_terminal_match_contents_refresh(VteTerminal *terminal)
1110 {
1111 	GArray *array;
1112 	vte_terminal_match_contents_clear(terminal);
1113 	array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
1114 	terminal->pvt->match_contents = vte_terminal_get_text(terminal,
1115 							      always_selected,
1116 							      NULL,
1117 							      array);
1118 	terminal->pvt->match_attributes = array;
1119 }
1120 
1121 static void
regex_match_clear_cursor(struct vte_match_regex * regex)1122 regex_match_clear_cursor (struct vte_match_regex *regex)
1123 {
1124         switch (regex->cursor_mode) {
1125                 case VTE_REGEX_CURSOR_GDKCURSOR:
1126                         if (regex->cursor.cursor != NULL) {
1127                                 g_object_unref(regex->cursor.cursor);
1128                                 regex->cursor.cursor = NULL;
1129                         }
1130                         break;
1131                 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1132                         break;
1133                 case VTE_REGEX_CURSOR_NAME:
1134                         g_free (regex->cursor.cursor_name);
1135                         regex->cursor.cursor_name = NULL;
1136                         break;
1137 		default:
1138 			g_assert_not_reached ();
1139 			return;
1140         }
1141 }
1142 
1143 static void
regex_match_clear(struct vte_match_regex * regex)1144 regex_match_clear (struct vte_match_regex *regex)
1145 {
1146         regex_match_clear_cursor(regex);
1147 
1148         g_regex_unref(regex->regex);
1149         regex->regex = NULL;
1150 
1151         regex->tag = -1;
1152 }
1153 
1154 static void
vte_terminal_set_cursor_from_regex_match(VteTerminal * terminal,struct vte_match_regex * regex)1155 vte_terminal_set_cursor_from_regex_match(VteTerminal *terminal, struct vte_match_regex *regex)
1156 {
1157         GdkCursor *cursor = NULL;
1158 
1159         if (! gtk_widget_get_realized (&terminal->widget))
1160                 return;
1161 
1162         switch (regex->cursor_mode) {
1163                 case VTE_REGEX_CURSOR_GDKCURSOR:
1164                         if (regex->cursor.cursor != NULL &&
1165                             gdk_cursor_get_display(regex->cursor.cursor) == gtk_widget_get_display(&terminal->widget)) {
1166                                 cursor = g_object_ref(regex->cursor.cursor);
1167                         }
1168                         break;
1169                 case VTE_REGEX_CURSOR_GDKCURSORTYPE:
1170                         cursor = gdk_cursor_new_for_display(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_type);
1171                         break;
1172                 case VTE_REGEX_CURSOR_NAME:
1173                         cursor = gdk_cursor_new_from_name(gtk_widget_get_display(GTK_WIDGET(terminal)), regex->cursor.cursor_name);
1174                         break;
1175 		default:
1176 			g_assert_not_reached ();
1177 			return;
1178         }
1179 
1180 	gdk_window_set_cursor (gtk_widget_get_window (&terminal->widget), cursor);
1181 
1182         if (cursor)
1183                 g_object_unref(cursor);
1184 }
1185 
1186 /**
1187  * vte_terminal_match_remove_all:
1188  * @terminal: a #VteTerminal
1189  *
1190  * Clears the list of regular expressions the terminal uses to highlight text
1191  * when the user moves the mouse cursor.
1192  */
1193 void
vte_terminal_match_remove_all(VteTerminal * terminal)1194 vte_terminal_match_remove_all(VteTerminal *terminal)
1195 {
1196 	struct vte_match_regex *regex;
1197 	guint i;
1198 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
1199 	for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1200 		regex = &g_array_index(terminal->pvt->match_regexes,
1201 				       struct vte_match_regex,
1202 				       i);
1203 		/* Unless this is a hole, clean it up. */
1204 		if (regex->tag >= 0) {
1205                         regex_match_clear (regex);
1206 		}
1207 	}
1208 	g_array_set_size(terminal->pvt->match_regexes, 0);
1209 	vte_terminal_match_hilite_clear(terminal);
1210 }
1211 
1212 /**
1213  * vte_terminal_match_remove:
1214  * @terminal: a #VteTerminal
1215  * @tag: the tag of the regex to remove
1216  *
1217  * Removes the regular expression which is associated with the given @tag from
1218  * the list of expressions which the terminal will highlight when the user
1219  * moves the mouse cursor over matching text.
1220  */
1221 void
vte_terminal_match_remove(VteTerminal * terminal,int tag)1222 vte_terminal_match_remove(VteTerminal *terminal, int tag)
1223 {
1224 	struct vte_match_regex *regex;
1225 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
1226 	if (terminal->pvt->match_regexes->len > (guint)tag) {
1227 		/* The tag is an index, so find the corresponding struct. */
1228 		regex = &g_array_index(terminal->pvt->match_regexes,
1229 				       struct vte_match_regex,
1230 				       tag);
1231 		/* If it's already been removed, return. */
1232 		if (regex->tag < 0) {
1233 			return;
1234 		}
1235 		/* Remove this item and leave a hole in its place. */
1236                 regex_match_clear (regex);
1237 	}
1238 	vte_terminal_match_hilite_clear(terminal);
1239 }
1240 
1241 static GdkCursor *
vte_terminal_cursor_new(VteTerminal * terminal,GdkCursorType cursor_type)1242 vte_terminal_cursor_new(VteTerminal *terminal, GdkCursorType cursor_type)
1243 {
1244 	GdkDisplay *display;
1245 	GdkCursor *cursor;
1246 
1247 	display = gtk_widget_get_display(&terminal->widget);
1248 	cursor = gdk_cursor_new_for_display(display, cursor_type);
1249 	return cursor;
1250 }
1251 
1252 /**
1253  * vte_terminal_match_add_gregex:
1254  * @terminal: a #VteTerminal
1255  * @regex: a #GRegex
1256  * @flags: the #GRegexMatchFlags to use when matching the regex
1257  *
1258  * Adds the regular expression @regex to the list of matching expressions.  When the
1259  * user moves the mouse cursor over a section of displayed text which matches
1260  * this expression, the text will be highlighted.
1261  *
1262  * Returns: an integer associated with this expression
1263  */
1264 int
vte_terminal_match_add_gregex(VteTerminal * terminal,GRegex * regex,GRegexMatchFlags flags)1265 vte_terminal_match_add_gregex(VteTerminal *terminal, GRegex *regex, GRegexMatchFlags flags)
1266 {
1267 	VteTerminalPrivate *pvt;
1268 	struct vte_match_regex new_regex_match, *regex_match;
1269 	guint ret, len;
1270 
1271 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1272 	g_return_val_if_fail(regex != NULL, -1);
1273 
1274         pvt = terminal->pvt;
1275 
1276 	/* Search for a hole. */
1277         len = pvt->match_regexes->len;
1278 	for (ret = 0; ret < len; ret++) {
1279 		regex_match = &g_array_index(pvt->match_regexes,
1280                                              struct vte_match_regex,
1281                                              ret);
1282 		if (regex_match->tag == -1) {
1283 			break;
1284 		}
1285 	}
1286 
1287 	/* Set the tag to the insertion point. */
1288         new_regex_match.regex = g_regex_ref(regex);
1289         new_regex_match.match_flags = flags;
1290 	new_regex_match.tag = ret;
1291         new_regex_match.cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1292         new_regex_match.cursor.cursor_type = VTE_DEFAULT_CURSOR;
1293 	if (ret < pvt->match_regexes->len) {
1294 		/* Overwrite. */
1295 		g_array_index(pvt->match_regexes,
1296 			      struct vte_match_regex,
1297 			      ret) = new_regex_match;
1298 	} else {
1299 		/* Append. */
1300 		g_array_append_val(pvt->match_regexes, new_regex_match);
1301 	}
1302 
1303 	return new_regex_match.tag;
1304 }
1305 
1306 /**
1307  * vte_terminal_match_set_cursor:
1308  * @terminal: a #VteTerminal
1309  * @tag: the tag of the regex which should use the specified cursor
1310  * @cursor: (allow-none): the #GdkCursor which the terminal should use when the pattern is
1311  *   highlighted, or %NULL to use the standard cursor
1312  *
1313  * Sets which cursor the terminal will use if the pointer is over the pattern
1314  * specified by @tag.  The terminal keeps a reference to @cursor.
1315  *
1316  * Deprecated: 0.40: Use vte_terminal_match_set_cursor_type() or vte_terminal_match_set_cursor_named() instead.
1317  */
1318 void
vte_terminal_match_set_cursor(VteTerminal * terminal,int tag,GdkCursor * cursor)1319 vte_terminal_match_set_cursor(VteTerminal *terminal, int tag, GdkCursor *cursor)
1320 {
1321 	struct vte_match_regex *regex;
1322 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
1323 	g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1324 	regex = &g_array_index(terminal->pvt->match_regexes,
1325 			       struct vte_match_regex,
1326 			       tag);
1327         regex_match_clear_cursor(regex);
1328         regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSOR;
1329 	regex->cursor.cursor = cursor ? g_object_ref(cursor) : NULL;
1330 	vte_terminal_match_hilite_clear(terminal);
1331 }
1332 
1333 /**
1334  * vte_terminal_match_set_cursor_type:
1335  * @terminal: a #VteTerminal
1336  * @tag: the tag of the regex which should use the specified cursor
1337  * @cursor_type: a #GdkCursorType
1338  *
1339  * Sets which cursor the terminal will use if the pointer is over the pattern
1340  * specified by @tag.
1341  */
1342 void
vte_terminal_match_set_cursor_type(VteTerminal * terminal,int tag,GdkCursorType cursor_type)1343 vte_terminal_match_set_cursor_type(VteTerminal *terminal,
1344 				   int tag, GdkCursorType cursor_type)
1345 {
1346 	struct vte_match_regex *regex;
1347 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
1348 	g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1349 	regex = &g_array_index(terminal->pvt->match_regexes,
1350 			       struct vte_match_regex,
1351 			       tag);
1352         regex_match_clear_cursor(regex);
1353         regex->cursor_mode = VTE_REGEX_CURSOR_GDKCURSORTYPE;
1354 	regex->cursor.cursor_type = cursor_type;
1355 	vte_terminal_match_hilite_clear(terminal);
1356 }
1357 
1358 /**
1359  * vte_terminal_match_set_cursor_name:
1360  * @terminal: a #VteTerminal
1361  * @tag: the tag of the regex which should use the specified cursor
1362  * @cursor_name: the name of the cursor
1363  *
1364  * Sets which cursor the terminal will use if the pointer is over the pattern
1365  * specified by @tag.
1366  */
1367 void
vte_terminal_match_set_cursor_name(VteTerminal * terminal,int tag,const char * cursor_name)1368 vte_terminal_match_set_cursor_name(VteTerminal *terminal,
1369 				   int tag, const char *cursor_name)
1370 {
1371 	struct vte_match_regex *regex;
1372 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
1373         g_return_if_fail(cursor_name != NULL);
1374 	g_return_if_fail((guint) tag < terminal->pvt->match_regexes->len);
1375 	regex = &g_array_index(terminal->pvt->match_regexes,
1376 			       struct vte_match_regex,
1377 			       tag);
1378         regex_match_clear_cursor(regex);
1379         regex->cursor_mode = VTE_REGEX_CURSOR_NAME;
1380 	regex->cursor.cursor_name = g_strdup (cursor_name);
1381 	vte_terminal_match_hilite_clear(terminal);
1382 }
1383 
1384 /* Check if a given cell on the screen contains part of a matched string.  If
1385  * it does, return the string, and store the match tag in the optional tag
1386  * argument. */
1387 static char *
vte_terminal_match_check_internal_gregex(VteTerminal * terminal,long column,glong row,int * tag,int * start,int * end)1388 vte_terminal_match_check_internal_gregex(VteTerminal *terminal,
1389                                          long column, glong row,
1390                                          int *tag, int *start, int *end)
1391 {
1392 	gint start_blank, end_blank;
1393         guint i;
1394 	int offset;
1395 	struct vte_match_regex *regex = NULL;
1396 	struct _VteCharAttributes *attr = NULL;
1397 	gssize sattr, eattr;
1398 	gchar *line, eol;
1399         GMatchInfo *match_info;
1400 
1401 	_vte_debug_print(VTE_DEBUG_EVENTS,
1402 			"Checking for gregex match at (%ld,%ld).\n", row, column);
1403 	if (tag != NULL) {
1404 		*tag = -1;
1405 	}
1406 	if (start != NULL) {
1407 		*start = 0;
1408 	}
1409 	if (end != NULL) {
1410 		*end = 0;
1411 	}
1412 	/* Map the pointer position to a portion of the string. */
1413 	eattr = terminal->pvt->match_attributes->len;
1414 	for (offset = eattr; offset--; ) {
1415 		attr = &g_array_index(terminal->pvt->match_attributes,
1416 				      struct _VteCharAttributes,
1417 				      offset);
1418 		if (row < attr->row) {
1419 			eattr = offset;
1420 		}
1421 		if (row == attr->row &&
1422 		    column == attr->column &&
1423 		    terminal->pvt->match_contents[offset] != ' ') {
1424 			break;
1425 		}
1426 	}
1427 
1428 	_VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1429 		if (offset < 0)
1430 			g_printerr("Cursor is not on a character.\n");
1431 		else
1432 			g_printerr("Cursor is on character '%c' at %d.\n",
1433 					g_utf8_get_char (terminal->pvt->match_contents + offset),
1434 					offset);
1435 	}
1436 
1437 	/* If the pointer isn't on a matchable character, bug out. */
1438 	if (offset < 0) {
1439 		return NULL;
1440 	}
1441 
1442 	/* If the pointer is on a newline, bug out. */
1443 	if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1444 	    (terminal->pvt->match_contents[offset] == '\0')) {
1445 		_vte_debug_print(VTE_DEBUG_EVENTS,
1446 				"Cursor is on whitespace.\n");
1447 		return NULL;
1448 	}
1449 
1450 	/* Snip off any final newlines. */
1451 	while (terminal->pvt->match_contents[eattr] == '\n' ||
1452 			terminal->pvt->match_contents[eattr] == '\0') {
1453 		eattr--;
1454 	}
1455 	/* and scan forwards to find the end of this line */
1456 	while (!(terminal->pvt->match_contents[eattr] == '\n' ||
1457 			terminal->pvt->match_contents[eattr] == '\0')) {
1458 		eattr++;
1459 	}
1460 
1461 	/* find the start of row */
1462 	if (row == 0) {
1463 		sattr = 0;
1464 	} else {
1465 		for (sattr = offset; sattr > 0; sattr--) {
1466 			attr = &g_array_index(terminal->pvt->match_attributes,
1467 					      struct _VteCharAttributes,
1468 					      sattr);
1469 			if (row > attr->row) {
1470 				break;
1471 			}
1472 		}
1473 	}
1474 	/* Scan backwards to find the start of this line */
1475 	while (sattr > 0 &&
1476 		! (terminal->pvt->match_contents[sattr] == '\n' ||
1477 		    terminal->pvt->match_contents[sattr] == '\0')) {
1478 		sattr--;
1479 	}
1480 	/* and skip any initial newlines. */
1481 	while (terminal->pvt->match_contents[sattr] == '\n' ||
1482 		terminal->pvt->match_contents[sattr] == '\0') {
1483 		sattr++;
1484 	}
1485 	if (eattr <= sattr) { /* blank line */
1486 		return NULL;
1487 	}
1488 	if (eattr <= offset || sattr > offset) {
1489 		/* nothing to match on this line */
1490 		return NULL;
1491 	}
1492 	offset -= sattr;
1493 	eattr -= sattr;
1494 
1495 	/* temporarily shorten the contents to this row */
1496 	line = terminal->pvt->match_contents + sattr;
1497 	eol = line[eattr];
1498 	line[eattr] = '\0';
1499 
1500 	start_blank = 0;
1501 	end_blank = eattr;
1502 
1503 	/* Now iterate over each regex we need to match against. */
1504 	for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1505 		regex = &g_array_index(terminal->pvt->match_regexes,
1506 				       struct vte_match_regex,
1507 				       i);
1508 		/* Skip holes. */
1509 		if (regex->tag < 0) {
1510 			continue;
1511 		}
1512 		/* We'll only match the first item in the buffer which
1513 		 * matches, so we'll have to skip each match until we
1514 		 * stop getting matches. */
1515                 if (!g_regex_match_full(regex->regex,
1516                                         line, -1, 0,
1517                                         regex->match_flags,
1518                                         &match_info,
1519                                         NULL)) {
1520                         g_match_info_free(match_info);
1521                         continue;
1522                 }
1523 
1524                 while (g_match_info_matches(match_info)) {
1525 			gint ko = offset;
1526 			gint sblank=G_MININT, eblank=G_MAXINT;
1527                         gint rm_so, rm_eo;
1528 
1529                         if (g_match_info_fetch_pos (match_info, 0, &rm_so, &rm_eo)) {
1530 				/* The offsets should be "sane". */
1531 				g_assert(rm_so < eattr);
1532 				g_assert(rm_eo <= eattr);
1533 				_VTE_DEBUG_IF(VTE_DEBUG_MISC) {
1534 					gchar *match;
1535 					struct _VteCharAttributes *_sattr, *_eattr;
1536 					match = g_strndup(line + rm_so, rm_eo - rm_so);
1537 					_sattr = &g_array_index(terminal->pvt->match_attributes,
1538 							struct _VteCharAttributes,
1539 							rm_so);
1540 					_eattr = &g_array_index(terminal->pvt->match_attributes,
1541 							struct _VteCharAttributes,
1542 							rm_eo - 1);
1543 					g_printerr("Match `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1544 							match,
1545 							rm_so,
1546 							_sattr->column,
1547 							_sattr->row,
1548 							rm_eo - 1,
1549 							_eattr->column,
1550 							_eattr->row,
1551 							offset);
1552 					g_free(match);
1553 
1554 				}
1555 				/* If the pointer is in this substring,
1556 				 * then we're done. */
1557 				if (ko >= rm_so &&
1558 				    ko < rm_eo) {
1559 					gchar *result;
1560 					if (tag != NULL) {
1561 						*tag = regex->tag;
1562 					}
1563 					if (start != NULL) {
1564 						*start = sattr + rm_so;
1565 					}
1566 					if (end != NULL) {
1567 						*end = sattr + rm_eo - 1;
1568 					}
1569                                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
1570                                         result = g_match_info_fetch(match_info, 0);
1571 					line[eattr] = eol;
1572 
1573                                         g_match_info_free(match_info);
1574 					return result;
1575 				}
1576 				if (ko > rm_eo &&
1577 						rm_eo > sblank) {
1578 					sblank = rm_eo;
1579 				}
1580 				if (ko < rm_so &&
1581 						rm_so < eblank) {
1582 					eblank = rm_so;
1583 				}
1584 			}
1585 			if (sblank > start_blank) {
1586 				start_blank = sblank;
1587 			}
1588 			if (eblank < end_blank) {
1589 				end_blank = eblank;
1590 			}
1591 
1592                         g_match_info_next(match_info, NULL);
1593 		}
1594 
1595                 g_match_info_free(match_info);
1596 	}
1597 	line[eattr] = eol;
1598 	if (start != NULL) {
1599 		*start = sattr + start_blank;
1600 	}
1601 	if (end != NULL) {
1602 		*end = sattr + end_blank;
1603 	}
1604 	return NULL;
1605 }
1606 
1607 static char *
vte_terminal_match_check_internal(VteTerminal * terminal,long column,glong row,int * tag,int * start,int * end)1608 vte_terminal_match_check_internal(VteTerminal *terminal,
1609                                   long column, glong row,
1610                                   int *tag, int *start, int *end)
1611 {
1612 	if (terminal->pvt->match_contents == NULL) {
1613 		vte_terminal_match_contents_refresh(terminal);
1614 	}
1615 
1616         return vte_terminal_match_check_internal_gregex(terminal, column, row, tag, start, end);
1617 }
1618 
1619 static gboolean
rowcol_inside_match(VteTerminal * terminal,glong row,glong col)1620 rowcol_inside_match (VteTerminal *terminal, glong row, glong col)
1621 {
1622 	if (terminal->pvt->match_start.row == terminal->pvt->match_end.row) {
1623 		return row == terminal->pvt->match_start.row &&
1624 			col >= terminal->pvt->match_start.col &&
1625 			col <= terminal->pvt->match_end.col;
1626 	} else {
1627 		if (row < terminal->pvt->match_start.row ||
1628 				row > terminal->pvt->match_end.row) {
1629 			return FALSE;
1630 		}
1631 		if (row == terminal->pvt->match_start.row) {
1632 			return col >= terminal->pvt->match_start.col;
1633 		}
1634 		if (row == terminal->pvt->match_end.row) {
1635 			return col <= terminal->pvt->match_end.col;
1636 		}
1637 		return TRUE;
1638 	}
1639 }
1640 
1641 /**
1642  * vte_terminal_match_check:
1643  * @terminal: a #VteTerminal
1644  * @column: the text column
1645  * @row: the text row
1646  * @tag: (out) (allow-none): a location to store the tag, or %NULL
1647  *
1648  * Checks if the text in and around the specified position matches any of the
1649  * regular expressions previously set using vte_terminal_match_add().  If a
1650  * match exists, the text string is returned and if @tag is not %NULL, the number
1651  * associated with the matched regular expression will be stored in @tag.
1652  *
1653  * If more than one regular expression has been set with
1654  * vte_terminal_match_add(), then expressions are checked in the order in
1655  * which they were added.
1656  *
1657  * Returns: (transfer full): a newly allocated string which matches one of the previously
1658  *   set regular expressions
1659  */
1660 char *
vte_terminal_match_check(VteTerminal * terminal,glong column,glong row,int * tag)1661 vte_terminal_match_check(VteTerminal *terminal, glong column, glong row,
1662 			 int *tag)
1663 {
1664 	long delta;
1665 	char *ret;
1666 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1667 	delta = terminal->pvt->screen->scroll_delta;
1668 	_vte_debug_print(VTE_DEBUG_EVENTS,
1669 			"Checking for match at (%ld,%ld).\n",
1670 			row, column);
1671 	if (rowcol_inside_match (terminal, row + delta, column)) {
1672 		if (tag) {
1673 			*tag = terminal->pvt->match_tag;
1674 		}
1675 		ret = terminal->pvt->match != NULL ?
1676 			g_strdup (terminal->pvt->match) :
1677 			NULL;
1678 	} else {
1679 		ret = vte_terminal_match_check_internal(terminal,
1680 				column, row + delta,
1681 				tag, NULL, NULL);
1682 	}
1683 	_VTE_DEBUG_IF(VTE_DEBUG_EVENTS) {
1684 		if (ret != NULL) g_printerr("Matched `%s'.\n", ret);
1685 	}
1686 	return ret;
1687 }
1688 
1689 /**
1690  * vte_terminal_match_check_event:
1691  * @terminal: a #VteTerminal
1692  * @event: a #GdkEvent
1693  * @tag: (out) (allow-none): a location to store the tag, or %NULL
1694  *
1695  * Checks if the text in and around the position of the event matches any of the
1696  * regular expressions previously set using vte_terminal_match_add().  If a
1697  * match exists, the text string is returned and if @tag is not %NULL, the number
1698  * associated with the matched regular expression will be stored in @tag.
1699  *
1700  * If more than one regular expression has been set with
1701  * vte_terminal_match_add(), then expressions are checked in the order in
1702  * which they were added.
1703  *
1704  * Returns: (transfer full): a newly allocated string which matches one of the previously
1705  *   set regular expressions
1706  */
1707 char *
vte_terminal_match_check_event(VteTerminal * terminal,GdkEvent * event,int * tag)1708 vte_terminal_match_check_event(VteTerminal *terminal,
1709                                GdkEvent *event,
1710                                int *tag)
1711 {
1712         double x, y;
1713         long col, row;
1714 
1715         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
1716 
1717         if (event == NULL)
1718                 return FALSE;
1719         if (((GdkEventAny*)event)->window != gtk_widget_get_window(&terminal->widget))
1720                 return FALSE;
1721         if (!gdk_event_get_coords(event, &x, &y))
1722                 return FALSE;
1723         if (!_vte_terminal_xy_to_grid(terminal, x, y, &col, &row))
1724                 return FALSE;
1725 
1726         return vte_terminal_match_check(terminal, col, row, tag);
1727 }
1728 
1729 /* Emit an adjustment changed signal on our adjustment object. */
1730 static void
vte_terminal_emit_adjustment_changed(VteTerminal * terminal)1731 vte_terminal_emit_adjustment_changed(VteTerminal *terminal)
1732 {
1733 	if (terminal->pvt->adjustment_changed_pending) {
1734 		VteScreen *screen = terminal->pvt->screen;
1735 		gboolean changed = FALSE;
1736 		glong v;
1737 		gdouble current;
1738 
1739 		g_object_freeze_notify (G_OBJECT (terminal->pvt->vadjustment));
1740 
1741 		v = _vte_ring_delta (screen->row_data);
1742 		current = gtk_adjustment_get_lower(terminal->pvt->vadjustment);
1743 		if (current != v) {
1744 			_vte_debug_print(VTE_DEBUG_ADJ,
1745 					"Changing lower bound from %.0f to %ld\n",
1746 					 current, v);
1747 			gtk_adjustment_set_lower(terminal->pvt->vadjustment, v);
1748 			changed = TRUE;
1749 		}
1750 
1751 		/* The upper value is the number of rows which might be visible.  (Add
1752 		 * one to the cursor offset because it's zero-based.) */
1753 		v = MAX(_vte_ring_next(screen->row_data),
1754                         terminal->pvt->cursor.row + 1);
1755 		current = gtk_adjustment_get_upper(terminal->pvt->vadjustment);
1756 		if (current != v) {
1757 			_vte_debug_print(VTE_DEBUG_ADJ,
1758 					"Changing upper bound from %.0f to %ld\n",
1759 					 current, v);
1760 			gtk_adjustment_set_upper(terminal->pvt->vadjustment, v);
1761 			changed = TRUE;
1762 		}
1763 
1764 		g_object_thaw_notify (G_OBJECT (terminal->pvt->vadjustment));
1765 
1766 		if (changed)
1767 			_vte_debug_print(VTE_DEBUG_SIGNALS,
1768 					"Emitting adjustment_changed.\n");
1769 		terminal->pvt->adjustment_changed_pending = FALSE;
1770 	}
1771 	if (terminal->pvt->adjustment_value_changed_pending) {
1772 		glong v, delta;
1773 		_vte_debug_print(VTE_DEBUG_SIGNALS,
1774 				"Emitting adjustment_value_changed.\n");
1775 		terminal->pvt->adjustment_value_changed_pending = FALSE;
1776 		v = round (gtk_adjustment_get_value(terminal->pvt->vadjustment));
1777 		if (v != terminal->pvt->screen->scroll_delta) {
1778 			/* this little dance is so that the scroll_delta is
1779 			 * updated immediately, but we still handled scrolling
1780 			 * via the adjustment - e.g. user interaction with the
1781 			 * scrollbar
1782 			 */
1783 			delta = terminal->pvt->screen->scroll_delta;
1784 			terminal->pvt->screen->scroll_delta = v;
1785 			gtk_adjustment_set_value(terminal->pvt->vadjustment, delta);
1786 		}
1787 	}
1788 }
1789 
1790 /* Queue an adjustment-changed signal to be delivered when convenient. */
1791 static inline void
vte_terminal_queue_adjustment_changed(VteTerminal * terminal)1792 vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
1793 {
1794 	terminal->pvt->adjustment_changed_pending = TRUE;
1795 	add_update_timeout (terminal);
1796 }
1797 
1798 static void
vte_terminal_queue_adjustment_value_changed(VteTerminal * terminal,glong v)1799 vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
1800 {
1801 	if (v != terminal->pvt->screen->scroll_delta) {
1802 		terminal->pvt->screen->scroll_delta = v;
1803 		terminal->pvt->adjustment_value_changed_pending = TRUE;
1804 		add_update_timeout (terminal);
1805 	}
1806 }
1807 
1808 static void
vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal * terminal,glong v)1809 vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal *terminal, glong v)
1810 {
1811 	gdouble lower, upper;
1812 
1813 	lower = gtk_adjustment_get_lower(terminal->pvt->vadjustment);
1814 	upper = gtk_adjustment_get_upper(terminal->pvt->vadjustment);
1815 
1816 	v = CLAMP(v, lower, MAX (lower, upper - terminal->pvt->row_count));
1817 
1818 	vte_terminal_queue_adjustment_value_changed (terminal, v);
1819 }
1820 
1821 
1822 void
_vte_terminal_adjust_adjustments(VteTerminal * terminal)1823 _vte_terminal_adjust_adjustments(VteTerminal *terminal)
1824 {
1825 	VteScreen *screen;
1826 	long delta;
1827 
1828 	g_assert(terminal->pvt->screen != NULL);
1829 	g_assert(terminal->pvt->screen->row_data != NULL);
1830 
1831 	vte_terminal_queue_adjustment_changed(terminal);
1832 
1833 	/* The lower value should be the first row in the buffer. */
1834 	screen = terminal->pvt->screen;
1835 	delta = _vte_ring_delta(screen->row_data);
1836 	/* Snap the insert delta and the cursor position to be in the visible
1837 	 * area.  Leave the scrolling delta alone because it will be updated
1838 	 * when the adjustment changes. */
1839 	screen->insert_delta = MAX(screen->insert_delta, delta);
1840         terminal->pvt->cursor.row = MAX(terminal->pvt->cursor.row,
1841                                         screen->insert_delta);
1842 
1843 	if (screen->scroll_delta > screen->insert_delta) {
1844 		vte_terminal_queue_adjustment_value_changed(terminal,
1845 				screen->insert_delta);
1846 	}
1847 }
1848 
1849 /* Update the adjustment field of the widget.  This function should be called
1850  * whenever we add rows to or remove rows from the history or switch screens. */
1851 static void
_vte_terminal_adjust_adjustments_full(VteTerminal * terminal)1852 _vte_terminal_adjust_adjustments_full (VteTerminal *terminal)
1853 {
1854 	gboolean changed = FALSE;
1855 	gdouble v;
1856 
1857 	g_assert(terminal->pvt->screen != NULL);
1858 	g_assert(terminal->pvt->screen->row_data != NULL);
1859 
1860 	_vte_terminal_adjust_adjustments(terminal);
1861 
1862         g_object_freeze_notify(G_OBJECT(terminal->pvt->vadjustment));
1863 
1864 	/* The step increment should always be one. */
1865 	v = gtk_adjustment_get_step_increment(terminal->pvt->vadjustment);
1866 	if (v != 1) {
1867 		_vte_debug_print(VTE_DEBUG_ADJ,
1868 				"Changing step increment from %.0lf to %ld\n",
1869 				v, terminal->pvt->row_count);
1870 		gtk_adjustment_set_step_increment(terminal->pvt->vadjustment, 1);
1871 		changed = TRUE;
1872 	}
1873 
1874 	/* Set the number of rows the user sees to the number of rows the
1875 	 * user sees. */
1876 	v = gtk_adjustment_get_page_size(terminal->pvt->vadjustment);
1877 	if (v != terminal->pvt->row_count) {
1878 		_vte_debug_print(VTE_DEBUG_ADJ,
1879 				"Changing page size from %.0f to %ld\n",
1880 				 v, terminal->pvt->row_count);
1881 		gtk_adjustment_set_page_size(terminal->pvt->vadjustment,
1882 					     terminal->pvt->row_count);
1883 		changed = TRUE;
1884 	}
1885 
1886 	/* Clicking in the empty area should scroll one screen, so set the
1887 	 * page size to the number of visible rows. */
1888 	v = gtk_adjustment_get_page_increment(terminal->pvt->vadjustment);
1889 	if (v != terminal->pvt->row_count) {
1890 		_vte_debug_print(VTE_DEBUG_ADJ,
1891 				"Changing page increment from "
1892 				"%.0f to %ld\n",
1893 				v, terminal->pvt->row_count);
1894 		gtk_adjustment_set_page_increment(terminal->pvt->vadjustment,
1895 						  terminal->pvt->row_count);
1896 		changed = TRUE;
1897 	}
1898 
1899 	g_object_thaw_notify(G_OBJECT(terminal->pvt->vadjustment));
1900 
1901 	if (changed)
1902 		_vte_debug_print(VTE_DEBUG_SIGNALS,
1903 				"Emitting adjustment_changed.\n");
1904 }
1905 
1906 /* Scroll a fixed number of lines up or down in the current screen. */
1907 static void
vte_terminal_scroll_lines(VteTerminal * terminal,gint lines)1908 vte_terminal_scroll_lines(VteTerminal *terminal, gint lines)
1909 {
1910 	glong destination;
1911 	_vte_debug_print(VTE_DEBUG_ADJ, "Scrolling %d lines.\n", lines);
1912 	/* Calculate the ideal position where we want to be before clamping. */
1913 	destination = terminal->pvt->screen->scroll_delta;
1914 	destination += lines;
1915 	/* Tell the scrollbar to adjust itself. */
1916 	vte_terminal_queue_adjustment_value_changed_clamped (terminal, destination);
1917 }
1918 
1919 /* Scroll a fixed number of pages up or down, in the current screen. */
1920 static void
vte_terminal_scroll_pages(VteTerminal * terminal,gint pages)1921 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
1922 {
1923 	vte_terminal_scroll_lines(terminal, pages * terminal->pvt->row_count);
1924 }
1925 
1926 /* Scroll so that the scroll delta is the minimum value. */
1927 static void
vte_terminal_maybe_scroll_to_top(VteTerminal * terminal)1928 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
1929 {
1930 	vte_terminal_queue_adjustment_value_changed (terminal,
1931 			_vte_ring_delta(terminal->pvt->screen->row_data));
1932 }
1933 
1934 static void
vte_terminal_maybe_scroll_to_bottom(VteTerminal * terminal)1935 vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
1936 {
1937 	glong delta;
1938 	delta = terminal->pvt->screen->insert_delta;
1939 	vte_terminal_queue_adjustment_value_changed (terminal, delta);
1940 	_vte_debug_print(VTE_DEBUG_ADJ,
1941 			"Snapping to bottom of screen\n");
1942 }
1943 
1944 static void
_vte_terminal_setup_utf8(VteTerminal * terminal)1945 _vte_terminal_setup_utf8 (VteTerminal *terminal)
1946 {
1947         VteTerminalPrivate *pvt = terminal->pvt;
1948         GError *error = NULL;
1949 
1950         if (!vte_pty_set_utf8(pvt->pty,
1951                               strcmp(terminal->pvt->encoding, "UTF-8") == 0,
1952                               &error)) {
1953                 g_warning ("Failed to set UTF8 mode: %s\n", error->message);
1954                 g_error_free (error);
1955         }
1956 }
1957 
1958 /**
1959  * vte_terminal_set_encoding:
1960  * @terminal: a #VteTerminal
1961  * @codeset: (allow-none): a valid #GIConv target, or %NULL to use UTF-8
1962  * @error: (allow-none): return location for a #GError, or %NULL
1963  *
1964  * Changes the encoding the terminal will expect data from the child to
1965  * be encoded with.  For certain terminal types, applications executing in the
1966  * terminal can change the encoding. If @codeset is %NULL, it uses "UTF-8".
1967  *
1968  * Returns: %TRUE if the encoding could be changed to the specified one,
1969  *  or %FALSE with @error set to %G_CONVERT_ERROR_NO_CONVERSION.
1970  */
1971 gboolean
vte_terminal_set_encoding(VteTerminal * terminal,const char * codeset,GError ** error)1972 vte_terminal_set_encoding(VteTerminal *terminal,
1973                           const char *codeset,
1974                           GError **error)
1975 {
1976         VteTerminalPrivate *pvt;
1977         GObject *object;
1978 	const char *old_codeset;
1979 	VteConv conv;
1980 	char *obuf1, *obuf2;
1981 	gsize bytes_written;
1982 
1983 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
1984         g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1985 
1986         object = G_OBJECT(terminal);
1987         pvt = terminal->pvt;
1988 
1989 	old_codeset = pvt->encoding;
1990 	if (codeset == NULL) {
1991                 codeset = "UTF-8";
1992 	}
1993 	if ((old_codeset != NULL) && g_str_equal(codeset, old_codeset)) {
1994 		/* Nothing to do! */
1995 		return TRUE;
1996 	}
1997 
1998 	/* Open new conversions. */
1999 	conv = _vte_conv_open(codeset, "UTF-8");
2000 	if (conv == VTE_INVALID_CONV) {
2001 		g_set_error(error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
2002                             _("Unable to convert characters from %s to %s."),
2003                             "UTF-8", codeset);
2004                 return FALSE;
2005         }
2006 
2007         g_object_freeze_notify(object);
2008 
2009 	if (terminal->pvt->outgoing_conv != VTE_INVALID_CONV) {
2010 		_vte_conv_close(terminal->pvt->outgoing_conv);
2011 	}
2012 	terminal->pvt->outgoing_conv = conv;
2013 
2014 	/* Set the terminal's encoding to the new value. */
2015 	terminal->pvt->encoding = g_intern_string(codeset);
2016 
2017 	/* Convert any buffered output bytes. */
2018 	if ((_vte_byte_array_length(terminal->pvt->outgoing) > 0) &&
2019 	    (old_codeset != NULL)) {
2020 		/* Convert back to UTF-8. */
2021 		obuf1 = g_convert((gchar *)terminal->pvt->outgoing->data,
2022 				  _vte_byte_array_length(terminal->pvt->outgoing),
2023 				  "UTF-8",
2024 				  old_codeset,
2025 				  NULL,
2026 				  &bytes_written,
2027 				  NULL);
2028 		if (obuf1 != NULL) {
2029 			/* Convert to the new encoding. */
2030 			obuf2 = g_convert(obuf1,
2031 					  bytes_written,
2032 					  codeset,
2033 					  "UTF-8",
2034 					  NULL,
2035 					  &bytes_written,
2036 					  NULL);
2037 			if (obuf2 != NULL) {
2038 				_vte_byte_array_clear(terminal->pvt->outgoing);
2039 				_vte_byte_array_append(terminal->pvt->outgoing,
2040 						   obuf2, bytes_written);
2041 				g_free(obuf2);
2042 			}
2043 			g_free(obuf1);
2044 		}
2045 	}
2046 
2047 	/* Set the encoding for incoming text. */
2048 	_vte_iso2022_state_set_codeset(terminal->pvt->iso2022,
2049 				       terminal->pvt->encoding);
2050 
2051 	_vte_debug_print(VTE_DEBUG_IO,
2052 			"Set terminal encoding to `%s'.\n",
2053 			terminal->pvt->encoding);
2054 	vte_terminal_emit_encoding_changed(terminal);
2055 
2056         g_object_thaw_notify(object);
2057 
2058         return TRUE;
2059 }
2060 
2061 /**
2062  * vte_terminal_get_encoding:
2063  * @terminal: a #VteTerminal
2064  *
2065  * Determines the name of the encoding in which the terminal expects data to be
2066  * encoded.
2067  *
2068  * Returns: (transfer none): the current encoding for the terminal
2069  */
2070 const char *
vte_terminal_get_encoding(VteTerminal * terminal)2071 vte_terminal_get_encoding(VteTerminal *terminal)
2072 {
2073 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
2074 	return terminal->pvt->encoding;
2075 }
2076 
2077 /**
2078  * vte_terminal_set_cjk_ambiguous_width:
2079  * @terminal: a #VteTerminal
2080  * @width: either 1 (narrow) or 2 (wide)
2081  *
2082  * This setting controls whether ambiguous-width characters are narrow or wide
2083  * when using the UTF-8 encoding (vte_terminal_set_encoding()). In all other encodings,
2084  * the width of ambiguous-width characters is fixed.
2085  */
2086 void
vte_terminal_set_cjk_ambiguous_width(VteTerminal * terminal,int width)2087 vte_terminal_set_cjk_ambiguous_width(VteTerminal *terminal, int width)
2088 {
2089         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2090         g_return_if_fail(width == 1 || width == 2);
2091 
2092         terminal->pvt->utf8_ambiguous_width = width;
2093 }
2094 
2095 /**
2096  * vte_terminal_get_cjk_ambiguous_width:
2097  * @terminal: a #VteTerminal
2098  *
2099  * Returns whether ambiguous-width characters are narrow or wide when using
2100  * the UTF-8 encoding (vte_terminal_set_encoding()).
2101  *
2102  * Returns: 1 if ambiguous-width characters are narrow, or 2 if they are wide
2103  */
2104 int
vte_terminal_get_cjk_ambiguous_width(VteTerminal * terminal)2105 vte_terminal_get_cjk_ambiguous_width(VteTerminal *terminal)
2106 {
2107         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1);
2108         return terminal->pvt->utf8_ambiguous_width;
2109 }
2110 
2111 static inline VteRowData *
vte_terminal_insert_rows(VteTerminal * terminal,guint cnt)2112 vte_terminal_insert_rows (VteTerminal *terminal, guint cnt)
2113 {
2114 	VteRowData *row;
2115 	do {
2116 		row = _vte_terminal_ring_append (terminal, FALSE);
2117 	} while(--cnt);
2118 	return row;
2119 }
2120 
2121 
2122 /* Make sure we have enough rows and columns to hold data at the current
2123  * cursor position. */
2124 VteRowData *
_vte_terminal_ensure_row(VteTerminal * terminal)2125 _vte_terminal_ensure_row (VteTerminal *terminal)
2126 {
2127 	VteRowData *row;
2128 	VteScreen *screen;
2129 	gint delta;
2130 	glong v;
2131 
2132 	/* Must make sure we're in a sane area. */
2133 	screen = terminal->pvt->screen;
2134         v = terminal->pvt->cursor.row;
2135 
2136 	/* Figure out how many rows we need to add. */
2137 	delta = v - _vte_ring_next(screen->row_data) + 1;
2138 	if (delta > 0) {
2139 		row = vte_terminal_insert_rows (terminal, delta);
2140 		_vte_terminal_adjust_adjustments(terminal);
2141 	} else {
2142 		/* Find the row the cursor is in. */
2143 		row = _vte_ring_index_writable (screen->row_data, v);
2144 	}
2145 	g_assert(row != NULL);
2146 
2147 	return row;
2148 }
2149 
2150 static VteRowData *
vte_terminal_ensure_cursor(VteTerminal * terminal)2151 vte_terminal_ensure_cursor(VteTerminal *terminal)
2152 {
2153 	VteRowData *row;
2154 
2155 	row = _vte_terminal_ensure_row (terminal);
2156         _vte_row_data_fill (row, &basic_cell.cell, terminal->pvt->cursor.col);
2157 
2158 	return row;
2159 }
2160 
2161 /* Update the insert delta so that the screen which includes it also
2162  * includes the end of the buffer. */
2163 void
_vte_terminal_update_insert_delta(VteTerminal * terminal)2164 _vte_terminal_update_insert_delta(VteTerminal *terminal)
2165 {
2166 	long delta, rows;
2167 	VteScreen *screen;
2168 
2169 	screen = terminal->pvt->screen;
2170 
2171 	/* The total number of lines.  Add one to the cursor offset
2172 	 * because it's zero-based. */
2173 	rows = _vte_ring_next (screen->row_data);
2174         delta = terminal->pvt->cursor.row - rows + 1;
2175 	if (G_UNLIKELY (delta > 0)) {
2176 		vte_terminal_insert_rows (terminal, delta);
2177 		rows = _vte_ring_next (screen->row_data);
2178 	}
2179 
2180 	/* Make sure that the bottom row is visible, and that it's in
2181 	 * the buffer (even if it's empty).  This usually causes the
2182 	 * top row to become a history-only row. */
2183 	delta = screen->insert_delta;
2184 	delta = MIN(delta, rows - terminal->pvt->row_count);
2185 	delta = MAX(delta,
2186                     terminal->pvt->cursor.row - (terminal->pvt->row_count - 1));
2187 	delta = MAX(delta, _vte_ring_delta(screen->row_data));
2188 
2189 	/* Adjust the insert delta and scroll if needed. */
2190 	if (delta != screen->insert_delta) {
2191 		screen->insert_delta = delta;
2192 		_vte_terminal_adjust_adjustments(terminal);
2193 	}
2194 }
2195 
2196 /* Show or hide the pointer. */
2197 void
_vte_terminal_set_pointer_visible(VteTerminal * terminal,gboolean visible)2198 _vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible)
2199 {
2200 	GdkWindow *window;
2201 	struct vte_match_regex *regex = NULL;
2202 
2203 	terminal->pvt->mouse_cursor_visible = visible;
2204 
2205         if (! gtk_widget_get_realized (&terminal->widget))
2206                 return;
2207 
2208 	window = gtk_widget_get_window (&terminal->widget);
2209 
2210 	if (visible || !terminal->pvt->mouse_autohide) {
2211 		if (terminal->pvt->mouse_tracking_mode) {
2212 			_vte_debug_print(VTE_DEBUG_CURSOR,
2213 					"Setting mousing cursor.\n");
2214 			gdk_window_set_cursor (window, terminal->pvt->mouse_mousing_cursor);
2215 		} else
2216 		if ( (guint)terminal->pvt->match_tag < terminal->pvt->match_regexes->len) {
2217 			regex = &g_array_index(terminal->pvt->match_regexes,
2218 					       struct vte_match_regex,
2219 					       terminal->pvt->match_tag);
2220                         vte_terminal_set_cursor_from_regex_match(terminal, regex);
2221 		} else {
2222 			_vte_debug_print(VTE_DEBUG_CURSOR,
2223 					"Setting default mouse cursor.\n");
2224 			gdk_window_set_cursor (window, terminal->pvt->mouse_default_cursor);
2225 		}
2226 	} else {
2227 		_vte_debug_print(VTE_DEBUG_CURSOR,
2228 				"Setting to invisible cursor.\n");
2229 		gdk_window_set_cursor (window, terminal->pvt->mouse_inviso_cursor);
2230 	}
2231 }
2232 
2233 /**
2234  * vte_terminal_new:
2235  *
2236  * Creates a new terminal widget.
2237  *
2238  * Returns: (transfer none) (type Vte.Terminal): a new #VteTerminal object
2239  */
2240 GtkWidget *
vte_terminal_new(void)2241 vte_terminal_new(void)
2242 {
2243 	return g_object_new(VTE_TYPE_TERMINAL, NULL);
2244 }
2245 
2246 /*
2247  * Get the actually used color from the palette.
2248  * The return value can be NULL only if entry is one of VTE_CURSOR_BG,
2249  * VTE_HIGHLIGHT_BG or VTE_HIGHLIGHT_FG.
2250  */
2251 PangoColor *
_vte_terminal_get_color(const VteTerminal * terminal,int entry)2252 _vte_terminal_get_color(const VteTerminal *terminal, int entry)
2253 {
2254 	VtePaletteColor *palette_color = &terminal->pvt->palette[entry];
2255 	guint source;
2256 	for (source = 0; source < G_N_ELEMENTS(palette_color->sources); source++)
2257 		if (palette_color->sources[source].is_set)
2258 			return &palette_color->sources[source].color;
2259 	return NULL;
2260 }
2261 
2262 /* Set up a palette entry with a more-or-less match for the requested color. */
2263 void
_vte_terminal_set_color_internal(VteTerminal * terminal,int entry,int source,const PangoColor * proposed)2264 _vte_terminal_set_color_internal(VteTerminal *terminal,
2265                                  int entry,
2266                                  int source,
2267                                  const PangoColor *proposed)
2268 {
2269 	VtePaletteColor *palette_color = &terminal->pvt->palette[entry];
2270 
2271 	/* Save the requested color. */
2272 	if (proposed != NULL) {
2273 		_vte_debug_print(VTE_DEBUG_MISC,
2274 				"Set %s color[%d] to (%04x,%04x,%04x).\n",
2275 				source == VTE_COLOR_SOURCE_ESCAPE ? "escape" : "API",
2276 				entry, proposed->red, proposed->green, proposed->blue);
2277 
2278 		if (palette_color->sources[source].is_set &&
2279 		    palette_color->sources[source].color.red == proposed->red &&
2280 		    palette_color->sources[source].color.green == proposed->green &&
2281 		    palette_color->sources[source].color.blue == proposed->blue) {
2282 			return;
2283 		}
2284 		palette_color->sources[source].is_set = TRUE;
2285 		palette_color->sources[source].color.red = proposed->red;
2286 		palette_color->sources[source].color.green = proposed->green;
2287 		palette_color->sources[source].color.blue = proposed->blue;
2288 	} else {
2289 		_vte_debug_print(VTE_DEBUG_MISC,
2290 				"Reset %s color[%d].\n",
2291 				source == VTE_COLOR_SOURCE_ESCAPE ? "escape" : "API",
2292 				entry);
2293 
2294 		if (!palette_color->sources[source].is_set) {
2295 			return;
2296 		}
2297 		palette_color->sources[source].is_set = FALSE;
2298 	}
2299 
2300 	/* If we're not realized yet, there's nothing else to do. */
2301 	if (! gtk_widget_get_realized (&terminal->widget)) {
2302 		return;
2303 	}
2304 
2305 	/* If we're setting the background color, set the background color
2306 	 * on the widget as well. */
2307 	if (entry == VTE_DEFAULT_BG) {
2308 		vte_terminal_background_update(terminal);
2309 	}
2310 
2311 	/* and redraw */
2312 	if (entry == VTE_CURSOR_BG)
2313 		_vte_invalidate_cursor_once(terminal, FALSE);
2314 	else
2315 		_vte_invalidate_all (terminal);
2316 }
2317 
2318 static void
vte_terminal_generate_bold(const PangoColor * foreground,const PangoColor * background,double factor,PangoColor * bold)2319 vte_terminal_generate_bold(const PangoColor *foreground,
2320 			   const PangoColor *background,
2321 			   double factor,
2322                            PangoColor *bold /* (out) (callee allocates) */)
2323 {
2324 	double fy, fcb, fcr, by, bcb, bcr, r, g, b;
2325 	g_assert(foreground != NULL);
2326 	g_assert(background != NULL);
2327 	g_assert(bold != NULL);
2328 	fy =   0.2990 * foreground->red +
2329 	       0.5870 * foreground->green +
2330 	       0.1140 * foreground->blue;
2331 	fcb = -0.1687 * foreground->red +
2332 	      -0.3313 * foreground->green +
2333 	       0.5000 * foreground->blue;
2334 	fcr =  0.5000 * foreground->red +
2335 	      -0.4187 * foreground->green +
2336 	      -0.0813 * foreground->blue;
2337 	by =   0.2990 * background->red +
2338 	       0.5870 * background->green +
2339 	       0.1140 * background->blue;
2340 	bcb = -0.1687 * background->red +
2341 	      -0.3313 * background->green +
2342 	       0.5000 * background->blue;
2343 	bcr =  0.5000 * background->red +
2344 	      -0.4187 * background->green +
2345 	      -0.0813 * background->blue;
2346 	fy = (factor * fy) + ((1.0 - factor) * by);
2347 	fcb = (factor * fcb) + ((1.0 - factor) * bcb);
2348 	fcr = (factor * fcr) + ((1.0 - factor) * bcr);
2349 	r = fy + 1.402 * fcr;
2350 	g = fy + 0.34414 * fcb - 0.71414 * fcr;
2351 	b = fy + 1.722 * fcb;
2352 	_vte_debug_print(VTE_DEBUG_MISC,
2353 			"Calculated bold (%d, %d, %d) = (%lf,%lf,%lf)",
2354 			foreground->red, foreground->green, foreground->blue,
2355 			r, g, b);
2356 	bold->red = CLAMP(r, 0, 0xffff);
2357 	bold->green = CLAMP(g, 0, 0xffff);
2358 	bold->blue = CLAMP(b, 0, 0xffff);
2359 	_vte_debug_print(VTE_DEBUG_MISC,
2360 			"= (%04x,%04x,%04x).\n",
2361 			bold->red, bold->green, bold->blue);
2362 }
2363 
2364 /*
2365  * _vte_terminal_set_color_bold:
2366  * @terminal: a #VteTerminal
2367  * @bold: the new bold color
2368  *
2369  * Sets the color used to draw bold text in the default foreground color.
2370  */
2371 static void
_vte_terminal_set_color_bold(VteTerminal * terminal,const PangoColor * bold)2372 _vte_terminal_set_color_bold(VteTerminal *terminal,
2373                              const PangoColor *bold)
2374 {
2375 	_vte_debug_print(VTE_DEBUG_MISC,
2376 			"Set bold color to (%04x,%04x,%04x).\n",
2377 			bold->red, bold->green, bold->blue);
2378 	_vte_terminal_set_color_internal(terminal, VTE_BOLD_FG, VTE_COLOR_SOURCE_API, bold);
2379 }
2380 
2381 /*
2382  * _vte_terminal_set_color_foreground:
2383  * @terminal: a #VteTerminal
2384  * @foreground: the new foreground color
2385  *
2386  * Sets the foreground color used to draw normal text
2387  */
2388 static void
_vte_terminal_set_color_foreground(VteTerminal * terminal,const PangoColor * foreground)2389 _vte_terminal_set_color_foreground(VteTerminal *terminal,
2390                                    const PangoColor *foreground)
2391 {
2392 	_vte_debug_print(VTE_DEBUG_MISC,
2393 			"Set foreground color to (%04x,%04x,%04x).\n",
2394 			foreground->red, foreground->green, foreground->blue);
2395 	_vte_terminal_set_color_internal(terminal, VTE_DEFAULT_FG, VTE_COLOR_SOURCE_API, foreground);
2396 }
2397 
2398 /*
2399  * _vte_terminal_set_color_background:
2400  * @terminal: a #VteTerminal
2401  * @background: the new background color
2402  *
2403  * Sets the background color for text which does not have a specific background
2404  * color assigned.  Only has effect when no background image is set.
2405  */
2406 static void
_vte_terminal_set_color_background(VteTerminal * terminal,const PangoColor * background)2407 _vte_terminal_set_color_background(VteTerminal *terminal,
2408                                    const PangoColor *background)
2409 {
2410 	_vte_debug_print(VTE_DEBUG_MISC,
2411 			"Set background color to (%04x,%04x,%04x).\n",
2412 			background->red, background->green, background->blue);
2413 	_vte_terminal_set_color_internal(terminal, VTE_DEFAULT_BG, VTE_COLOR_SOURCE_API, background);
2414 }
2415 
2416 /*
2417  * _vte_terminal_set_background_alpha:
2418  * @terminal: a #VteTerminal
2419  * @alpha: an alpha value from 0.0 to 1.0
2420  */
2421 static void
_vte_terminal_set_background_alpha(VteTerminal * terminal,gdouble alpha)2422 _vte_terminal_set_background_alpha(VteTerminal *terminal,
2423                                    gdouble alpha)
2424 {
2425         VteTerminalPrivate *pvt = terminal->pvt;
2426 
2427         if (alpha == pvt->background_alpha)
2428                 return;
2429 
2430         _vte_debug_print(VTE_DEBUG_MISC,
2431                          "Setting background alpha to %.3f\n", alpha);
2432 
2433         pvt->background_alpha = alpha;
2434 
2435         vte_terminal_background_update(terminal);
2436 }
2437 
2438 /*
2439  * _vte_terminal_set_color_cursor:
2440  * @terminal: a #VteTerminal
2441  * @cursor_background: (allow-none): the new color to use for the text cursor, or %NULL
2442  *
2443  * Sets the background color for text which is under the cursor.  If %NULL, text
2444  * under the cursor will be drawn with foreground and background colors
2445  * reversed.
2446  */
2447 static void
_vte_terminal_set_color_cursor(VteTerminal * terminal,const PangoColor * cursor_background)2448 _vte_terminal_set_color_cursor(VteTerminal *terminal,
2449                                const PangoColor *cursor_background)
2450 {
2451 	if (cursor_background != NULL) {
2452 		_vte_debug_print(VTE_DEBUG_MISC,
2453 				"Set cursor color to (%04x,%04x,%04x).\n",
2454 				cursor_background->red,
2455 				cursor_background->green,
2456 				cursor_background->blue);
2457 	} else {
2458 		_vte_debug_print(VTE_DEBUG_MISC,
2459 				"Reset cursor color.\n");
2460 	}
2461 	_vte_terminal_set_color_internal(terminal, VTE_CURSOR_BG, VTE_COLOR_SOURCE_API, cursor_background);
2462 }
2463 
2464 /*
2465  * _vte_terminal_set_color_highlight:
2466  * @terminal: a #VteTerminal
2467  * @highlight_background: (allow-none): the new color to use for highlighted text, or %NULL
2468  *
2469  * Sets the background color for text which is highlighted.  If %NULL,
2470  * it is unset.  If neither highlight background nor highlight foreground are set,
2471  * highlighted text (which is usually highlighted because it is selected) will
2472  * be drawn with foreground and background colors reversed.
2473  */
2474 static void
_vte_terminal_set_color_highlight(VteTerminal * terminal,const PangoColor * highlight_background)2475 _vte_terminal_set_color_highlight(VteTerminal *terminal,
2476                                   const PangoColor *highlight_background)
2477 {
2478 	if (highlight_background != NULL) {
2479 		_vte_debug_print(VTE_DEBUG_MISC,
2480 				"Set highlight background color to (%04x,%04x,%04x).\n",
2481 				highlight_background->red,
2482 				highlight_background->green,
2483 				highlight_background->blue);
2484 	} else {
2485 		_vte_debug_print(VTE_DEBUG_MISC,
2486 				"Reset highlight background color.\n");
2487 	}
2488 	_vte_terminal_set_color_internal(terminal, VTE_HIGHLIGHT_BG, VTE_COLOR_SOURCE_API, highlight_background);
2489 }
2490 
2491 /*
2492  * _vte_terminal_set_color_highlight_foreground:
2493  * @terminal: a #VteTerminal
2494  * @highlight_foreground: (allow-none): the new color to use for highlighted text, or %NULL
2495  *
2496  * Sets the foreground color for text which is highlighted.  If %NULL,
2497  * it is unset.  If neither highlight background nor highlight foreground are set,
2498  * highlighted text (which is usually highlighted because it is selected) will
2499  * be drawn with foreground and background colors reversed.
2500  */
2501 static void
_vte_terminal_set_color_highlight_foreground(VteTerminal * terminal,const PangoColor * highlight_foreground)2502 _vte_terminal_set_color_highlight_foreground(VteTerminal *terminal,
2503                                              const PangoColor *highlight_foreground)
2504 {
2505 	if (highlight_foreground != NULL) {
2506 		_vte_debug_print(VTE_DEBUG_MISC,
2507 				"Set highlight foreground color to (%04x,%04x,%04x).\n",
2508 				highlight_foreground->red,
2509 				highlight_foreground->green,
2510 				highlight_foreground->blue);
2511 	} else {
2512 		_vte_debug_print(VTE_DEBUG_MISC,
2513 				"Reset highlight foreground color.\n");
2514 	}
2515 	_vte_terminal_set_color_internal(terminal, VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_API, highlight_foreground);
2516 }
2517 
2518 /*
2519  * _vte_terminal_set_colors:
2520  * @terminal: a #VteTerminal
2521  * @foreground: (allow-none): the new foreground color, or %NULL
2522  * @background: (allow-none): the new background color, or %NULL
2523  * @palette: (array length=palette_size zero-terminated=0) (element-type Gdk.Color): the color palette
2524  * @palette_size: the number of entries in @palette
2525  *
2526  * @palette specifies the new values for the 256 palette colors: 8 standard colors,
2527  * their 8 bright counterparts, 6x6x6 color cube, and 24 grayscale colors.
2528  * Omitted entries will default to a hardcoded value.
2529  *
2530  * @palette_size must be 0, 8, 16, 232 or 256.
2531  *
2532  * If @foreground is %NULL and @palette_size is greater than 0, the new foreground
2533  * color is taken from @palette[7].  If @background is %NULL and @palette_size is
2534  * greater than 0, the new background color is taken from @palette[0].
2535  */
2536 static void
_vte_terminal_set_colors(VteTerminal * terminal,const PangoColor * foreground,const PangoColor * background,const PangoColor * palette,gsize palette_size)2537 _vte_terminal_set_colors(VteTerminal *terminal,
2538                          const PangoColor *foreground,
2539                          const PangoColor *background,
2540                          const PangoColor *palette,
2541                          gsize palette_size)
2542 {
2543 	gsize i;
2544 	PangoColor color;
2545 	gboolean unset = FALSE;
2546 
2547 	_vte_debug_print(VTE_DEBUG_MISC,
2548 			"Set color palette [%" G_GSIZE_FORMAT " elements].\n",
2549 			palette_size);
2550 
2551 	/* Accept NULL as the default foreground and background colors if we
2552 	 * got a palette. */
2553 	if ((foreground == NULL) && (palette_size >= 8)) {
2554 		foreground = &palette[7];
2555 	}
2556 	if ((background == NULL) && (palette_size >= 8)) {
2557 		background = &palette[0];
2558 	}
2559 
2560 	memset(&color, 0, sizeof(color));
2561 
2562 	/* Initialize each item in the palette if we got any entries to work
2563 	 * with. */
2564 	for (i=0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
2565 		unset = FALSE;
2566 		if (i < 16) {
2567 			color.blue = (i & 4) ? 0xc000 : 0;
2568 			color.green = (i & 2) ? 0xc000 : 0;
2569 			color.red = (i & 1) ? 0xc000 : 0;
2570 			if (i > 7) {
2571 				color.blue += 0x3fff;
2572 				color.green += 0x3fff;
2573 				color.red += 0x3fff;
2574 			}
2575 		}
2576 		else if (i < 232) {
2577 			int j = i - 16;
2578 			int r = j / 36, g = (j / 6) % 6, b = j % 6;
2579 			int red =   (r == 0) ? 0 : r * 40 + 55;
2580 			int green = (g == 0) ? 0 : g * 40 + 55;
2581 			int blue =  (b == 0) ? 0 : b * 40 + 55;
2582 			color.red   = red | red << 8  ;
2583 			color.green = green | green << 8;
2584 			color.blue  = blue | blue << 8;
2585 		} else if (i < 256) {
2586 			int shade = 8 + (i - 232) * 10;
2587 			color.red = color.green = color.blue = shade | shade << 8;
2588 		}
2589 		else switch (i) {
2590 			case VTE_DEFAULT_BG:
2591 				if (background != NULL) {
2592 					color = *background;
2593 				} else {
2594 					color.red = 0;
2595 					color.blue = 0;
2596 					color.green = 0;
2597 				}
2598 				break;
2599 			case VTE_DEFAULT_FG:
2600 				if (foreground != NULL) {
2601 					color = *foreground;
2602 				} else {
2603 					color.red = 0xc000;
2604 					color.blue = 0xc000;
2605 					color.green = 0xc000;
2606 				}
2607 				break;
2608 			case VTE_BOLD_FG:
2609 				vte_terminal_generate_bold(_vte_terminal_get_color(terminal, VTE_DEFAULT_FG),
2610 							   _vte_terminal_get_color(terminal, VTE_DEFAULT_BG),
2611 							   1.8,
2612 							   &color);
2613 				break;
2614 			case VTE_HIGHLIGHT_BG:
2615 				unset = TRUE;
2616 				break;
2617 			case VTE_HIGHLIGHT_FG:
2618 				unset = TRUE;
2619 				break;
2620 			case VTE_CURSOR_BG:
2621 				unset = TRUE;
2622 				break;
2623 			}
2624 
2625 		/* Override from the supplied palette if there is one. */
2626 		if (i < palette_size) {
2627 			color = palette[i];
2628 		}
2629 
2630 		/* Set up the color entry. */
2631 		_vte_terminal_set_color_internal(terminal, i, VTE_COLOR_SOURCE_API, unset ? NULL : &color);
2632 	}
2633 }
2634 
2635 static PangoColor *
_pango_color_from_rgba(PangoColor * color,const GdkRGBA * rgba)2636 _pango_color_from_rgba(PangoColor *color,
2637                        const GdkRGBA *rgba)
2638 {
2639         if (rgba == NULL)
2640                 return NULL;
2641 
2642         color->red = rgba->red * 65535.;
2643         color->green = rgba->green * 65535.;
2644         color->blue = rgba->blue * 65535.;
2645 
2646 	return color;
2647 }
2648 
2649 /**
2650  * vte_terminal_set_color_bold:
2651  * @terminal: a #VteTerminal
2652  * @bold: (allow-none): the new bold color or %NULL
2653  *
2654  * Sets the color used to draw bold text in the default foreground color.
2655  * If @bold is %NULL then the default color is used.
2656  */
2657 void
vte_terminal_set_color_bold(VteTerminal * terminal,const GdkRGBA * bold)2658 vte_terminal_set_color_bold(VteTerminal *terminal,
2659                                  const GdkRGBA *bold)
2660 {
2661 	PangoColor color;
2662 
2663         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2664 
2665 	if (bold == NULL)
2666 	{
2667 		vte_terminal_generate_bold(_vte_terminal_get_color(terminal, VTE_DEFAULT_FG),
2668 					   _vte_terminal_get_color(terminal, VTE_DEFAULT_BG),
2669 					   1.8,
2670 					   &color);
2671 	}
2672 	else
2673 	{
2674 		_pango_color_from_rgba(&color, bold);
2675 	}
2676 
2677 	_vte_terminal_set_color_bold(terminal, &color);
2678 }
2679 
2680 /**
2681  * vte_terminal_set_color_foreground:
2682  * @terminal: a #VteTerminal
2683  * @foreground: the new foreground color
2684  *
2685  * Sets the foreground color used to draw normal text.
2686  */
2687 void
vte_terminal_set_color_foreground(VteTerminal * terminal,const GdkRGBA * foreground)2688 vte_terminal_set_color_foreground(VteTerminal *terminal,
2689 				       const GdkRGBA *foreground)
2690 {
2691 	PangoColor color;
2692 
2693         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2694         g_return_if_fail(foreground != NULL);
2695 
2696 	_vte_terminal_set_color_foreground(terminal,
2697                                            _pango_color_from_rgba(&color, foreground));
2698 }
2699 
2700 /**
2701  * vte_terminal_set_color_background:
2702  * @terminal: a #VteTerminal
2703  * @background: the new background color
2704  *
2705  * Sets the background color for text which does not have a specific background
2706  * color assigned.  Only has effect when no background image is set and when
2707  * the terminal is not transparent.
2708  */
2709 void
vte_terminal_set_color_background(VteTerminal * terminal,const GdkRGBA * background)2710 vte_terminal_set_color_background(VteTerminal *terminal,
2711 				       const GdkRGBA *background)
2712 {
2713 	PangoColor color;
2714 
2715         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2716         g_return_if_fail(background != NULL);
2717 
2718 	_vte_terminal_set_color_background(terminal,
2719                                            _pango_color_from_rgba(&color, background));
2720         _vte_terminal_set_background_alpha(terminal, background->alpha);
2721 }
2722 
2723 /**
2724  * vte_terminal_set_color_cursor:
2725  * @terminal: a #VteTerminal
2726  * @cursor_background: (allow-none): the new color to use for the text cursor, or %NULL
2727  *
2728  * Sets the background color for text which is under the cursor.  If %NULL, text
2729  * under the cursor will be drawn with foreground and background colors
2730  * reversed.
2731  */
2732 void
vte_terminal_set_color_cursor(VteTerminal * terminal,const GdkRGBA * cursor_background)2733 vte_terminal_set_color_cursor(VteTerminal *terminal,
2734 				   const GdkRGBA *cursor_background)
2735 {
2736         PangoColor color;
2737 
2738         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2739 
2740 	_vte_terminal_set_color_cursor(terminal,
2741                                        _pango_color_from_rgba(&color, cursor_background));
2742 }
2743 
2744 /**
2745  * vte_terminal_set_color_highlight:
2746  * @terminal: a #VteTerminal
2747  * @highlight_background: (allow-none): the new color to use for highlighted text, or %NULL
2748  *
2749  * Sets the background color for text which is highlighted.  If %NULL,
2750  * it is unset.  If neither highlight background nor highlight foreground are set,
2751  * highlighted text (which is usually highlighted because it is selected) will
2752  * be drawn with foreground and background colors reversed.
2753  */
2754 void
vte_terminal_set_color_highlight(VteTerminal * terminal,const GdkRGBA * highlight_background)2755 vte_terminal_set_color_highlight(VteTerminal *terminal,
2756 				      const GdkRGBA *highlight_background)
2757 {
2758 	PangoColor color;
2759 
2760         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2761 
2762 	_vte_terminal_set_color_highlight(terminal,
2763                                           _pango_color_from_rgba(&color, highlight_background));
2764 }
2765 
2766 /**
2767  * vte_terminal_set_color_highlight_foreground:
2768  * @terminal: a #VteTerminal
2769  * @highlight_foreground: (allow-none): the new color to use for highlighted text, or %NULL
2770  *
2771  * Sets the foreground color for text which is highlighted.  If %NULL,
2772  * it is unset.  If neither highlight background nor highlight foreground are set,
2773  * highlighted text (which is usually highlighted because it is selected) will
2774  * be drawn with foreground and background colors reversed.
2775  */
2776 void
vte_terminal_set_color_highlight_foreground(VteTerminal * terminal,const GdkRGBA * highlight_foreground)2777 vte_terminal_set_color_highlight_foreground(VteTerminal *terminal,
2778 						 const GdkRGBA *highlight_foreground)
2779 {
2780 	PangoColor color;
2781 
2782         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2783 
2784 	_vte_terminal_set_color_highlight_foreground(terminal,
2785                                                      _pango_color_from_rgba(&color, highlight_foreground));
2786 }
2787 
2788 /**
2789  * vte_terminal_set_colors:
2790  * @terminal: a #VteTerminal
2791  * @foreground: (allow-none): the new foreground color, or %NULL
2792  * @background: (allow-none): the new background color, or %NULL
2793  * @palette: (array length=palette_size zero-terminated=0) (element-type Gdk.RGBA) (allow-none): the color palette
2794  * @palette_size: the number of entries in @palette
2795  *
2796  * @palette specifies the new values for the 256 palette colors: 8 standard colors,
2797  * their 8 bright counterparts, 6x6x6 color cube, and 24 grayscale colors.
2798  * Omitted entries will default to a hardcoded value.
2799  *
2800  * @palette_size must be 0, 8, 16, 232 or 256.
2801  *
2802  * If @foreground is %NULL and @palette_size is greater than 0, the new foreground
2803  * color is taken from @palette[7].  If @background is %NULL and @palette_size is
2804  * greater than 0, the new background color is taken from @palette[0].
2805  */
2806 void
vte_terminal_set_colors(VteTerminal * terminal,const GdkRGBA * foreground,const GdkRGBA * background,const GdkRGBA * palette,gsize palette_size)2807 vte_terminal_set_colors(VteTerminal *terminal,
2808 			     const GdkRGBA *foreground,
2809 			     const GdkRGBA *background,
2810 			     const GdkRGBA *palette,
2811 			     gsize palette_size)
2812 {
2813 	PangoColor fg, bg, *pal;
2814 	gsize i;
2815 
2816         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2817 	g_return_if_fail((palette_size == 0) ||
2818 			 (palette_size == 8) ||
2819 			 (palette_size == 16) ||
2820 			 (palette_size == 232) ||
2821 			 (palette_size == 256));
2822 
2823 	pal = g_new (PangoColor, palette_size);
2824 	for (i = 0; i < palette_size; ++i)
2825                 _pango_color_from_rgba(&pal[i], &palette[i]);
2826 
2827 	_vte_terminal_set_colors(terminal,
2828                                  _pango_color_from_rgba(&fg, foreground),
2829                                  _pango_color_from_rgba(&bg, background),
2830                                  pal, palette_size);
2831 
2832         _vte_terminal_set_background_alpha(terminal, background ? background->alpha : 1.0);
2833 
2834 	g_free (pal);
2835 }
2836 
2837 /**
2838  * vte_terminal_set_default_colors:
2839  * @terminal: a #VteTerminal
2840  *
2841  * Reset the terminal palette to reasonable compiled-in default color.
2842  */
2843 void
vte_terminal_set_default_colors(VteTerminal * terminal)2844 vte_terminal_set_default_colors(VteTerminal *terminal)
2845 {
2846 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
2847 	_vte_terminal_set_colors(terminal, NULL, NULL, NULL, 0);
2848 }
2849 
2850 /*
2851  * _vte_terminal_cleanup_fragments:
2852  * @terminal: a #VteTerminal
2853  * @start: the starting column, inclusive
2854  * @end: the end column, exclusive
2855  *
2856  * Needs to be called before modifying the contents in the cursor's row,
2857  * between the two given columns.  Cleans up TAB and CJK fragments to the
2858  * left of @start and to the right of @end.  If a CJK is split in half,
2859  * the remaining half is replaced by a space.  If a TAB at @start is split,
2860  * it is replaced by spaces.  If a TAB at @end is split, it is replaced by
2861  * a shorter TAB.  @start and @end can be equal if characters will be
2862  * inserted at the location rather than overwritten.
2863  *
2864  * The area between @start and @end is not cleaned up, hence the whole row
2865  * can be left in an inconsistent state.  It is expected that the caller
2866  * will fill up that range afterwards, resulting in a consistent row again.
2867  *
2868  * Invalidates the cells that visually change outside of the range,
2869  * because the caller can't reasonably be expected to take care of this.
2870  */
2871 void
_vte_terminal_cleanup_fragments(VteTerminal * terminal,long start,long end)2872 _vte_terminal_cleanup_fragments(VteTerminal *terminal,
2873                                 long start, long end)
2874 {
2875         VteRowData *row = _vte_terminal_ensure_row (terminal);
2876         const VteCell *cell_start;
2877         VteCell *cell_end, *cell_col;
2878         gboolean cell_start_is_fragment;
2879         long col;
2880 
2881         g_assert(end >= start);
2882 
2883         /* Remember whether the cell at start is a fragment.  We'll need to know it when
2884          * handling the left hand side, but handling the right hand side first might
2885          * overwrite it if start == end (inserting to the middle of a character). */
2886         cell_start = _vte_row_data_get (row, start);
2887         cell_start_is_fragment = cell_start != NULL && cell_start->attr.fragment;
2888 
2889         /* On the right hand side, try to replace a TAB by a shorter TAB if we can.
2890          * This requires that the TAB on the left (which might be the same TAB) is
2891          * not yet converted to spaces, so start on the right hand side. */
2892         cell_end = _vte_row_data_get_writable (row, end);
2893         if (G_UNLIKELY (cell_end != NULL && cell_end->attr.fragment)) {
2894                 col = end;
2895                 do {
2896                         col--;
2897                         g_assert(col >= 0);  /* The first cell can't be a fragment. */
2898                         cell_col = _vte_row_data_get_writable (row, col);
2899                 } while (cell_col->attr.fragment);
2900                 if (cell_col->c == '\t') {
2901                         _vte_debug_print(VTE_DEBUG_MISC,
2902                                          "Replacing right part of TAB with a shorter one at %ld (%d cells) => %ld (%ld cells)\n",
2903                                          col, cell_col->attr.columns, end, cell_col->attr.columns - (end - col));
2904                         cell_end->c = '\t';
2905                         cell_end->attr.fragment = 0;
2906                         g_assert(cell_col->attr.columns > end - col);
2907                         cell_end->attr.columns = cell_col->attr.columns - (end - col);
2908                 } else {
2909                         _vte_debug_print(VTE_DEBUG_MISC,
2910                                          "Cleaning CJK right half at %ld\n",
2911                                          end);
2912                         g_assert(end - col == 1 && cell_col->attr.columns == 2);
2913                         cell_end->c = ' ';
2914                         cell_end->attr.fragment = 0;
2915                         cell_end->attr.columns = 1;
2916                         _vte_invalidate_cells(terminal,
2917                                               end, 1,
2918                                               terminal->pvt->cursor.row, 1);
2919                 }
2920         }
2921 
2922         /* Handle the left hand side.  Converting longer TABs to shorter ones probably
2923          * wouldn't make that much sense here, so instead convert to spaces. */
2924         if (G_UNLIKELY (cell_start_is_fragment)) {
2925                 gboolean keep_going = TRUE;
2926                 col = start;
2927                 do {
2928                         col--;
2929                         g_assert(col >= 0);  /* The first cell can't be a fragment. */
2930                         cell_col = _vte_row_data_get_writable (row, col);
2931                         if (!cell_col->attr.fragment) {
2932                                 if (cell_col->c == '\t') {
2933                                         _vte_debug_print(VTE_DEBUG_MISC,
2934                                                          "Replacing left part of TAB with spaces at %ld (%d => %ld cells)\n",
2935                                                          col, cell_col->attr.columns, start - col);
2936                                         /* nothing to do here */
2937                                 } else {
2938                                         _vte_debug_print(VTE_DEBUG_MISC,
2939                                                          "Cleaning CJK left half at %ld\n",
2940                                                          col);
2941                                         g_assert(start - col == 1);
2942                                         _vte_invalidate_cells(terminal,
2943                                                               col, 1,
2944                                                               terminal->pvt->cursor.row, 1);
2945                                 }
2946                                 keep_going = FALSE;
2947                         }
2948                         cell_col->c = ' ';
2949                         cell_col->attr.fragment = 0;
2950                         cell_col->attr.columns = 1;
2951                 } while (keep_going);
2952         }
2953 }
2954 
2955 /* Cursor down, with scrolling. */
2956 void
_vte_terminal_cursor_down(VteTerminal * terminal)2957 _vte_terminal_cursor_down (VteTerminal *terminal)
2958 {
2959 	long start, end;
2960 	VteScreen *screen;
2961 
2962 	screen = terminal->pvt->screen;
2963 
2964         if (terminal->pvt->scrolling_restricted) {
2965                 start = screen->insert_delta + terminal->pvt->scrolling_region.start;
2966                 end = screen->insert_delta + terminal->pvt->scrolling_region.end;
2967 	} else {
2968 		start = screen->insert_delta;
2969 		end = start + terminal->pvt->row_count - 1;
2970 	}
2971         if (terminal->pvt->cursor.row == end) {
2972                 if (terminal->pvt->scrolling_restricted) {
2973 			if (start == screen->insert_delta) {
2974 				/* Scroll this line into the scrollback
2975 				 * buffer by inserting a line at the next
2976 				 * line and scrolling the area up. */
2977 				screen->insert_delta++;
2978                                 terminal->pvt->cursor.row++;
2979 				/* update start and end, as they are relative
2980 				 * to insert_delta. */
2981 				start++;
2982 				end++;
2983                                 _vte_terminal_ring_insert (terminal, terminal->pvt->cursor.row, FALSE);
2984 				/* Force the areas below the region to be
2985 				 * redrawn -- they've moved. */
2986 				_vte_terminal_scroll_region(terminal, start,
2987 							    end - start + 1, 1);
2988 				/* Force scroll. */
2989 				_vte_terminal_adjust_adjustments(terminal);
2990 			} else {
2991 				/* If we're at the bottom of the scrolling
2992 				 * region, add a line at the top to scroll the
2993 				 * bottom off. */
2994 				_vte_terminal_ring_remove (terminal, start);
2995 				_vte_terminal_ring_insert (terminal, end, TRUE);
2996 				/* Update the display. */
2997 				_vte_terminal_scroll_region(terminal, start,
2998 							   end - start + 1, -1);
2999 				_vte_invalidate_cells(terminal,
3000 						      0, terminal->pvt->column_count,
3001 						      end - 2, 2);
3002 			}
3003 		} else {
3004 			/* Scroll up with history. */
3005                         terminal->pvt->cursor.row++;
3006 			_vte_terminal_update_insert_delta(terminal);
3007 		}
3008 
3009 		/* Match xterm and fill the new row when scrolling. */
3010                 if (terminal->pvt->fill_defaults.attr.back != VTE_DEFAULT_BG) {
3011 			VteRowData *rowdata;
3012 			rowdata = _vte_terminal_ensure_row (terminal);
3013                         _vte_row_data_fill (rowdata, &terminal->pvt->fill_defaults, terminal->pvt->column_count);
3014 		}
3015 	} else {
3016 		/* Otherwise, just move the cursor down. */
3017                 terminal->pvt->cursor.row++;
3018 	}
3019 }
3020 
3021 /* Drop the scrollback. */
3022 void
_vte_terminal_drop_scrollback(VteTerminal * terminal)3023 _vte_terminal_drop_scrollback (VteTerminal *terminal)
3024 {
3025         /* Only for normal screen; alternate screen doesn't have a scrollback. */
3026         _vte_ring_drop_scrollback (terminal->pvt->normal_screen.row_data,
3027                                    terminal->pvt->normal_screen.insert_delta);
3028 
3029         if (terminal->pvt->screen == &terminal->pvt->normal_screen) {
3030                 vte_terminal_queue_adjustment_value_changed (terminal, terminal->pvt->normal_screen.insert_delta);
3031                 _vte_terminal_adjust_adjustments_full (terminal);
3032         }
3033 }
3034 
3035 /* Restore cursor on a screen. */
3036 void
_vte_terminal_restore_cursor(VteTerminal * terminal,VteScreen * screen)3037 _vte_terminal_restore_cursor (VteTerminal *terminal, VteScreen *screen)
3038 {
3039         terminal->pvt->cursor.col = screen->saved.cursor.col;
3040         terminal->pvt->cursor.row = screen->insert_delta + CLAMP(screen->saved.cursor.row,
3041                                                                  0, terminal->pvt->row_count - 1);
3042 
3043         terminal->pvt->reverse_mode = screen->saved.reverse_mode;
3044         terminal->pvt->origin_mode = screen->saved.origin_mode;
3045         terminal->pvt->sendrecv_mode = screen->saved.sendrecv_mode;
3046         terminal->pvt->insert_mode = screen->saved.insert_mode;
3047         terminal->pvt->linefeed_mode = screen->saved.linefeed_mode;
3048         terminal->pvt->defaults = screen->saved.defaults;
3049         terminal->pvt->color_defaults = screen->saved.color_defaults;
3050         terminal->pvt->fill_defaults = screen->saved.fill_defaults;
3051         terminal->pvt->character_replacements[0] = screen->saved.character_replacements[0];
3052         terminal->pvt->character_replacements[1] = screen->saved.character_replacements[1];
3053         terminal->pvt->character_replacement = screen->saved.character_replacement;
3054 }
3055 
3056 /* Save cursor on a screen. */
3057 void
_vte_terminal_save_cursor(VteTerminal * terminal,VteScreen * screen)3058 _vte_terminal_save_cursor (VteTerminal *terminal, VteScreen *screen)
3059 {
3060         screen->saved.cursor.col = terminal->pvt->cursor.col;
3061         screen->saved.cursor.row = terminal->pvt->cursor.row - screen->insert_delta;
3062 
3063         screen->saved.reverse_mode = terminal->pvt->reverse_mode;
3064         screen->saved.origin_mode = terminal->pvt->origin_mode;
3065         screen->saved.sendrecv_mode = terminal->pvt->sendrecv_mode;
3066         screen->saved.insert_mode = terminal->pvt->insert_mode;
3067         screen->saved.linefeed_mode = terminal->pvt->linefeed_mode;
3068         screen->saved.defaults = terminal->pvt->defaults;
3069         screen->saved.color_defaults = terminal->pvt->color_defaults;
3070         screen->saved.fill_defaults = terminal->pvt->fill_defaults;
3071         screen->saved.character_replacements[0] = terminal->pvt->character_replacements[0];
3072         screen->saved.character_replacements[1] = terminal->pvt->character_replacements[1];
3073         screen->saved.character_replacement = terminal->pvt->character_replacement;
3074 }
3075 
3076 /* Insert a single character into the stored data array. */
3077 gboolean
_vte_terminal_insert_char(VteTerminal * terminal,gunichar c,gboolean insert,gboolean invalidate_now)3078 _vte_terminal_insert_char(VteTerminal *terminal, gunichar c,
3079 			 gboolean insert, gboolean invalidate_now)
3080 {
3081 	VteCellAttr attr;
3082 	VteRowData *row;
3083 	long col;
3084 	int columns, i;
3085 	VteScreen *screen;
3086 	gboolean line_wrapped = FALSE; /* cursor moved before char inserted */
3087 
3088         /* DEC Special Character and Line Drawing Set.  VT100 and higher (per XTerm docs). */
3089         static gunichar line_drawing_map[31] = {
3090                 0x25c6,  /* ` => diamond */
3091                 0x2592,  /* a => checkerboard */
3092                 0x2409,  /* b => HT symbol */
3093                 0x240c,  /* c => FF symbol */
3094                 0x240d,  /* d => CR symbol */
3095                 0x240a,  /* e => LF symbol */
3096                 0x00b0,  /* f => degree */
3097                 0x00b1,  /* g => plus/minus */
3098                 0x2424,  /* h => NL symbol */
3099                 0x240b,  /* i => VT symbol */
3100                 0x2518,  /* j => downright corner */
3101                 0x2510,  /* k => upright corner */
3102                 0x250c,  /* l => upleft corner */
3103                 0x2514,  /* m => downleft corner */
3104                 0x253c,  /* n => cross */
3105                 0x23ba,  /* o => scan line 1/9 */
3106                 0x23bb,  /* p => scan line 3/9 */
3107                 0x2500,  /* q => horizontal line (also scan line 5/9) */
3108                 0x23bc,  /* r => scan line 7/9 */
3109                 0x23bd,  /* s => scan line 9/9 */
3110                 0x251c,  /* t => left t */
3111                 0x2524,  /* u => right t */
3112                 0x2534,  /* v => bottom t */
3113                 0x252c,  /* w => top t */
3114                 0x2502,  /* x => vertical line */
3115                 0x2264,  /* y => <= */
3116                 0x2265,  /* z => >= */
3117                 0x03c0,  /* { => pi */
3118                 0x2260,  /* | => not equal */
3119                 0x00a3,  /* } => pound currency sign */
3120                 0x00b7,  /* ~ => bullet */
3121         };
3122 
3123 	screen = terminal->pvt->screen;
3124         insert |= terminal->pvt->insert_mode;
3125 	invalidate_now |= insert;
3126 
3127 	/* If we've enabled the special drawing set, map the characters to
3128 	 * Unicode. */
3129         if (G_UNLIKELY (*terminal->pvt->character_replacement == VTE_CHARACTER_REPLACEMENT_LINE_DRAWING)) {
3130                 if (c >= 96 && c <= 126)
3131                         c = line_drawing_map[c - 96];
3132         } else if (G_UNLIKELY (*terminal->pvt->character_replacement == VTE_CHARACTER_REPLACEMENT_BRITISH)) {
3133                 if (G_UNLIKELY (c == '#'))
3134                         c = 0x00a3;  /* pound sign */
3135         }
3136 
3137 	/* Figure out how many columns this character should occupy. */
3138         columns = _vte_unichar_width(c, terminal->pvt->utf8_ambiguous_width);
3139 
3140 	/* If we're autowrapping here, do it. */
3141         col = terminal->pvt->cursor.col;
3142 	if (G_UNLIKELY (columns && col + columns > terminal->pvt->column_count)) {
3143 		if (terminal->pvt->autowrap) {
3144 			_vte_debug_print(VTE_DEBUG_ADJ,
3145 					"Autowrapping before character\n");
3146 			/* Wrap. */
3147 			/* XXX clear to the end of line */
3148                         col = terminal->pvt->cursor.col = 0;
3149 			/* Mark this line as soft-wrapped. */
3150 			row = _vte_terminal_ensure_row (terminal);
3151 			row->attr.soft_wrapped = 1;
3152 			_vte_terminal_cursor_down (terminal);
3153 		} else {
3154 			/* Don't wrap, stay at the rightmost column. */
3155                         col = terminal->pvt->cursor.col =
3156 				terminal->pvt->column_count - columns;
3157 		}
3158 		line_wrapped = TRUE;
3159 	}
3160 
3161 	_vte_debug_print(VTE_DEBUG_PARSE,
3162 			"Inserting %ld '%c' (%d/%d) (%ld+%d, %ld), delta = %ld; ",
3163 			(long)c, c < 256 ? c : ' ',
3164                         terminal->pvt->color_defaults.attr.fore,
3165                         terminal->pvt->color_defaults.attr.back,
3166                         col, columns, (long)terminal->pvt->cursor.row,
3167 			(long)screen->insert_delta);
3168 
3169 
3170 	if (G_UNLIKELY (columns == 0)) {
3171 
3172 		/* It's a combining mark */
3173 
3174 		long row_num;
3175 		VteCell *cell;
3176 
3177 		_vte_debug_print(VTE_DEBUG_PARSE, "combining U+%04X", c);
3178 
3179                 row_num = terminal->pvt->cursor.row;
3180 		row = NULL;
3181 		if (G_UNLIKELY (col == 0)) {
3182 			/* We are at first column.  See if the previous line softwrapped.
3183 			 * If it did, move there.  Otherwise skip inserting. */
3184 
3185 			if (G_LIKELY (row_num > 0)) {
3186 				row_num--;
3187 				row = _vte_terminal_find_row_data_writable (terminal, row_num);
3188 
3189 				if (row) {
3190 					if (!row->attr.soft_wrapped)
3191 						row = NULL;
3192 					else
3193 						col = _vte_row_data_length (row);
3194 				}
3195 			}
3196 		} else {
3197 			row = _vte_terminal_find_row_data_writable (terminal, row_num);
3198 		}
3199 
3200 		if (G_UNLIKELY (!row || !col))
3201 			goto not_inserted;
3202 
3203 		/* Combine it on the previous cell */
3204 
3205 		col--;
3206 		cell = _vte_row_data_get_writable (row, col);
3207 
3208 		if (G_UNLIKELY (!cell))
3209 			goto not_inserted;
3210 
3211 		/* Find the previous cell */
3212 		while (cell && cell->attr.fragment && col > 0)
3213 			cell = _vte_row_data_get_writable (row, --col);
3214 		if (G_UNLIKELY (!cell || cell->c == '\t'))
3215 			goto not_inserted;
3216 
3217 		/* Combine the new character on top of the cell string */
3218 		c = _vte_unistr_append_unichar (cell->c, c);
3219 
3220 		/* And set it */
3221 		columns = cell->attr.columns;
3222 		for (i = 0; i < columns; i++) {
3223 			cell = _vte_row_data_get_writable (row, col++);
3224 			cell->c = c;
3225 		}
3226 
3227 		/* Always invalidate since we put the mark on the *previous* cell
3228 		 * and the higher level code doesn't know this. */
3229 		_vte_invalidate_cells(terminal,
3230 				      col - columns,
3231 				      columns,
3232 				      row_num, 1);
3233 
3234 		goto done;
3235 	}
3236 
3237 	/* Make sure we have enough rows to hold this data. */
3238 	row = vte_terminal_ensure_cursor (terminal);
3239 	g_assert(row != NULL);
3240 
3241 	if (insert) {
3242                 _vte_terminal_cleanup_fragments (terminal, col, col);
3243 		for (i = 0; i < columns; i++)
3244                         _vte_row_data_insert (row, col + i, &terminal->pvt->color_defaults);
3245 	} else {
3246                 _vte_terminal_cleanup_fragments (terminal, col, col + columns);
3247 		_vte_row_data_fill (row, &basic_cell.cell, col + columns);
3248 	}
3249 
3250         attr = terminal->pvt->defaults.attr;
3251         attr.fore = terminal->pvt->color_defaults.attr.fore;
3252         attr.back = terminal->pvt->color_defaults.attr.back;
3253 	attr.columns = columns;
3254 
3255 	{
3256 		VteCell *pcell = _vte_row_data_get_writable (row, col);
3257 		pcell->c = c;
3258 		pcell->attr = attr;
3259 		col++;
3260 	}
3261 
3262 	/* insert wide-char fragments */
3263 	attr.fragment = 1;
3264 	for (i = 1; i < columns; i++) {
3265 		VteCell *pcell = _vte_row_data_get_writable (row, col);
3266 		pcell->c = c;
3267 		pcell->attr = attr;
3268 		col++;
3269 	}
3270 	if (_vte_row_data_length (row) > terminal->pvt->column_count)
3271 		_vte_terminal_cleanup_fragments (terminal, terminal->pvt->column_count, _vte_row_data_length (row));
3272 	_vte_row_data_shrink (row, terminal->pvt->column_count);
3273 
3274 	/* Signal that this part of the window needs drawing. */
3275 	if (G_UNLIKELY (invalidate_now)) {
3276 		_vte_invalidate_cells(terminal,
3277 				col - columns,
3278 				insert ? terminal->pvt->column_count : columns,
3279                                 terminal->pvt->cursor.row, 1);
3280 	}
3281 
3282         terminal->pvt->cursor.col = col;
3283 
3284 done:
3285 	/* We added text, so make a note of it. */
3286 	terminal->pvt->text_inserted_flag = TRUE;
3287 
3288 not_inserted:
3289 	_vte_debug_print(VTE_DEBUG_ADJ|VTE_DEBUG_PARSE,
3290 			"insertion delta => %ld.\n",
3291 			(long)screen->insert_delta);
3292 	return line_wrapped;
3293 }
3294 
3295 static void
vte_terminal_child_watch_cb(GPid pid,int status,VteTerminal * terminal)3296 vte_terminal_child_watch_cb(GPid pid,
3297                             int status,
3298                             VteTerminal *terminal)
3299 {
3300 	if (terminal == NULL) {
3301 		/* The child outlived VteTerminal. Do nothing, we're happy that Glib
3302 		 * read its exit data and hence it's no longer there as zombie. */
3303 		return;
3304 	}
3305 
3306 	if (pid == terminal->pvt->pty_pid) {
3307                 GObject *object = G_OBJECT(terminal);
3308 
3309                 g_object_ref(object);
3310                 g_object_freeze_notify(object);
3311 
3312 		_VTE_DEBUG_IF (VTE_DEBUG_LIFECYCLE) {
3313 			g_printerr ("Child[%d] exited with status %d\n",
3314 					pid, status);
3315 #ifdef HAVE_SYS_WAIT_H
3316 			if (WIFEXITED (status)) {
3317 				g_printerr ("Child[%d] exit code %d.\n",
3318 						pid, WEXITSTATUS (status));
3319 			}else if (WIFSIGNALED (status)) {
3320 				g_printerr ("Child[%d] dies with signal %d.\n",
3321 						pid, WTERMSIG (status));
3322 			}
3323 #endif
3324 		}
3325 
3326 		terminal->pvt->child_watch_source = 0;
3327 		terminal->pvt->pty_pid = -1;
3328 
3329 		/* Close out the PTY. */
3330                 vte_terminal_set_pty(terminal, NULL);
3331 
3332 		/* Tell observers what's happened. */
3333 		vte_terminal_emit_child_exited(terminal, status);
3334 
3335                 g_object_thaw_notify(object);
3336                 g_object_unref(object);
3337 
3338                 /* Note: terminal may be destroyed at this point */
3339 	}
3340 }
3341 
mark_input_source_invalid(VteTerminal * terminal)3342 static void mark_input_source_invalid(VteTerminal *terminal)
3343 {
3344 	_vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_read\n");
3345 	terminal->pvt->pty_input_source = 0;
3346 }
3347 static void
_vte_terminal_connect_pty_read(VteTerminal * terminal)3348 _vte_terminal_connect_pty_read(VteTerminal *terminal)
3349 {
3350 	if (terminal->pvt->pty_channel == NULL) {
3351 		return;
3352 	}
3353 
3354 	if (terminal->pvt->pty_input_source == 0) {
3355 		_vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_read\n");
3356 		terminal->pvt->pty_input_source =
3357 			g_io_add_watch_full(terminal->pvt->pty_channel,
3358 					    VTE_CHILD_INPUT_PRIORITY,
3359 					    G_IO_IN | G_IO_HUP,
3360 					    (GIOFunc) vte_terminal_io_read,
3361 					    terminal,
3362 					    (GDestroyNotify) mark_input_source_invalid);
3363 	}
3364 }
3365 
mark_output_source_invalid(VteTerminal * terminal)3366 static void mark_output_source_invalid(VteTerminal *terminal)
3367 {
3368 	_vte_debug_print (VTE_DEBUG_IO, "removed poll of vte_terminal_io_write\n");
3369 	terminal->pvt->pty_output_source = 0;
3370 }
3371 static void
_vte_terminal_connect_pty_write(VteTerminal * terminal)3372 _vte_terminal_connect_pty_write(VteTerminal *terminal)
3373 {
3374         VteTerminalPrivate *pvt = terminal->pvt;
3375 
3376         g_assert(pvt->pty != NULL);
3377         g_warn_if_fail(pvt->input_enabled);
3378 
3379 	if (terminal->pvt->pty_channel == NULL) {
3380 		pvt->pty_channel =
3381 			g_io_channel_unix_new(vte_pty_get_fd(pvt->pty));
3382 	}
3383 
3384 	if (terminal->pvt->pty_output_source == 0) {
3385 		if (vte_terminal_io_write (terminal->pvt->pty_channel,
3386 					     G_IO_OUT,
3387 					     terminal))
3388 		{
3389 			_vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_write\n");
3390 			terminal->pvt->pty_output_source =
3391 				g_io_add_watch_full(terminal->pvt->pty_channel,
3392 						    VTE_CHILD_OUTPUT_PRIORITY,
3393 						    G_IO_OUT,
3394 						    (GIOFunc) vte_terminal_io_write,
3395 						    terminal,
3396 						    (GDestroyNotify) mark_output_source_invalid);
3397 		}
3398 	}
3399 }
3400 
3401 static void
_vte_terminal_disconnect_pty_read(VteTerminal * terminal)3402 _vte_terminal_disconnect_pty_read(VteTerminal *terminal)
3403 {
3404 	if (terminal->pvt->pty_input_source != 0) {
3405 		_vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_read\n");
3406 		g_source_remove(terminal->pvt->pty_input_source);
3407 		terminal->pvt->pty_input_source = 0;
3408 	}
3409 }
3410 
3411 static void
_vte_terminal_disconnect_pty_write(VteTerminal * terminal)3412 _vte_terminal_disconnect_pty_write(VteTerminal *terminal)
3413 {
3414 	if (terminal->pvt->pty_output_source != 0) {
3415 		_vte_debug_print (VTE_DEBUG_IO, "disconnecting poll of vte_terminal_io_write\n");
3416 
3417 		g_source_remove(terminal->pvt->pty_output_source);
3418 		terminal->pvt->pty_output_source = 0;
3419 	}
3420 }
3421 
3422 /**
3423  * vte_terminal_pty_new_sync:
3424  * @terminal: a #VteTerminal
3425  * @flags: flags from #VtePtyFlags
3426  * @cancellable: (allow-none): a #GCancellable, or %NULL
3427  * @error: (allow-none): return location for a #GError, or %NULL
3428  *
3429  * Creates a new #VtePty, and sets the emulation property
3430  * from #VteTerminal:emulation.
3431  *
3432  * See vte_pty_new() for more information.
3433  *
3434  * Returns: (transfer full): a new #VtePty
3435  */
3436 VtePty *
vte_terminal_pty_new_sync(VteTerminal * terminal,VtePtyFlags flags,GCancellable * cancellable,GError ** error)3437 vte_terminal_pty_new_sync(VteTerminal *terminal,
3438                           VtePtyFlags flags,
3439                           GCancellable *cancellable,
3440                           GError **error)
3441 {
3442         VtePty *pty;
3443 
3444         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
3445 
3446         pty = vte_pty_new_sync(flags, cancellable, error);
3447         if (pty == NULL)
3448                 return NULL;
3449 
3450         return pty;
3451 }
3452 
3453 /**
3454  * vte_terminal_watch_child:
3455  * @terminal: a #VteTerminal
3456  * @child_pid: a #GPid
3457  *
3458  * Watches @child_pid. When the process exists, the #VteTerminal::child-exited
3459  * signal will be called with the child's exit status.
3460  *
3461  * Prior to calling this function, a #VtePty must have been set in @terminal
3462  * using vte_terminal_set_pty().
3463  * When the child exits, the terminal's #VtePty will be set to %NULL.
3464  *
3465  * Note: g_child_watch_add() or g_child_watch_add_full() must not have
3466  * been called for @child_pid, nor a #GSource for it been created with
3467  * g_child_watch_source_new().
3468  *
3469  * Note: when using the g_spawn_async() family of functions,
3470  * the %G_SPAWN_DO_NOT_REAP_CHILD flag MUST have been passed.
3471  */
3472 void
vte_terminal_watch_child(VteTerminal * terminal,GPid child_pid)3473 vte_terminal_watch_child (VteTerminal *terminal,
3474                           GPid child_pid)
3475 {
3476         VteTerminalPrivate *pvt;
3477         GObject *object;
3478 
3479         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3480         g_return_if_fail(child_pid != -1);
3481 
3482         pvt = terminal->pvt;
3483         g_return_if_fail(pvt->pty != NULL);
3484 
3485         // FIXMEchpe: support passing child_pid = -1 to remove the wathch
3486 
3487         object = G_OBJECT(terminal);
3488 
3489         g_object_freeze_notify(object);
3490 
3491         /* Set this as the child's pid. */
3492         pvt->pty_pid = child_pid;
3493 
3494         /* Catch a child-exited signal from the child pid. */
3495         if (terminal->pvt->child_watch_source != 0) {
3496                 g_source_remove (terminal->pvt->child_watch_source);
3497         }
3498         terminal->pvt->child_watch_source =
3499                 g_child_watch_add_full(G_PRIORITY_HIGH,
3500                                        child_pid,
3501                                        (GChildWatchFunc)vte_terminal_child_watch_cb,
3502                                        terminal, NULL);
3503 
3504         /* FIXMEchpe: call vte_terminal_set_size here? */
3505 
3506         g_object_thaw_notify(object);
3507 }
3508 
3509 /**
3510  * vte_get_user_shell:
3511  *
3512  * Gets the user's shell, or %NULL. In the latter case, the
3513  * system default (usually "/bin/sh") should be used.
3514  *
3515  * Returns: (transfer full) (type filename): a newly allocated string with the
3516  *   user's shell, or %NULL
3517  */
3518 char *
vte_get_user_shell(void)3519 vte_get_user_shell (void)
3520 {
3521 	struct passwd *pwd;
3522 
3523 	pwd = getpwuid(getuid());
3524         if (pwd && pwd->pw_shell)
3525                 return g_strdup (pwd->pw_shell);
3526 
3527         return NULL;
3528 }
3529 
3530 /**
3531  * vte_get_features:
3532  *
3533  * Gets a list of features vte was compiled with.
3534  *
3535  * Returns: (transfer none): a string with features
3536  *
3537  * Since: 0.40
3538  */
3539 const char *
vte_get_features(void)3540 vte_get_features (void)
3541 {
3542         return
3543 #ifdef WITH_GNUTLS
3544                 "+GNUTLS"
3545 #else
3546                 "-GNUTLS"
3547 #endif
3548                 ;
3549 }
3550 
3551 /**
3552  * vte_terminal_spawn_sync:
3553  * @terminal: a #VteTerminal
3554  * @pty_flags: flags from #VtePtyFlags
3555  * @working_directory: (allow-none): the name of a directory the command should start
3556  *   in, or %NULL to use the current working directory
3557  * @argv: (array zero-terminated=1) (element-type filename): child's argument vector
3558  * @envv: (allow-none) (array zero-terminated=1) (element-type filename): a list of environment
3559  *   variables to be added to the environment before starting the process, or %NULL
3560  * @spawn_flags: flags from #GSpawnFlags
3561  * @child_setup: (allow-none) (scope call): an extra child setup function to run in the child just before exec(), or %NULL
3562  * @child_setup_data: user data for @child_setup
3563  * @child_pid: (out) (allow-none) (transfer full): a location to store the child PID, or %NULL
3564  * @cancellable: (allow-none): a #GCancellable, or %NULL
3565  * @error: (allow-none): return location for a #GError, or %NULL
3566  *
3567  * Starts the specified command under a newly-allocated controlling
3568  * pseudo-terminal.  The @argv and @envv lists should be %NULL-terminated.
3569  * The "TERM" environment variable is automatically set to a default value,
3570  * but can be overridden from @envv.
3571  * @pty_flags controls logging the session to the specified system log files.
3572  *
3573  * Note that %G_SPAWN_DO_NOT_REAP_CHILD will always be added to @spawn_flags.
3574  *
3575  * Note that unless @spawn_flags contains %G_SPAWN_LEAVE_DESCRIPTORS_OPEN, all file
3576  * descriptors except stdin/stdout/stderr will be closed before calling exec()
3577  * in the child.
3578  *
3579  * See vte_pty_new(), g_spawn_async() and vte_terminal_watch_child() for more information.
3580  *
3581  * Returns: %TRUE on success, or %FALSE on error with @error filled in
3582  */
3583 gboolean
vte_terminal_spawn_sync(VteTerminal * terminal,VtePtyFlags pty_flags,const char * working_directory,char ** argv,char ** envv,GSpawnFlags spawn_flags,GSpawnChildSetupFunc child_setup,gpointer child_setup_data,GPid * child_pid,GCancellable * cancellable,GError ** error)3584 vte_terminal_spawn_sync(VteTerminal *terminal,
3585                                VtePtyFlags pty_flags,
3586                                const char *working_directory,
3587                                char **argv,
3588                                char **envv,
3589                                GSpawnFlags spawn_flags,
3590                                GSpawnChildSetupFunc child_setup,
3591                                gpointer child_setup_data,
3592                                GPid *child_pid /* out */,
3593                                GCancellable *cancellable,
3594                                GError **error)
3595 {
3596         VtePty *pty;
3597         GPid pid;
3598 
3599         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
3600         g_return_val_if_fail(argv != NULL, FALSE);
3601         g_return_val_if_fail(child_setup_data == NULL || child_setup, FALSE);
3602         g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
3603 
3604         pty = vte_terminal_pty_new_sync(terminal, pty_flags, cancellable, error);
3605         if (pty == NULL)
3606                 return FALSE;
3607 
3608         /* FIXMEchpe: is this flag needed */
3609         spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN;
3610 
3611         if (!__vte_pty_spawn(pty,
3612                              working_directory,
3613                              argv,
3614                              envv,
3615                              spawn_flags,
3616                              child_setup, child_setup_data,
3617                              &pid,
3618                              error)) {
3619                 g_object_unref(pty);
3620                 return FALSE;
3621         }
3622 
3623         vte_terminal_set_pty(terminal, pty);
3624         vte_terminal_watch_child(terminal, pid);
3625         g_object_unref (pty);
3626 
3627         if (child_pid)
3628                 *child_pid = pid;
3629 
3630         return TRUE;
3631 }
3632 
3633 /* Handle an EOF from the client. */
3634 static void
vte_terminal_eof(GIOChannel * channel,VteTerminal * terminal)3635 vte_terminal_eof(GIOChannel *channel, VteTerminal *terminal)
3636 {
3637         GObject *object = G_OBJECT(terminal);
3638 
3639         g_object_freeze_notify(object);
3640 
3641         vte_terminal_set_pty(terminal, NULL);
3642 
3643 	/* Emit a signal that we read an EOF. */
3644 	vte_terminal_queue_eof(terminal);
3645 
3646         g_object_thaw_notify(object);
3647 }
3648 
3649 /* Reset the input method context. */
3650 static void
vte_terminal_im_reset(VteTerminal * terminal)3651 vte_terminal_im_reset(VteTerminal *terminal)
3652 {
3653 	if (gtk_widget_get_realized (&terminal->widget)) {
3654 		gtk_im_context_reset(terminal->pvt->im_context);
3655 		if (terminal->pvt->im_preedit != NULL) {
3656 			g_free(terminal->pvt->im_preedit);
3657 			terminal->pvt->im_preedit = NULL;
3658 		}
3659 		if (terminal->pvt->im_preedit_attrs != NULL) {
3660 			pango_attr_list_unref(terminal->pvt->im_preedit_attrs);
3661 			terminal->pvt->im_preedit_attrs = NULL;
3662 		}
3663 	}
3664 }
3665 
3666 /* Emit whichever signals are called for here. */
3667 static void
vte_terminal_emit_pending_text_signals(VteTerminal * terminal)3668 vte_terminal_emit_pending_text_signals(VteTerminal *terminal)
3669 {
3670 	if (terminal->pvt->text_modified_flag) {
3671 		_vte_debug_print(VTE_DEBUG_SIGNALS,
3672 				"Emitting buffered `text-modified'.\n");
3673 		vte_terminal_emit_text_modified(terminal);
3674 		terminal->pvt->text_modified_flag = FALSE;
3675 	}
3676 	if (terminal->pvt->text_inserted_flag) {
3677 		_vte_debug_print(VTE_DEBUG_SIGNALS,
3678 				"Emitting buffered `text-inserted'\n");
3679 		_vte_terminal_emit_text_inserted(terminal);
3680 		terminal->pvt->text_inserted_flag = FALSE;
3681 	}
3682 	if (terminal->pvt->text_deleted_flag) {
3683 		_vte_debug_print(VTE_DEBUG_SIGNALS,
3684 				"Emitting buffered `text-deleted'\n");
3685 		_vte_terminal_emit_text_deleted(terminal);
3686 		terminal->pvt->text_deleted_flag = FALSE;
3687 	}
3688 }
3689 
3690 /* Process incoming data, first converting it to unicode characters, and then
3691  * processing control sequences. */
3692 static void
vte_terminal_process_incoming(VteTerminal * terminal)3693 vte_terminal_process_incoming(VteTerminal *terminal)
3694 {
3695 	VteScreen *screen;
3696 	VteVisualPosition cursor;
3697 	gboolean cursor_visible;
3698 	GdkPoint bbox_topleft, bbox_bottomright;
3699 	gunichar *wbuf, c;
3700 	long wcount, start, delta;
3701 	gboolean leftovers, modified, bottom, again;
3702 	gboolean invalidated_text;
3703 	gboolean in_scroll_region;
3704 	GArray *unichars;
3705 	struct _vte_incoming_chunk *chunk, *next_chunk, *achunk = NULL;
3706 
3707 	_vte_debug_print(VTE_DEBUG_IO,
3708 			"Handler processing %"G_GSIZE_FORMAT" bytes over %"G_GSIZE_FORMAT" chunks + %d bytes pending.\n",
3709 			_vte_incoming_chunks_length(terminal->pvt->incoming),
3710 			_vte_incoming_chunks_count(terminal->pvt->incoming),
3711 			terminal->pvt->pending->len);
3712 	_vte_debug_print (VTE_DEBUG_WORK, "(");
3713 
3714 	screen = terminal->pvt->screen;
3715 
3716 	delta = screen->scroll_delta;
3717 	bottom = screen->insert_delta == delta;
3718 
3719 	/* Save the current cursor position. */
3720         cursor = terminal->pvt->cursor;
3721 	cursor_visible = terminal->pvt->cursor_visible;
3722 
3723         in_scroll_region = terminal->pvt->scrolling_restricted
3724             && (terminal->pvt->cursor.row >= (screen->insert_delta + terminal->pvt->scrolling_region.start))
3725             && (terminal->pvt->cursor.row <= (screen->insert_delta + terminal->pvt->scrolling_region.end));
3726 
3727 	/* We should only be called when there's data to process. */
3728 	g_assert(terminal->pvt->incoming ||
3729 		 (terminal->pvt->pending->len > 0));
3730 
3731 	/* Convert the data into unicode characters. */
3732 	unichars = terminal->pvt->pending;
3733 	for (chunk = _vte_incoming_chunks_reverse (terminal->pvt->incoming);
3734 			chunk != NULL;
3735 			chunk = next_chunk) {
3736 		gsize processed;
3737 		next_chunk = chunk->next;
3738 		if (chunk->len == 0) {
3739 			goto skip_chunk;
3740 		}
3741 		processed = _vte_iso2022_process(terminal->pvt->iso2022,
3742 				chunk->data, chunk->len,
3743 				unichars);
3744 		if (G_UNLIKELY (processed != chunk->len)) {
3745 			/* shuffle the data about */
3746 			g_memmove (chunk->data, chunk->data + processed,
3747 					chunk->len - processed);
3748 			chunk->len = chunk->len - processed;
3749 			processed = sizeof (chunk->data) - chunk->len;
3750 			if (processed != 0 && next_chunk !=  NULL) {
3751 				if (next_chunk->len <= processed) {
3752 					/* consume it entirely */
3753 					memcpy (chunk->data + chunk->len,
3754 							next_chunk->data,
3755 							next_chunk->len);
3756 					chunk->len += next_chunk->len;
3757 					chunk->next = next_chunk->next;
3758 					release_chunk (next_chunk);
3759 				} else {
3760 					/* next few bytes */
3761 					memcpy (chunk->data + chunk->len,
3762 							next_chunk->data,
3763 							processed);
3764 					chunk->len += processed;
3765 					g_memmove (next_chunk->data,
3766 							next_chunk->data + processed,
3767 							next_chunk->len - processed);
3768 					next_chunk->len -= processed;
3769 				}
3770 				next_chunk = chunk; /* repeat */
3771 			} else {
3772 				break;
3773 			}
3774 		} else {
3775 skip_chunk:
3776 			/* cache the last chunk */
3777 			if (achunk) {
3778 				release_chunk (achunk);
3779 			}
3780 			achunk = chunk;
3781 		}
3782 	}
3783 	if (achunk) {
3784 		if (chunk != NULL) {
3785 			release_chunk (achunk);
3786 		} else {
3787 			chunk = achunk;
3788 			chunk->next = NULL;
3789 			chunk->len = 0;
3790 		}
3791 	}
3792 	terminal->pvt->incoming = chunk;
3793 
3794 	/* Compute the number of unicode characters we got. */
3795 	wbuf = &g_array_index(unichars, gunichar, 0);
3796 	wcount = unichars->len;
3797 
3798 	/* Try initial substrings. */
3799 	start = 0;
3800 	modified = leftovers = again = FALSE;
3801 	invalidated_text = FALSE;
3802 
3803 	bbox_bottomright.x = bbox_bottomright.y = -G_MAXINT;
3804 	bbox_topleft.x = bbox_topleft.y = G_MAXINT;
3805 
3806 	while (start < wcount && !leftovers) {
3807 		const char *match;
3808 		const gunichar *next;
3809 		GValueArray *params = NULL;
3810 
3811 		/* Try to match any control sequences. */
3812 		_vte_matcher_match(terminal->pvt->matcher,
3813 				   &wbuf[start],
3814 				   wcount - start,
3815 				   &match,
3816 				   &next,
3817 				   &params);
3818 		/* We're in one of three possible situations now.
3819 		 * First, the match string is a non-empty string and next
3820 		 * points to the first character which isn't part of this
3821 		 * sequence. */
3822 		if ((match != NULL) && (match[0] != '\0')) {
3823 			gboolean new_in_scroll_region;
3824 
3825 			/* Call the right sequence handler for the requested
3826 			 * behavior. */
3827 			_vte_terminal_handle_sequence(terminal,
3828 						      match,
3829 						      params);
3830 			/* Skip over the proper number of unicode chars. */
3831 			start = (next - wbuf);
3832 			modified = TRUE;
3833 
3834                         new_in_scroll_region = terminal->pvt->scrolling_restricted
3835                             && (terminal->pvt->cursor.row >= (screen->insert_delta + terminal->pvt->scrolling_region.start))
3836                             && (terminal->pvt->cursor.row <= (screen->insert_delta + terminal->pvt->scrolling_region.end));
3837 
3838 			delta = screen->scroll_delta;	/* delta may have changed from sequence. */
3839 
3840 			/* if we have moved greatly during the sequence handler, or moved
3841                          * into a scroll_region from outside it, restart the bbox.
3842                          */
3843 			if (invalidated_text &&
3844 					((new_in_scroll_region && !in_scroll_region) ||
3845                                          (terminal->pvt->cursor.col > bbox_bottomright.x + VTE_CELL_BBOX_SLACK ||
3846                                           terminal->pvt->cursor.col < bbox_topleft.x - VTE_CELL_BBOX_SLACK     ||
3847                                           terminal->pvt->cursor.row > bbox_bottomright.y + VTE_CELL_BBOX_SLACK ||
3848                                           terminal->pvt->cursor.row < bbox_topleft.y - VTE_CELL_BBOX_SLACK))) {
3849 				/* Clip off any part of the box which isn't already on-screen. */
3850 				bbox_topleft.x = MAX(bbox_topleft.x, 0);
3851 				bbox_topleft.y = MAX(bbox_topleft.y, delta);
3852 				bbox_bottomright.x = MIN(bbox_bottomright.x,
3853 						terminal->pvt->column_count);
3854 				/* lazily apply the +1 to the cursor_row */
3855 				bbox_bottomright.y = MIN(bbox_bottomright.y + 1,
3856 						delta + terminal->pvt->row_count);
3857 
3858 				_vte_invalidate_cells(terminal,
3859 						bbox_topleft.x,
3860 						bbox_bottomright.x - bbox_topleft.x,
3861 						bbox_topleft.y,
3862 						bbox_bottomright.y - bbox_topleft.y);
3863 
3864 				invalidated_text = FALSE;
3865 				bbox_bottomright.x = bbox_bottomright.y = -G_MAXINT;
3866 				bbox_topleft.x = bbox_topleft.y = G_MAXINT;
3867 			}
3868 
3869 			in_scroll_region = new_in_scroll_region;
3870 		} else
3871 		/* Second, we have a NULL match, and next points to the very
3872 		 * next character in the buffer.  Insert the character which
3873 		 * we're currently examining into the screen. */
3874 		if (match == NULL) {
3875 			c = wbuf[start];
3876 			/* If it's a control character, permute the order, per
3877 			 * vttest. */
3878 			if ((c != *next) &&
3879 			    ((*next & 0x1f) == *next) &&
3880 			    (start + 1 < next - wbuf)) {
3881 				const gunichar *tnext = NULL;
3882 				const char *tmatch = NULL;
3883 				gunichar ctrl;
3884 				int i;
3885 				/* We don't want to permute it if it's another
3886 				 * control sequence, so check if it is. */
3887 				_vte_matcher_match(terminal->pvt->matcher,
3888 						   next,
3889 						   wcount - (next - wbuf),
3890 						   &tmatch,
3891 						   &tnext,
3892 						   NULL);
3893 				/* We only do this for non-control-sequence
3894 				 * characters and random garbage. */
3895 				if (tnext == next + 1) {
3896 					/* Save the control character. */
3897 					ctrl = *next;
3898 					/* Move everything before it up a
3899 					 * slot.  */
3900 					for (i = next - wbuf; i > start; i--) {
3901 						wbuf[i] = wbuf[i - 1];
3902 					}
3903 					/* Move the control character to the
3904 					 * front. */
3905 					wbuf[i] = ctrl;
3906 					goto next_match;
3907 				}
3908 			}
3909 			_VTE_DEBUG_IF(VTE_DEBUG_PARSE) {
3910                                 if (c > 255) {
3911                                         g_printerr("U+%04lx\n", (long) c);
3912 				} else {
3913                                         if (c > 127) {
3914 						g_printerr("%ld = ",
3915                                                                 (long) c);
3916 					}
3917                                         if (c < 32) {
3918 						g_printerr("^%lc\n",
3919                                                                 (wint_t)c + 64);
3920 					} else {
3921 						g_printerr("`%lc'\n",
3922                                                                 (wint_t)c);
3923 					}
3924 				}
3925 			}
3926 
3927 			bbox_topleft.x = MIN(bbox_topleft.x,
3928                                         terminal->pvt->cursor.col);
3929 			bbox_topleft.y = MIN(bbox_topleft.y,
3930                                         terminal->pvt->cursor.row);
3931 
3932 			/* Insert the character. */
3933 			if (G_UNLIKELY (_vte_terminal_insert_char(terminal, c,
3934 						 FALSE, FALSE))) {
3935 				/* line wrapped, correct bbox */
3936 				if (invalidated_text &&
3937                                                 (terminal->pvt->cursor.col > bbox_bottomright.x + VTE_CELL_BBOX_SLACK	||
3938                                                  terminal->pvt->cursor.col < bbox_topleft.x - VTE_CELL_BBOX_SLACK	||
3939                                                  terminal->pvt->cursor.row > bbox_bottomright.y + VTE_CELL_BBOX_SLACK	||
3940                                                  terminal->pvt->cursor.row < bbox_topleft.y - VTE_CELL_BBOX_SLACK)) {
3941 					/* Clip off any part of the box which isn't already on-screen. */
3942 					bbox_topleft.x = MAX(bbox_topleft.x, 0);
3943 					bbox_topleft.y = MAX(bbox_topleft.y, delta);
3944 					bbox_bottomright.x = MIN(bbox_bottomright.x,
3945 							terminal->pvt->column_count);
3946 					/* lazily apply the +1 to the cursor_row */
3947 					bbox_bottomright.y = MIN(bbox_bottomright.y + 1,
3948 							delta + terminal->pvt->row_count);
3949 
3950 					_vte_invalidate_cells(terminal,
3951 							bbox_topleft.x,
3952 							bbox_bottomright.x - bbox_topleft.x,
3953 							bbox_topleft.y,
3954 							bbox_bottomright.y - bbox_topleft.y);
3955 					bbox_bottomright.x = bbox_bottomright.y = -G_MAXINT;
3956 					bbox_topleft.x = bbox_topleft.y = G_MAXINT;
3957 
3958 				}
3959 				bbox_topleft.x = MIN(bbox_topleft.x, 0);
3960 				bbox_topleft.y = MIN(bbox_topleft.y,
3961                                                      terminal->pvt->cursor.row);
3962 			}
3963 			/* Add the cells over which we have moved to the region
3964 			 * which we need to refresh for the user. */
3965 			bbox_bottomright.x = MAX(bbox_bottomright.x,
3966                                                  terminal->pvt->cursor.col);
3967                         /* cursor.row + 1 (defer until inv.) */
3968 			bbox_bottomright.y = MAX(bbox_bottomright.y,
3969                                                  terminal->pvt->cursor.row);
3970 			invalidated_text = TRUE;
3971 
3972 			/* We *don't* emit flush pending signals here. */
3973 			modified = TRUE;
3974 			start++;
3975 		} else {
3976 			/* Case three: the read broke in the middle of a
3977 			 * control sequence, so we're undecided with no more
3978 			 * data to consult. If we have data following the
3979 			 * middle of the sequence, then it's just garbage data,
3980 			 * and for compatibility, we should discard it. */
3981 			if (wbuf + wcount > next) {
3982 				_vte_debug_print(VTE_DEBUG_PARSE,
3983 						"Invalid control "
3984 						"sequence, discarding %ld "
3985 						"characters.\n",
3986 						(long)(next - (wbuf + start)));
3987 				/* Discard. */
3988 				start = next - wbuf + 1;
3989 			} else {
3990 				/* Pause processing here and wait for more
3991 				 * data before continuing. */
3992 				leftovers = TRUE;
3993 			}
3994 		}
3995 
3996 #ifdef VTE_DEBUG
3997 		/* Some safety checks: ensure the visible parts of the buffer
3998 		 * are all in the buffer. */
3999 		g_assert(screen->insert_delta >=
4000 			 _vte_ring_delta(screen->row_data));
4001 		/* The cursor shouldn't be above or below the addressable
4002 		 * part of the display buffer. */
4003                 g_assert(terminal->pvt->cursor.row >= terminal->pvt->screen->insert_delta);
4004 #endif
4005 
4006 next_match:
4007 		if (G_LIKELY(params != NULL)) {
4008 			/* Free any parameters we don't care about any more. */
4009 			_vte_matcher_free_params_array(terminal->pvt->matcher,
4010 					params);
4011 		}
4012 	}
4013 
4014 	/* Remove most of the processed characters. */
4015 	if (start < wcount) {
4016 		g_array_remove_range(terminal->pvt->pending, 0, start);
4017 	} else {
4018 		g_array_set_size(terminal->pvt->pending, 0);
4019 		/* If we're out of data, we needn't pause to let the
4020 		 * controlling application respond to incoming data, because
4021 		 * the main loop is already going to do that. */
4022 	}
4023 
4024 	if (modified) {
4025 		/* Keep the cursor on-screen if we scroll on output, or if
4026 		 * we're currently at the bottom of the buffer. */
4027 		_vte_terminal_update_insert_delta(terminal);
4028 		if (terminal->pvt->scroll_on_output || bottom) {
4029 			vte_terminal_maybe_scroll_to_bottom(terminal);
4030 		}
4031 		/* Deselect the current selection if its contents are changed
4032 		 * by this insertion. */
4033 		if (terminal->pvt->has_selection) {
4034 			char *selection;
4035 			selection =
4036 			vte_terminal_get_text_range(terminal,
4037 						    terminal->pvt->selection_start.row,
4038 						    0,
4039 						    terminal->pvt->selection_end.row,
4040 						    terminal->pvt->column_count,
4041 						    vte_cell_is_selected,
4042 						    NULL,
4043 						    NULL);
4044 			if ((selection == NULL) || (terminal->pvt->selection == NULL) ||
4045 			    (strcmp(selection, terminal->pvt->selection) != 0)) {
4046 				vte_terminal_deselect_all(terminal);
4047 			}
4048 			g_free(selection);
4049 		}
4050 	}
4051 
4052 	if (modified || (screen != terminal->pvt->screen)) {
4053 		/* Signal that the visible contents changed. */
4054 		_vte_terminal_queue_contents_changed(terminal);
4055 	}
4056 
4057 	vte_terminal_emit_pending_signals (terminal);
4058 
4059 	if (invalidated_text) {
4060 		/* Clip off any part of the box which isn't already on-screen. */
4061 		bbox_topleft.x = MAX(bbox_topleft.x, 0);
4062 		bbox_topleft.y = MAX(bbox_topleft.y, delta);
4063 		bbox_bottomright.x = MIN(bbox_bottomright.x,
4064 				terminal->pvt->column_count);
4065 		/* lazily apply the +1 to the cursor_row */
4066 		bbox_bottomright.y = MIN(bbox_bottomright.y + 1,
4067 				delta + terminal->pvt->row_count);
4068 
4069 		_vte_invalidate_cells(terminal,
4070 				bbox_topleft.x,
4071 				bbox_bottomright.x - bbox_topleft.x,
4072 				bbox_topleft.y,
4073 				bbox_bottomright.y - bbox_topleft.y);
4074 	}
4075 
4076 
4077         if ((cursor.col != terminal->pvt->cursor.col) ||
4078             (cursor.row != terminal->pvt->cursor.row)) {
4079 		/* invalidate the old and new cursor positions */
4080 		if (cursor_visible)
4081 			_vte_invalidate_cell(terminal, cursor.col, cursor.row);
4082 		_vte_invalidate_cursor_once(terminal, FALSE);
4083 		_vte_check_cursor_blink(terminal);
4084 		/* Signal that the cursor moved. */
4085 		vte_terminal_queue_cursor_moved(terminal);
4086 	} else if (cursor_visible != terminal->pvt->cursor_visible) {
4087 		_vte_invalidate_cell(terminal, cursor.col, cursor.row);
4088 		_vte_check_cursor_blink(terminal);
4089 	}
4090 
4091 	/* Tell the input method where the cursor is. */
4092 	if (gtk_widget_get_realized (&terminal->widget)) {
4093 		GdkRectangle rect;
4094                 rect.x = terminal->pvt->cursor.col *
4095 			 terminal->pvt->char_width + terminal->pvt->padding.left;
4096 		rect.width = terminal->pvt->char_width;
4097                 rect.y = (terminal->pvt->cursor.row - delta) *
4098 			 terminal->pvt->char_height + terminal->pvt->padding.top;
4099 		rect.height = terminal->pvt->char_height;
4100 		gtk_im_context_set_cursor_location(terminal->pvt->im_context,
4101 						   &rect);
4102 	}
4103 
4104 	_vte_debug_print (VTE_DEBUG_WORK, ")");
4105 	_vte_debug_print (VTE_DEBUG_IO,
4106 			"%ld chars and %ld bytes in %"G_GSIZE_FORMAT" chunks left to process.\n",
4107 			(long) unichars->len,
4108 			(long) _vte_incoming_chunks_length(terminal->pvt->incoming),
4109 			_vte_incoming_chunks_count(terminal->pvt->incoming));
4110 }
4111 
4112 static inline void
_vte_terminal_enable_input_source(VteTerminal * terminal)4113 _vte_terminal_enable_input_source (VteTerminal *terminal)
4114 {
4115 	if (terminal->pvt->pty_channel == NULL) {
4116 		return;
4117 	}
4118 
4119 	if (terminal->pvt->pty_input_source == 0) {
4120 		_vte_debug_print (VTE_DEBUG_IO, "polling vte_terminal_io_read\n");
4121 		terminal->pvt->pty_input_source =
4122 			g_io_add_watch_full(terminal->pvt->pty_channel,
4123 					    VTE_CHILD_INPUT_PRIORITY,
4124 					    G_IO_IN | G_IO_HUP,
4125 					    (GIOFunc) vte_terminal_io_read,
4126 					    terminal,
4127 					    (GDestroyNotify) mark_input_source_invalid);
4128 	}
4129 }
4130 static void
_vte_terminal_feed_chunks(VteTerminal * terminal,struct _vte_incoming_chunk * chunks)4131 _vte_terminal_feed_chunks (VteTerminal *terminal, struct _vte_incoming_chunk *chunks)
4132 {
4133 	struct _vte_incoming_chunk *last;
4134 
4135 	_vte_debug_print(VTE_DEBUG_IO, "Feed %"G_GSIZE_FORMAT" bytes, in %"G_GSIZE_FORMAT" chunks.\n",
4136 			_vte_incoming_chunks_length(chunks),
4137 			_vte_incoming_chunks_count(chunks));
4138 
4139 	for (last = chunks; last->next != NULL; last = last->next) ;
4140 	last->next = terminal->pvt->incoming;
4141 	terminal->pvt->incoming = chunks;
4142 }
4143 /* Read and handle data from the child. */
4144 static gboolean
vte_terminal_io_read(GIOChannel * channel,GIOCondition condition,VteTerminal * terminal)4145 vte_terminal_io_read(GIOChannel *channel,
4146 		     GIOCondition condition,
4147 		     VteTerminal *terminal)
4148 {
4149 	int err = 0;
4150 	gboolean eof, again = TRUE;
4151 
4152 	_vte_debug_print (VTE_DEBUG_WORK, ".");
4153 
4154 	/* Check for end-of-file. */
4155 	eof = condition & G_IO_HUP;
4156 
4157 	/* Read some data in from this channel. */
4158 	if (condition & G_IO_IN) {
4159 		struct _vte_incoming_chunk *chunk, *chunks = NULL;
4160 		const int fd = g_io_channel_unix_get_fd (channel);
4161 		guchar *bp;
4162 		int rem, len;
4163 		guint bytes, max_bytes;
4164 
4165 		/* Limit the amount read between updates, so as to
4166 		 * 1. maintain fairness between multiple terminals;
4167 		 * 2. prevent reading the entire output of a command in one
4168 		 *    pass, i.e. we always try to refresh the terminal ~40Hz.
4169 		 *    See time_process_incoming() where we estimate the
4170 		 *    maximum number of bytes we can read/process in between
4171 		 *    updates.
4172 		 */
4173 		max_bytes = terminal->pvt->active ?
4174 		            g_list_length (active_terminals) - 1 : 0;
4175 		if (max_bytes) {
4176 			max_bytes = terminal->pvt->max_input_bytes / max_bytes;
4177 		} else {
4178 			max_bytes = terminal->pvt->max_input_bytes;
4179 		}
4180 		bytes = terminal->pvt->input_bytes;
4181 
4182 		chunk = terminal->pvt->incoming;
4183 		do {
4184 			if (!chunk || chunk->len >= 3*sizeof (chunk->data)/4) {
4185 				chunk = get_chunk ();
4186 				chunk->next = chunks;
4187 				chunks = chunk;
4188 			}
4189 			rem = sizeof (chunk->data) - chunk->len;
4190 			bp = chunk->data + chunk->len;
4191 			len = 0;
4192 			do {
4193 				int ret = read (fd, bp, rem);
4194 				switch (ret){
4195 					case -1:
4196 						err = errno;
4197 						goto out;
4198 					case 0:
4199 						eof = TRUE;
4200 						goto out;
4201 					default:
4202 						bp += ret;
4203 						rem -= ret;
4204 						len += ret;
4205 						break;
4206 				}
4207 			} while (rem);
4208 out:
4209 			chunk->len += len;
4210 			bytes += len;
4211 		} while (bytes < max_bytes &&
4212 		         chunk->len == sizeof (chunk->data));
4213 		if (chunk->len == 0 && chunk == chunks) {
4214 			chunks = chunks->next;
4215 			release_chunk (chunk);
4216 		}
4217 
4218 		if (chunks != NULL) {
4219 			_vte_terminal_feed_chunks (terminal, chunks);
4220 		}
4221 		if (!vte_terminal_is_processing (terminal)) {
4222                         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4223 			gdk_threads_enter ();
4224                         G_GNUC_END_IGNORE_DEPRECATIONS;
4225 
4226 			vte_terminal_add_process_timeout (terminal);
4227                         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4228 			gdk_threads_leave ();
4229                         G_GNUC_END_IGNORE_DEPRECATIONS;
4230 		}
4231 		terminal->pvt->pty_input_active = len != 0;
4232 		terminal->pvt->input_bytes = bytes;
4233 		again = bytes < max_bytes;
4234 
4235 		_vte_debug_print (VTE_DEBUG_IO, "read %d/%d bytes, again? %s, active? %s\n",
4236 				bytes, max_bytes,
4237 				again ? "yes" : "no",
4238 				terminal->pvt->pty_input_active ? "yes" : "no");
4239 	}
4240 
4241 	/* Error? */
4242 	switch (err) {
4243 		case 0: /* no error */
4244 			break;
4245 		case EIO: /* Fake an EOF. */
4246 			eof = TRUE;
4247 			break;
4248 		case EAGAIN:
4249 		case EBUSY: /* do nothing */
4250 			break;
4251 		default:
4252 			/* Translators: %s is replaced with error message returned by strerror(). */
4253 			g_warning (_("Error reading from child: " "%s."),
4254 					g_strerror (err));
4255 			break;
4256 	}
4257 
4258 	/* If we detected an eof condition, signal one. */
4259 	if (eof) {
4260 		/* potential deadlock ... */
4261 		if (!vte_terminal_is_processing (terminal)) {
4262                         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4263 			gdk_threads_enter ();
4264                         G_GNUC_END_IGNORE_DEPRECATIONS;
4265 
4266 			vte_terminal_eof (channel, terminal);
4267 
4268                         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4269 			gdk_threads_leave ();
4270                         G_GNUC_END_IGNORE_DEPRECATIONS;
4271 		} else {
4272 			vte_terminal_eof (channel, terminal);
4273 		}
4274 
4275 		again = FALSE;
4276 	}
4277 
4278 	return again;
4279 }
4280 
4281 /**
4282  * vte_terminal_feed:
4283  * @terminal: a #VteTerminal
4284  * @data: (array length=length) (element-type guint8): a string in the terminal's current encoding
4285  * @length: the length of the string, or -1 to use the full length or a nul-terminated string
4286  *
4287  * Interprets @data as if it were data received from a child process.  This
4288  * can either be used to drive the terminal without a child process, or just
4289  * to mess with your users.
4290  */
4291 void
vte_terminal_feed(VteTerminal * terminal,const char * data,gssize length)4292 vte_terminal_feed(VteTerminal *terminal, const char *data, gssize length)
4293 {
4294 	/* If length == -1, use the length of the data string. */
4295 	if (length == -1) {
4296 		length = strlen(data);
4297 	}
4298 
4299 	/* If we have data, modify the incoming buffer. */
4300 	if (length > 0) {
4301 		struct _vte_incoming_chunk *chunk;
4302 		if (terminal->pvt->incoming &&
4303 				(gsize)length < sizeof (terminal->pvt->incoming->data) - terminal->pvt->incoming->len) {
4304 			chunk = terminal->pvt->incoming;
4305 		} else {
4306 			chunk = get_chunk ();
4307 			_vte_terminal_feed_chunks (terminal, chunk);
4308 		}
4309 		do { /* break the incoming data into chunks */
4310 			gsize rem = sizeof (chunk->data) - chunk->len;
4311 			gsize len = (gsize) length < rem ? (gsize) length : rem;
4312 			memcpy (chunk->data + chunk->len, data, len);
4313 			chunk->len += len;
4314 			length -= len;
4315 			if (length == 0) {
4316 				break;
4317 			}
4318 			data += len;
4319 
4320 			chunk = get_chunk ();
4321 			_vte_terminal_feed_chunks (terminal, chunk);
4322 		} while (1);
4323 		vte_terminal_start_processing (terminal);
4324 	}
4325 }
4326 
4327 /* Send locally-encoded characters to the child. */
4328 static gboolean
vte_terminal_io_write(GIOChannel * channel,GIOCondition condition,VteTerminal * terminal)4329 vte_terminal_io_write(GIOChannel *channel,
4330 		      GIOCondition condition,
4331 		      VteTerminal *terminal)
4332 {
4333 	gssize count;
4334 	int fd;
4335 	gboolean leave_open;
4336 
4337 	fd = g_io_channel_unix_get_fd(channel);
4338 
4339 	count = write(fd, terminal->pvt->outgoing->data,
4340 		      _vte_byte_array_length(terminal->pvt->outgoing));
4341 	if (count != -1) {
4342 		_VTE_DEBUG_IF (VTE_DEBUG_IO) {
4343 			gssize i;
4344 			for (i = 0; i < count; i++) {
4345 				g_printerr("Wrote %c%c\n",
4346 					((guint8)terminal->pvt->outgoing->data[i]) >= 32 ?
4347 					' ' : '^',
4348 					((guint8)terminal->pvt->outgoing->data[i]) >= 32 ?
4349 					terminal->pvt->outgoing->data[i] :
4350 					((guint8)terminal->pvt->outgoing->data[i])  + 64);
4351 			}
4352 		}
4353 		_vte_byte_array_consume(terminal->pvt->outgoing, count);
4354 	}
4355 
4356 	if (_vte_byte_array_length(terminal->pvt->outgoing) == 0) {
4357 		leave_open = FALSE;
4358 	} else {
4359 		leave_open = TRUE;
4360 	}
4361 
4362 	return leave_open;
4363 }
4364 
4365 /* Convert some arbitrarily-encoded data to send to the child. */
4366 static void
vte_terminal_send(VteTerminal * terminal,const char * encoding,const void * data,gssize length,gboolean local_echo,gboolean newline_stuff)4367 vte_terminal_send(VteTerminal *terminal, const char *encoding,
4368 		  const void *data, gssize length,
4369 		  gboolean local_echo, gboolean newline_stuff)
4370 {
4371 	gsize icount, ocount;
4372 	const guchar *ibuf;
4373 	guchar *obuf, *obufptr;
4374 	gchar *cooked;
4375 	VteConv conv;
4376 	long crcount, cooked_length, i;
4377 
4378 	g_assert(VTE_IS_TERMINAL(terminal));
4379 	g_assert(encoding && strcmp(encoding, "UTF-8") == 0);
4380 
4381         if (!terminal->pvt->input_enabled)
4382                 return;
4383 
4384 	conv = VTE_INVALID_CONV;
4385 	if (strcmp(encoding, "UTF-8") == 0) {
4386 		conv = terminal->pvt->outgoing_conv;
4387 	}
4388 	if (conv == VTE_INVALID_CONV) {
4389 		g_warning (_("Unable to send data to child, invalid charset convertor"));
4390 		return;
4391 	}
4392 
4393 	icount = length;
4394 	ibuf =  data;
4395 	ocount = ((length + 1) * VTE_UTF8_BPC) + 1;
4396 	_vte_byte_array_set_minimum_size(terminal->pvt->conv_buffer, ocount);
4397 	obuf = obufptr = terminal->pvt->conv_buffer->data;
4398 
4399 	if (_vte_conv(conv, &ibuf, &icount, &obuf, &ocount) == (gsize)-1) {
4400 		g_warning(_("Error (%s) converting data for child, dropping."),
4401 			  g_strerror(errno));
4402 	} else {
4403 		crcount = 0;
4404 		if (newline_stuff) {
4405 			for (i = 0; i < obuf - obufptr; i++) {
4406 				switch (obufptr[i]) {
4407 				case '\015':
4408 					crcount++;
4409 					break;
4410 				default:
4411 					break;
4412 				}
4413 			}
4414 		}
4415 		if (crcount > 0) {
4416 			cooked = g_malloc(obuf - obufptr + crcount);
4417 			cooked_length = 0;
4418 			for (i = 0; i < obuf - obufptr; i++) {
4419 				switch (obufptr[i]) {
4420 				case '\015':
4421 					cooked[cooked_length++] = '\015';
4422 					cooked[cooked_length++] = '\012';
4423 					break;
4424 				default:
4425 					cooked[cooked_length++] = obufptr[i];
4426 					break;
4427 				}
4428 			}
4429 		} else {
4430 			cooked = (gchar *)obufptr;
4431 			cooked_length = obuf - obufptr;
4432 		}
4433 		/* Tell observers that we're sending this to the child. */
4434 		if (cooked_length > 0) {
4435 			vte_terminal_emit_commit(terminal,
4436 						 cooked, cooked_length);
4437 		}
4438 		/* Echo the text if we've been asked to do so. */
4439 		if ((cooked_length > 0) && local_echo) {
4440 			gunichar *ucs4;
4441 			ucs4 = g_utf8_to_ucs4(cooked, cooked_length,
4442 					      NULL, NULL, NULL);
4443 			if (ucs4 != NULL) {
4444 				int len;
4445 				len = g_utf8_strlen(cooked, cooked_length);
4446 				for (i = 0; i < len; i++) {
4447 					_vte_terminal_insert_char(terminal,
4448 								 ucs4[i],
4449 								 FALSE,
4450 								 TRUE);
4451 				}
4452 				g_free(ucs4);
4453 			}
4454 		}
4455 		/* If there's a place for it to go, add the data to the
4456 		 * outgoing buffer. */
4457 		if ((cooked_length > 0) && (terminal->pvt->pty != NULL)) {
4458 			_vte_byte_array_append(terminal->pvt->outgoing,
4459 					   cooked, cooked_length);
4460 			_VTE_DEBUG_IF(VTE_DEBUG_KEYBOARD) {
4461 				for (i = 0; i < cooked_length; i++) {
4462 					if ((((guint8) cooked[i]) < 32) ||
4463 					    (((guint8) cooked[i]) > 127)) {
4464 						g_printerr(
4465 							"Sending <%02x> "
4466 							"to child.\n",
4467 							cooked[i]);
4468 					} else {
4469 						g_printerr(
4470 							"Sending '%c' "
4471 							"to child.\n",
4472 							cooked[i]);
4473 					}
4474 				}
4475 			}
4476 			/* If we need to start waiting for the child pty to
4477 			 * become available for writing, set that up here. */
4478 			_vte_terminal_connect_pty_write(terminal);
4479 		}
4480 		if (crcount > 0) {
4481 			g_free(cooked);
4482 		}
4483 	}
4484 	return;
4485 }
4486 
4487 /**
4488  * vte_terminal_feed_child:
4489  * @terminal: a #VteTerminal
4490  * @text: data to send to the child
4491  * @length: length of @text in bytes, or -1 if @text is NUL-terminated
4492  *
4493  * Sends a block of UTF-8 text to the child as if it were entered by the user
4494  * at the keyboard.
4495  */
4496 void
vte_terminal_feed_child(VteTerminal * terminal,const char * text,gssize length)4497 vte_terminal_feed_child(VteTerminal *terminal, const char *text, gssize length)
4498 {
4499 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
4500 
4501         if (!terminal->pvt->input_enabled)
4502                 return;
4503 
4504 	if (length == -1) {
4505 		length = strlen(text);
4506 	}
4507 	if (length > 0) {
4508 		vte_terminal_send(terminal, "UTF-8", text, length,
4509 				  FALSE, FALSE);
4510 	}
4511 }
4512 
4513 /**
4514  * vte_terminal_feed_child_binary:
4515  * @terminal: a #VteTerminal
4516  * @data: data to send to the child
4517  * @length: length of @data
4518  *
4519  * Sends a block of binary data to the child.
4520  */
4521 void
vte_terminal_feed_child_binary(VteTerminal * terminal,const guint8 * data,gsize length)4522 vte_terminal_feed_child_binary(VteTerminal *terminal, const guint8 *data, gsize length)
4523 {
4524 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
4525 
4526         if (!terminal->pvt->input_enabled)
4527                 return;
4528 
4529 	/* Tell observers that we're sending this to the child. */
4530 	if (length > 0) {
4531 		vte_terminal_emit_commit(terminal,
4532 					 (char*)data, length);
4533 
4534 		/* If there's a place for it to go, add the data to the
4535 		 * outgoing buffer. */
4536 		if (terminal->pvt->pty != NULL) {
4537 			_vte_byte_array_append(terminal->pvt->outgoing,
4538 					   data, length);
4539 			/* If we need to start waiting for the child pty to
4540 			 * become available for writing, set that up here. */
4541 			_vte_terminal_connect_pty_write(terminal);
4542 		}
4543 	}
4544 }
4545 
4546 static void
vte_terminal_feed_child_using_modes(VteTerminal * terminal,const char * data,glong length)4547 vte_terminal_feed_child_using_modes(VteTerminal *terminal,
4548 				    const char *data, glong length)
4549 {
4550 	if (length == ((gssize)-1)) {
4551 		length = strlen(data);
4552 	}
4553 	if (length > 0) {
4554 		vte_terminal_send(terminal, "UTF-8", data, length,
4555                                   !terminal->pvt->sendrecv_mode,
4556                                   terminal->pvt->linefeed_mode);
4557 	}
4558 }
4559 
4560 /* Send text from the input method to the child. */
4561 static void
vte_terminal_im_commit(GtkIMContext * im_context,gchar * text,VteTerminal * terminal)4562 vte_terminal_im_commit(GtkIMContext *im_context, gchar *text, VteTerminal *terminal)
4563 {
4564 	_vte_debug_print(VTE_DEBUG_EVENTS,
4565 			"Input method committed `%s'.\n", text);
4566 	vte_terminal_feed_child_using_modes(terminal, text, -1);
4567 	/* Committed text was committed because the user pressed a key, so
4568 	 * we need to obey the scroll-on-keystroke setting. */
4569 	if (terminal->pvt->scroll_on_keystroke) {
4570 		vte_terminal_maybe_scroll_to_bottom(terminal);
4571 	}
4572 }
4573 
4574 /* We've started pre-editing. */
4575 static void
vte_terminal_im_preedit_start(GtkIMContext * im_context,VteTerminal * terminal)4576 vte_terminal_im_preedit_start(GtkIMContext *im_context, VteTerminal *terminal)
4577 {
4578 	_vte_debug_print(VTE_DEBUG_EVENTS,
4579 			"Input method pre-edit started.\n");
4580 	terminal->pvt->im_preedit_active = TRUE;
4581 }
4582 
4583 /* We've stopped pre-editing. */
4584 static void
vte_terminal_im_preedit_end(GtkIMContext * im_context,VteTerminal * terminal)4585 vte_terminal_im_preedit_end(GtkIMContext *im_context, VteTerminal *terminal)
4586 {
4587 	_vte_debug_print(VTE_DEBUG_EVENTS,
4588 			"Input method pre-edit ended.\n");
4589 	terminal->pvt->im_preedit_active = FALSE;
4590 }
4591 
4592 /* The pre-edit string changed. */
4593 static void
vte_terminal_im_preedit_changed(GtkIMContext * im_context,VteTerminal * terminal)4594 vte_terminal_im_preedit_changed(GtkIMContext *im_context, VteTerminal *terminal)
4595 {
4596 	gchar *str;
4597 	PangoAttrList *attrs;
4598 	gint cursor;
4599 
4600 	gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor);
4601 	_vte_debug_print(VTE_DEBUG_EVENTS,
4602 			"Input method pre-edit changed (%s,%d).\n",
4603 			str, cursor);
4604 
4605 	/* Queue the area where the current preedit string is being displayed
4606 	 * for repainting. */
4607 	_vte_invalidate_cursor_once(terminal, FALSE);
4608 
4609 	g_free(terminal->pvt->im_preedit);
4610 	terminal->pvt->im_preedit = str;
4611 
4612 	if (terminal->pvt->im_preedit_attrs != NULL) {
4613 		pango_attr_list_unref(terminal->pvt->im_preedit_attrs);
4614 	}
4615 	terminal->pvt->im_preedit_attrs = attrs;
4616 
4617 	terminal->pvt->im_preedit_cursor = cursor;
4618 
4619 	_vte_invalidate_cursor_once(terminal, FALSE);
4620 }
4621 
4622 static void
vte_terminal_set_padding(VteTerminal * terminal)4623 vte_terminal_set_padding(VteTerminal *terminal)
4624 {
4625         VteTerminalPrivate *pvt = terminal->pvt;
4626         GtkWidget *widget = GTK_WIDGET(terminal);
4627         GtkBorder padding;
4628 
4629         gtk_style_context_get_padding(gtk_widget_get_style_context(widget),
4630                                       gtk_widget_get_state_flags(widget),
4631                                       &padding);
4632 
4633         _vte_debug_print(VTE_DEBUG_MISC,
4634                          "Setting padding to (%d,%d,%d,%d)\n",
4635                          padding.left, padding.right,
4636                          padding.top, padding.bottom);
4637 
4638         if (memcmp(&padding, &pvt->padding, sizeof(GtkBorder)) == 0)
4639                 return;
4640 
4641         pvt->padding = padding;
4642 
4643         gtk_widget_queue_resize(widget);
4644 }
4645 
4646 static void
vte_terminal_style_updated(GtkWidget * widget)4647 vte_terminal_style_updated (GtkWidget *widget)
4648 {
4649 	VteTerminal *terminal = VTE_TERMINAL(widget);
4650         float aspect;
4651 
4652         GTK_WIDGET_CLASS (vte_terminal_parent_class)->style_updated (widget);
4653 
4654         vte_terminal_set_font(terminal, terminal->pvt->unscaled_font_desc);
4655         vte_terminal_set_padding(terminal);
4656 
4657         gtk_widget_style_get(widget, "cursor-aspect-ratio", &aspect, NULL);
4658         if (aspect != terminal->pvt->cursor_aspect_ratio) {
4659                 terminal->pvt->cursor_aspect_ratio = aspect;
4660                 _vte_invalidate_cursor_once(terminal, FALSE);
4661         }
4662 }
4663 
4664 static void
add_cursor_timeout(VteTerminal * terminal)4665 add_cursor_timeout (VteTerminal *terminal)
4666 {
4667 	if (terminal->pvt->cursor_blink_tag)
4668 		return; /* already added */
4669 
4670 	terminal->pvt->cursor_blink_time = 0;
4671 	terminal->pvt->cursor_blink_tag = g_timeout_add_full(G_PRIORITY_LOW,
4672 							     terminal->pvt->cursor_blink_cycle,
4673 							     (GSourceFunc)vte_invalidate_cursor_periodic,
4674 							     terminal,
4675 							     NULL);
4676 }
4677 
4678 static void
remove_cursor_timeout(VteTerminal * terminal)4679 remove_cursor_timeout (VteTerminal *terminal)
4680 {
4681 	if (terminal->pvt->cursor_blink_tag == 0)
4682 		return; /* already removed */
4683 
4684 	g_source_remove (terminal->pvt->cursor_blink_tag);
4685 	terminal->pvt->cursor_blink_tag = 0;
4686 }
4687 
4688 /* Activates / disactivates the cursor blink timer to reduce wakeups */
4689 static void
_vte_check_cursor_blink(VteTerminal * terminal)4690 _vte_check_cursor_blink(VteTerminal *terminal)
4691 {
4692 	if (terminal->pvt->has_focus &&
4693 	    terminal->pvt->cursor_blinks &&
4694 	    terminal->pvt->cursor_visible)
4695 		add_cursor_timeout(terminal);
4696 	else
4697 		remove_cursor_timeout(terminal);
4698 }
4699 
4700 void
_vte_terminal_audible_beep(VteTerminal * terminal)4701 _vte_terminal_audible_beep(VteTerminal *terminal)
4702 {
4703 	GdkDisplay *display;
4704 
4705 	g_assert(VTE_IS_TERMINAL(terminal));
4706 	display = gtk_widget_get_display(&terminal->widget);
4707 	gdk_display_beep(display);
4708 }
4709 
4710 void
_vte_terminal_beep(VteTerminal * terminal)4711 _vte_terminal_beep(VteTerminal *terminal)
4712 {
4713 	if (terminal->pvt->audible_bell) {
4714 		_vte_terminal_audible_beep (terminal);
4715 	}
4716 }
4717 
4718 
4719 static guint
vte_translate_ctrlkey(GdkEventKey * event)4720 vte_translate_ctrlkey (GdkEventKey *event)
4721 {
4722 	guint keyval;
4723 	GdkKeymap *keymap;
4724 	unsigned int i;
4725 
4726 	if (event->keyval < 128)
4727 		return event->keyval;
4728 
4729         keymap = gdk_keymap_get_for_display(gdk_window_get_display (event->window));
4730 
4731 	/* Try groups in order to find one mapping the key to ASCII */
4732 	for (i = 0; i < 4; i++) {
4733 		GdkModifierType consumed_modifiers;
4734 
4735 		gdk_keymap_translate_keyboard_state (keymap,
4736 				event->hardware_keycode, event->state,
4737 				i,
4738 				&keyval, NULL, NULL, &consumed_modifiers);
4739 		if (keyval < 128) {
4740 			_vte_debug_print (VTE_DEBUG_EVENTS,
4741 					"ctrl+Key, group=%d de-grouped into keyval=0x%x\n",
4742 					event->group, keyval);
4743 			return keyval;
4744 		}
4745 	}
4746 
4747 	return event->keyval;
4748 }
4749 
4750 static void
vte_terminal_read_modifiers(VteTerminal * terminal,GdkEvent * event)4751 vte_terminal_read_modifiers (VteTerminal *terminal,
4752 			     GdkEvent *event)
4753 {
4754         GdkKeymap *keymap;
4755 	GdkModifierType modifiers;
4756 
4757 	/* Read the modifiers. */
4758 	if (!gdk_event_get_state((GdkEvent*)event, &modifiers))
4759                 return;
4760 
4761         keymap = gdk_keymap_get_for_display(gdk_window_get_display(((GdkEventAny*)event)->window));
4762 
4763         gdk_keymap_add_virtual_modifiers (keymap, &modifiers);
4764 
4765 #if 1
4766         /* HACK! Treat ALT as META; see bug #663779. */
4767         if (modifiers & GDK_MOD1_MASK)
4768                 modifiers |= VTE_META_MASK;
4769 #endif
4770 
4771         terminal->pvt->modifiers = modifiers;
4772 }
4773 
4774 /* Read and handle a keypress event. */
4775 static gint
vte_terminal_key_press(GtkWidget * widget,GdkEventKey * event)4776 vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
4777 {
4778 	VteTerminal *terminal;
4779 	GdkModifierType modifiers;
4780 	char *normal = NULL, *output;
4781 	gssize normal_length = 0;
4782 	int i;
4783 	struct termios tio;
4784 	gboolean scrolled = FALSE, steal = FALSE, modifier = FALSE, handled,
4785 		 suppress_meta_esc = FALSE, add_modifiers = FALSE;
4786 	guint keyval = 0;
4787 	gunichar keychar = 0;
4788 	char keybuf[VTE_UTF8_BPC];
4789 
4790 	terminal = VTE_TERMINAL(widget);
4791 
4792         /* We do NOT want chain up to GtkWidget::key-press-event, since that would
4793          * cause GtkWidget's keybindings to be handled and consumed. However we'll
4794          * have to handle the one sane binding (Shift-F10 or MenuKey, to pop up the
4795          * context menu) ourself, so for now we simply skip the offending keybinding
4796          * in class_init.
4797          */
4798 
4799 	/* First, check if GtkWidget's behavior already does something with
4800 	 * this key. */
4801 	if (GTK_WIDGET_CLASS(vte_terminal_parent_class)->key_press_event) {
4802 		if ((GTK_WIDGET_CLASS(vte_terminal_parent_class))->key_press_event(widget,
4803 								      event)) {
4804 			return TRUE;
4805 		}
4806 	}
4807 
4808 	/* If it's a keypress, record that we got the event, in case the
4809 	 * input method takes the event from us. */
4810 	if (event->type == GDK_KEY_PRESS) {
4811 		/* Store a copy of the key. */
4812 		keyval = event->keyval;
4813 		vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
4814 
4815 		/* If we're in margin bell mode and on the border of the
4816 		 * margin, bell. */
4817 		if (terminal->pvt->margin_bell) {
4818                         if ((terminal->pvt->cursor.col +
4819 			     (glong) terminal->pvt->bell_margin) ==
4820 			     terminal->pvt->column_count) {
4821 				_vte_terminal_beep (terminal);
4822 			}
4823 		}
4824 
4825 		if (terminal->pvt->cursor_blink_tag != 0)
4826 		{
4827 			remove_cursor_timeout (terminal);
4828                         if (terminal->pvt->cursor_blink_state == FALSE) {
4829                                 _vte_invalidate_cursor_once(terminal, FALSE);
4830                                 terminal->pvt->cursor_blink_state = TRUE;
4831                         }
4832 			add_cursor_timeout (terminal);
4833 		}
4834 
4835 		/* Determine if this is just a modifier key. */
4836 		modifier = _vte_keymap_key_is_modifier(keyval);
4837 
4838 		/* Unless it's a modifier key, hide the pointer. */
4839 		if (!modifier) {
4840 			_vte_terminal_set_pointer_visible(terminal, FALSE);
4841 		}
4842 
4843 		_vte_debug_print(VTE_DEBUG_EVENTS,
4844 				"Keypress, modifiers=0x%x, "
4845 				"keyval=0x%x, raw string=`%s'.\n",
4846 				terminal->pvt->modifiers,
4847 				keyval, event->string);
4848 
4849 		/* We steal many keypad keys here. */
4850 		if (!terminal->pvt->im_preedit_active) {
4851 			switch (keyval) {
4852 			case GDK_KEY_KP_Add:
4853 			case GDK_KEY_KP_Subtract:
4854 			case GDK_KEY_KP_Multiply:
4855 			case GDK_KEY_KP_Divide:
4856 			case GDK_KEY_KP_Enter:
4857 				steal = TRUE;
4858 				break;
4859 			default:
4860 				break;
4861 			}
4862 			if (terminal->pvt->modifiers & VTE_META_MASK) {
4863 				steal = TRUE;
4864 			}
4865 			switch (keyval) {
4866                         case GDK_KEY_ISO_Lock:
4867                         case GDK_KEY_ISO_Level2_Latch:
4868                         case GDK_KEY_ISO_Level3_Shift:
4869                         case GDK_KEY_ISO_Level3_Latch:
4870                         case GDK_KEY_ISO_Level3_Lock:
4871                         case GDK_KEY_ISO_Level5_Shift:
4872                         case GDK_KEY_ISO_Level5_Latch:
4873                         case GDK_KEY_ISO_Level5_Lock:
4874                         case GDK_KEY_ISO_Group_Shift:
4875                         case GDK_KEY_ISO_Group_Latch:
4876                         case GDK_KEY_ISO_Group_Lock:
4877                         case GDK_KEY_ISO_Next_Group:
4878                         case GDK_KEY_ISO_Next_Group_Lock:
4879                         case GDK_KEY_ISO_Prev_Group:
4880                         case GDK_KEY_ISO_Prev_Group_Lock:
4881                         case GDK_KEY_ISO_First_Group:
4882                         case GDK_KEY_ISO_First_Group_Lock:
4883                         case GDK_KEY_ISO_Last_Group:
4884                         case GDK_KEY_ISO_Last_Group_Lock:
4885 			case GDK_KEY_Multi_key:
4886 			case GDK_KEY_Codeinput:
4887 			case GDK_KEY_SingleCandidate:
4888 			case GDK_KEY_MultipleCandidate:
4889 			case GDK_KEY_PreviousCandidate:
4890 			case GDK_KEY_Kanji:
4891 			case GDK_KEY_Muhenkan:
4892                         case GDK_KEY_Henkan_Mode:
4893                         /* case GDK_KEY_Henkan: is GDK_KEY_Henkan_Mode */
4894 			case GDK_KEY_Romaji:
4895 			case GDK_KEY_Hiragana:
4896 			case GDK_KEY_Katakana:
4897 			case GDK_KEY_Hiragana_Katakana:
4898 			case GDK_KEY_Zenkaku:
4899 			case GDK_KEY_Hankaku:
4900 			case GDK_KEY_Zenkaku_Hankaku:
4901 			case GDK_KEY_Touroku:
4902 			case GDK_KEY_Massyo:
4903 			case GDK_KEY_Kana_Lock:
4904 			case GDK_KEY_Kana_Shift:
4905 			case GDK_KEY_Eisu_Shift:
4906 			case GDK_KEY_Eisu_toggle:
4907                         /* case GDK_KEY_Kanji_Bangou: is GDK_KEY_Codeinput */
4908                         /* case GDK_KEY_Zen_Koho: is GDK_KEY_MultipleCandidate */
4909                         /* case GDK_KEY_Mae_Koho: is GDK_KEY_PreviousCandidate */
4910                         /* case GDK_KEY_kana_switch: is GDK_KEY_ISO_Group_Shift */
4911                         case GDK_KEY_Hangul:
4912                         case GDK_KEY_Hangul_Start:
4913                         case GDK_KEY_Hangul_End:
4914                         case GDK_KEY_Hangul_Hanja:
4915                         case GDK_KEY_Hangul_Jamo:
4916                         case GDK_KEY_Hangul_Romaja:
4917                         /* case GDK_KEY_Hangul_Codeinput: is GDK_KEY_Codeinput */
4918                         case GDK_KEY_Hangul_Jeonja:
4919                         case GDK_KEY_Hangul_Banja:
4920                         case GDK_KEY_Hangul_PreHanja:
4921                         case GDK_KEY_Hangul_PostHanja:
4922                         /* case GDK_KEY_Hangul_SingleCandidate: is GDK_KEY_SingleCandidate */
4923                         /* case GDK_KEY_Hangul_MultipleCandidate: is GDK_KEY_MultipleCandidate */
4924                         /* case GDK_KEY_Hangul_PreviousCandidate: is GDK_KEY_PreviousCandidate */
4925                         case GDK_KEY_Hangul_Special:
4926                         /* case GDK_KEY_Hangul_switch: is GDK_KEY_ISO_Group_Shift */
4927 
4928 				steal = FALSE;
4929 				break;
4930 			default:
4931 				break;
4932 			}
4933 		}
4934 	}
4935 
4936 	modifiers = terminal->pvt->modifiers;
4937 
4938 	/* Let the input method at this one first. */
4939 	if (!steal && terminal->pvt->input_enabled) {
4940 		if (gtk_widget_get_realized (&terminal->widget)
4941 				&& gtk_im_context_filter_keypress (terminal->pvt->im_context, event)) {
4942 			_vte_debug_print(VTE_DEBUG_EVENTS,
4943 					"Keypress taken by IM.\n");
4944 			return TRUE;
4945 		}
4946 	}
4947 
4948 	/* Now figure out what to send to the child. */
4949 	if ((event->type == GDK_KEY_PRESS) && !modifier) {
4950 		handled = FALSE;
4951 		/* Map the key to a sequence name if we can. */
4952 		switch (keyval) {
4953 		case GDK_KEY_BackSpace:
4954 			switch (terminal->pvt->backspace_binding) {
4955 			case VTE_ERASE_ASCII_BACKSPACE:
4956 				normal = g_strdup("");
4957 				normal_length = 1;
4958 				suppress_meta_esc = FALSE;
4959 				break;
4960 			case VTE_ERASE_ASCII_DELETE:
4961 				normal = g_strdup("");
4962 				normal_length = 1;
4963 				suppress_meta_esc = FALSE;
4964 				break;
4965 			case VTE_ERASE_DELETE_SEQUENCE:
4966                                 normal = g_strdup("\e[3~");
4967                                 normal_length = 4;
4968                                 add_modifiers = TRUE;
4969 				suppress_meta_esc = TRUE;
4970 				break;
4971 			case VTE_ERASE_TTY:
4972 				if (terminal->pvt->pty != NULL &&
4973 				    tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1)
4974 				{
4975 					normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
4976 					normal_length = 1;
4977 				}
4978 				suppress_meta_esc = FALSE;
4979 				break;
4980 			case VTE_ERASE_AUTO:
4981 			default:
4982 #ifndef _POSIX_VDISABLE
4983 #define _POSIX_VDISABLE '\0'
4984 #endif
4985 				if (terminal->pvt->pty != NULL &&
4986 				    tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1 &&
4987 				    tio.c_cc[VERASE] != _POSIX_VDISABLE)
4988 				{
4989 					normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
4990 					normal_length = 1;
4991 				}
4992 				else
4993 				{
4994 					normal = g_strdup("");
4995 					normal_length = 1;
4996 					suppress_meta_esc = FALSE;
4997 				}
4998 				suppress_meta_esc = FALSE;
4999 				break;
5000 			}
5001 			handled = TRUE;
5002 			break;
5003 		case GDK_KEY_KP_Delete:
5004 		case GDK_KEY_Delete:
5005 			switch (terminal->pvt->delete_binding) {
5006 			case VTE_ERASE_ASCII_BACKSPACE:
5007 				normal = g_strdup("\010");
5008 				normal_length = 1;
5009 				break;
5010 			case VTE_ERASE_ASCII_DELETE:
5011 				normal = g_strdup("\177");
5012 				normal_length = 1;
5013 				break;
5014 			case VTE_ERASE_TTY:
5015 				if (terminal->pvt->pty != NULL &&
5016 				    tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &tio) != -1)
5017 				{
5018 					normal = g_strdup_printf("%c", tio.c_cc[VERASE]);
5019 					normal_length = 1;
5020 				}
5021 				suppress_meta_esc = FALSE;
5022 				break;
5023 			case VTE_ERASE_DELETE_SEQUENCE:
5024 			case VTE_ERASE_AUTO:
5025 			default:
5026                                 normal = g_strdup("\e[3~");
5027                                 normal_length = 4;
5028                                 add_modifiers = TRUE;
5029 				break;
5030 			}
5031 			handled = TRUE;
5032                         /* FIXMEchpe: why? this overrides the FALSE set above? */
5033 			suppress_meta_esc = TRUE;
5034 			break;
5035 		case GDK_KEY_KP_Insert:
5036 		case GDK_KEY_Insert:
5037 			if (modifiers & GDK_SHIFT_MASK) {
5038 				if (modifiers & GDK_CONTROL_MASK) {
5039 					vte_terminal_paste_clipboard(terminal);
5040 					handled = TRUE;
5041 					suppress_meta_esc = TRUE;
5042 				} else {
5043 					vte_terminal_paste_primary(terminal);
5044 					handled = TRUE;
5045 					suppress_meta_esc = TRUE;
5046 				}
5047 			} else if (modifiers & GDK_CONTROL_MASK) {
5048 				vte_terminal_copy_clipboard(terminal);
5049 				handled = TRUE;
5050 				suppress_meta_esc = TRUE;
5051 			}
5052 			break;
5053 		/* Keypad/motion keys. */
5054 		case GDK_KEY_KP_Up:
5055 		case GDK_KEY_Up:
5056 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5057 			    && modifiers & GDK_CONTROL_MASK
5058                             && modifiers & GDK_SHIFT_MASK) {
5059 				vte_terminal_scroll_lines(terminal, -1);
5060 				scrolled = TRUE;
5061 				handled = TRUE;
5062 				suppress_meta_esc = TRUE;
5063 			}
5064 			break;
5065 		case GDK_KEY_KP_Down:
5066 		case GDK_KEY_Down:
5067 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5068 			    && modifiers & GDK_CONTROL_MASK
5069                             && modifiers & GDK_SHIFT_MASK) {
5070 				vte_terminal_scroll_lines(terminal, 1);
5071 				scrolled = TRUE;
5072 				handled = TRUE;
5073 				suppress_meta_esc = TRUE;
5074 			}
5075 			break;
5076 		case GDK_KEY_KP_Page_Up:
5077 		case GDK_KEY_Page_Up:
5078 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5079 			    && modifiers & GDK_SHIFT_MASK) {
5080 				vte_terminal_scroll_pages(terminal, -1);
5081 				scrolled = TRUE;
5082 				handled = TRUE;
5083 				suppress_meta_esc = TRUE;
5084 			}
5085 			break;
5086 		case GDK_KEY_KP_Page_Down:
5087 		case GDK_KEY_Page_Down:
5088 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5089 			    && modifiers & GDK_SHIFT_MASK) {
5090 				vte_terminal_scroll_pages(terminal, 1);
5091 				scrolled = TRUE;
5092 				handled = TRUE;
5093 				suppress_meta_esc = TRUE;
5094 			}
5095 			break;
5096 		case GDK_KEY_KP_Home:
5097 		case GDK_KEY_Home:
5098 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5099 			    && modifiers & GDK_SHIFT_MASK) {
5100 				vte_terminal_maybe_scroll_to_top(terminal);
5101 				scrolled = TRUE;
5102 				handled = TRUE;
5103 			}
5104 			break;
5105 		case GDK_KEY_KP_End:
5106 		case GDK_KEY_End:
5107 			if (terminal->pvt->screen == &terminal->pvt->normal_screen
5108 			    && modifiers & GDK_SHIFT_MASK) {
5109 				vte_terminal_maybe_scroll_to_bottom(terminal);
5110 				scrolled = TRUE;
5111 				handled = TRUE;
5112 			}
5113 			break;
5114 		/* Let Shift +/- tweak the font, like XTerm does. */
5115 		case GDK_KEY_KP_Add:
5116 		case GDK_KEY_KP_Subtract:
5117 			if (modifiers &
5118 			    (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
5119 				switch (keyval) {
5120 				case GDK_KEY_KP_Add:
5121 					vte_terminal_emit_increase_font_size(terminal);
5122 					handled = TRUE;
5123 					suppress_meta_esc = TRUE;
5124 					break;
5125 				case GDK_KEY_KP_Subtract:
5126 					vte_terminal_emit_decrease_font_size(terminal);
5127 					handled = TRUE;
5128 					suppress_meta_esc = TRUE;
5129 					break;
5130 				}
5131 			}
5132 			break;
5133 		default:
5134 			break;
5135 		}
5136 		/* If the above switch statement didn't do the job, try mapping
5137 		 * it to a literal or capability name. */
5138                 if (handled == FALSE) {
5139 			_vte_keymap_map(keyval, modifiers,
5140 					terminal->pvt->cursor_mode == VTE_KEYMODE_APPLICATION,
5141 					terminal->pvt->keypad_mode == VTE_KEYMODE_APPLICATION,
5142 					&normal,
5143 					&normal_length);
5144 			/* If we found something this way, suppress
5145 			 * escape-on-meta. */
5146                         if (normal != NULL && normal_length > 0) {
5147 				suppress_meta_esc = TRUE;
5148 			}
5149 		}
5150 
5151 		/* Shall we do this here or earlier?  See bug 375112 and bug 589557 */
5152 		if (modifiers & GDK_CONTROL_MASK)
5153 			keyval = vte_translate_ctrlkey(event);
5154 
5155 		/* If we didn't manage to do anything, try to salvage a
5156 		 * printable string. */
5157 		if (handled == FALSE && normal == NULL) {
5158 
5159 			/* Convert the keyval to a gunichar. */
5160 			keychar = gdk_keyval_to_unicode(keyval);
5161 			normal_length = 0;
5162 			if (keychar != 0) {
5163 				/* Convert the gunichar to a string. */
5164 				normal_length = g_unichar_to_utf8(keychar,
5165 								  keybuf);
5166 				if (normal_length != 0) {
5167 					normal = g_malloc(normal_length + 1);
5168 					memcpy(normal, keybuf, normal_length);
5169 					normal[normal_length] = '\0';
5170 				} else {
5171 					normal = NULL;
5172 				}
5173 			}
5174 			if ((normal != NULL) &&
5175 			    (modifiers & GDK_CONTROL_MASK)) {
5176 				/* Replace characters which have "control"
5177 				 * counterparts with those counterparts. */
5178 				for (i = 0; i < normal_length; i++) {
5179 					if ((((guint8)normal[i]) >= 0x40) &&
5180 					    (((guint8)normal[i]) <  0x80)) {
5181 						normal[i] &= (~(0x60));
5182 					}
5183 				}
5184 			}
5185 			_VTE_DEBUG_IF (VTE_DEBUG_EVENTS) {
5186 				if (normal) g_printerr(
5187 						"Keypress, modifiers=0x%x, "
5188 						"keyval=0x%x, cooked string=`%s'.\n",
5189 						modifiers,
5190 						keyval, normal);
5191 			}
5192 		}
5193 		/* If we got normal characters, send them to the child. */
5194 		if (normal != NULL) {
5195                         if (add_modifiers) {
5196                                 _vte_keymap_key_add_key_modifiers(keyval,
5197                                                                   modifiers,
5198                                                                   terminal->pvt->cursor_mode == VTE_KEYMODE_APPLICATION,
5199                                                                   &normal,
5200                                                                   &normal_length);
5201                         }
5202 			if (terminal->pvt->meta_sends_escape &&
5203 			    !suppress_meta_esc &&
5204 			    (normal_length > 0) &&
5205 			    (modifiers & VTE_META_MASK)) {
5206 				vte_terminal_feed_child(terminal,
5207 							_VTE_CAP_ESC,
5208 							1);
5209 			}
5210 			if (normal_length > 0) {
5211 				vte_terminal_feed_child_using_modes(terminal,
5212 								    normal,
5213 								    normal_length);
5214 			}
5215 			g_free(normal);
5216 		}
5217 		/* Keep the cursor on-screen. */
5218 		if (!scrolled && !modifier &&
5219 		    terminal->pvt->scroll_on_keystroke) {
5220 			vte_terminal_maybe_scroll_to_bottom(terminal);
5221 		}
5222 		return TRUE;
5223 	}
5224 	return FALSE;
5225 }
5226 
5227 static gboolean
vte_terminal_key_release(GtkWidget * widget,GdkEventKey * event)5228 vte_terminal_key_release(GtkWidget *widget, GdkEventKey *event)
5229 {
5230 	VteTerminal *terminal;
5231 
5232 	terminal = VTE_TERMINAL(widget);
5233 
5234 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
5235 
5236 	if (gtk_widget_get_realized (&terminal->widget) &&
5237             terminal->pvt->input_enabled &&
5238             gtk_im_context_filter_keypress (terminal->pvt->im_context, event))
5239                 return TRUE;
5240 
5241         return FALSE;
5242 }
5243 
5244 static int
compare_unichar_p(const void * u1p,const void * u2p)5245 compare_unichar_p(const void *u1p,
5246                   const void *u2p)
5247 {
5248         const gunichar u1 = *(gunichar*)u1p;
5249         const gunichar u2 = *(gunichar*)u2p;
5250         return u1 < u2 ? -1 : u1 > u2 ? 1 : 0;
5251 }
5252 
5253 static const guint8 word_char_by_category[] = {
5254         [G_UNICODE_CONTROL]             = 2,
5255         [G_UNICODE_FORMAT]              = 2,
5256         [G_UNICODE_UNASSIGNED]          = 2,
5257         [G_UNICODE_PRIVATE_USE]         = 0,
5258         [G_UNICODE_SURROGATE]           = 2,
5259         [G_UNICODE_LOWERCASE_LETTER]    = 1,
5260         [G_UNICODE_MODIFIER_LETTER]     = 1,
5261         [G_UNICODE_OTHER_LETTER]        = 1,
5262         [G_UNICODE_TITLECASE_LETTER]    = 1,
5263         [G_UNICODE_UPPERCASE_LETTER]    = 1,
5264         [G_UNICODE_SPACING_MARK]        = 0,
5265         [G_UNICODE_ENCLOSING_MARK]      = 0,
5266         [G_UNICODE_NON_SPACING_MARK]    = 0,
5267         [G_UNICODE_DECIMAL_NUMBER]      = 1,
5268         [G_UNICODE_LETTER_NUMBER]       = 1,
5269         [G_UNICODE_OTHER_NUMBER]        = 1,
5270         [G_UNICODE_CONNECT_PUNCTUATION] = 0,
5271         [G_UNICODE_DASH_PUNCTUATION]    = 0,
5272         [G_UNICODE_CLOSE_PUNCTUATION]   = 0,
5273         [G_UNICODE_FINAL_PUNCTUATION]   = 0,
5274         [G_UNICODE_INITIAL_PUNCTUATION] = 0,
5275         [G_UNICODE_OTHER_PUNCTUATION]   = 0,
5276         [G_UNICODE_OPEN_PUNCTUATION]    = 0,
5277         [G_UNICODE_CURRENCY_SYMBOL]     = 0,
5278         [G_UNICODE_MODIFIER_SYMBOL]     = 0,
5279         [G_UNICODE_MATH_SYMBOL]         = 0,
5280         [G_UNICODE_OTHER_SYMBOL]        = 0,
5281         [G_UNICODE_LINE_SEPARATOR]      = 2,
5282         [G_UNICODE_PARAGRAPH_SEPARATOR] = 2,
5283         [G_UNICODE_SPACE_SEPARATOR]     = 2,
5284 };
5285 
5286 /*
5287  * _vte_terminal_is_word_char:
5288  * @terminal: a #VteTerminal
5289  * @c: a candidate Unicode code point
5290  *
5291  * Checks if a particular character is considered to be part of a word or not.
5292  *
5293  * Returns: %TRUE if the character is considered to be part of a word
5294  */
5295 gboolean
_vte_terminal_is_word_char(VteTerminal * terminal,gunichar c)5296 _vte_terminal_is_word_char(VteTerminal *terminal,
5297                            gunichar c)
5298 {
5299         const guint8 v = word_char_by_category[g_unichar_type(c)];
5300 
5301         if (v)
5302                 return v == 1;
5303 
5304         /* Do we have an exception? */
5305         return bsearch(&c,
5306                        terminal->pvt->word_char_exceptions,
5307                        terminal->pvt->word_char_exceptions_len,
5308                        sizeof(gunichar),
5309                        compare_unichar_p) != NULL;
5310 }
5311 
5312 /* Check if the characters in the two given locations are in the same class
5313  * (word vs. non-word characters). */
5314 static gboolean
vte_same_class(VteTerminal * terminal,glong acol,glong arow,glong bcol,glong brow)5315 vte_same_class(VteTerminal *terminal, glong acol, glong arow,
5316 	       glong bcol, glong brow)
5317 {
5318 	const VteCell *pcell = NULL;
5319 	gboolean word_char;
5320 	if ((pcell = vte_terminal_find_charcell(terminal, acol, arow)) != NULL && pcell->c != 0) {
5321 		word_char = _vte_terminal_is_word_char(terminal, _vte_unistr_get_base (pcell->c));
5322 
5323 		/* Lets not group non-wordchars together (bug #25290) */
5324 		if (!word_char)
5325 			return FALSE;
5326 
5327 		pcell = vte_terminal_find_charcell(terminal, bcol, brow);
5328 		if (pcell == NULL || pcell->c == 0) {
5329 			return FALSE;
5330 		}
5331 		if (word_char != _vte_terminal_is_word_char(terminal, _vte_unistr_get_base (pcell->c))) {
5332 			return FALSE;
5333 		}
5334 		return TRUE;
5335 	}
5336 	return FALSE;
5337 }
5338 
5339 /* Check if we soft-wrapped on the given line. */
5340 static gboolean
vte_line_is_wrappable(VteTerminal * terminal,glong row)5341 vte_line_is_wrappable(VteTerminal *terminal, glong row)
5342 {
5343 	const VteRowData *rowdata;
5344 	rowdata = _vte_terminal_find_row_data(terminal, row);
5345 	return rowdata && rowdata->attr.soft_wrapped;
5346 }
5347 
5348 /* Check if the given point is in the region between the two points,
5349  * optionally treating the second point as included in the region or not. */
5350 static gboolean
vte_cell_is_between(glong col,glong row,glong acol,glong arow,glong bcol,glong brow,gboolean inclusive)5351 vte_cell_is_between(glong col, glong row,
5352 		    glong acol, glong arow, glong bcol, glong brow,
5353 		    gboolean inclusive)
5354 {
5355 	/* Negative between never allowed. */
5356 	if ((arow > brow) || ((arow == brow) && (acol > bcol))) {
5357 		return FALSE;
5358 	}
5359 	/* Zero-length between only allowed if we're being inclusive. */
5360 	if ((row == arow) && (row == brow) && (col == acol) && (col == bcol)) {
5361 		return inclusive;
5362 	}
5363 	/* A cell is between two points if it's on a line after the
5364 	 * specified area starts, or before the line where it ends,
5365 	 * or any of the lines in between. */
5366 	if ((row > arow) && (row < brow)) {
5367 		return TRUE;
5368 	}
5369 	/* It's also between the two points if they're on the same row
5370 	 * the cell lies between the start and end columns. */
5371 	if ((row == arow) && (row == brow)) {
5372 		if (col >= acol) {
5373 			if (col < bcol) {
5374 				return TRUE;
5375 			} else {
5376 				if ((col == bcol) && inclusive) {
5377 					return TRUE;
5378 				} else {
5379 					return FALSE;
5380 				}
5381 			}
5382 		} else {
5383 			return FALSE;
5384 		}
5385 	}
5386 	/* It's also "between" if it's on the line where the area starts and
5387 	 * at or after the start column, or on the line where the area ends and
5388 	 * before the end column. */
5389 	if ((row == arow) && (col >= acol)) {
5390 		return TRUE;
5391 	} else {
5392 		if (row == brow) {
5393 			if (col < bcol) {
5394 				return TRUE;
5395 			} else {
5396 				if ((col == bcol) && inclusive) {
5397 					return TRUE;
5398 				} else {
5399 					return FALSE;
5400 				}
5401 			}
5402 		} else {
5403 			return FALSE;
5404 		}
5405 	}
5406 	return FALSE;
5407 }
5408 
5409 /* Check if a cell is selected or not. */
5410 static gboolean
vte_cell_is_selected(VteTerminal * terminal,glong col,glong row,gpointer data)5411 vte_cell_is_selected(VteTerminal *terminal, glong col, glong row, gpointer data)
5412 {
5413 	VteVisualPosition ss, se;
5414 
5415 	/* If there's nothing selected, it's an easy question to answer. */
5416 	if (!terminal->pvt->has_selection) {
5417 		return FALSE;
5418 	}
5419 
5420 	/* If the selection is obviously bogus, then it's also very easy. */
5421 	ss = terminal->pvt->selection_start;
5422 	se = terminal->pvt->selection_end;
5423 	if ((ss.row < 0) || (se.row < 0)) {
5424 		return FALSE;
5425 	}
5426 
5427 	/* Limit selection in block mode. */
5428 	if (terminal->pvt->selection_block_mode) {
5429 		if (col < ss.col || col > se.col) {
5430 			return FALSE;
5431 		}
5432 	}
5433 
5434 	/* Now it boils down to whether or not the point is between the
5435 	 * begin and endpoint of the selection. */
5436 	return vte_cell_is_between(col, row, ss.col, ss.row, se.col, se.row, TRUE);
5437 }
5438 
5439 /* Once we get text data, actually paste it in. */
5440 static void
vte_terminal_paste_cb(GtkClipboard * clipboard,const gchar * text,gpointer data)5441 vte_terminal_paste_cb(GtkClipboard *clipboard, const gchar *text, gpointer data)
5442 {
5443 	VteTerminal *terminal;
5444 	gchar *paste, *p;
5445 	long length;
5446 	terminal = data;
5447 	if (text != NULL) {
5448 		_vte_debug_print(VTE_DEBUG_SELECTION,
5449 				"Pasting %"G_GSIZE_FORMAT" UTF-8 bytes.\n",
5450 				strlen(text));
5451 		if (!g_utf8_validate(text, -1, NULL)) {
5452 			g_warning(_("Error (%s) converting data for child, dropping."), g_strerror(EINVAL));
5453 			return;
5454 		}
5455 
5456 		/* Convert newlines to carriage returns, which more software
5457 		 * is able to cope with (cough, pico, cough). */
5458 		paste = g_strdup(text);
5459 		length = strlen(paste);
5460 		p = paste;
5461 		while ((p != NULL) && (p - paste < length)) {
5462 			p = memchr(p, '\n', length - (p - paste));
5463 			if (p != NULL) {
5464 				*p = '\r';
5465 				p++;
5466 			}
5467 		}
5468 		if (terminal->pvt->bracketed_paste_mode)
5469 			vte_terminal_feed_child(terminal, "\e[200~", -1);
5470 		vte_terminal_feed_child(terminal, paste, length);
5471 		if (terminal->pvt->bracketed_paste_mode)
5472 			vte_terminal_feed_child(terminal, "\e[201~", -1);
5473 		g_free(paste);
5474 	}
5475 }
5476 
5477 /**
5478  * _vte_terminal_xy_to_grid:
5479  * @x: the X coordinate
5480  * @y: the Y coordinate
5481  * @col: return location to store the column
5482  * @row: return location to store the row
5483  *
5484  * Translates from widget coordinates to grid coordinates.
5485  *
5486  * If the coordinates are outside the grid, returns %FALSE.
5487  */
5488 gboolean
_vte_terminal_xy_to_grid(VteTerminal * terminal,long x,long y,long * col,long * row)5489 _vte_terminal_xy_to_grid(VteTerminal *terminal,
5490                          long x,
5491                          long y,
5492                          long *col,
5493                          long *row)
5494 {
5495         VteTerminalPrivate *pvt = terminal->pvt;
5496         long c, r;
5497 
5498         /* FIXMEchpe: is this correct for RTL? */
5499         c = (x - pvt->padding.left) / pvt->char_width;
5500         r = (y - pvt->padding.top) / pvt->char_height;
5501 
5502         if ((c < 0 || c >= pvt->column_count) ||
5503             (r < 0 || r >= pvt->row_count))
5504           return FALSE;
5505 
5506         *col = c;
5507         *row = r;
5508         return TRUE;
5509 }
5510 
5511 /*
5512  * _vte_terminal_size_to_grid_size:
5513  * @w: the width in px
5514  * @h: the height in px
5515  * @col: return location to store the column count
5516  * @row: return location to store the row count
5517  *
5518  * Translates from widget size to grid size.
5519  *
5520  * If the given width or height are insufficient to show even
5521  * one column or row (i.e due to padding), returns %FALSE.
5522  */
5523 gboolean
_vte_terminal_size_to_grid_size(VteTerminal * terminal,long w,long h,long * cols,long * rows)5524 _vte_terminal_size_to_grid_size(VteTerminal *terminal,
5525                                 long w,
5526                                 long h,
5527                                 long *cols,
5528                                 long *rows)
5529 {
5530         VteTerminalPrivate *pvt = terminal->pvt;
5531         long n_cols, n_rows;
5532 
5533         n_cols = (w - pvt->padding.left - pvt->padding.right) / pvt->char_width;
5534         n_rows = (h - pvt->padding.top -pvt->padding.bottom) / pvt->char_height;
5535 
5536         if (n_cols <= 0 || n_rows <= 0)
5537                 return FALSE;
5538 
5539         *cols = n_cols;
5540         *rows = n_rows;
5541         return TRUE;
5542 }
5543 
5544 static void
vte_terminal_feed_mouse_event(VteTerminal * terminal,int button,gboolean is_drag,gboolean is_release,long col,long row)5545 vte_terminal_feed_mouse_event(VteTerminal *terminal,
5546 			      int          button,
5547 			      gboolean     is_drag,
5548 			      gboolean     is_release,
5549 			      long         col,
5550 			      long         row)
5551 {
5552 	unsigned char cb = 0;
5553 	long cx, cy;
5554 	char buf[LINE_MAX];
5555 	gint len = 0;
5556 
5557 	/* Encode the button information in cb. */
5558 	switch (button) {
5559         case 0:                 /* No button, just dragging. */
5560                 cb = 3;
5561                 break;
5562 	case 1:			/* Left. */
5563 		cb = 0;
5564 		break;
5565 	case 2:			/* Middle. */
5566 		cb = 1;
5567 		break;
5568 	case 3:			/* Right. */
5569 		cb = 2;
5570 		break;
5571 	case 4:
5572 		cb = 64;	/* Scroll up. */
5573 		break;
5574 	case 5:
5575 		cb = 65;	/* Scroll down. */
5576 		break;
5577 	}
5578 
5579 	/* With the exception of the 1006 mode, button release is also encoded here. */
5580 	/* Note that if multiple extensions are enabled, the 1006 is used, so it's okay to check for only that. */
5581 	if (is_release && !terminal->pvt->mouse_xterm_extension) {
5582 		cb = 3;
5583 	}
5584 
5585 	/* Encode the modifiers. */
5586 	if (terminal->pvt->modifiers & GDK_SHIFT_MASK) {
5587 		cb |= 4;
5588 	}
5589 	if (terminal->pvt->modifiers & VTE_META_MASK) {
5590 		cb |= 8;
5591 	}
5592 	if (terminal->pvt->modifiers & GDK_CONTROL_MASK) {
5593 		cb |= 16;
5594 	}
5595 
5596 	/* Encode a drag event. */
5597 	if (is_drag) {
5598 		cb |= 32;
5599 	}
5600 
5601 	/* Clamp the cursor coordinates. Make them 1-based. */
5602 	cx = CLAMP(1 + col,
5603 		   1, terminal->pvt->column_count);
5604 	cy = CLAMP(1 + row,
5605 		   1, terminal->pvt->row_count);
5606 
5607 	/* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */
5608 	if (terminal->pvt->mouse_xterm_extension) {
5609 		/* xterm's extended mode (1006) */
5610 		len = g_snprintf(buf, sizeof(buf), _VTE_CAP_CSI "<%d;%ld;%ld%c", cb, cx, cy, is_release ? 'm' : 'M');
5611 	} else if (terminal->pvt->mouse_urxvt_extension) {
5612 		/* urxvt's extended mode (1015) */
5613 		len = g_snprintf(buf, sizeof(buf), _VTE_CAP_CSI "%d;%ld;%ldM", 32 + cb, cx, cy);
5614 	} else if (cx <= 231 && cy <= 231) {
5615 		/* legacy mode */
5616 		len = g_snprintf(buf, sizeof(buf), _VTE_CAP_CSI "M%c%c%c", 32 + cb, 32 + (guchar)cx, 32 + (guchar)cy);
5617 	}
5618 
5619 	/* Send event direct to the child, this is binary not text data */
5620 	vte_terminal_feed_child_binary(terminal, (guint8*) buf, len);
5621 }
5622 
5623 static void
vte_terminal_send_mouse_button_internal(VteTerminal * terminal,int button,gboolean is_release,long x,long y)5624 vte_terminal_send_mouse_button_internal(VteTerminal *terminal,
5625 					int          button,
5626 					gboolean     is_release,
5627 					long         x,
5628 					long         y)
5629 {
5630 	int width = terminal->pvt->char_width;
5631 	int height = terminal->pvt->char_height;
5632 	long col = (x - terminal->pvt->padding.left) / width;
5633 	long row = (y - terminal->pvt->padding.top) / height;
5634 
5635 	vte_terminal_feed_mouse_event(terminal, button, FALSE /* not drag */, is_release, col, row);
5636 }
5637 
5638 /*
5639  * vte_terminal_maybe_send_mouse_button:
5640  * @terminal:
5641  * @event:
5642  *
5643  * Sends a mouse button click or release notification to the application,
5644  * if the terminal is in mouse tracking mode.
5645  *
5646  * Returns: %TRUE iff the event was consumed
5647  */
5648 static gboolean
vte_terminal_maybe_send_mouse_button(VteTerminal * terminal,GdkEventButton * event)5649 vte_terminal_maybe_send_mouse_button(VteTerminal *terminal,
5650 				     GdkEventButton *event)
5651 {
5652 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
5653 
5654 	switch (event->type) {
5655 	case GDK_BUTTON_PRESS:
5656 		if (terminal->pvt->mouse_tracking_mode < MOUSE_TRACKING_SEND_XY_ON_CLICK) {
5657 			return FALSE;
5658 		}
5659 		break;
5660 	case GDK_BUTTON_RELEASE: {
5661 		if (terminal->pvt->mouse_tracking_mode < MOUSE_TRACKING_SEND_XY_ON_BUTTON) {
5662 			return FALSE;
5663 		}
5664 		break;
5665 	}
5666 	default:
5667 		return FALSE;
5668 		break;
5669 	}
5670 
5671 	vte_terminal_send_mouse_button_internal(terminal,
5672 						event->button,
5673 						event->type == GDK_BUTTON_RELEASE,
5674 						event->x, event->y);
5675 	return TRUE;
5676 }
5677 
5678 /*
5679  * vte_terminal_maybe_send_mouse_drag:
5680  * @terminal:
5681  * @event:
5682  *
5683  * Sends a mouse motion notification to the application,
5684  * if the terminal is in mouse tracking mode.
5685  *
5686  * Returns: %TRUE iff the event was consumed
5687  */
5688 static gboolean
vte_terminal_maybe_send_mouse_drag(VteTerminal * terminal,GdkEventMotion * event)5689 vte_terminal_maybe_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event)
5690 {
5691 	int width = terminal->pvt->char_width;
5692 	int height = terminal->pvt->char_height;
5693 	long col = ((long) event->x - terminal->pvt->padding.left) / width;
5694 	long row = ((long) event->y - terminal->pvt->padding.top) / height;
5695         int button;
5696 
5697 	/* First determine if we even want to send notification. */
5698 	switch (event->type) {
5699 	case GDK_MOTION_NOTIFY:
5700 		if (terminal->pvt->mouse_tracking_mode < MOUSE_TRACKING_CELL_MOTION_TRACKING)
5701 			return FALSE;
5702 
5703 		if (terminal->pvt->mouse_tracking_mode < MOUSE_TRACKING_ALL_MOTION_TRACKING) {
5704 
5705                         if (terminal->pvt->mouse_pressed_buttons == 0) {
5706 				return FALSE;
5707 			}
5708 			/* the xterm doc is not clear as to whether
5709 			 * all-tracking also sends degenerate same-cell events */
5710 			if (col == terminal->pvt->mouse_last_x / width &&
5711 			    row == terminal->pvt->mouse_last_y / height)
5712 				return FALSE;
5713 		}
5714 		break;
5715 	default:
5716 		return FALSE;
5717 		break;
5718 	}
5719 
5720         /* As per xterm, report the leftmost pressed button - if any. */
5721         if (terminal->pvt->mouse_pressed_buttons & 1)
5722                 button = 1;
5723         else if (terminal->pvt->mouse_pressed_buttons & 2)
5724                 button = 2;
5725         else if (terminal->pvt->mouse_pressed_buttons & 4)
5726                 button = 3;
5727         else
5728                 button = 0;
5729         vte_terminal_feed_mouse_event(terminal, button,
5730 				      TRUE /* drag */, FALSE /* not release */,
5731 				      col, row);
5732 	return TRUE;
5733 }
5734 
5735 /* Clear all match hilites. */
5736 static void
vte_terminal_match_hilite_clear(VteTerminal * terminal)5737 vte_terminal_match_hilite_clear(VteTerminal *terminal)
5738 {
5739 	long srow, scolumn, erow, ecolumn;
5740 	srow = terminal->pvt->match_start.row;
5741 	scolumn = terminal->pvt->match_start.col;
5742 	erow = terminal->pvt->match_end.row;
5743 	ecolumn = terminal->pvt->match_end.col;
5744 	terminal->pvt->match_start.row = -1;
5745 	terminal->pvt->match_start.col = -1;
5746 	terminal->pvt->match_end.row = -2;
5747 	terminal->pvt->match_end.col = -2;
5748 	if (terminal->pvt->match_tag != -1) {
5749 		_vte_debug_print(VTE_DEBUG_EVENTS,
5750 				"Clearing hilite (%ld,%ld) to (%ld,%ld).\n",
5751 				srow, scolumn, erow, ecolumn);
5752 		_vte_invalidate_region (terminal,
5753 				scolumn, ecolumn, srow, erow, FALSE);
5754 		terminal->pvt->match_tag = -1;
5755 	}
5756 	terminal->pvt->show_match = FALSE;
5757 	if (terminal->pvt->match) {
5758 		g_free (terminal->pvt->match);
5759 		terminal->pvt->match = NULL;
5760 	}
5761 }
5762 
5763 static gboolean
cursor_inside_match(VteTerminal * terminal,long x,long y)5764 cursor_inside_match (VteTerminal *terminal, long x, long y)
5765 {
5766 	gint width = terminal->pvt->char_width;
5767 	gint height = terminal->pvt->char_height;
5768 	glong col = x / width;
5769 	glong row = y / height + terminal->pvt->screen->scroll_delta;
5770 	if (terminal->pvt->match_start.row == terminal->pvt->match_end.row) {
5771 		return row == terminal->pvt->match_start.row &&
5772 			col >= terminal->pvt->match_start.col &&
5773 			col <= terminal->pvt->match_end.col;
5774 	} else {
5775 		if (row < terminal->pvt->match_start.row ||
5776 				row > terminal->pvt->match_end.row) {
5777 			return FALSE;
5778 		}
5779 		if (row == terminal->pvt->match_start.row) {
5780 			return col >= terminal->pvt->match_start.col;
5781 		}
5782 		if (row == terminal->pvt->match_end.row) {
5783 			return col <= terminal->pvt->match_end.col;
5784 		}
5785 		return TRUE;
5786 	}
5787 }
5788 
5789 static void
vte_terminal_match_hilite_show(VteTerminal * terminal,long x,long y)5790 vte_terminal_match_hilite_show(VteTerminal *terminal, long x, long y)
5791 {
5792 	if(terminal->pvt->match != NULL && !terminal->pvt->show_match){
5793 		if (cursor_inside_match (terminal, x, y)) {
5794 			_vte_invalidate_region (terminal,
5795 					terminal->pvt->match_start.col,
5796 					terminal->pvt->match_end.col,
5797 					terminal->pvt->match_start.row,
5798 					terminal->pvt->match_end.row,
5799 					FALSE);
5800 			terminal->pvt->show_match = TRUE;
5801 		}
5802 	}
5803 }
5804 static void
vte_terminal_match_hilite_hide(VteTerminal * terminal)5805 vte_terminal_match_hilite_hide(VteTerminal *terminal)
5806 {
5807 	if(terminal->pvt->match != NULL && terminal->pvt->show_match){
5808 		_vte_invalidate_region (terminal,
5809 				terminal->pvt->match_start.col,
5810 				terminal->pvt->match_end.col,
5811 				terminal->pvt->match_start.row,
5812 				terminal->pvt->match_end.row,
5813 				FALSE);
5814 		terminal->pvt->show_match = FALSE;
5815 	}
5816 }
5817 
5818 
5819 static void
vte_terminal_match_hilite_update(VteTerminal * terminal,long x,long y)5820 vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y)
5821 {
5822 	int start, end, width, height;
5823 	char *match;
5824 	struct _VteCharAttributes *attr;
5825 	VteScreen *screen;
5826 	long delta;
5827 
5828 	width = terminal->pvt->char_width;
5829 	height = terminal->pvt->char_height;
5830 
5831 	/* Check for matches. */
5832 	screen = terminal->pvt->screen;
5833 	delta = screen->scroll_delta;
5834 
5835 	_vte_debug_print(VTE_DEBUG_EVENTS,
5836 			"Match hilite update (%ld, %ld) -> %ld, %ld\n",
5837 			x, y,
5838 			x / width,
5839 			y / height + delta);
5840 
5841 	match = vte_terminal_match_check_internal(terminal,
5842 						  x / width,
5843 						  y / height + delta,
5844 						  &terminal->pvt->match_tag,
5845 						  &start,
5846 						  &end);
5847 	if (terminal->pvt->show_match) {
5848 		/* Repaint what used to be hilited, if anything. */
5849 		_vte_invalidate_region(terminal,
5850 				terminal->pvt->match_start.col,
5851 				terminal->pvt->match_end.col,
5852 				terminal->pvt->match_start.row,
5853 				terminal->pvt->match_end.row,
5854 				FALSE);
5855 	}
5856 
5857 	/* Read the new locations. */
5858 	attr = NULL;
5859 	if ((guint) start < terminal->pvt->match_attributes->len) {
5860 		attr = &g_array_index(terminal->pvt->match_attributes,
5861 				struct _VteCharAttributes,
5862 				start);
5863 		terminal->pvt->match_start.row = attr->row;
5864 		terminal->pvt->match_start.col = attr->column;
5865 
5866 		attr = NULL;
5867 		if ((guint) end < terminal->pvt->match_attributes->len) {
5868 			attr = &g_array_index(terminal->pvt->match_attributes,
5869 					struct _VteCharAttributes,
5870 					end);
5871 			terminal->pvt->match_end.row = attr->row;
5872 			terminal->pvt->match_end.col = attr->column;
5873 		}
5874 	}
5875 	if (attr == NULL) { /* i.e. if either endpoint is not found */
5876 		terminal->pvt->match_start.row = -1;
5877 		terminal->pvt->match_start.col = -1;
5878 		terminal->pvt->match_end.row = -2;
5879 		terminal->pvt->match_end.col = -2;
5880 		g_assert (match == NULL);
5881 	}
5882 
5883 	g_free (terminal->pvt->match);
5884 	terminal->pvt->match = match;
5885 
5886 	/* If there are no matches, repaint what we had matched before. */
5887 	if (match == NULL) {
5888 		_vte_debug_print(VTE_DEBUG_EVENTS,
5889 				"No matches. [(%ld,%ld) to (%ld,%ld)]\n",
5890 				terminal->pvt->match_start.col,
5891 				terminal->pvt->match_start.row,
5892 				terminal->pvt->match_end.col,
5893 				terminal->pvt->match_end.row);
5894 		terminal->pvt->show_match = FALSE;
5895 	} else {
5896 		terminal->pvt->show_match = TRUE;
5897 		/* Repaint the newly-hilited area. */
5898 		_vte_invalidate_region(terminal,
5899 				terminal->pvt->match_start.col,
5900 				terminal->pvt->match_end.col,
5901 				terminal->pvt->match_start.row,
5902 				terminal->pvt->match_end.row,
5903 				FALSE);
5904 		_vte_debug_print(VTE_DEBUG_EVENTS,
5905 				"Matched (%ld,%ld) to (%ld,%ld).\n",
5906 				terminal->pvt->match_start.col,
5907 				terminal->pvt->match_start.row,
5908 				terminal->pvt->match_end.col,
5909 				terminal->pvt->match_end.row);
5910 	}
5911 }
5912 /* Update the hilited text if the pointer has moved to a new character cell. */
5913 static void
vte_terminal_match_hilite(VteTerminal * terminal,long x,long y)5914 vte_terminal_match_hilite(VteTerminal *terminal, long x, long y)
5915 {
5916 	int width, height;
5917 	GtkAllocation allocation;
5918 
5919 	width = terminal->pvt->char_width;
5920 	height = terminal->pvt->char_height;
5921 
5922 	gtk_widget_get_allocation (&terminal->widget, &allocation);
5923 
5924 	/* if the cursor is not above a cell, skip */
5925 	if (x < 0 || x > allocation.width
5926 			|| y < 0 || y > allocation.height) {
5927 		return;
5928 	}
5929 
5930 	/* If the pointer hasn't moved to another character cell, then we
5931 	 * need do nothing. */
5932 	if (x / width  == terminal->pvt->mouse_last_x / width &&
5933 	    y / height == terminal->pvt->mouse_last_y / height) {
5934 		terminal->pvt->show_match = terminal->pvt->match != NULL;
5935 		return;
5936 	}
5937 
5938 	if (cursor_inside_match (terminal, x, y)) {
5939 		terminal->pvt->show_match = terminal->pvt->match != NULL;
5940 		return;
5941 	}
5942 
5943 	vte_terminal_match_hilite_update(terminal, x, y);
5944 }
5945 
5946 
5947 /* Note that the clipboard has cleared. */
5948 static void
vte_terminal_clear_cb(GtkClipboard * clipboard,gpointer owner)5949 vte_terminal_clear_cb(GtkClipboard *clipboard, gpointer owner)
5950 {
5951 	VteTerminal *terminal;
5952 	terminal = owner;
5953 	if (terminal->pvt->has_selection) {
5954 		_vte_debug_print(VTE_DEBUG_SELECTION, "Lost selection.\n");
5955 		vte_terminal_deselect_all(terminal);
5956 	}
5957 }
5958 
5959 /* Supply the selected text to the clipboard. */
5960 static void
vte_terminal_copy_cb(GtkClipboard * clipboard,GtkSelectionData * data,guint info,gpointer owner)5961 vte_terminal_copy_cb(GtkClipboard *clipboard, GtkSelectionData *data,
5962 		     guint info, gpointer owner)
5963 {
5964 	VteTerminal *terminal;
5965 	terminal = owner;
5966 	if (terminal->pvt->selection != NULL) {
5967 		_VTE_DEBUG_IF(VTE_DEBUG_SELECTION) {
5968 			int i;
5969 			g_printerr("Setting selection (%"G_GSIZE_FORMAT" UTF-8 bytes.)\n",
5970 				strlen(terminal->pvt->selection));
5971 			for (i = 0; terminal->pvt->selection[i] != '\0'; i++) {
5972 				g_printerr("0x%04x\n",
5973 					terminal->pvt->selection[i]);
5974 			}
5975 		}
5976 		gtk_selection_data_set_text(data, terminal->pvt->selection, -1);
5977 	}
5978 }
5979 
5980 /* Convert the internal color code (either index or RGB, see vte-private.h) into RGB. */
5981 static void
vte_terminal_get_rgb_from_index(const VteTerminal * terminal,guint index,PangoColor * color)5982 vte_terminal_get_rgb_from_index(const VteTerminal *terminal, guint index, PangoColor *color)
5983 {
5984         gboolean dim = FALSE;
5985         if (!(index & VTE_RGB_COLOR) && (index & VTE_DIM_COLOR)) {
5986                 index &= ~VTE_DIM_COLOR;
5987                 dim = TRUE;
5988         }
5989 
5990 	if (index >= VTE_LEGACY_COLORS_OFFSET && index < VTE_LEGACY_COLORS_OFFSET + VTE_LEGACY_FULL_COLOR_SET_SIZE)
5991 		index -= VTE_LEGACY_COLORS_OFFSET;
5992 	if (index < VTE_PALETTE_SIZE) {
5993 		memcpy(color, _vte_terminal_get_color(terminal, index), sizeof(PangoColor));
5994                 if (dim) {
5995                         /* magic formula taken from xterm */
5996                         color->red = color->red * 2 / 3;
5997                         color->green = color->green * 2 / 3;
5998                         color->blue = color->blue * 2 / 3;
5999                 }
6000 	} else if (index & VTE_RGB_COLOR) {
6001 		color->red = ((index >> 16) & 0xFF) * 257;
6002 		color->green = ((index >> 8) & 0xFF) * 257;
6003 		color->blue = (index & 0xFF) * 257;
6004 	} else {
6005 		g_assert_not_reached();
6006 	}
6007 }
6008 
6009 /**
6010  * VteSelectionFunc:
6011  * @terminal: terminal in which the cell is.
6012  * @column: column in which the cell is.
6013  * @row: row in which the cell is.
6014  * @data: (closure): user data.
6015  *
6016  * Specifies the type of a selection function used to check whether
6017  * a cell has to be selected or not.
6018  *
6019  * Returns: %TRUE if cell has to be selected; %FALSE if otherwise.
6020  */
6021 
6022 /**
6023  * vte_terminal_get_text_range:
6024  * @terminal: a #VteTerminal
6025  * @start_row: first row to search for data
6026  * @start_col: first column to search for data
6027  * @end_row: last row to search for data
6028  * @end_col: last column to search for data
6029  * @is_selected: (scope call) (allow-none): a #VteSelectionFunc callback
6030  * @user_data: (closure): user data to be passed to the callback
6031  * @attributes: (out caller-allocates) (transfer full) (array) (element-type Vte.CharAttributes): location for storing text attributes
6032  *
6033  * Extracts a view of the visible part of the terminal.  If @is_selected is not
6034  * %NULL, characters will only be read if @is_selected returns %TRUE after being
6035  * passed the column and row, respectively.  A #VteCharAttributes structure
6036  * is added to @attributes for each byte added to the returned string detailing
6037  * the character's position, colors, and other characteristics.  The
6038  * entire scrollback buffer is scanned, so it is possible to read the entire
6039  * contents of the buffer using this function.
6040  *
6041  * Returns: (transfer full): a newly allocated text string, or %NULL.
6042  */
6043 char *
vte_terminal_get_text_range(VteTerminal * terminal,glong start_row,glong start_col,glong end_row,glong end_col,VteSelectionFunc is_selected,gpointer user_data,GArray * attributes)6044 vte_terminal_get_text_range(VteTerminal *terminal,
6045 			    glong start_row, glong start_col,
6046 			    glong end_row, glong end_col,
6047 			    VteSelectionFunc is_selected,
6048 			    gpointer user_data,
6049 			    GArray *attributes)
6050 {
6051 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
6052 	return vte_terminal_get_text_range_maybe_wrapped(terminal,
6053 							 start_row, start_col,
6054 							 end_row, end_col,
6055 							 TRUE,
6056 							 is_selected,
6057 							 user_data,
6058 							 attributes,
6059 							 FALSE);
6060 }
6061 
6062 static char *
vte_terminal_get_text_range_maybe_wrapped(VteTerminal * terminal,glong start_row,glong start_col,glong end_row,glong end_col,gboolean wrap,VteSelectionFunc is_selected,gpointer data,GArray * attributes,gboolean include_trailing_spaces)6063 vte_terminal_get_text_range_maybe_wrapped(VteTerminal *terminal,
6064 					  glong start_row, glong start_col,
6065 					  glong end_row, glong end_col,
6066 					  gboolean wrap,
6067 					  VteSelectionFunc is_selected,
6068 					  gpointer data,
6069 					  GArray *attributes,
6070 					  gboolean include_trailing_spaces)
6071 {
6072 	glong col, row, last_empty, last_emptycol, last_nonempty, last_nonemptycol;
6073 	const VteCell *pcell = NULL;
6074 	GString *string;
6075 	struct _VteCharAttributes attr;
6076 	PangoColor fore, back;
6077 
6078 	if (!is_selected)
6079 		is_selected = always_selected;
6080 
6081 	if (attributes)
6082 		g_array_set_size (attributes, 0);
6083 
6084 	string = g_string_new(NULL);
6085 	memset(&attr, 0, sizeof(attr));
6086 
6087 	col = start_col;
6088 	for (row = start_row; row < end_row + 1; row++, col = 0) {
6089 		const VteRowData *row_data = _vte_terminal_find_row_data (terminal, row);
6090 		last_empty = last_nonempty = string->len;
6091 		last_emptycol = last_nonemptycol = -1;
6092 
6093 		attr.row = row;
6094 		attr.column = col;
6095 		pcell = NULL;
6096 		if (row_data != NULL) {
6097 			while ((pcell = _vte_row_data_get (row_data, col))) {
6098 
6099 				attr.column = col;
6100 
6101 				/* If it's not part of a multi-column character,
6102 				 * and passes the selection criterion, add it to
6103 				 * the selection. */
6104 				if (!pcell->attr.fragment && is_selected(terminal, col, row, data)) {
6105 					/* Store the attributes of this character. */
6106 					vte_terminal_get_rgb_from_index(terminal, pcell->attr.fore, &fore);
6107 					vte_terminal_get_rgb_from_index(terminal, pcell->attr.back, &back);
6108 					attr.fore.red = fore.red;
6109 					attr.fore.green = fore.green;
6110 					attr.fore.blue = fore.blue;
6111 					attr.back.red = back.red;
6112 					attr.back.green = back.green;
6113 					attr.back.blue = back.blue;
6114 					attr.underline = pcell->attr.underline;
6115 					attr.strikethrough = pcell->attr.strikethrough;
6116 
6117 					/* Store the cell string */
6118 					if (pcell->c == 0) {
6119 						g_string_append_c (string, ' ');
6120 						last_empty = string->len;
6121 						last_emptycol = col;
6122 					} else {
6123 						_vte_unistr_append_to_string (pcell->c, string);
6124 						last_nonempty = string->len;
6125 						last_nonemptycol = col;
6126 					}
6127 
6128 					/* If we added text to the string, record its
6129 					 * attributes, one per byte. */
6130 					if (attributes) {
6131 						vte_g_array_fill(attributes,
6132 								&attr, string->len);
6133 					}
6134 				}
6135 				/* If we're on the last line, and have just looked in
6136 				 * the last column, stop. */
6137 				if ((row == end_row) && (col >= end_col)) {
6138 					break;
6139 				}
6140 
6141 				col++;
6142 			}
6143 		}
6144 
6145 	       /* If the last thing we saw was a empty, and we stopped at the
6146 		* right edge of the selected area, trim the trailing spaces
6147 		* off of the line. */
6148 		if (!include_trailing_spaces && last_empty > last_nonempty) {
6149 
6150 			col = last_emptycol + 1;
6151 
6152 			if (row_data != NULL) {
6153 				while ((pcell = _vte_row_data_get (row_data, col))) {
6154 					col++;
6155 
6156 					if (pcell->attr.fragment)
6157 						continue;
6158 
6159 					if (pcell->c != 0)
6160 						break;
6161 				}
6162 			}
6163 			if (pcell == NULL) {
6164 				g_string_truncate(string, last_nonempty);
6165 				if (attributes)
6166 					g_array_set_size(attributes, string->len);
6167 				attr.column = last_nonemptycol;
6168 			}
6169 		}
6170 
6171 		/* Adjust column, in case we want to append a newline */
6172 		attr.column = MAX(terminal->pvt->column_count, attr.column + 1);
6173 
6174 		/* Add a newline in block mode. */
6175 		if (terminal->pvt->selection_block_mode) {
6176 			string = g_string_append_c(string, '\n');
6177 		}
6178 		/* Else, if the last visible column on this line was selected and
6179 		 * not soft-wrapped, append a newline. */
6180 		else if (is_selected(terminal, terminal->pvt->column_count, row, data)) {
6181 			/* If we didn't softwrap, add a newline. */
6182 			/* XXX need to clear row->soft_wrap on deletion! */
6183 			if (!vte_line_is_wrappable(terminal, row)) {
6184 				string = g_string_append_c(string, '\n');
6185 			}
6186 		}
6187 
6188 		/* Make sure that the attributes array is as long as the string. */
6189 		if (attributes) {
6190 			vte_g_array_fill (attributes, &attr, string->len);
6191 		}
6192 	}
6193 	/* Sanity check. */
6194 	g_assert(attributes == NULL || string->len == attributes->len);
6195 	return g_string_free(string, FALSE);
6196 }
6197 
6198 static char *
vte_terminal_get_text_maybe_wrapped(VteTerminal * terminal,gboolean wrap,VteSelectionFunc is_selected,gpointer data,GArray * attributes,gboolean include_trailing_spaces)6199 vte_terminal_get_text_maybe_wrapped(VteTerminal *terminal,
6200 				    gboolean wrap,
6201 				    VteSelectionFunc is_selected,
6202 				    gpointer data,
6203 				    GArray *attributes,
6204 				    gboolean include_trailing_spaces)
6205 {
6206 	long start_row, start_col, end_row, end_col;
6207 	start_row = terminal->pvt->screen->scroll_delta;
6208 	start_col = 0;
6209 	end_row = start_row + terminal->pvt->row_count - 1;
6210 	end_col = terminal->pvt->column_count - 1;
6211 	return vte_terminal_get_text_range_maybe_wrapped(terminal,
6212 							 start_row, start_col,
6213 							 end_row, end_col,
6214 							 wrap,
6215 							 is_selected,
6216 							 data,
6217 							 attributes,
6218 							 include_trailing_spaces);
6219 }
6220 
6221 /**
6222  * vte_terminal_get_text:
6223  * @terminal: a #VteTerminal
6224  * @is_selected: (scope call) (allow-none): a #VteSelectionFunc callback
6225  * @user_data: (closure): user data to be passed to the callback
6226  * @attributes: (out caller-allocates) (transfer full) (array) (element-type Vte.CharAttributes): location for storing text attributes
6227  *
6228  * Extracts a view of the visible part of the terminal.  If @is_selected is not
6229  * %NULL, characters will only be read if @is_selected returns %TRUE after being
6230  * passed the column and row, respectively.  A #VteCharAttributes structure
6231  * is added to @attributes for each byte added to the returned string detailing
6232  * the character's position, colors, and other characteristics.
6233  *
6234  * Returns: (transfer full): a newly allocated text string, or %NULL.
6235  */
6236 char *
vte_terminal_get_text(VteTerminal * terminal,VteSelectionFunc is_selected,gpointer user_data,GArray * attributes)6237 vte_terminal_get_text(VteTerminal *terminal,
6238 		      VteSelectionFunc is_selected,
6239 		      gpointer user_data,
6240 		      GArray *attributes)
6241 {
6242 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
6243 	return vte_terminal_get_text_maybe_wrapped(terminal,
6244 						   TRUE,
6245 						   is_selected,
6246 						   user_data,
6247 						   attributes,
6248 						   FALSE);
6249 }
6250 
6251 /**
6252  * vte_terminal_get_text_include_trailing_spaces:
6253  * @terminal: a #VteTerminal
6254  * @is_selected: (scope call) (allow-none): a #VteSelectionFunc callback
6255  * @user_data: (closure): user data to be passed to the callback
6256  * @attributes: (out caller-allocates) (transfer full) (array) (element-type Vte.CharAttributes): location for storing text attributes
6257  *
6258  * Extracts a view of the visible part of the terminal.  If @is_selected is not
6259  * %NULL, characters will only be read if @is_selected returns %TRUE after being
6260  * passed the column and row, respectively.  A #VteCharAttributes structure
6261  * is added to @attributes for each byte added to the returned string detailing
6262  * the character's position, colors, and other characteristics. This function
6263  * differs from vte_terminal_get_text() in that trailing spaces at the end of
6264  * lines are included.
6265  *
6266  * Returns: (transfer full): a newly allocated text string, or %NULL.
6267  */
6268 char *
vte_terminal_get_text_include_trailing_spaces(VteTerminal * terminal,VteSelectionFunc is_selected,gpointer user_data,GArray * attributes)6269 vte_terminal_get_text_include_trailing_spaces(VteTerminal *terminal,
6270 					      VteSelectionFunc is_selected,
6271 					      gpointer user_data,
6272 					      GArray *attributes)
6273 {
6274 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
6275 	return vte_terminal_get_text_maybe_wrapped(terminal,
6276 						   TRUE,
6277 						   is_selected,
6278 						   user_data,
6279 						   attributes,
6280 						   TRUE);
6281 }
6282 
6283 /**
6284  * vte_terminal_get_cursor_position:
6285  * @terminal: a #VteTerminal
6286  * @column: (out) (allow-none): a location to store the column, or %NULL
6287  * @row: (out) (allow-none): a location to store the row, or %NULL
6288  *
6289  * Reads the location of the insertion cursor and returns it.  The row
6290  * coordinate is absolute.
6291  */
6292 void
vte_terminal_get_cursor_position(VteTerminal * terminal,glong * column,glong * row)6293 vte_terminal_get_cursor_position(VteTerminal *terminal,
6294 				 glong *column, glong *row)
6295 {
6296 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
6297 	if (column) {
6298                 *column = terminal->pvt->cursor.col;
6299 	}
6300 	if (row) {
6301                 *row = terminal->pvt->cursor.row;
6302 	}
6303 }
6304 
6305 static GtkClipboard *
vte_terminal_clipboard_get(VteTerminal * terminal,GdkAtom board)6306 vte_terminal_clipboard_get(VteTerminal *terminal, GdkAtom board)
6307 {
6308 	GdkDisplay *display;
6309 	display = gtk_widget_get_display(&terminal->widget);
6310 	return gtk_clipboard_get_for_display(display, board);
6311 }
6312 
6313 /* Place the selected text onto the clipboard.  Do this asynchronously so that
6314  * we get notified when the selection we placed on the clipboard is replaced. */
6315 static void
vte_terminal_copy(VteTerminal * terminal,GdkAtom board)6316 vte_terminal_copy(VteTerminal *terminal, GdkAtom board)
6317 {
6318 	GtkClipboard *clipboard;
6319 	static GtkTargetEntry *targets = NULL;
6320 	static gint n_targets = 0;
6321 
6322 	clipboard = vte_terminal_clipboard_get(terminal, board);
6323 
6324 	/* Chuck old selected text and retrieve the newly-selected text. */
6325 	g_free(terminal->pvt->selection);
6326 	terminal->pvt->selection =
6327 		vte_terminal_get_text_range(terminal,
6328 					    terminal->pvt->selection_start.row,
6329 					    0,
6330 					    terminal->pvt->selection_end.row,
6331 					    terminal->pvt->column_count,
6332 					    vte_cell_is_selected,
6333 					    NULL,
6334 					    NULL);
6335 	terminal->pvt->has_selection = TRUE;
6336 
6337 	/* Place the text on the clipboard. */
6338 	if (terminal->pvt->selection != NULL) {
6339 		_vte_debug_print(VTE_DEBUG_SELECTION,
6340 				"Assuming ownership of selection.\n");
6341 		if (!targets) {
6342 			GtkTargetList *list;
6343 
6344 			list = gtk_target_list_new (NULL, 0);
6345 			gtk_target_list_add_text_targets (list, 0);
6346                         targets = gtk_target_table_new_from_list (list, &n_targets);
6347 			gtk_target_list_unref (list);
6348 		}
6349 
6350 		gtk_clipboard_set_with_owner(clipboard,
6351 					     targets,
6352 					     n_targets,
6353 					     vte_terminal_copy_cb,
6354 					     vte_terminal_clear_cb,
6355 					     G_OBJECT(terminal));
6356 		gtk_clipboard_set_can_store(clipboard, NULL, 0);
6357 	}
6358 }
6359 
6360 /* Paste from the given clipboard. */
6361 static void
vte_terminal_paste(VteTerminal * terminal,GdkAtom board)6362 vte_terminal_paste(VteTerminal *terminal, GdkAtom board)
6363 {
6364 	GtkClipboard *clipboard;
6365 
6366         if (!terminal->pvt->input_enabled)
6367                 return;
6368 
6369 	clipboard = vte_terminal_clipboard_get(terminal, board);
6370 	if (clipboard != NULL) {
6371 		_vte_debug_print(VTE_DEBUG_SELECTION,
6372 				"Requesting clipboard contents.\n");
6373 		gtk_clipboard_request_text(clipboard,
6374 					   vte_terminal_paste_cb,
6375 					   terminal);
6376 	}
6377 }
6378 
6379 static void
vte_terminal_invalidate_selection(VteTerminal * terminal)6380 vte_terminal_invalidate_selection (VteTerminal *terminal)
6381 {
6382 	_vte_invalidate_region (terminal,
6383 				terminal->pvt->selection_start.col,
6384 				terminal->pvt->selection_end.col,
6385 				terminal->pvt->selection_start.row,
6386 				terminal->pvt->selection_end.row,
6387 				terminal->pvt->selection_block_mode);
6388 }
6389 
6390 /* Confine coordinates into the visible area. Padding is alreday subtracted. */
6391 static void
vte_terminal_confine_coordinates(VteTerminal * terminal,long * xp,long * yp)6392 vte_terminal_confine_coordinates (VteTerminal *terminal, long *xp, long *yp)
6393 {
6394 	long x = *xp;
6395 	long y = *yp;
6396 
6397 	if (y < 0) {
6398 		y = 0;
6399 		if (!terminal->pvt->selection_block_mode)
6400 			x = 0;
6401 	} else if (y >= terminal->pvt->row_count * terminal->pvt->char_height) {
6402 		y = terminal->pvt->row_count * terminal->pvt->char_height - 1;
6403 		if (!terminal->pvt->selection_block_mode)
6404 			x = terminal->pvt->column_count * terminal->pvt->char_width - 1;
6405 	}
6406 	if (x < 0) {
6407 		x = 0;
6408 	} else if (x >= terminal->pvt->column_count * terminal->pvt->char_width) {
6409 		x = terminal->pvt->column_count * terminal->pvt->char_width - 1;
6410 	}
6411 
6412 	*xp = x;
6413 	*yp = y;
6414 }
6415 
6416 /* Start selection at the location of the event. */
6417 static void
vte_terminal_start_selection(VteTerminal * terminal,long x,long y,enum vte_selection_type selection_type)6418 vte_terminal_start_selection(VteTerminal *terminal, long x, long y,
6419 			     enum vte_selection_type selection_type)
6420 {
6421 	long delta;
6422 
6423 	if (terminal->pvt->selection_block_mode)
6424 		selection_type = selection_type_char;
6425 
6426 	/* Confine coordinates into the visible area. (#563024, #722635c7) */
6427 	vte_terminal_confine_coordinates(terminal, &x, &y);
6428 
6429 	/* Record that we have the selection, and where it started. */
6430 	delta = terminal->pvt->screen->scroll_delta;
6431 	terminal->pvt->has_selection = TRUE;
6432 	terminal->pvt->selection_last.x = x;
6433 	terminal->pvt->selection_last.y = y + (terminal->pvt->char_height * delta);
6434 
6435 	/* Decide whether or not to restart on the next drag. */
6436 	switch (selection_type) {
6437 	case selection_type_char:
6438 		/* Restart selection once we register a drag. */
6439 		terminal->pvt->selecting_restart = TRUE;
6440 		terminal->pvt->has_selection = FALSE;
6441 		terminal->pvt->selecting_had_delta = FALSE;
6442 
6443 		terminal->pvt->selection_origin = terminal->pvt->selection_last;
6444 		break;
6445 	case selection_type_word:
6446 	case selection_type_line:
6447 		/* Mark the newly-selected areas now. */
6448 		terminal->pvt->selecting_restart = FALSE;
6449 		terminal->pvt->has_selection = FALSE;
6450 		terminal->pvt->selecting_had_delta = FALSE;
6451 		break;
6452 	}
6453 
6454 	/* Record the selection type. */
6455 	terminal->pvt->selection_type = selection_type;
6456 	terminal->pvt->selecting = TRUE;
6457 	terminal->pvt->selecting_after_threshold = FALSE;
6458 
6459 	_vte_debug_print(VTE_DEBUG_SELECTION,
6460 			"Selection started at (%ld,%ld).\n",
6461 			terminal->pvt->selection_start.col,
6462 			terminal->pvt->selection_start.row);
6463 
6464         /* Take care of updating the display. */
6465         vte_terminal_extend_selection(terminal, x, y, FALSE, TRUE);
6466 
6467 	/* Temporarily stop caring about input from the child. */
6468 	_vte_terminal_disconnect_pty_read(terminal);
6469 }
6470 
6471 static gboolean
_vte_terminal_maybe_end_selection(VteTerminal * terminal)6472 _vte_terminal_maybe_end_selection (VteTerminal *terminal)
6473 {
6474 	if (terminal->pvt->selecting) {
6475 		/* Copy only if something was selected. */
6476 		if (terminal->pvt->has_selection &&
6477 		    !terminal->pvt->selecting_restart &&
6478 		    terminal->pvt->selecting_had_delta) {
6479 			vte_terminal_copy_primary(terminal);
6480 			vte_terminal_emit_selection_changed(terminal);
6481 		}
6482 		terminal->pvt->selecting = FALSE;
6483 
6484 		/* Reconnect to input from the child if we paused it. */
6485 		_vte_terminal_connect_pty_read(terminal);
6486 
6487 		return TRUE;
6488 	}
6489 
6490         if (terminal->pvt->selecting_after_threshold)
6491                 return TRUE;
6492 
6493         return FALSE;
6494 }
6495 
6496 static long
math_div(long a,long b)6497 math_div (long a, long b)
6498 {
6499 	if (G_LIKELY (a >= 0))
6500 		return a / b;
6501 	else
6502 		return (a / b) - 1;
6503 }
6504 
6505 /* Helper */
6506 static void
vte_terminal_extend_selection_expand(VteTerminal * terminal)6507 vte_terminal_extend_selection_expand (VteTerminal *terminal)
6508 {
6509 	long i, j;
6510 	VteScreen *screen;
6511 	const VteRowData *rowdata;
6512 	const VteCell *cell;
6513 	VteVisualPosition *sc, *ec;
6514 
6515 	if (terminal->pvt->selection_block_mode)
6516 		return;
6517 
6518 	screen = terminal->pvt->screen;
6519 	sc = &terminal->pvt->selection_start;
6520 	ec = &terminal->pvt->selection_end;
6521 
6522 	/* Extend the selection to handle end-of-line cases, word, and line
6523 	 * selection.  We do this here because calculating it once is cheaper
6524 	 * than recalculating for each cell as we render it. */
6525 
6526 	/* Handle end-of-line at the start-cell. */
6527 	rowdata = _vte_terminal_find_row_data(terminal, sc->row);
6528 	if (rowdata != NULL) {
6529 		/* Find the last non-empty character on the first line. */
6530 		for (i = _vte_row_data_length (rowdata); i > 0; i--) {
6531 			cell = _vte_row_data_get (rowdata, i - 1);
6532 			if (cell->attr.fragment || cell->c != 0)
6533 				break;
6534 		}
6535 	} else {
6536                 i = 0;
6537 	}
6538         if (sc->col > i) {
6539                 if (terminal->pvt->selection_type == selection_type_char) {
6540                         /* If the start point is neither over the used cells, nor over the first
6541                          * unused one, then move it to the next line. This way you can still start
6542                          * selecting at the newline character by clicking over the first unused cell.
6543                          * See bug 725909. */
6544                         sc->col = -1;
6545                         sc->row++;
6546                 } else if (terminal->pvt->selection_type == selection_type_word) {
6547                         sc->col = i;
6548                 }
6549         }
6550         sc->col = find_start_column (terminal, sc->col, sc->row);
6551 
6552 	/* Handle end-of-line at the end-cell. */
6553 	rowdata = _vte_terminal_find_row_data(terminal, ec->row);
6554 	if (rowdata != NULL) {
6555 		/* Find the last non-empty character on the last line. */
6556 		for (i = _vte_row_data_length (rowdata); i > 0; i--) {
6557 			cell = _vte_row_data_get (rowdata, i - 1);
6558 			if (cell->attr.fragment || cell->c != 0)
6559 				break;
6560 		}
6561 		/* If the end point is to its right, then extend the
6562 		 * endpoint to the beginning of the next row. */
6563 		if (ec->col >= i) {
6564 			ec->col = -1;
6565 			ec->row++;
6566 		}
6567 	} else {
6568 		/* Snap to the beginning of the next line, only if
6569 		 * selecting anything of this row. */
6570 		if (ec->col >= 0) {
6571 			ec->col = -1;
6572 			ec->row++;
6573 		}
6574 	}
6575 	ec->col = find_end_column (terminal, ec->col, ec->row);
6576 
6577 
6578 	/* Now extend again based on selection type. */
6579 	switch (terminal->pvt->selection_type) {
6580 	case selection_type_char:
6581 		/* Nothing more to do. */
6582 		break;
6583 	case selection_type_word:
6584 		/* Keep selecting to the left as long as the next character we
6585 		 * look at is of the same class as the current start point. */
6586 		i = sc->col;
6587 		j = sc->row;
6588 		while (_vte_ring_contains (screen->row_data, j)) {
6589 			/* Get the data for the row we're looking at. */
6590 			rowdata = _vte_ring_index(screen->row_data, j);
6591 			if (rowdata == NULL) {
6592 				break;
6593 			}
6594 			/* Back up. */
6595 			for (i = (j == sc->row) ?
6596 				 sc->col :
6597 				 terminal->pvt->column_count;
6598 			     i > 0;
6599 			     i--) {
6600 				if (vte_same_class(terminal,
6601 						   i - 1,
6602 						   j,
6603 						   i,
6604 						   j)) {
6605 					sc->col = i - 1;
6606 					sc->row = j;
6607 				} else {
6608 					break;
6609 				}
6610 			}
6611 			if (i > 0) {
6612 				/* We hit a stopping point, so stop. */
6613 				break;
6614 			} else {
6615 				if (vte_line_is_wrappable(terminal, j - 1) &&
6616 				    vte_same_class(terminal,
6617 						   terminal->pvt->column_count - 1,
6618 						   j - 1,
6619 						   0,
6620 						   j)) {
6621 					/* Move on to the previous line. */
6622 					j--;
6623 					sc->col = terminal->pvt->column_count - 1;
6624 					sc->row = j;
6625 				} else {
6626 					break;
6627 				}
6628 			}
6629 		}
6630 		/* Keep selecting to the right as long as the next character we
6631 		 * look at is of the same class as the current end point. */
6632 		i = ec->col;
6633 		j = ec->row;
6634 		while (_vte_ring_contains (screen->row_data, j)) {
6635 			/* Get the data for the row we're looking at. */
6636 			rowdata = _vte_ring_index(screen->row_data, j);
6637 			if (rowdata == NULL) {
6638 				break;
6639 			}
6640 			/* Move forward. */
6641 			for (i = (j == ec->row) ?
6642 				 ec->col :
6643 				 0;
6644 			     i < terminal->pvt->column_count - 1;
6645 			     i++) {
6646 				if (vte_same_class(terminal,
6647 						   i,
6648 						   j,
6649 						   i + 1,
6650 						   j)) {
6651 					ec->col = i + 1;
6652 					ec->row = j;
6653 				} else {
6654 					break;
6655 				}
6656 			}
6657 			if (i < terminal->pvt->column_count - 1) {
6658 				/* We hit a stopping point, so stop. */
6659 				break;
6660 			} else {
6661 				if (vte_line_is_wrappable(terminal, j) &&
6662 				    vte_same_class(terminal,
6663 						   terminal->pvt->column_count - 1,
6664 						   j,
6665 						   0,
6666 						   j + 1)) {
6667 					/* Move on to the next line. */
6668 					j++;
6669 					ec->col = 0;
6670 					ec->row = j;
6671 				} else {
6672 					break;
6673 				}
6674 			}
6675 		}
6676 		break;
6677 	case selection_type_line:
6678 		/* Extend the selection to the beginning of the start line. */
6679 		sc->col = 0;
6680 		/* Now back up as far as we can go. */
6681 		j = sc->row;
6682 		while (_vte_ring_contains (screen->row_data, j - 1) &&
6683 		       vte_line_is_wrappable(terminal, j - 1)) {
6684 			j--;
6685 			sc->row = j;
6686 		}
6687 		/* And move forward as far as we can go. */
6688                 if (ec->col < 0) {
6689                         /* If triple clicking on an unused area, ec already points
6690                          * to the beginning of the next line after the second click.
6691                          * Go back to the actual row we're at. See bug 725909. */
6692                         ec->row--;
6693                 }
6694 		j = ec->row;
6695 		while (_vte_ring_contains (screen->row_data, j) &&
6696 		       vte_line_is_wrappable(terminal, j)) {
6697 			j++;
6698 			ec->row = j;
6699 		}
6700 		/* Make sure we include all of the last line by extending
6701 		 * to the beginning of the next line. */
6702 		ec->row++;
6703 		ec->col = -1;
6704 		break;
6705 	}
6706 }
6707 
6708 /* Extend selection to include the given event coordinates. */
6709 static void
vte_terminal_extend_selection(VteTerminal * terminal,long x,long y,gboolean always_grow,gboolean force)6710 vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
6711 			      gboolean always_grow, gboolean force)
6712 {
6713 	VteScreen *screen;
6714 	int width, height;
6715 	long delta, residual;
6716 	struct selection_event_coords *origin, *last, *start, *end;
6717 	VteVisualPosition old_start, old_end, *sc, *ec, *so, *eo;
6718 	gboolean invalidate_selected = FALSE;
6719 	gboolean had_selection;
6720 
6721 	height = terminal->pvt->char_height;
6722 	width = terminal->pvt->char_width;
6723 
6724 	/* Confine coordinates into the visible area. (#563024, #722635c7) */
6725 	vte_terminal_confine_coordinates(terminal, &x, &y);
6726 
6727 	screen = terminal->pvt->screen;
6728 	old_start = terminal->pvt->selection_start;
6729 	old_end = terminal->pvt->selection_end;
6730 	so = &old_start;
6731 	eo = &old_end;
6732 
6733 	/* Convert the event coordinates to cell coordinates. */
6734 	delta = screen->scroll_delta;
6735 
6736 	/* If we're restarting on a drag, then mark this as the start of
6737 	 * the selected block. */
6738 	if (terminal->pvt->selecting_restart) {
6739 		vte_terminal_deselect_all(terminal);
6740 		invalidate_selected = TRUE;
6741 		_vte_debug_print(VTE_DEBUG_SELECTION,
6742 				"Selection delayed start at (%ld,%ld).\n",
6743 				terminal->pvt->selection_origin.x / width,
6744 				terminal->pvt->selection_origin.y / height);
6745 	}
6746 
6747 	/* Recognize that we've got a selected block. */
6748 	had_selection = terminal->pvt->has_selection;
6749 	terminal->pvt->has_selection = TRUE;
6750 	terminal->pvt->selecting_had_delta = TRUE;
6751 	terminal->pvt->selecting_restart = FALSE;
6752 
6753 	/* If we're not in always-grow mode, update the last location of
6754 	 * the selection. */
6755 	last = &terminal->pvt->selection_last;
6756 
6757 	/* Map the origin and last selected points to a start and end. */
6758 	origin = &terminal->pvt->selection_origin;
6759 	if (terminal->pvt->selection_block_mode) {
6760 		last->x = x;
6761 		last->y = y + height * delta;
6762 
6763 		/* We don't support always_grow in block mode */
6764 		if (always_grow)
6765 			vte_terminal_invalidate_selection (terminal);
6766 
6767 		if (origin->y <= last->y) {
6768 			/* The origin point is "before" the last point. */
6769 			start = origin;
6770 			end = last;
6771 		} else {
6772 			/* The last point is "before" the origin point. */
6773 			start = last;
6774 			end = origin;
6775 		}
6776 	} else {
6777 		if (!always_grow) {
6778 			last->x = x;
6779 			last->y = y + height * delta;
6780 		}
6781 
6782 		if ((origin->y / height < last->y / height) ||
6783 		    ((origin->y / height == last->y / height) &&
6784 		     (origin->x / width < last->x / width ))) {
6785 			/* The origin point is "before" the last point. */
6786 			start = origin;
6787 			end = last;
6788 		} else {
6789 			/* The last point is "before" the origin point. */
6790 			start = last;
6791 			end = origin;
6792 		}
6793 
6794 		/* Extend the selection by moving whichever end of the selection is
6795 		 * closer to the new point. */
6796 		if (always_grow) {
6797 			/* New endpoint is before existing selection. */
6798 			if ((y / height < ((start->y / height) - delta)) ||
6799 			    ((y / height == ((start->y / height) - delta)) &&
6800 			     (x / width < start->x / width))) {
6801 				start->x = x;
6802 				start->y = y + height * delta;
6803 			} else {
6804 				/* New endpoint is after existing selection. */
6805 				end->x = x;
6806 				end->y = y + height * delta;
6807 			}
6808 		}
6809 	}
6810 
6811 #if 0
6812 	_vte_debug_print(VTE_DEBUG_SELECTION,
6813 			"Selection is (%ld,%ld) to (%ld,%ld).\n",
6814 			start->x, start->y, end->x, end->y);
6815 #endif
6816 
6817 	/* Recalculate the selection area in terms of cell positions. */
6818 
6819 	sc = &terminal->pvt->selection_start;
6820 	ec = &terminal->pvt->selection_end;
6821 
6822 	sc->row = MAX (0, start->y / height);
6823 	ec->row = MAX (0, end->y   / height);
6824 
6825 	/* Sort x using row cell coordinates */
6826 	if ((terminal->pvt->selection_block_mode || sc->row == ec->row) && (start->x > end->x)) {
6827 		struct selection_event_coords *tmp;
6828 		tmp = start;
6829 		start = end;
6830 		end = tmp;
6831 	}
6832 
6833 	/* We want to be more lenient on the user with their column selection.
6834 	 * We round to the closest logical position (positions are located between
6835 	 * cells).  But we don't want to fully round.  So we divide the cell
6836 	 * width into three parts.  The side parts round to their nearest
6837 	 * position.  The middle part is always inclusive in the selection.
6838 	 *
6839 	 * math_div and no MAX, to allow selecting no cells in the line,
6840 	 * ie. ec->col = -1, which is essentially equal to copying the
6841 	 * newline from previous line but no chars from current line. */
6842 	residual = (width + 1) / 3;
6843 	sc->col = math_div (start->x + residual, width);
6844 	ec->col = math_div (end->x - residual, width);
6845 
6846 
6847 	vte_terminal_extend_selection_expand (terminal);
6848 
6849 	if (!invalidate_selected && !force &&
6850 	    0 == memcmp (sc, so, sizeof (*sc)) &&
6851 	    0 == memcmp (ec, eo, sizeof (*ec)))
6852 		/* No change */
6853 		return;
6854 
6855 	/* Invalidate */
6856 
6857 	if (had_selection) {
6858 
6859 		if (terminal->pvt->selection_block_mode) {
6860 			/* Update the selection area diff in block mode. */
6861 
6862 			/* The top band */
6863 			_vte_invalidate_region (terminal,
6864 						MIN(sc->col, so->col),
6865 						MAX(ec->col, eo->col),
6866 						MIN(sc->row, so->row),
6867 						MAX(sc->row, so->row) - 1,
6868 						TRUE);
6869 			/* The bottom band */
6870 			_vte_invalidate_region (terminal,
6871 						MIN(sc->col, so->col),
6872 						MAX(ec->col, eo->col),
6873 						MIN(ec->row, eo->row) + 1,
6874 						MAX(ec->row, eo->row),
6875 						TRUE);
6876 			/* The left band */
6877 			_vte_invalidate_region (terminal,
6878 						MIN(sc->col, so->col),
6879 						MAX(sc->col, so->col) - 1 + (VTE_TAB_WIDTH_MAX - 1),
6880 						MIN(sc->row, so->row),
6881 						MAX(ec->row, eo->row),
6882 						TRUE);
6883 			/* The right band */
6884 			_vte_invalidate_region (terminal,
6885 						MIN(ec->col, eo->col) + 1,
6886 						MAX(ec->col, eo->col) + (VTE_TAB_WIDTH_MAX - 1),
6887 						MIN(sc->row, so->row),
6888 						MAX(ec->row, eo->row),
6889 						TRUE);
6890 		} else {
6891 			/* Update the selection area diff in non-block mode. */
6892 
6893 			/* The before band */
6894 			if (sc->row < so->row)
6895 				_vte_invalidate_region (terminal,
6896 							sc->col, so->col - 1,
6897 							sc->row, so->row,
6898 							FALSE);
6899 			else if (sc->row > so->row)
6900 				_vte_invalidate_region (terminal,
6901 							so->col, sc->col - 1,
6902 							so->row, sc->row,
6903 							FALSE);
6904 			else
6905 				_vte_invalidate_region (terminal,
6906 							MIN(sc->col, so->col), MAX(sc->col, so->col) - 1,
6907 							sc->row, sc->row,
6908 							TRUE);
6909 
6910 			/* The after band */
6911 			if (ec->row < eo->row)
6912 				_vte_invalidate_region (terminal,
6913 							ec->col + 1, eo->col,
6914 							ec->row, eo->row,
6915 							FALSE);
6916 			else if (ec->row > eo->row)
6917 				_vte_invalidate_region (terminal,
6918 							eo->col + 1, ec->col,
6919 							eo->row, ec->row,
6920 							FALSE);
6921 			else
6922 				_vte_invalidate_region (terminal,
6923 							MIN(ec->col, eo->col) + 1, MAX(ec->col, eo->col),
6924 							ec->row, ec->row,
6925 							TRUE);
6926 		}
6927 	}
6928 
6929 	if (invalidate_selected || !had_selection) {
6930 		_vte_debug_print(VTE_DEBUG_SELECTION, "Invalidating selection.");
6931 		vte_terminal_invalidate_selection (terminal);
6932 	}
6933 
6934 	_vte_debug_print(VTE_DEBUG_SELECTION,
6935 			"Selection changed to "
6936 			"(%ld,%ld) to (%ld,%ld).\n",
6937 			sc->col, sc->row, ec->col, ec->row);
6938 }
6939 
6940 /**
6941  * vte_terminal_select_all:
6942  * @terminal: a #VteTerminal
6943  *
6944  * Selects all text within the terminal (including the scrollback buffer).
6945  */
6946 void
vte_terminal_select_all(VteTerminal * terminal)6947 vte_terminal_select_all (VteTerminal *terminal)
6948 {
6949 	g_return_if_fail (VTE_IS_TERMINAL (terminal));
6950 
6951 	vte_terminal_deselect_all (terminal);
6952 
6953 	terminal->pvt->has_selection = TRUE;
6954 	terminal->pvt->selecting_had_delta = TRUE;
6955 	terminal->pvt->selecting_restart = FALSE;
6956 
6957 	terminal->pvt->selection_start.row = _vte_ring_delta (terminal->pvt->screen->row_data);
6958 	terminal->pvt->selection_start.col = 0;
6959 	terminal->pvt->selection_end.row = _vte_ring_next (terminal->pvt->screen->row_data);
6960 	terminal->pvt->selection_end.col = -1;
6961 
6962 	_vte_debug_print(VTE_DEBUG_SELECTION, "Selecting *all* text.\n");
6963 
6964 	vte_terminal_copy_primary(terminal);
6965 	vte_terminal_emit_selection_changed (terminal);
6966 	_vte_invalidate_all (terminal);
6967 }
6968 
6969 /**
6970  * vte_terminal_unselect_all:
6971  * @terminal: a #VteTerminal
6972  *
6973  * Clears the current selection.
6974  */
6975 void
vte_terminal_unselect_all(VteTerminal * terminal)6976 vte_terminal_unselect_all(VteTerminal *terminal)
6977 {
6978 	g_return_if_fail (VTE_IS_TERMINAL (terminal));
6979 
6980 	_vte_debug_print(VTE_DEBUG_SELECTION, "Clearing selection.\n");
6981 
6982 	vte_terminal_deselect_all (terminal);
6983 }
6984 
6985 /* Autoscroll a bit. */
6986 static gboolean
vte_terminal_autoscroll(VteTerminal * terminal)6987 vte_terminal_autoscroll(VteTerminal *terminal)
6988 {
6989 	gboolean extend = FALSE;
6990 	long x, y, xmax, ymax;
6991 	glong adj;
6992 
6993 	/* Provide an immediate effect for mouse wigglers. */
6994 	if (terminal->pvt->mouse_last_y < 0) {
6995 		if (terminal->pvt->vadjustment) {
6996 			/* Try to scroll up by one line. */
6997 			adj = terminal->pvt->screen->scroll_delta - 1;
6998 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, adj);
6999 			extend = TRUE;
7000 		}
7001 		_vte_debug_print(VTE_DEBUG_EVENTS, "Autoscrolling down.\n");
7002 	}
7003 	if (terminal->pvt->mouse_last_y >=
7004 	    terminal->pvt->row_count * terminal->pvt->char_height) {
7005 		if (terminal->pvt->vadjustment) {
7006 			/* Try to scroll up by one line. */
7007 			adj = terminal->pvt->screen->scroll_delta + 1;
7008 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, adj);
7009 			extend = TRUE;
7010 		}
7011 		_vte_debug_print(VTE_DEBUG_EVENTS, "Autoscrolling up.\n");
7012 	}
7013 	if (extend) {
7014 		/* Don't select off-screen areas.  That just confuses people. */
7015 		xmax = terminal->pvt->column_count * terminal->pvt->char_width;
7016 		ymax = terminal->pvt->row_count * terminal->pvt->char_height;
7017 
7018 		x = CLAMP(terminal->pvt->mouse_last_x, 0, xmax);
7019 		y = CLAMP(terminal->pvt->mouse_last_y, 0, ymax);
7020 		/* If we clamped the Y, mess with the X to get the entire
7021 		 * lines. */
7022 		if (terminal->pvt->mouse_last_y < 0 && !terminal->pvt->selection_block_mode) {
7023 			x = 0;
7024 		}
7025 		if (terminal->pvt->mouse_last_y >= ymax && !terminal->pvt->selection_block_mode) {
7026 			x = terminal->pvt->column_count * terminal->pvt->char_width;
7027 		}
7028 		/* Extend selection to cover the newly-scrolled area. */
7029 		vte_terminal_extend_selection(terminal, x, y, FALSE, TRUE);
7030 	} else {
7031 		/* Stop autoscrolling. */
7032 		terminal->pvt->mouse_autoscroll_tag = 0;
7033 	}
7034 	return (terminal->pvt->mouse_autoscroll_tag != 0);
7035 }
7036 
7037 /* Start autoscroll. */
7038 static void
vte_terminal_start_autoscroll(VteTerminal * terminal)7039 vte_terminal_start_autoscroll(VteTerminal *terminal)
7040 {
7041 	if (terminal->pvt->mouse_autoscroll_tag == 0) {
7042 		terminal->pvt->mouse_autoscroll_tag =
7043 			g_timeout_add_full(G_PRIORITY_LOW,
7044 					   666 / terminal->pvt->row_count,
7045 					   (GSourceFunc)vte_terminal_autoscroll,
7046 					   terminal,
7047 					   NULL);
7048 	}
7049 }
7050 
7051 /* Stop autoscroll. */
7052 static void
vte_terminal_stop_autoscroll(VteTerminal * terminal)7053 vte_terminal_stop_autoscroll(VteTerminal *terminal)
7054 {
7055 	if (terminal->pvt->mouse_autoscroll_tag != 0) {
7056 		g_source_remove(terminal->pvt->mouse_autoscroll_tag);
7057 		terminal->pvt->mouse_autoscroll_tag = 0;
7058 	}
7059 }
7060 
7061 /* Read and handle a motion event. */
7062 static gboolean
vte_terminal_motion_notify(GtkWidget * widget,GdkEventMotion * event)7063 vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event)
7064 {
7065 	VteTerminal *terminal = VTE_TERMINAL(widget);
7066 	int width, height;
7067 	long x, y;
7068 	gboolean handled = FALSE;
7069 
7070 	/* check to see if it matters */
7071         if (G_UNLIKELY(!gtk_widget_get_realized(&terminal->widget)))
7072                 return FALSE;
7073 
7074 	x = event->x - terminal->pvt->padding.left;
7075 	y = event->y - terminal->pvt->padding.top;
7076 	width = terminal->pvt->char_width;
7077 	height = terminal->pvt->char_height;
7078 
7079 	_vte_debug_print(VTE_DEBUG_EVENTS,
7080 			"Motion notify (%ld,%ld) [%ld, %ld].\n",
7081 			x, y,
7082 			x / width, y / height + terminal->pvt->screen->scroll_delta);
7083 
7084 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
7085 
7086         if (terminal->pvt->mouse_pressed_buttons != 0) {
7087 		vte_terminal_match_hilite_hide (terminal);
7088 	} else {
7089 		/* Hilite any matches. */
7090 		vte_terminal_match_hilite(terminal, x, y);
7091 		/* Show the cursor. */
7092 		_vte_terminal_set_pointer_visible(terminal, TRUE);
7093 	}
7094 
7095 	switch (event->type) {
7096 	case GDK_MOTION_NOTIFY:
7097 		if (terminal->pvt->selecting_after_threshold) {
7098 			if (!gtk_drag_check_threshold (widget,
7099 						       terminal->pvt->mouse_last_x,
7100 						       terminal->pvt->mouse_last_y,
7101 						       x, y))
7102 				return TRUE;
7103 
7104 			vte_terminal_start_selection(terminal,
7105 						     terminal->pvt->mouse_last_x,
7106 						     terminal->pvt->mouse_last_y,
7107 						     selection_type_char);
7108 		}
7109 
7110 		if (terminal->pvt->selecting &&
7111                     (terminal->pvt->mouse_handled_buttons & 1) != 0) {
7112 			_vte_debug_print(VTE_DEBUG_EVENTS, "Mousing drag 1.\n");
7113 			vte_terminal_extend_selection(terminal,
7114 						      x, y, FALSE, FALSE);
7115 
7116 			/* Start scrolling if we need to. */
7117 			if (event->y < terminal->pvt->padding.top ||
7118 			    event->y >= terminal->pvt->row_count * height +
7119                                         terminal->pvt->padding.top)
7120 			{
7121 				/* Give mouse wigglers something. */
7122 				vte_terminal_autoscroll(terminal);
7123 				/* Start a timed autoscroll if we're not doing it
7124 				 * already. */
7125 				vte_terminal_start_autoscroll(terminal);
7126 			}
7127 
7128 			handled = TRUE;
7129 		}
7130 
7131 		if (!handled && terminal->pvt->input_enabled)
7132 			vte_terminal_maybe_send_mouse_drag(terminal, event);
7133 		break;
7134 	default:
7135 		break;
7136 	}
7137 
7138 	/* Save the pointer coordinates for later use. */
7139 	terminal->pvt->mouse_last_x = x;
7140 	terminal->pvt->mouse_last_y = y;
7141 
7142 	return handled;
7143 }
7144 
7145 /* Read and handle a pointing device buttonpress event. */
7146 static gint
vte_terminal_button_press(GtkWidget * widget,GdkEventButton * event)7147 vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
7148 {
7149 	VteTerminal *terminal;
7150 	long height, width, delta;
7151 	gboolean handled = FALSE;
7152 	gboolean start_selecting = FALSE, extend_selecting = FALSE;
7153 	long cellx, celly;
7154 	long x,y;
7155 
7156 	terminal = VTE_TERMINAL(widget);
7157 
7158 	x = event->x - terminal->pvt->padding.left;
7159 	y = event->y - terminal->pvt->padding.top;
7160 
7161 	height = terminal->pvt->char_height;
7162 	width = terminal->pvt->char_width;
7163 	delta = terminal->pvt->screen->scroll_delta;
7164 
7165 	vte_terminal_match_hilite(terminal, x, y);
7166 
7167 	_vte_terminal_set_pointer_visible(terminal, TRUE);
7168 
7169 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
7170 
7171 	/* Convert the event coordinates to cell coordinates. */
7172 	cellx = x / width;
7173 	celly = y / height + delta;
7174 
7175 	switch (event->type) {
7176 	case GDK_BUTTON_PRESS:
7177 		_vte_debug_print(VTE_DEBUG_EVENTS,
7178 				"Button %d single-click at (%ld,%ld)\n",
7179 				event->button,
7180 				x, y + terminal->pvt->char_height * delta);
7181 		/* Handle this event ourselves. */
7182 		switch (event->button) {
7183 		case 1:
7184 			_vte_debug_print(VTE_DEBUG_EVENTS,
7185 					"Handling click ourselves.\n");
7186 			/* Grab focus. */
7187 			if (! gtk_widget_has_focus (widget)) {
7188 				gtk_widget_grab_focus(widget);
7189 			}
7190 
7191 			/* If we're in event mode, and the user held down the
7192 			 * shift key, we start selecting. */
7193 			if (terminal->pvt->mouse_tracking_mode) {
7194 				if (terminal->pvt->modifiers & GDK_SHIFT_MASK) {
7195 					start_selecting = TRUE;
7196 				}
7197 			} else {
7198 				/* If the user hit shift, then extend the
7199 				 * selection instead. */
7200 				if ((terminal->pvt->modifiers & GDK_SHIFT_MASK) &&
7201 				    (terminal->pvt->has_selection ||
7202 				     terminal->pvt->selecting_restart) &&
7203 				    !vte_cell_is_selected(terminal,
7204 							  cellx,
7205 							  celly,
7206 							  NULL)) {
7207 					extend_selecting = TRUE;
7208 				} else {
7209 					start_selecting = TRUE;
7210 				}
7211 			}
7212 			if (start_selecting) {
7213 				vte_terminal_deselect_all(terminal);
7214 				terminal->pvt->selecting_after_threshold = TRUE;
7215                                 terminal->pvt->selection_block_mode = !!(terminal->pvt->modifiers & GDK_CONTROL_MASK);
7216 				handled = TRUE;
7217 			}
7218 			if (extend_selecting) {
7219 				vte_terminal_extend_selection(terminal,
7220 							      x, y,
7221 							      !terminal->pvt->selecting_restart, TRUE);
7222 				/* The whole selection code needs to be
7223 				 * rewritten.  For now, put this here to
7224 				 * fix bug 614658 */
7225 				terminal->pvt->selecting = TRUE;
7226 				handled = TRUE;
7227 			}
7228 			break;
7229 		/* Paste if the user pressed shift or we're not sending events
7230 		 * to the app. */
7231 		case 2:
7232 			if ((terminal->pvt->modifiers & GDK_SHIFT_MASK) ||
7233 			    !terminal->pvt->mouse_tracking_mode) {
7234                                 gboolean do_paste;
7235 
7236                                 g_object_get (gtk_widget_get_settings(widget),
7237                                               "gtk-enable-primary-paste",
7238                                               &do_paste, NULL);
7239                                 if (do_paste)
7240                                         vte_terminal_paste_primary(terminal);
7241 				handled = do_paste;
7242 			}
7243 			break;
7244 		case 3:
7245 		default:
7246 			break;
7247 		}
7248                 if (event->button >= 1 && event->button <= 3) {
7249                         if (handled)
7250                                 terminal->pvt->mouse_handled_buttons |= (1 << (event->button - 1));
7251                         else
7252                                 terminal->pvt->mouse_handled_buttons &= ~(1 << (event->button - 1));
7253                 }
7254 		/* If we haven't done anything yet, try sending the mouse
7255 		 * event to the app. */
7256 		if (handled == FALSE) {
7257 			handled = vte_terminal_maybe_send_mouse_button(terminal, event);
7258 		}
7259 		break;
7260 	case GDK_2BUTTON_PRESS:
7261 		_vte_debug_print(VTE_DEBUG_EVENTS,
7262 				"Button %d double-click at (%ld,%ld)\n",
7263 				event->button,
7264 				x, y + (terminal->pvt->char_height * delta));
7265 		switch (event->button) {
7266 		case 1:
7267 			if (terminal->pvt->selecting_after_threshold) {
7268 				vte_terminal_start_selection(terminal,
7269 							     x, y,
7270 							     selection_type_char);
7271 				handled = TRUE;
7272 			}
7273                         if ((terminal->pvt->mouse_handled_buttons & 1) != 0) {
7274 				vte_terminal_start_selection(terminal,
7275 							     x, y,
7276 							     selection_type_word);
7277 				handled = TRUE;
7278 			}
7279 			break;
7280 		case 2:
7281 		case 3:
7282 		default:
7283 			break;
7284 		}
7285 		break;
7286 	case GDK_3BUTTON_PRESS:
7287 		_vte_debug_print(VTE_DEBUG_EVENTS,
7288 				"Button %d triple-click at (%ld,%ld).\n",
7289 				event->button,
7290 				x, y + (terminal->pvt->char_height * delta));
7291 		switch (event->button) {
7292 		case 1:
7293                         if ((terminal->pvt->mouse_handled_buttons & 1) != 0) {
7294 				vte_terminal_start_selection(terminal,
7295 							     x, y,
7296 							     selection_type_line);
7297 				handled = TRUE;
7298 			}
7299 			break;
7300 		case 2:
7301 		case 3:
7302 		default:
7303 			break;
7304 		}
7305 	default:
7306 		break;
7307 	}
7308 
7309 	/* Save the pointer state for later use. */
7310         if (event->button >= 1 && event->button <= 3)
7311                 terminal->pvt->mouse_pressed_buttons |= (1 << (event->button - 1));
7312 	terminal->pvt->mouse_last_x = x;
7313 	terminal->pvt->mouse_last_y = y;
7314 
7315 	return handled;
7316 }
7317 
7318 /* Read and handle a pointing device buttonrelease event. */
7319 static gint
vte_terminal_button_release(GtkWidget * widget,GdkEventButton * event)7320 vte_terminal_button_release(GtkWidget *widget, GdkEventButton *event)
7321 {
7322 	VteTerminal *terminal;
7323 	gboolean handled = FALSE;
7324 	int x, y;
7325 
7326 	terminal = VTE_TERMINAL(widget);
7327 
7328 	x = event->x - terminal->pvt->padding.left;
7329 	y = event->y - terminal->pvt->padding.top;
7330 
7331 	vte_terminal_match_hilite(terminal, x, y);
7332 
7333 	_vte_terminal_set_pointer_visible(terminal, TRUE);
7334 
7335 	vte_terminal_stop_autoscroll(terminal);
7336 
7337 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
7338 
7339 	switch (event->type) {
7340 	case GDK_BUTTON_RELEASE:
7341 		_vte_debug_print(VTE_DEBUG_EVENTS,
7342 				"Button %d released at (%d,%d).\n",
7343 				event->button, x, y);
7344 		switch (event->button) {
7345 		case 1:
7346                         if ((terminal->pvt->mouse_handled_buttons & 1) != 0)
7347                                 handled = _vte_terminal_maybe_end_selection (terminal);
7348 			break;
7349 		case 2:
7350                         handled = (terminal->pvt->mouse_handled_buttons & 2) != 0;
7351                         terminal->pvt->mouse_handled_buttons &= ~2;
7352 			break;
7353 		case 3:
7354 		default:
7355 			break;
7356 		}
7357 		if (!handled && terminal->pvt->input_enabled) {
7358 			handled = vte_terminal_maybe_send_mouse_button(terminal, event);
7359 		}
7360 		break;
7361 	default:
7362 		break;
7363 	}
7364 
7365 	/* Save the pointer state for later use. */
7366         if (event->button >= 1 && event->button <= 3)
7367                 terminal->pvt->mouse_pressed_buttons &= ~(1 << (event->button - 1));
7368 	terminal->pvt->mouse_last_x = x;
7369 	terminal->pvt->mouse_last_y = y;
7370 	terminal->pvt->selecting_after_threshold = FALSE;
7371 
7372 	return handled;
7373 }
7374 
7375 /* Handle receiving or losing focus. */
7376 static gboolean
vte_terminal_focus_in(GtkWidget * widget,GdkEventFocus * event)7377 vte_terminal_focus_in(GtkWidget *widget, GdkEventFocus *event)
7378 {
7379 	VteTerminal *terminal;
7380 
7381 	_vte_debug_print(VTE_DEBUG_EVENTS, "Focus in.\n");
7382 
7383 	terminal = VTE_TERMINAL(widget);
7384 	gtk_widget_grab_focus (widget);
7385 
7386 	/* Read the keyboard modifiers, though they're probably garbage. */
7387 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
7388 
7389 	/* We only have an IM context when we're realized, and there's not much
7390 	 * point to painting the cursor if we don't have a window. */
7391 	if (gtk_widget_get_realized (widget)) {
7392 		terminal->pvt->cursor_blink_state = TRUE;
7393 		terminal->pvt->has_focus = TRUE;
7394 
7395 		_vte_check_cursor_blink (terminal);
7396 
7397 		gtk_im_context_focus_in(terminal->pvt->im_context);
7398 		_vte_invalidate_cursor_once(terminal, FALSE);
7399 		_vte_terminal_set_pointer_visible(terminal, TRUE);
7400 	}
7401 
7402 	return FALSE;
7403 }
7404 
7405 static gboolean
vte_terminal_focus_out(GtkWidget * widget,GdkEventFocus * event)7406 vte_terminal_focus_out(GtkWidget *widget, GdkEventFocus *event)
7407 {
7408 	VteTerminal *terminal;
7409 	_vte_debug_print(VTE_DEBUG_EVENTS, "Focus out.\n");
7410 	terminal = VTE_TERMINAL(widget);
7411 	/* Read the keyboard modifiers, though they're probably garbage. */
7412 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
7413 	/* We only have an IM context when we're realized, and there's not much
7414 	 * point to painting ourselves if we don't have a window. */
7415 	if (gtk_widget_get_realized (widget)) {
7416 		_vte_terminal_maybe_end_selection (terminal);
7417 
7418 		gtk_im_context_focus_out(terminal->pvt->im_context);
7419 		_vte_invalidate_cursor_once(terminal, FALSE);
7420 
7421 		/* XXX Do we want to hide the match just because the terminal
7422 		 * lost keyboard focus, but the pointer *is* still within our
7423 		 * area top? */
7424 		vte_terminal_match_hilite_hide (terminal);
7425 		/* Mark the cursor as invisible to disable hilite updating */
7426 		terminal->pvt->mouse_cursor_visible = FALSE;
7427                 terminal->pvt->mouse_pressed_buttons = 0;
7428                 terminal->pvt->mouse_handled_buttons = 0;
7429 	}
7430 
7431 	terminal->pvt->has_focus = FALSE;
7432 	_vte_check_cursor_blink (terminal);
7433 
7434 	return FALSE;
7435 }
7436 
7437 static gboolean
vte_terminal_enter(GtkWidget * widget,GdkEventCrossing * event)7438 vte_terminal_enter(GtkWidget *widget, GdkEventCrossing *event)
7439 {
7440 	gboolean ret = FALSE;
7441 	_vte_debug_print(VTE_DEBUG_EVENTS, "Enter.\n");
7442 	if (GTK_WIDGET_CLASS (vte_terminal_parent_class)->enter_notify_event) {
7443 		ret = GTK_WIDGET_CLASS (vte_terminal_parent_class)->enter_notify_event (widget, event);
7444 	}
7445 	if (gtk_widget_get_realized (widget)) {
7446 		VteTerminal *terminal = VTE_TERMINAL (widget);
7447 		/* Hilite any matches. */
7448 		vte_terminal_match_hilite_show(terminal,
7449 					       event->x - terminal->pvt->padding.left,
7450 					       event->y - terminal->pvt->padding.top);
7451 	}
7452 	return ret;
7453 }
7454 static gboolean
vte_terminal_leave(GtkWidget * widget,GdkEventCrossing * event)7455 vte_terminal_leave(GtkWidget *widget, GdkEventCrossing *event)
7456 {
7457 	gboolean ret = FALSE;
7458 	_vte_debug_print(VTE_DEBUG_EVENTS, "Leave.\n");
7459 	if (GTK_WIDGET_CLASS (vte_terminal_parent_class)->leave_notify_event) {
7460 		ret = GTK_WIDGET_CLASS (vte_terminal_parent_class)->leave_notify_event (widget, event);
7461 	}
7462 	if (gtk_widget_get_realized (widget)) {
7463 		VteTerminal *terminal = VTE_TERMINAL (widget);
7464 		vte_terminal_match_hilite_hide (terminal);
7465 		/* Mark the cursor as invisible to disable hilite updating,
7466 		 * whilst the cursor is absent (otherwise we copy the entire
7467 		 * buffer after each update for nothing...)
7468 		 */
7469 		terminal->pvt->mouse_cursor_visible = FALSE;
7470 	}
7471 	return ret;
7472 }
7473 
7474 static G_GNUC_UNUSED inline const char *
visibility_state_str(GdkVisibilityState state)7475 visibility_state_str(GdkVisibilityState state)
7476 {
7477 	switch(state){
7478 		case GDK_VISIBILITY_FULLY_OBSCURED:
7479 			return "fully-obscured";
7480 		case GDK_VISIBILITY_UNOBSCURED:
7481 			return "unobscured";
7482 		default:
7483 			return "partial";
7484 	}
7485 }
7486 
7487 static void
vte_terminal_set_visibility(VteTerminal * terminal,GdkVisibilityState state)7488 vte_terminal_set_visibility (VteTerminal *terminal, GdkVisibilityState state)
7489 {
7490 	_vte_debug_print(VTE_DEBUG_MISC, "change visibility: %s -> %s.\n",
7491 			visibility_state_str(terminal->pvt->visibility_state),
7492 			visibility_state_str(state));
7493 
7494 	if (state == terminal->pvt->visibility_state) {
7495 		return;
7496 	}
7497 
7498 	/* fully obscured to visible switch, force the fast path */
7499 	if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
7500 		/* set invalidated_all false, since we didn't really mean it
7501 		 * when we set it to TRUE when becoming obscured */
7502 		terminal->pvt->invalidated_all = FALSE;
7503 
7504 		/* if all unobscured now, invalidate all, otherwise, wait
7505 		 * for the expose event */
7506 		if (state == GDK_VISIBILITY_UNOBSCURED) {
7507 			_vte_invalidate_all (terminal);
7508 		}
7509 	}
7510 
7511 	terminal->pvt->visibility_state = state;
7512 
7513 	/* no longer visible, stop processing display updates */
7514 	if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
7515 		remove_update_timeout (terminal);
7516 		/* if fully obscured, just act like we have invalidated all,
7517 		 * so no updates are accumulated. */
7518 		terminal->pvt->invalidated_all = TRUE;
7519 	}
7520 }
7521 
7522 static gboolean
vte_terminal_visibility_notify(GtkWidget * widget,GdkEventVisibility * event)7523 vte_terminal_visibility_notify(GtkWidget *widget, GdkEventVisibility *event)
7524 {
7525 	VteTerminal *terminal;
7526 	terminal = VTE_TERMINAL(widget);
7527 
7528 	_vte_debug_print(VTE_DEBUG_EVENTS, "Visibility (%s -> %s).\n",
7529 			visibility_state_str(terminal->pvt->visibility_state),
7530 			visibility_state_str(event->state));
7531 	vte_terminal_set_visibility(terminal, event->state);
7532 
7533 	return FALSE;
7534 }
7535 
7536 /* Apply the changed metrics, and queue a resize if need be. */
7537 static void
vte_terminal_apply_metrics(VteTerminal * terminal,gint width,gint height,gint ascent,gint descent)7538 vte_terminal_apply_metrics(VteTerminal *terminal,
7539 			   gint width, gint height, gint ascent, gint descent)
7540 {
7541 	gboolean resize = FALSE, cresize = FALSE;
7542 	gint line_thickness;
7543 
7544 	/* Sanity check for broken font changes. */
7545 	width = MAX(width, 1);
7546 	height = MAX(height, 2);
7547 	ascent = MAX(ascent, 1);
7548 	descent = MAX(descent, 1);
7549 
7550 	/* Change settings, and keep track of when we've changed anything. */
7551 	if (width != terminal->pvt->char_width) {
7552 		resize = cresize = TRUE;
7553 		terminal->pvt->char_width = width;
7554 	}
7555 	if (height != terminal->pvt->char_height) {
7556 		resize = cresize = TRUE;
7557 		terminal->pvt->char_height = height;
7558 	}
7559 	if (ascent != terminal->pvt->char_ascent) {
7560 		resize = TRUE;
7561 		terminal->pvt->char_ascent = ascent;
7562 	}
7563 	if (descent != terminal->pvt->char_descent) {
7564 		resize = TRUE;
7565 		terminal->pvt->char_descent = descent;
7566 	}
7567 	terminal->pvt->line_thickness = line_thickness = MAX (MIN ((height - ascent) / 2, height / 14), 1);
7568 	terminal->pvt->underline_position = MIN (ascent + line_thickness, height - line_thickness);
7569 	terminal->pvt->strikethrough_position =  ascent - height / 4;
7570 
7571 	/* Queue a resize if anything's changed. */
7572 	if (resize) {
7573 		if (gtk_widget_get_realized (&terminal->widget)) {
7574 			gtk_widget_queue_resize_no_redraw(&terminal->widget);
7575 		}
7576 	}
7577 	/* Emit a signal that the font changed. */
7578 	if (cresize) {
7579 		vte_terminal_emit_char_size_changed(terminal,
7580 						    terminal->pvt->char_width,
7581 						    terminal->pvt->char_height);
7582 	}
7583 	/* Repaint. */
7584 	_vte_invalidate_all(terminal);
7585 }
7586 
7587 
7588 static void
vte_terminal_ensure_font(VteTerminal * terminal)7589 vte_terminal_ensure_font (VteTerminal *terminal)
7590 {
7591 	if (terminal->pvt->draw != NULL) {
7592 		/* Load default fonts, if no fonts have been loaded. */
7593 		if (!terminal->pvt->has_fonts) {
7594 			vte_terminal_set_font(terminal,
7595                                               terminal->pvt->unscaled_font_desc);
7596 		}
7597 		if (terminal->pvt->fontdirty) {
7598 			gint width, height, ascent;
7599 			terminal->pvt->fontdirty = FALSE;
7600 			_vte_draw_set_text_font (terminal->pvt->draw,
7601                                                  &terminal->widget,
7602 					terminal->pvt->fontdesc);
7603 			_vte_draw_get_text_metrics (terminal->pvt->draw,
7604 						    &width, &height, &ascent);
7605 			vte_terminal_apply_metrics(terminal,
7606 						   width, height, ascent, height - ascent);
7607 		}
7608 	}
7609 }
7610 
7611 static void
vte_terminal_update_font(VteTerminal * terminal)7612 vte_terminal_update_font(VteTerminal *terminal)
7613 {
7614         VteTerminalPrivate *pvt = terminal->pvt;
7615         PangoFontDescription *desc;
7616         gdouble size;
7617 
7618         /* We'll get called again later */
7619         if (pvt->unscaled_font_desc == NULL)
7620                 return;
7621 
7622         desc = pango_font_description_copy(pvt->unscaled_font_desc);
7623 
7624         size = pango_font_description_get_size(desc);
7625         if (pango_font_description_get_size_is_absolute(desc)) {
7626                 pango_font_description_set_absolute_size(desc, pvt->font_scale * size);
7627         } else {
7628                 pango_font_description_set_size(desc, pvt->font_scale * size);
7629         }
7630 
7631         if (pvt->fontdesc) {
7632                 pango_font_description_free(pvt->fontdesc);
7633         }
7634         pvt->fontdesc = desc;
7635 
7636         pvt->fontdirty = TRUE;
7637         pvt->has_fonts = TRUE;
7638 
7639         /* Set the drawing font. */
7640         if (gtk_widget_get_realized (&terminal->widget)) {
7641                 vte_terminal_ensure_font (terminal);
7642         }
7643 }
7644 
7645 /**
7646  * vte_terminal_set_font:
7647  * @terminal: a #VteTerminal
7648  * @font_desc: (allow-none): a #PangoFontDescription for the desired font, or %NULL
7649  *
7650  * Sets the font used for rendering all text displayed by the terminal,
7651  * overriding any fonts set using gtk_widget_modify_font().  The terminal
7652  * will immediately attempt to load the desired font, retrieve its
7653  * metrics, and attempt to resize itself to keep the same number of rows
7654  * and columns.  The font scale is applied to the specified font.
7655  */
7656 void
vte_terminal_set_font(VteTerminal * terminal,const PangoFontDescription * font_desc)7657 vte_terminal_set_font(VteTerminal *terminal,
7658                       const PangoFontDescription *font_desc)
7659 {
7660         GObject *object;
7661         GtkStyleContext *context;
7662 	VteTerminalPrivate *pvt;
7663         PangoFontDescription *desc;
7664         gboolean same_desc;
7665 
7666 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
7667 
7668         object = G_OBJECT(terminal);
7669         pvt = terminal->pvt;
7670 
7671 	/* Create an owned font description. */
7672         context = gtk_widget_get_style_context(&terminal->widget);
7673         gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, "font", &desc, NULL);
7674 	pango_font_description_set_family_static (desc, "monospace");
7675 	if (font_desc != NULL) {
7676 		pango_font_description_merge (desc, font_desc, TRUE);
7677 		_VTE_DEBUG_IF(VTE_DEBUG_MISC) {
7678 			if (desc) {
7679 				char *tmp;
7680 				tmp = pango_font_description_to_string(desc);
7681 				g_printerr("Using pango font \"%s\".\n", tmp);
7682 				g_free (tmp);
7683 			}
7684 		}
7685 	} else {
7686 		_vte_debug_print(VTE_DEBUG_MISC,
7687 				"Using default monospace font.\n");
7688 	}
7689 
7690         same_desc = pvt->unscaled_font_desc &&
7691                     pango_font_description_equal (pvt->unscaled_font_desc, desc);
7692 
7693 	/* Note that we proceed to recreating the font even if the description
7694 	 * are the same.  This is because maybe screen
7695 	 * font options were changed, or new fonts installed.  Those will be
7696 	 * detected at font creation time and respected.
7697 	 */
7698 
7699 	/* Free the old font description and save the new one. */
7700 	if (terminal->pvt->unscaled_font_desc != NULL) {
7701 		pango_font_description_free(terminal->pvt->unscaled_font_desc);
7702 	}
7703 
7704         terminal->pvt->unscaled_font_desc = desc /* adopted */;
7705 
7706         vte_terminal_update_font(terminal);
7707 
7708         if (!same_desc)
7709                 g_object_notify(object, "font-desc");
7710 }
7711 
7712 /**
7713  * vte_terminal_get_font:
7714  * @terminal: a #VteTerminal
7715  *
7716  * Queries the terminal for information about the fonts which will be
7717  * used to draw text in the terminal.  The actual font takes the font scale
7718  * into account, this is not reflected in the return value, the unscaled
7719  * font is returned.
7720  *
7721  * Returns: (transfer none): a #PangoFontDescription describing the font the
7722  * terminal uses to render text at the default font scale of 1.0.
7723  */
7724 const PangoFontDescription *
vte_terminal_get_font(VteTerminal * terminal)7725 vte_terminal_get_font(VteTerminal *terminal)
7726 {
7727         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
7728 
7729         return terminal->pvt->unscaled_font_desc;
7730 }
7731 
7732 /**
7733  * vte_terminal_set_font_scale:
7734  * @terminal: a #VteTerminal
7735  * @scale: the font scale
7736  *
7737  * Sets the terminal's font scale to @scale.
7738  */
7739 void
vte_terminal_set_font_scale(VteTerminal * terminal,gdouble scale)7740 vte_terminal_set_font_scale(VteTerminal *terminal,
7741                             gdouble scale)
7742 {
7743         g_return_if_fail(VTE_IS_TERMINAL(terminal));
7744 
7745         terminal->pvt->font_scale = CLAMP(scale, VTE_FONT_SCALE_MIN, VTE_FONT_SCALE_MAX);
7746         vte_terminal_update_font(terminal);
7747 
7748         g_object_notify(G_OBJECT(terminal), "font-scale");
7749 }
7750 
7751 /**
7752  * vte_terminal_get_font_scale:
7753  * @terminal: a #VteTerminal
7754  *
7755  * Returns: the terminal's font scale
7756  */
7757 gdouble
vte_terminal_get_font_scale(VteTerminal * terminal)7758 vte_terminal_get_font_scale(VteTerminal *terminal)
7759 {
7760         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1.);
7761 
7762         return terminal->pvt->font_scale;
7763 }
7764 
7765 /* Read and refresh our perception of the size of the PTY. */
7766 static void
vte_terminal_refresh_size(VteTerminal * terminal)7767 vte_terminal_refresh_size(VteTerminal *terminal)
7768 {
7769         VteTerminalPrivate *pvt = terminal->pvt;
7770 	int rows, columns;
7771         GError *error = NULL;
7772 
7773         if (pvt->pty == NULL)
7774                 return;
7775 
7776         if (vte_pty_get_size(pvt->pty, &rows, &columns, &error)) {
7777                 terminal->pvt->row_count = rows;
7778                 terminal->pvt->column_count = columns;
7779         } else {
7780                 g_warning(_("Error reading PTY size, using defaults: %s\n"), error->message);
7781                 g_error_free(error);
7782 	}
7783 }
7784 
7785 /* Resize the given screen (normal or alternate) of the terminal. */
7786 static void
vte_terminal_screen_set_size(VteTerminal * terminal,VteScreen * screen,glong old_columns,glong old_rows,gboolean do_rewrap)7787 vte_terminal_screen_set_size(VteTerminal *terminal, VteScreen *screen, glong old_columns, glong old_rows, gboolean do_rewrap)
7788 {
7789 	VteRing *ring = screen->row_data;
7790 	VteVisualPosition cursor_saved_absolute;
7791 	VteVisualPosition below_viewport;
7792 	VteVisualPosition below_current_paragraph;
7793 	VteVisualPosition *markers[7];
7794 	gboolean was_scrolled_to_top = (screen->scroll_delta == _vte_ring_delta(ring));
7795 	gboolean was_scrolled_to_bottom = (screen->scroll_delta == screen->insert_delta);
7796 	glong old_top_lines;
7797 	glong new_scroll_delta;
7798 
7799         if (terminal->pvt->selection_block_mode && do_rewrap && old_columns != terminal->pvt->column_count)
7800                 vte_terminal_deselect_all(terminal);
7801 
7802 	_vte_debug_print(VTE_DEBUG_RESIZE,
7803 			"Resizing %s screen\n"
7804 			"Old  insert_delta=%ld  scroll_delta=%ld\n"
7805                         "     cursor (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
7806 			"     cursor_saved (relative to insert_delta)  row=%ld  col=%ld\n",
7807 			screen == &terminal->pvt->normal_screen ? "normal" : "alternate",
7808 			screen->insert_delta, screen->scroll_delta,
7809                         terminal->pvt->cursor.row, terminal->pvt->cursor.row - screen->scroll_delta + 1, terminal->pvt->cursor.col,
7810                         screen->saved.cursor.row, screen->saved.cursor.col);
7811 
7812         cursor_saved_absolute.row = screen->saved.cursor.row + screen->insert_delta;
7813         cursor_saved_absolute.col = screen->saved.cursor.col;
7814 	below_viewport.row = screen->scroll_delta + old_rows;
7815 	below_viewport.col = 0;
7816         below_current_paragraph.row = terminal->pvt->cursor.row + 1;
7817 	while (below_current_paragraph.row < _vte_ring_next(ring)
7818 	    && _vte_ring_index(ring, below_current_paragraph.row - 1)->attr.soft_wrapped) {
7819 		below_current_paragraph.row++;
7820 	}
7821 	below_current_paragraph.col = 0;
7822         memset(&markers, 0, sizeof(markers));
7823         markers[0] = &cursor_saved_absolute;
7824         markers[1] = &below_viewport;
7825         markers[2] = &below_current_paragraph;
7826         if (screen == terminal->pvt->screen)
7827                 /* Tracking the current cursor only makes sense on the active screen. */
7828                 markers[3] = &terminal->pvt->cursor;
7829                 if (terminal->pvt->has_selection) {
7830                         /* selection_end is inclusive, make it non-inclusive, see bug 722635. */
7831                         terminal->pvt->selection_end.col++;
7832                         markers[4] = &terminal->pvt->selection_start;
7833                         markers[5] = &terminal->pvt->selection_end;
7834 	}
7835 
7836 	old_top_lines = below_current_paragraph.row - screen->insert_delta;
7837 
7838 	if (do_rewrap && old_columns != terminal->pvt->column_count)
7839 		_vte_ring_rewrap(ring, terminal->pvt->column_count, markers);
7840 
7841 	if (_vte_ring_length(ring) > terminal->pvt->row_count) {
7842 		/* The content won't fit without scrollbars. Before figuring out the position, we might need to
7843 		   drop some lines from the ring if the cursor is not at the bottom, as XTerm does. See bug 708213.
7844 		   This code is really tricky, see ../doc/rewrap.txt for details! */
7845 		glong new_top_lines, drop1, drop2, drop3, drop;
7846 		screen->insert_delta = _vte_ring_next(ring) - terminal->pvt->row_count;
7847 		new_top_lines = below_current_paragraph.row - screen->insert_delta;
7848 		drop1 = _vte_ring_length(ring) - terminal->pvt->row_count;
7849 		drop2 = _vte_ring_next(ring) - below_current_paragraph.row;
7850 		drop3 = old_top_lines - new_top_lines;
7851 		drop = MIN(MIN(drop1, drop2), drop3);
7852 		if (drop > 0) {
7853 			int new_ring_next = screen->insert_delta + terminal->pvt->row_count - drop;
7854 			_vte_debug_print(VTE_DEBUG_RESIZE,
7855 					"Dropping %ld [== MIN(%ld, %ld, %ld)] rows at the bottom\n",
7856 					drop, drop1, drop2, drop3);
7857 			_vte_ring_shrink(ring, new_ring_next - _vte_ring_delta(ring));
7858 		}
7859 	}
7860 
7861 	if (screen == terminal->pvt->screen && terminal->pvt->has_selection) {
7862 		/* Make selection_end inclusive again, see above. */
7863 		terminal->pvt->selection_end.col--;
7864 	}
7865 
7866 	/* Figure out new insert and scroll deltas */
7867 	if (_vte_ring_length(ring) <= terminal->pvt->row_count) {
7868 		/* Everything fits without scrollbars. Align at top. */
7869 		screen->insert_delta = _vte_ring_delta(ring);
7870 		new_scroll_delta = screen->insert_delta;
7871 		_vte_debug_print(VTE_DEBUG_RESIZE,
7872 				"Everything fits without scrollbars\n");
7873 	} else {
7874 		/* Scrollbar required. Can't afford unused lines at bottom. */
7875 		screen->insert_delta = _vte_ring_next(ring) - terminal->pvt->row_count;
7876 		if (was_scrolled_to_bottom) {
7877 			/* Was scrolled to bottom, keep this way. */
7878 			new_scroll_delta = screen->insert_delta;
7879 			_vte_debug_print(VTE_DEBUG_RESIZE,
7880 					"Scroll to bottom\n");
7881 		} else if (was_scrolled_to_top) {
7882 			/* Was scrolled to top, keep this way. Not sure if this special case is worth it. */
7883 			new_scroll_delta = _vte_ring_delta(ring);
7884 			_vte_debug_print(VTE_DEBUG_RESIZE,
7885 					"Scroll to top\n");
7886 		} else {
7887 			/* Try to scroll so that the bottom visible row stays.
7888 			   More precisely, the character below the bottom left corner stays in that
7889 			   (invisible) row.
7890 			   So if the bottom of the screen was at a hard line break then that hard
7891 			   line break will stay there.
7892 			   TODO: What would be the best behavior if the bottom of the screen is a
7893 			   soft line break, i.e. only a partial line is visible at the bottom? */
7894 			new_scroll_delta = below_viewport.row - terminal->pvt->row_count;
7895 			_vte_debug_print(VTE_DEBUG_RESIZE,
7896 					"Scroll so bottom row stays\n");
7897 		}
7898 	}
7899 
7900 	/* Don't clamp, they'll be clamped when restored. Until then remember off-screen values
7901 	   since they might become on-screen again on subsequent resizes. */
7902         screen->saved.cursor.row = cursor_saved_absolute.row - screen->insert_delta;
7903         screen->saved.cursor.col = cursor_saved_absolute.col;
7904 
7905 	_vte_debug_print(VTE_DEBUG_RESIZE,
7906 			"New  insert_delta=%ld  scroll_delta=%ld\n"
7907                         "     cursor (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
7908 			"     cursor_saved (relative to insert_delta)  row=%ld  col=%ld\n\n",
7909 			screen->insert_delta, new_scroll_delta,
7910                         terminal->pvt->cursor.row, terminal->pvt->cursor.row - new_scroll_delta + 1, terminal->pvt->cursor.col,
7911                         screen->saved.cursor.row, screen->saved.cursor.col);
7912 
7913 	if (screen == terminal->pvt->screen)
7914 		vte_terminal_queue_adjustment_value_changed (
7915 				terminal,
7916 				new_scroll_delta);
7917 	else
7918 		screen->scroll_delta = new_scroll_delta;
7919 }
7920 
7921 /**
7922  * vte_terminal_set_size:
7923  * @terminal: a #VteTerminal
7924  * @columns: the desired number of columns
7925  * @rows: the desired number of rows
7926  *
7927  * Attempts to change the terminal's size in terms of rows and columns.  If
7928  * the attempt succeeds, the widget will resize itself to the proper size.
7929  */
7930 void
vte_terminal_set_size(VteTerminal * terminal,glong columns,glong rows)7931 vte_terminal_set_size(VteTerminal *terminal, glong columns, glong rows)
7932 {
7933 	glong old_columns, old_rows;
7934 
7935 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
7936 
7937 	_vte_debug_print(VTE_DEBUG_RESIZE,
7938 			"Setting PTY size to %ldx%ld.\n",
7939 			columns, rows);
7940 
7941 	old_rows = terminal->pvt->row_count;
7942 	old_columns = terminal->pvt->column_count;
7943 
7944 	if (terminal->pvt->pty != NULL) {
7945                 GError *error = NULL;
7946 
7947 		/* Try to set the terminal size, and read it back,
7948 		 * in case something went awry.
7949                  */
7950 		if (!vte_pty_set_size(terminal->pvt->pty, rows, columns, &error)) {
7951 			g_warning("%s\n", error->message);
7952                         g_error_free(error);
7953 		}
7954 		vte_terminal_refresh_size(terminal);
7955 	} else {
7956 		terminal->pvt->row_count = rows;
7957 		terminal->pvt->column_count = columns;
7958 	}
7959 	if (old_rows != terminal->pvt->row_count || old_columns != terminal->pvt->column_count) {
7960                 terminal->pvt->scrolling_restricted = FALSE;
7961 
7962                 _vte_ring_set_visible_rows(terminal->pvt->normal_screen.row_data, terminal->pvt->row_count);
7963                 _vte_ring_set_visible_rows(terminal->pvt->alternate_screen.row_data, terminal->pvt->row_count);
7964 
7965 		/* Resize the normal screen and (if rewrapping is enabled) rewrap it even if the alternate screen is visible: bug 415277 */
7966 		vte_terminal_screen_set_size(terminal, &terminal->pvt->normal_screen, old_columns, old_rows, terminal->pvt->rewrap_on_resize);
7967 		/* Resize the alternate screen if it's the current one, but never rewrap it: bug 336238 comment 60 */
7968 		if (terminal->pvt->screen == &terminal->pvt->alternate_screen)
7969 			vte_terminal_screen_set_size(terminal, &terminal->pvt->alternate_screen, old_columns, old_rows, FALSE);
7970 
7971                 /* Ensure scrollback buffers cover the screen. */
7972                 vte_terminal_set_scrollback_lines(terminal,
7973                                                   terminal->pvt->scrollback_lines);
7974                 /* Ensure the cursor is valid */
7975                 terminal->pvt->cursor.row = CLAMP (terminal->pvt->cursor.row,
7976                                                     _vte_ring_delta (terminal->pvt->screen->row_data),
7977                                                     MAX (_vte_ring_delta (terminal->pvt->screen->row_data),
7978                                                          _vte_ring_next (terminal->pvt->screen->row_data) - 1));
7979 
7980 		_vte_terminal_adjust_adjustments_full (terminal);
7981 		gtk_widget_queue_resize_no_redraw (&terminal->widget);
7982 		/* Our visible text changed. */
7983 		vte_terminal_emit_text_modified(terminal);
7984 	}
7985 }
7986 
7987 /* Redraw the widget. */
7988 static void
vte_terminal_handle_scroll(VteTerminal * terminal)7989 vte_terminal_handle_scroll(VteTerminal *terminal)
7990 {
7991 	long dy, adj;
7992 	VteScreen *screen;
7993 
7994 	screen = terminal->pvt->screen;
7995 
7996 	/* Read the new adjustment value and save the difference. */
7997 	adj = round (gtk_adjustment_get_value(terminal->pvt->vadjustment));
7998 	dy = adj - screen->scroll_delta;
7999 	screen->scroll_delta = adj;
8000 
8001 	/* Sanity checks. */
8002         if (G_UNLIKELY(!gtk_widget_get_realized(&terminal->widget)))
8003                 return;
8004 	if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
8005 		return;
8006 	}
8007 
8008 	if (dy != 0) {
8009 		_vte_debug_print(VTE_DEBUG_ADJ,
8010 			    "Scrolling by %ld\n", dy);
8011 		_vte_terminal_scroll_region(terminal, screen->scroll_delta,
8012 					   terminal->pvt->row_count, -dy);
8013 		vte_terminal_emit_text_scrolled(terminal, dy);
8014 		_vte_terminal_queue_contents_changed(terminal);
8015 	} else {
8016 		_vte_debug_print(VTE_DEBUG_ADJ, "Not scrolling\n");
8017 	}
8018 }
8019 
8020 static void
vte_terminal_set_hadjustment(VteTerminal * terminal,GtkAdjustment * adjustment)8021 vte_terminal_set_hadjustment(VteTerminal *terminal,
8022                              GtkAdjustment *adjustment)
8023 {
8024   VteTerminalPrivate *pvt = terminal->pvt;
8025 
8026   if (adjustment == pvt->hadjustment)
8027     return;
8028 
8029   if (pvt->hadjustment)
8030     g_object_unref (pvt->hadjustment);
8031 
8032   pvt->hadjustment = adjustment ? g_object_ref_sink (adjustment) : NULL;
8033 }
8034 
8035 static void
vte_terminal_set_vadjustment(VteTerminal * terminal,GtkAdjustment * adjustment)8036 vte_terminal_set_vadjustment(VteTerminal *terminal,
8037                              GtkAdjustment *adjustment)
8038 {
8039 	if (adjustment != NULL && adjustment == terminal->pvt->vadjustment)
8040 		return;
8041 	if (adjustment == NULL && terminal->pvt->vadjustment != NULL)
8042 		return;
8043 
8044 	if (adjustment == NULL)
8045 		adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
8046 	else
8047 		g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
8048 
8049 	/* Add a reference to the new adjustment object. */
8050 	g_object_ref_sink(adjustment);
8051 	/* Get rid of the old adjustment object. */
8052 	if (terminal->pvt->vadjustment != NULL) {
8053 		/* Disconnect our signal handlers from this object. */
8054 		g_signal_handlers_disconnect_by_func(terminal->pvt->vadjustment,
8055 						     vte_terminal_handle_scroll,
8056 						     terminal);
8057 		g_object_unref(terminal->pvt->vadjustment);
8058 	}
8059 
8060 	/* Set the new adjustment object. */
8061 	terminal->pvt->vadjustment = adjustment;
8062 
8063 	/* We care about the offset, not the top or bottom. */
8064 	g_signal_connect_swapped(terminal->pvt->vadjustment,
8065 				 "value-changed",
8066 				 G_CALLBACK(vte_terminal_handle_scroll),
8067 				 terminal);
8068 }
8069 
8070 void
_vte_terminal_inline_error_message(VteTerminal * terminal,const char * format,...)8071 _vte_terminal_inline_error_message(VteTerminal *terminal, const char *format, ...)
8072 {
8073 	va_list ap;
8074 	char *str;
8075 
8076 	va_start (ap, format);
8077 	str = g_strdup_vprintf (format, ap);
8078 	va_end (ap);
8079 
8080 	vte_terminal_feed (terminal, "*** VTE ***: ", 13);
8081 	vte_terminal_feed (terminal, str, -1);
8082 	vte_terminal_feed (terminal, "\r\n", 2);
8083 	g_free (str);
8084 }
8085 
8086 /* Initialize the terminal widget after the base widget stuff is initialized.
8087  * We need to create a new psuedo-terminal pair, and set ourselves up to do
8088  * the interpretation of sequences. */
8089 static void
vte_terminal_init(VteTerminal * terminal)8090 vte_terminal_init(VteTerminal *terminal)
8091 {
8092 	VteTerminalPrivate *pvt;
8093 	GtkStyleContext *context;
8094 	int i;
8095 
8096 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_init()\n");
8097 
8098 	/* Initialize private data. */
8099 	pvt = terminal->pvt = G_TYPE_INSTANCE_GET_PRIVATE (terminal, VTE_TYPE_TERMINAL, VteTerminalPrivate);
8100 
8101 	gtk_widget_set_can_focus(&terminal->widget, TRUE);
8102 
8103 	gtk_widget_set_app_paintable (&terminal->widget, TRUE);
8104 
8105 	/* We do our own redrawing. */
8106 	gtk_widget_set_redraw_on_allocate (&terminal->widget, FALSE);
8107 
8108 	/* Set an adjustment for the application to use to control scrolling. */
8109         terminal->pvt->vadjustment = NULL;
8110         pvt->hadjustment = NULL;
8111         /* GtkScrollable */
8112         pvt->hscroll_policy = GTK_SCROLL_NATURAL;
8113         pvt->vscroll_policy = GTK_SCROLL_NATURAL;
8114 
8115         vte_terminal_set_hadjustment(terminal, NULL);
8116 	vte_terminal_set_vadjustment(terminal, NULL);
8117 
8118 	/* Set up dummy metrics, value != 0 to avoid division by 0 */
8119 	terminal->pvt->char_width = 1;
8120 	terminal->pvt->char_height = 1;
8121 	terminal->pvt->char_ascent = 1;
8122 	terminal->pvt->char_descent = 1;
8123 	terminal->pvt->line_thickness = 1;
8124 	terminal->pvt->underline_position = 1;
8125 	terminal->pvt->strikethrough_position = 1;
8126 
8127 	/* We allocated zeroed memory, just fill in non-zero stuff. */
8128 
8129 	/* Initialize the screens and histories. */
8130 	_vte_ring_init (pvt->alternate_screen.row_data, terminal->pvt->row_count, FALSE);
8131 	pvt->screen = &terminal->pvt->alternate_screen;
8132 	_vte_ring_init (pvt->normal_screen.row_data,  VTE_SCROLLBACK_INIT, TRUE);
8133 	pvt->screen = &terminal->pvt->normal_screen;
8134 
8135 	_vte_terminal_set_default_attributes(terminal);
8136 
8137         /* Initialize charset modes. */
8138         pvt->character_replacements[0] = VTE_CHARACTER_REPLACEMENT_NONE;
8139         pvt->character_replacements[1] = VTE_CHARACTER_REPLACEMENT_NONE;
8140         pvt->character_replacement = &pvt->character_replacements[0];
8141 
8142 	/* Set up the desired palette. */
8143 	vte_terminal_set_default_colors(terminal);
8144 	for (i = 0; i < VTE_PALETTE_SIZE; i++)
8145 		terminal->pvt->palette[i].sources[VTE_COLOR_SOURCE_ESCAPE].is_set = FALSE;
8146 
8147 	/* Set up I/O encodings. */
8148         pvt->utf8_ambiguous_width = VTE_DEFAULT_UTF8_AMBIGUOUS_WIDTH;
8149         pvt->iso2022 = _vte_iso2022_state_new(pvt->encoding);
8150 	pvt->incoming = NULL;
8151 	pvt->pending = g_array_new(FALSE, TRUE, sizeof(gunichar));
8152 	pvt->max_input_bytes = VTE_MAX_INPUT_READ;
8153 	pvt->cursor_blink_tag = 0;
8154 	pvt->outgoing = _vte_byte_array_new();
8155 	pvt->outgoing_conv = VTE_INVALID_CONV;
8156 	pvt->conv_buffer = _vte_byte_array_new();
8157 	vte_terminal_set_encoding(terminal, NULL /* UTF-8 */, NULL);
8158 	g_assert_cmpstr(terminal->pvt->encoding, ==, "UTF-8");
8159 
8160         /* Set up the emulation. */
8161 	pvt->keypad_mode = VTE_KEYMODE_NORMAL;
8162 	pvt->cursor_mode = VTE_KEYMODE_NORMAL;
8163         pvt->autowrap = TRUE;
8164         pvt->sendrecv_mode = TRUE;
8165 	pvt->dec_saved = g_hash_table_new(NULL, NULL);
8166         pvt->matcher = _vte_matcher_new();
8167         pvt->alternate_screen_scroll = TRUE;
8168 
8169 	/* Setting the terminal type and size requires the PTY master to
8170 	 * be set up properly first. */
8171         pvt->pty = NULL;
8172         vte_terminal_set_size(terminal, VTE_COLUMNS, VTE_ROWS);
8173 	pvt->pty_input_source = 0;
8174 	pvt->pty_output_source = 0;
8175 	pvt->pty_pid = -1;
8176 
8177 	/* Scrolling options. */
8178 	pvt->scroll_on_keystroke = TRUE;
8179 	pvt->alternate_screen_scroll = TRUE;
8180         pvt->scrollback_lines = -1; /* force update in vte_terminal_set_scrollback_lines */
8181 	vte_terminal_set_scrollback_lines(terminal, VTE_SCROLLBACK_INIT);
8182 
8183 	/* Miscellaneous options. */
8184 	vte_terminal_set_backspace_binding(terminal, VTE_ERASE_AUTO);
8185 	vte_terminal_set_delete_binding(terminal, VTE_ERASE_AUTO);
8186 	pvt->meta_sends_escape = TRUE;
8187 	pvt->audible_bell = TRUE;
8188 	pvt->bell_margin = 10;
8189 	pvt->allow_bold = TRUE;
8190         pvt->deccolm_mode = FALSE;
8191         pvt->rewrap_on_resize = TRUE;
8192 	vte_terminal_set_default_tabstops(terminal);
8193 
8194         pvt->input_enabled = TRUE;
8195 
8196 	/* Cursor shape. */
8197 	pvt->cursor_shape = VTE_CURSOR_SHAPE_BLOCK;
8198         pvt->cursor_aspect_ratio = 0.04;
8199 
8200 	/* Cursor blinking. */
8201 	pvt->cursor_visible = TRUE;
8202 	pvt->cursor_blink_timeout = 500;
8203         pvt->cursor_blinks = FALSE;
8204         pvt->cursor_blink_mode = VTE_CURSOR_BLINK_SYSTEM;
8205 
8206         /* DECSCUSR cursor style (shape and blinking possibly overridden
8207          * via escape sequence) */
8208         pvt->cursor_style = VTE_CURSOR_STYLE_TERMINAL_DEFAULT;
8209 
8210         /* Initialize the saved cursor. */
8211         _vte_terminal_save_cursor(terminal, &terminal->pvt->normal_screen);
8212         _vte_terminal_save_cursor(terminal, &terminal->pvt->alternate_screen);
8213 
8214 	/* Matching data. */
8215 	pvt->match_regexes = g_array_new(FALSE, TRUE,
8216 					 sizeof(struct vte_match_regex));
8217         pvt->match_tag = -1;
8218 	vte_terminal_match_hilite_clear(terminal);
8219 
8220 	/* Rendering data */
8221 	pvt->draw = _vte_draw_new();
8222 
8223 	/* Set up background information. */
8224         pvt->background_alpha = 1.;
8225 
8226         /* Word chars */
8227         vte_terminal_set_word_char_exceptions(terminal, WORD_CHAR_EXCEPTIONS_DEFAULT);
8228 
8229         /* Selection */
8230 	pvt->selection_block_mode = FALSE;
8231         pvt->unscaled_font_desc = pvt->fontdesc = NULL;
8232         pvt->font_scale = 1.;
8233 	pvt->has_fonts = FALSE;
8234 
8235 	/* Not all backends generate GdkVisibilityNotify, so mark the
8236 	 * window as unobscured initially. */
8237 	pvt->visibility_state = GDK_VISIBILITY_UNOBSCURED;
8238 
8239         pvt->padding = default_padding;
8240 
8241         context = gtk_widget_get_style_context (&terminal->widget);
8242         gtk_style_context_add_provider (context,
8243                                         VTE_TERMINAL_GET_CLASS (terminal)->priv->style_provider,
8244                                         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
8245 }
8246 
8247 /* Tell GTK+ how much space we need. */
8248 static void
vte_terminal_get_preferred_width(GtkWidget * widget,int * minimum_width,int * natural_width)8249 vte_terminal_get_preferred_width(GtkWidget *widget,
8250 				 int       *minimum_width,
8251 				 int       *natural_width)
8252 {
8253 	VteTerminal *terminal;
8254 
8255 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_get_preferred_width()\n");
8256 
8257 	terminal = VTE_TERMINAL(widget);
8258 
8259 	vte_terminal_ensure_font (terminal);
8260 
8261         vte_terminal_refresh_size(terminal);
8262 	*minimum_width = terminal->pvt->char_width * 1;
8263         *natural_width = terminal->pvt->char_width * terminal->pvt->column_count;
8264 
8265 	*minimum_width += terminal->pvt->padding.left +
8266                           terminal->pvt->padding.right;
8267 	*natural_width += terminal->pvt->padding.left +
8268                           terminal->pvt->padding.right;
8269 
8270 	_vte_debug_print(VTE_DEBUG_WIDGET_SIZE,
8271 			"[Terminal %p] minimum_width=%d, natural_width=%d for %ldx%ld cells.\n",
8272                         terminal,
8273 			*minimum_width, *natural_width,
8274 			terminal->pvt->column_count,
8275 			terminal->pvt->row_count);
8276 }
8277 
8278 static void
vte_terminal_get_preferred_height(GtkWidget * widget,int * minimum_height,int * natural_height)8279 vte_terminal_get_preferred_height(GtkWidget *widget,
8280 				  int       *minimum_height,
8281 				  int       *natural_height)
8282 {
8283 	VteTerminal *terminal;
8284 
8285 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_get_preferred_height()\n");
8286 
8287 	terminal = VTE_TERMINAL(widget);
8288 
8289 	vte_terminal_ensure_font (terminal);
8290 
8291         vte_terminal_refresh_size(terminal);
8292 	*minimum_height = terminal->pvt->char_height * 1;
8293         *natural_height = terminal->pvt->char_height * terminal->pvt->row_count;
8294 
8295 	*minimum_height += terminal->pvt->padding.left +
8296 			   terminal->pvt->padding.right;
8297 	*natural_height += terminal->pvt->padding.left +
8298 			   terminal->pvt->padding.right;
8299 
8300 	_vte_debug_print(VTE_DEBUG_WIDGET_SIZE,
8301 			"[Terminal %p] minimum_height=%d, natural_height=%d for %ldx%ld cells.\n",
8302                         terminal,
8303 			*minimum_height, *natural_height,
8304 			terminal->pvt->column_count,
8305 			terminal->pvt->row_count);
8306 }
8307 
8308 /* Accept a given size from GTK+. */
8309 static void
vte_terminal_size_allocate(GtkWidget * widget,GtkAllocation * allocation)8310 vte_terminal_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
8311 {
8312 	VteTerminal *terminal;
8313 	glong width, height;
8314 	GtkAllocation current_allocation;
8315 	gboolean repaint, update_scrollback;
8316 
8317 	_vte_debug_print(VTE_DEBUG_LIFECYCLE,
8318 			"vte_terminal_size_allocate()\n");
8319 
8320 	terminal = VTE_TERMINAL(widget);
8321 
8322 	width = (allocation->width - (terminal->pvt->padding.left + terminal->pvt->padding.right)) /
8323 		terminal->pvt->char_width;
8324 	height = (allocation->height - (terminal->pvt->padding.top + terminal->pvt->padding.bottom)) /
8325 		 terminal->pvt->char_height;
8326 	width = MAX(width, 1);
8327 	height = MAX(height, 1);
8328 
8329 	_vte_debug_print(VTE_DEBUG_WIDGET_SIZE,
8330 			"[Terminal %p] Sizing window to %dx%d (%ldx%ld).\n",
8331                         terminal,
8332 			allocation->width, allocation->height,
8333 			width, height);
8334 
8335 	gtk_widget_get_allocation (widget, &current_allocation);
8336 
8337 	repaint = current_allocation.width != allocation->width
8338 			|| current_allocation.height != allocation->height;
8339 	update_scrollback = current_allocation.height != allocation->height;
8340 
8341 	/* Set our allocation to match the structure. */
8342 	gtk_widget_set_allocation (widget, allocation);
8343 
8344 	if (width != terminal->pvt->column_count
8345 			|| height != terminal->pvt->row_count
8346 			|| update_scrollback)
8347 	{
8348 		/* Set the size of the pseudo-terminal. */
8349 		vte_terminal_set_size(terminal, width, height);
8350 
8351 		/* Notify viewers that the contents have changed. */
8352 		_vte_terminal_queue_contents_changed(terminal);
8353 	}
8354 
8355 	/* Resize the GDK window. */
8356 	if (gtk_widget_get_realized (widget)) {
8357 		gdk_window_move_resize (gtk_widget_get_window (widget),
8358 					allocation->x,
8359 					allocation->y,
8360 					allocation->width,
8361 					allocation->height);
8362 		/* Force a repaint if we were resized. */
8363 		if (repaint) {
8364 			reset_update_regions (terminal);
8365 			_vte_invalidate_all(terminal);
8366 		}
8367 	}
8368 }
8369 
8370 /* The window is being destroyed. */
8371 static void
vte_terminal_unrealize(GtkWidget * widget)8372 vte_terminal_unrealize(GtkWidget *widget)
8373 {
8374 	GdkWindow *window;
8375 	VteTerminal *terminal;
8376 
8377 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_unrealize()\n");
8378 
8379 	terminal = VTE_TERMINAL (widget);
8380 	window = gtk_widget_get_window (widget);
8381 
8382 	/* Deallocate the cursors. */
8383 	terminal->pvt->mouse_cursor_visible = FALSE;
8384 	g_object_unref(terminal->pvt->mouse_default_cursor);
8385 	terminal->pvt->mouse_default_cursor = NULL;
8386 	g_object_unref(terminal->pvt->mouse_mousing_cursor);
8387 	terminal->pvt->mouse_mousing_cursor = NULL;
8388 	g_object_unref(terminal->pvt->mouse_inviso_cursor);
8389 	terminal->pvt->mouse_inviso_cursor = NULL;
8390 
8391 	vte_terminal_match_hilite_clear(terminal);
8392 
8393 	/* Shut down input methods. */
8394 	if (terminal->pvt->im_context != NULL) {
8395 	        g_signal_handlers_disconnect_by_func (terminal->pvt->im_context,
8396 						      vte_terminal_im_preedit_changed,
8397 						      terminal);
8398 		vte_terminal_im_reset(terminal);
8399 		gtk_im_context_set_client_window(terminal->pvt->im_context,
8400 						 NULL);
8401 		g_object_unref(terminal->pvt->im_context);
8402 		terminal->pvt->im_context = NULL;
8403 	}
8404 	terminal->pvt->im_preedit_active = FALSE;
8405 	if (terminal->pvt->im_preedit != NULL) {
8406 		g_free(terminal->pvt->im_preedit);
8407 		terminal->pvt->im_preedit = NULL;
8408 	}
8409 	if (terminal->pvt->im_preedit_attrs != NULL) {
8410 		pango_attr_list_unref(terminal->pvt->im_preedit_attrs);
8411 		terminal->pvt->im_preedit_attrs = NULL;
8412 	}
8413 	terminal->pvt->im_preedit_cursor = 0;
8414 
8415 	/* Clean up our draw structure. */
8416 	if (terminal->pvt->draw != NULL) {
8417 		_vte_draw_free(terminal->pvt->draw);
8418 		terminal->pvt->draw = NULL;
8419 	}
8420 	terminal->pvt->fontdirty = TRUE;
8421 
8422 	/* Unmap the widget if it hasn't been already. */
8423 	if (gtk_widget_get_mapped (widget)) {
8424 		gtk_widget_unmap (widget);
8425 	}
8426 
8427 	/* Remove the GDK window. */
8428 	if (window != NULL) {
8429 		gdk_window_set_user_data (window, NULL);
8430 		gtk_widget_set_window (widget, NULL);
8431 
8432 		gdk_window_destroy (window);
8433 	}
8434 
8435 	/* Remove the blink timeout function. */
8436 	remove_cursor_timeout(terminal);
8437 
8438 	/* Cancel any pending redraws. */
8439 	remove_update_timeout (terminal);
8440 
8441 	/* Cancel any pending signals */
8442 	terminal->pvt->contents_changed_pending = FALSE;
8443 	terminal->pvt->cursor_moved_pending = FALSE;
8444 	terminal->pvt->text_modified_flag = FALSE;
8445 	terminal->pvt->text_inserted_flag = FALSE;
8446 	terminal->pvt->text_deleted_flag = FALSE;
8447 
8448 	/* Clear modifiers. */
8449 	terminal->pvt->modifiers = 0;
8450 
8451 	/* Mark that we no longer have a GDK window. */
8452 	gtk_widget_set_realized (widget, FALSE);
8453 }
8454 
8455 static void
vte_terminal_sync_settings(GtkSettings * settings,GParamSpec * pspec,VteTerminal * terminal)8456 vte_terminal_sync_settings (GtkSettings *settings,
8457                             GParamSpec *pspec,
8458                             VteTerminal *terminal)
8459 {
8460         VteTerminalPrivate *pvt = terminal->pvt;
8461         gboolean blink;
8462         int blink_time = 1000;
8463         int blink_timeout = G_MAXINT;
8464 
8465         g_object_get(G_OBJECT (settings),
8466                      "gtk-cursor-blink", &blink,
8467                      "gtk-cursor-blink-time", &blink_time,
8468                      "gtk-cursor-blink-timeout", &blink_timeout,
8469                      NULL);
8470 
8471         _vte_debug_print(VTE_DEBUG_MISC,
8472                          "Cursor blinking settings setting: blink=%d time=%d timeout=%d\n",
8473                          blink, blink_time, blink_timeout);
8474 
8475         pvt->cursor_blink_cycle = blink_time / 2;
8476         pvt->cursor_blink_timeout = blink_timeout;
8477 
8478         vte_terminal_update_cursor_blinks_internal(terminal);
8479 }
8480 
8481 static void
vte_terminal_screen_changed(GtkWidget * widget,GdkScreen * previous_screen)8482 vte_terminal_screen_changed (GtkWidget *widget,
8483                              GdkScreen *previous_screen)
8484 {
8485         VteTerminal *terminal = VTE_TERMINAL (widget);
8486         GdkScreen *screen;
8487         GtkSettings *settings;
8488 
8489         screen = gtk_widget_get_screen (widget);
8490         if (previous_screen != NULL &&
8491             (screen != previous_screen || screen == NULL)) {
8492                 settings = gtk_settings_get_for_screen (previous_screen);
8493                 g_signal_handlers_disconnect_matched (settings, G_SIGNAL_MATCH_DATA,
8494                                                       0, 0, NULL, NULL,
8495                                                       widget);
8496         }
8497 
8498         if (GTK_WIDGET_CLASS (vte_terminal_parent_class)->screen_changed) {
8499                 GTK_WIDGET_CLASS (vte_terminal_parent_class)->screen_changed (widget, previous_screen);
8500         }
8501 
8502         if (screen == previous_screen || screen == NULL)
8503                 return;
8504 
8505         settings = gtk_widget_get_settings (widget);
8506         vte_terminal_sync_settings (settings, NULL, terminal);
8507         g_signal_connect (settings, "notify::gtk-cursor-blink",
8508                           G_CALLBACK (vte_terminal_sync_settings), widget);
8509         g_signal_connect (settings, "notify::gtk-cursor-blink-time",
8510                           G_CALLBACK (vte_terminal_sync_settings), widget);
8511         g_signal_connect (settings, "notify::gtk-cursor-blink-timeout",
8512                           G_CALLBACK (vte_terminal_sync_settings), widget);
8513 }
8514 
8515 /* Perform final cleanups for the widget before it's freed. */
8516 static void
vte_terminal_finalize(GObject * object)8517 vte_terminal_finalize(GObject *object)
8518 {
8519     	GtkWidget *widget = GTK_WIDGET (object);
8520     	VteTerminal *terminal = VTE_TERMINAL (object);
8521         VteTerminalPrivate *pvt = terminal->pvt;
8522 	GtkClipboard *clipboard;
8523         GtkSettings *settings;
8524 	struct vte_match_regex *regex;
8525 	guint i;
8526 
8527 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_finalize()\n");
8528 
8529 	/* Free the draw structure. */
8530 	if (terminal->pvt->draw != NULL) {
8531 		_vte_draw_free(terminal->pvt->draw);
8532 	}
8533 
8534 	/* The NLS maps. */
8535 	_vte_iso2022_state_free(terminal->pvt->iso2022);
8536 
8537 	/* Free the font description. */
8538         if (pvt->unscaled_font_desc != NULL) {
8539                 pango_font_description_free(pvt->unscaled_font_desc);
8540         }
8541 	if (terminal->pvt->fontdesc != NULL) {
8542 		pango_font_description_free(terminal->pvt->fontdesc);
8543 	}
8544 
8545 	/* Free matching data. */
8546 	if (terminal->pvt->match_attributes != NULL) {
8547 		g_array_free(terminal->pvt->match_attributes, TRUE);
8548 	}
8549 	g_free(terminal->pvt->match_contents);
8550 	if (terminal->pvt->match_regexes != NULL) {
8551 		for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
8552 			regex = &g_array_index(terminal->pvt->match_regexes,
8553 					       struct vte_match_regex,
8554 					       i);
8555 			/* Skip holes. */
8556 			if (regex->tag < 0) {
8557 				continue;
8558 			}
8559                         regex_match_clear(regex);
8560 		}
8561 		g_array_free(terminal->pvt->match_regexes, TRUE);
8562 	}
8563 
8564 	if (terminal->pvt->search_regex)
8565 		g_regex_unref (terminal->pvt->search_regex);
8566 	if (terminal->pvt->search_attrs)
8567 		g_array_free (terminal->pvt->search_attrs, TRUE);
8568 
8569 	/* Disconnect from autoscroll requests. */
8570 	vte_terminal_stop_autoscroll(terminal);
8571 
8572 	/* Cancel pending adjustment change notifications. */
8573 	terminal->pvt->adjustment_changed_pending = FALSE;
8574 
8575 	/* Tabstop information. */
8576 	if (terminal->pvt->tabstops != NULL) {
8577 		g_hash_table_destroy(terminal->pvt->tabstops);
8578 	}
8579 
8580 	/* Free any selected text, but if we currently own the selection,
8581 	 * throw the text onto the clipboard without an owner so that it
8582 	 * doesn't just disappear. */
8583 	if (terminal->pvt->selection != NULL) {
8584 		clipboard = vte_terminal_clipboard_get(terminal,
8585 						       GDK_SELECTION_PRIMARY);
8586 		if (gtk_clipboard_get_owner(clipboard) == object) {
8587 			gtk_clipboard_set_text(clipboard,
8588 					       terminal->pvt->selection,
8589 					       -1);
8590 		}
8591 		g_free(terminal->pvt->selection);
8592 	}
8593 
8594 	/* Clear the output histories. */
8595 	_vte_ring_fini(terminal->pvt->normal_screen.row_data);
8596 	_vte_ring_fini(terminal->pvt->alternate_screen.row_data);
8597 
8598 	/* Free conversion descriptors. */
8599 	if (terminal->pvt->outgoing_conv != VTE_INVALID_CONV) {
8600 		_vte_conv_close(terminal->pvt->outgoing_conv);
8601 		terminal->pvt->outgoing_conv = VTE_INVALID_CONV;
8602 	}
8603 
8604 	/* Start listening for child-exited signals and ignore them, so that no zombie child is left behind. */
8605         if (terminal->pvt->child_watch_source != 0) {
8606                 g_source_remove (terminal->pvt->child_watch_source);
8607                 terminal->pvt->child_watch_source = 0;
8608                 g_child_watch_add_full(G_PRIORITY_HIGH,
8609                                        terminal->pvt->pty_pid,
8610                                        (GChildWatchFunc)vte_terminal_child_watch_cb,
8611                                        NULL, NULL);
8612         }
8613 
8614 	/* Stop processing input. */
8615 	vte_terminal_stop_processing (terminal);
8616 
8617 	/* Discard any pending data. */
8618 	_vte_incoming_chunks_release (terminal->pvt->incoming);
8619 	_vte_byte_array_free(terminal->pvt->outgoing);
8620 	g_array_free(terminal->pvt->pending, TRUE);
8621 	_vte_byte_array_free(terminal->pvt->conv_buffer);
8622 
8623 	/* Stop the child and stop watching for input from the child. */
8624 	if (terminal->pvt->pty_pid != -1) {
8625 #ifdef HAVE_GETPGID
8626 		pid_t pgrp;
8627 		pgrp = getpgid(terminal->pvt->pty_pid);
8628 		if (pgrp != -1) {
8629 			kill(-pgrp, SIGHUP);
8630 		}
8631 #endif
8632 		kill(terminal->pvt->pty_pid, SIGHUP);
8633 	}
8634 	_vte_terminal_disconnect_pty_read(terminal);
8635 	_vte_terminal_disconnect_pty_write(terminal);
8636 	if (terminal->pvt->pty_channel != NULL) {
8637 		g_io_channel_unref (terminal->pvt->pty_channel);
8638 	}
8639 	if (terminal->pvt->pty != NULL) {
8640                 vte_pty_close(terminal->pvt->pty);
8641                 g_object_unref(terminal->pvt->pty);
8642 	}
8643 
8644 	/* Remove hash tables. */
8645 	if (terminal->pvt->dec_saved != NULL) {
8646 		g_hash_table_destroy(terminal->pvt->dec_saved);
8647 	}
8648 
8649 	/* Clean up emulation structures. */
8650 	if (terminal->pvt->matcher != NULL) {
8651 		_vte_matcher_free(terminal->pvt->matcher);
8652 	}
8653 
8654 	remove_update_timeout (terminal);
8655 
8656 	/* discard title updates */
8657         g_free(terminal->pvt->window_title);
8658         g_free(terminal->pvt->window_title_changed);
8659 	g_free(terminal->pvt->icon_title_changed);
8660         g_free(terminal->pvt->current_directory_uri_changed);
8661         g_free(terminal->pvt->current_directory_uri);
8662         g_free(terminal->pvt->current_file_uri_changed);
8663         g_free(terminal->pvt->current_file_uri);
8664 
8665         /* Word char exceptions */
8666         g_free(terminal->pvt->word_char_exceptions_string);
8667         g_free(terminal->pvt->word_char_exceptions);
8668 
8669 	/* Free public-facing data. */
8670 	g_free(terminal->pvt->icon_title);
8671 	if (terminal->pvt->vadjustment != NULL) {
8672 		g_object_unref(terminal->pvt->vadjustment);
8673 	}
8674 
8675         settings = gtk_widget_get_settings (widget);
8676         g_signal_handlers_disconnect_matched (settings, G_SIGNAL_MATCH_DATA,
8677                                               0, 0, NULL, NULL,
8678                                               terminal);
8679 
8680 	/* Call the inherited finalize() method. */
8681 	G_OBJECT_CLASS(vte_terminal_parent_class)->finalize(object);
8682 }
8683 
8684 /* Handle realizing the widget.  Most of this is copy-paste from GGAD. */
8685 static void
vte_terminal_realize(GtkWidget * widget)8686 vte_terminal_realize(GtkWidget *widget)
8687 {
8688 	GdkWindow *window;
8689 	VteTerminal *terminal;
8690 	GdkWindowAttr attributes;
8691 	GtkAllocation allocation;
8692 	guint attributes_mask = 0;
8693 
8694 	_vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_realize()\n");
8695 
8696 	terminal = VTE_TERMINAL(widget);
8697 	gtk_widget_get_allocation (widget, &allocation);
8698 
8699 	/* Create the stock cursors. */
8700 	terminal->pvt->mouse_cursor_visible = TRUE;
8701 	terminal->pvt->mouse_default_cursor =
8702 		vte_terminal_cursor_new(terminal, VTE_DEFAULT_CURSOR);
8703 	terminal->pvt->mouse_mousing_cursor =
8704 		vte_terminal_cursor_new(terminal, VTE_MOUSING_CURSOR);
8705 
8706 	/* Create a GDK window for the widget. */
8707 	attributes.window_type = GDK_WINDOW_CHILD;
8708 	attributes.x = allocation.x;
8709 	attributes.y = allocation.y;
8710 	attributes.width = allocation.width;
8711 	attributes.height = allocation.height;
8712 	attributes.wclass = GDK_INPUT_OUTPUT;
8713 	attributes.visual = gtk_widget_get_visual (widget);
8714 	attributes.event_mask = gtk_widget_get_events(widget) |
8715 				GDK_EXPOSURE_MASK |
8716 				GDK_VISIBILITY_NOTIFY_MASK |
8717 				GDK_FOCUS_CHANGE_MASK |
8718 #if GTK_CHECK_VERSION (3, 4, 0)
8719 				GDK_SMOOTH_SCROLL_MASK |
8720 #endif
8721 				GDK_SCROLL_MASK |
8722 				GDK_BUTTON_PRESS_MASK |
8723 				GDK_BUTTON_RELEASE_MASK |
8724 				GDK_POINTER_MOTION_MASK |
8725 				GDK_BUTTON1_MOTION_MASK |
8726 				GDK_ENTER_NOTIFY_MASK |
8727 				GDK_LEAVE_NOTIFY_MASK |
8728 				GDK_KEY_PRESS_MASK |
8729 				GDK_KEY_RELEASE_MASK;
8730 	attributes.cursor = terminal->pvt->mouse_default_cursor;
8731 	attributes_mask = GDK_WA_X |
8732 			  GDK_WA_Y |
8733 			  (attributes.visual ? GDK_WA_VISUAL : 0) |
8734 			  GDK_WA_CURSOR;
8735 
8736 	window = gdk_window_new (gtk_widget_get_parent_window (widget),
8737 				 &attributes, attributes_mask);
8738 
8739 	gtk_widget_set_window (widget, window);
8740 	gdk_window_set_user_data (window, widget);
8741 	gtk_style_context_set_background(gtk_widget_get_style_context(widget), window);
8742 	_VTE_DEBUG_IF (VTE_DEBUG_UPDATES) gdk_window_set_debug_updates (TRUE);
8743 
8744 	/* Set the realized flag. */
8745 	gtk_widget_set_realized (widget, TRUE);
8746 
8747 	/* Create rendering data if this is a re-realise */
8748         if (terminal->pvt->draw == NULL) {
8749                 terminal->pvt->draw = _vte_draw_new();
8750         }
8751 
8752 	/* Set up input method support.  FIXME: do we need to handle the
8753 	 * "retrieve-surrounding" and "delete-surrounding" events? */
8754 	if (terminal->pvt->im_context != NULL) {
8755 		vte_terminal_im_reset(terminal);
8756 		g_object_unref(terminal->pvt->im_context);
8757 		terminal->pvt->im_context = NULL;
8758 	}
8759 	terminal->pvt->im_preedit_active = FALSE;
8760 	terminal->pvt->im_context = gtk_im_multicontext_new();
8761 	gtk_im_context_set_client_window (terminal->pvt->im_context, window);
8762 	g_signal_connect(terminal->pvt->im_context, "commit",
8763 			 G_CALLBACK(vte_terminal_im_commit), terminal);
8764 	g_signal_connect(terminal->pvt->im_context, "preedit-start",
8765 			 G_CALLBACK(vte_terminal_im_preedit_start),
8766 			 terminal);
8767 	g_signal_connect(terminal->pvt->im_context, "preedit-changed",
8768 			 G_CALLBACK(vte_terminal_im_preedit_changed),
8769 			 terminal);
8770 	g_signal_connect(terminal->pvt->im_context, "preedit-end",
8771 			 G_CALLBACK(vte_terminal_im_preedit_end),
8772 			 terminal);
8773 	gtk_im_context_set_use_preedit(terminal->pvt->im_context, TRUE);
8774 
8775 	/* Clear modifiers. */
8776 	terminal->pvt->modifiers = 0;
8777 
8778 	/* Create our invisible cursor. */
8779 	terminal->pvt->mouse_inviso_cursor = gdk_cursor_new_for_display(gtk_widget_get_display(widget), GDK_BLANK_CURSOR);
8780 
8781         /* Make sure the style is set, bug 727614. */
8782         vte_terminal_style_updated (widget);
8783 
8784 	vte_terminal_ensure_font (terminal);
8785 
8786 	/* Set up the background, *now*. */
8787 	vte_terminal_background_update(terminal);
8788 }
8789 
8790 static inline void
swap(guint * a,guint * b)8791 swap (guint *a, guint *b)
8792 {
8793 	guint tmp;
8794 	tmp = *a, *a = *b, *b = tmp;
8795 }
8796 
8797 static void
vte_terminal_determine_colors_internal(VteTerminal * terminal,const VteCell * cell,gboolean selected,gboolean cursor,guint * pfore,guint * pback)8798 vte_terminal_determine_colors_internal(VteTerminal *terminal,
8799 				       const VteCell *cell,
8800 				       gboolean selected,
8801 				       gboolean cursor,
8802 				       guint *pfore, guint *pback)
8803 {
8804 	guint fore, back;
8805 
8806 	if (!cell)
8807 		cell = &basic_cell.cell;
8808 
8809 	/* Start with cell colors */
8810 	fore = cell->attr.fore;
8811 	back = cell->attr.back;
8812 
8813 	/* Reverse-mode switches default fore and back colors */
8814         if (G_UNLIKELY (terminal->pvt->reverse_mode)) {
8815 		if (fore == VTE_DEFAULT_FG)
8816 			fore = VTE_DEFAULT_BG;
8817 		if (back == VTE_DEFAULT_BG)
8818 			back = VTE_DEFAULT_FG;
8819 	}
8820 
8821 	/* Handle bold by using set bold color or brightening */
8822 	if (cell->attr.bold) {
8823 		if (fore == VTE_DEFAULT_FG)
8824 			fore = VTE_BOLD_FG;
8825 		else if (fore >= VTE_LEGACY_COLORS_OFFSET && fore < VTE_LEGACY_COLORS_OFFSET + VTE_LEGACY_COLOR_SET_SIZE) {
8826 			fore += VTE_COLOR_BRIGHT_OFFSET;
8827 		}
8828 	}
8829 
8830         /* Handle dim colors.  Only apply to palette colors, dimming direct RGB wouldn't make sense.
8831          * Apply to the foreground color only, but do this before handling reverse/highlight so that
8832          * those can be used to dim the background instead. */
8833         if (cell->attr.dim && !(fore & VTE_RGB_COLOR)) {
8834 	        fore |= VTE_DIM_COLOR;
8835         }
8836 
8837 	/* Reverse cell? */
8838 	if (cell->attr.reverse) {
8839 		swap (&fore, &back);
8840 	}
8841 
8842 	/* Selection: use hightlight back/fore, or inverse */
8843 	if (selected) {
8844 		/* XXX what if hightlight back is same color as current back? */
8845 		gboolean do_swap = TRUE;
8846 		if (_vte_terminal_get_color(terminal, VTE_HIGHLIGHT_BG) != NULL) {
8847 			back = VTE_HIGHLIGHT_BG;
8848 			do_swap = FALSE;
8849 		}
8850 		if (_vte_terminal_get_color(terminal, VTE_HIGHLIGHT_FG) != NULL) {
8851 			fore = VTE_HIGHLIGHT_FG;
8852 			do_swap = FALSE;
8853 		}
8854 		if (do_swap)
8855 			swap (&fore, &back);
8856 	}
8857 
8858 	/* Cursor: use cursor back, or inverse */
8859 	if (cursor) {
8860 		/* XXX what if cursor back is same color as current back? */
8861 		if (_vte_terminal_get_color(terminal, VTE_CURSOR_BG) != NULL)
8862 			back = VTE_CURSOR_BG;
8863 		else
8864 			swap (&fore, &back);
8865 	}
8866 
8867 	/* Invisible? */
8868 	if (cell && cell->attr.invisible) {
8869 		fore = back;
8870 	}
8871 
8872 	*pfore = fore;
8873 	*pback = back;
8874 }
8875 
8876 static inline void
vte_terminal_determine_colors(VteTerminal * terminal,const VteCell * cell,gboolean highlight,guint * fore,guint * back)8877 vte_terminal_determine_colors (VteTerminal *terminal,
8878 			       const VteCell *cell,
8879 			       gboolean highlight,
8880 			       guint *fore, guint *back)
8881 {
8882 	vte_terminal_determine_colors_internal (terminal, cell,
8883 						       highlight, FALSE,
8884 						       fore, back);
8885 }
8886 
8887 static inline void
vte_terminal_determine_cursor_colors(VteTerminal * terminal,const VteCell * cell,gboolean highlight,guint * fore,guint * back)8888 vte_terminal_determine_cursor_colors (VteTerminal *terminal,
8889 				      const VteCell *cell,
8890 				      gboolean highlight,
8891 				      guint *fore, guint *back)
8892 {
8893 	vte_terminal_determine_colors_internal (terminal, cell,
8894 						       highlight, TRUE,
8895 						       fore, back);
8896 }
8897 
8898 static void
vte_terminal_fill_rectangle(VteTerminal * terminal,const PangoColor * color,gint x,gint y,gint width,gint height)8899 vte_terminal_fill_rectangle(VteTerminal *terminal,
8900 			    const PangoColor *color,
8901 			    gint x,
8902 			    gint y,
8903 			    gint width,
8904 			    gint height)
8905 {
8906 	_vte_draw_fill_rectangle(terminal->pvt->draw,
8907 				 x + terminal->pvt->padding.left,
8908                                  y + terminal->pvt->padding.top,
8909 				 width, height,
8910 				 color, VTE_DRAW_OPAQUE);
8911 }
8912 
8913 static void
vte_terminal_draw_line(VteTerminal * terminal,const PangoColor * color,gint x,gint y,gint xp,gint yp)8914 vte_terminal_draw_line(VteTerminal *terminal,
8915 		       const PangoColor *color,
8916 		       gint x,
8917 		       gint y,
8918 		       gint xp,
8919 		       gint yp)
8920 {
8921 	vte_terminal_fill_rectangle(terminal, color,
8922 				    x, y,
8923 				    MAX(VTE_LINE_WIDTH, xp - x + 1), MAX(VTE_LINE_WIDTH, yp - y + 1));
8924 }
8925 
8926 static void
vte_terminal_draw_rectangle(VteTerminal * terminal,const PangoColor * color,gint x,gint y,gint width,gint height)8927 vte_terminal_draw_rectangle(VteTerminal *terminal,
8928 			    const PangoColor *color,
8929 			    gint x,
8930 			    gint y,
8931 			    gint width,
8932 			    gint height)
8933 {
8934 	_vte_draw_draw_rectangle(terminal->pvt->draw,
8935 				 x + terminal->pvt->padding.left,
8936                                  y + terminal->pvt->padding.top,
8937 				 width, height,
8938 				 color, VTE_DRAW_OPAQUE);
8939 }
8940 
8941 /* Draw a string of characters with similar attributes. */
8942 static void
vte_terminal_draw_cells(VteTerminal * terminal,struct _vte_draw_text_request * items,gssize n,guint fore,guint back,gboolean clear,gboolean draw_default_bg,gboolean bold,gboolean italic,gboolean underline,gboolean strikethrough,gboolean hilite,gboolean boxed,gint column_width,gint row_height)8943 vte_terminal_draw_cells(VteTerminal *terminal,
8944 			struct _vte_draw_text_request *items, gssize n,
8945 			guint fore, guint back, gboolean clear,
8946 			gboolean draw_default_bg,
8947 			gboolean bold, gboolean italic, gboolean underline,
8948 			gboolean strikethrough, gboolean hilite, gboolean boxed,
8949 			gint column_width, gint row_height)
8950 {
8951 	int i, x, y;
8952 	gint columns = 0;
8953 	PangoColor fg, bg;
8954 
8955 	g_assert(n > 0);
8956 	_VTE_DEBUG_IF(VTE_DEBUG_CELLS) {
8957 		GString *str = g_string_new (NULL);
8958 		gchar *tmp;
8959 		for (i = 0; i < n; i++) {
8960 			g_string_append_unichar (str, items[i].c);
8961 		}
8962 		tmp = g_string_free (str, FALSE);
8963 		g_printerr ("draw_cells('%s', fore=%d, back=%d, bold=%d,"
8964 				" ul=%d, strike=%d, hilite=%d, boxed=%d)\n",
8965 				tmp, fore, back, bold,
8966 				underline, strikethrough, hilite, boxed);
8967 		g_free (tmp);
8968 	}
8969 
8970 	bold = bold && terminal->pvt->allow_bold;
8971 	vte_terminal_get_rgb_from_index(terminal, fore, &fg);
8972 	vte_terminal_get_rgb_from_index(terminal, back, &bg);
8973 
8974 	i = 0;
8975 	do {
8976 		columns = 0;
8977 		x = items[i].x;
8978 		y = items[i].y;
8979 		for (; i < n && items[i].y == y; i++) {
8980 			/* Adjust for the border. */
8981 			items[i].x += terminal->pvt->padding.left;
8982 			items[i].y += terminal->pvt->padding.top;
8983 			columns += items[i].columns;
8984 		}
8985 		if (clear && (draw_default_bg || back != VTE_DEFAULT_BG)) {
8986 			gint bold_offset = _vte_draw_has_bold(terminal->pvt->draw,
8987 									VTE_DRAW_BOLD) ? 0 : bold;
8988 			_vte_draw_fill_rectangle(terminal->pvt->draw,
8989 					x + terminal->pvt->padding.left,
8990                                         y + terminal->pvt->padding.top,
8991 					columns * column_width + bold_offset, row_height,
8992 					&bg, VTE_DRAW_OPAQUE);
8993 		}
8994 	} while (i < n);
8995 
8996 	_vte_draw_text(terminal->pvt->draw,
8997 			items, n,
8998 			&fg, VTE_DRAW_OPAQUE,
8999 			_vte_draw_get_style(bold, italic));
9000 
9001 	for (i = 0; i < n; i++) {
9002 		/* Deadjust for the border. */
9003 		items[i].x -= terminal->pvt->padding.left;
9004 		items[i].y -= terminal->pvt->padding.top;
9005 	}
9006 
9007 	/* Draw whatever SFX are required. */
9008 	if (underline | strikethrough | hilite | boxed) {
9009 		i = 0;
9010 		do {
9011 			x = items[i].x;
9012 			y = items[i].y;
9013 			for (columns = 0; i < n && items[i].y == y; i++) {
9014 				columns += items[i].columns;
9015 			}
9016 			if (underline) {
9017 				vte_terminal_draw_line(terminal,
9018 						&fg,
9019 						x,
9020 						y + terminal->pvt->underline_position,
9021 						x + (columns * column_width) - 1,
9022 						y + terminal->pvt->underline_position + terminal->pvt->line_thickness - 1);
9023 			}
9024 			if (strikethrough) {
9025 				vte_terminal_draw_line(terminal,
9026 						&fg,
9027 						x,
9028 						y + terminal->pvt->strikethrough_position,
9029 						x + (columns * column_width) - 1,
9030 						y + terminal->pvt->strikethrough_position + terminal->pvt->line_thickness - 1);
9031 			}
9032 			if (hilite) {
9033 				vte_terminal_draw_line(terminal,
9034 						&fg,
9035 						x,
9036 						y + row_height - 1,
9037 						x + (columns * column_width) - 1,
9038 						y + row_height - 1);
9039 			}
9040 			if (boxed) {
9041 				vte_terminal_draw_rectangle(terminal,
9042 						&fg,
9043 						x, y,
9044 						MAX(0, (columns * column_width)),
9045 						MAX(0, row_height));
9046 			}
9047 		}while (i < n);
9048 	}
9049 }
9050 
9051 /* FIXME: we don't have a way to tell GTK+ what the default text attributes
9052  * should be, so for now at least it's assuming white-on-black is the norm and
9053  * is using "black-on-white" to signify "inverse".  Pick up on that state and
9054  * fix things.  Do this here, so that if we suddenly get red-on-black, we'll do
9055  * the right thing. */
9056 static void
_vte_terminal_fudge_pango_colors(VteTerminal * terminal,GSList * attributes,VteCell * cells,gssize n)9057 _vte_terminal_fudge_pango_colors(VteTerminal *terminal, GSList *attributes,
9058 				 VteCell *cells, gssize n)
9059 {
9060 	int i, sumlen = 0;
9061 	struct _fudge_cell_props{
9062 		gboolean saw_fg, saw_bg;
9063 		PangoColor fg, bg;
9064 		guint index;
9065 	}*props = g_newa (struct _fudge_cell_props, n);
9066 
9067 	for (i = 0; i < n; i++) {
9068 		gchar ubuf[7];
9069 		gint len = g_unichar_to_utf8 (cells[i].c, ubuf);
9070 		props[i].index = sumlen;
9071 		props[i].saw_fg = props[i].saw_bg = FALSE;
9072 		sumlen += len;
9073 	}
9074 
9075 	while (attributes != NULL) {
9076 		PangoAttribute *attr = attributes->data;
9077 		PangoAttrColor *color;
9078 		switch (attr->klass->type) {
9079 		case PANGO_ATTR_FOREGROUND:
9080 			for (i = 0; i < n; i++) {
9081 				if (props[i].index < attr->start_index) {
9082 					continue;
9083 				}
9084 				if (props[i].index >= attr->end_index) {
9085 					break;
9086 				}
9087 				props[i].saw_fg = TRUE;
9088 				color = (PangoAttrColor*) attr;
9089 				props[i].fg = color->color;
9090 			}
9091 			break;
9092 		case PANGO_ATTR_BACKGROUND:
9093 			for (i = 0; i < n; i++) {
9094 				if (props[i].index < attr->start_index) {
9095 					continue;
9096 				}
9097 				if (props[i].index >= attr->end_index) {
9098 					break;
9099 				}
9100 				props[i].saw_bg = TRUE;
9101 				color = (PangoAttrColor*) attr;
9102 				props[i].bg = color->color;
9103 			}
9104 			break;
9105 		default:
9106 			break;
9107 		}
9108 		attributes = g_slist_next(attributes);
9109 	}
9110 
9111 	for (i = 0; i < n; i++) {
9112 		if (props[i].saw_fg && props[i].saw_bg &&
9113 				(props[i].fg.red == 0xffff) &&
9114 				(props[i].fg.green == 0xffff) &&
9115 				(props[i].fg.blue == 0xffff) &&
9116 				(props[i].bg.red == 0) &&
9117 				(props[i].bg.green == 0) &&
9118 				(props[i].bg.blue == 0)) {
9119                         cells[i].attr.fore = terminal->pvt->color_defaults.attr.fore;
9120                         cells[i].attr.back = terminal->pvt->color_defaults.attr.back;
9121 			cells[i].attr.reverse = TRUE;
9122 		}
9123 	}
9124 }
9125 
9126 /* Apply the attribute given in the PangoAttribute to the list of cells. */
9127 static void
_vte_terminal_apply_pango_attr(VteTerminal * terminal,PangoAttribute * attr,VteCell * cells,guint n_cells)9128 _vte_terminal_apply_pango_attr(VteTerminal *terminal, PangoAttribute *attr,
9129 			       VteCell *cells, guint n_cells)
9130 {
9131 	guint i, ival;
9132 	PangoAttrInt *attrint;
9133 	PangoAttrColor *attrcolor;
9134 
9135 	switch (attr->klass->type) {
9136 	case PANGO_ATTR_FOREGROUND:
9137 	case PANGO_ATTR_BACKGROUND:
9138 		attrcolor = (PangoAttrColor*) attr;
9139 		ival = VTE_RGB_COLOR |
9140 		       ((attrcolor->color.red & 0xFF00) << 8) |
9141 		       ((attrcolor->color.green & 0xFF00)) |
9142 		       ((attrcolor->color.blue & 0xFF00) >> 8);
9143 		for (i = attr->start_index;
9144 		     i < attr->end_index && i < n_cells;
9145 		     i++) {
9146 			if (attr->klass->type == PANGO_ATTR_FOREGROUND) {
9147 				cells[i].attr.fore = ival;
9148 			}
9149 			if (attr->klass->type == PANGO_ATTR_BACKGROUND) {
9150 				cells[i].attr.back = ival;
9151 			}
9152 		}
9153 		break;
9154 	case PANGO_ATTR_STRIKETHROUGH:
9155 		attrint = (PangoAttrInt*) attr;
9156 		ival = attrint->value;
9157 		for (i = attr->start_index;
9158 		     (i < attr->end_index) && (i < n_cells);
9159 		     i++) {
9160 			cells[i].attr.strikethrough = (ival != FALSE);
9161 		}
9162 		break;
9163 	case PANGO_ATTR_UNDERLINE:
9164 		attrint = (PangoAttrInt*) attr;
9165 		ival = attrint->value;
9166 		for (i = attr->start_index;
9167 		     (i < attr->end_index) && (i < n_cells);
9168 		     i++) {
9169 			cells[i].attr.underline = (ival != PANGO_UNDERLINE_NONE);
9170 		}
9171 		break;
9172 	case PANGO_ATTR_WEIGHT:
9173 		attrint = (PangoAttrInt*) attr;
9174 		ival = attrint->value;
9175 		for (i = attr->start_index;
9176 		     (i < attr->end_index) && (i < n_cells);
9177 		     i++) {
9178 			cells[i].attr.bold = (ival >= PANGO_WEIGHT_BOLD);
9179 		}
9180 		break;
9181 	default:
9182 		break;
9183 	}
9184 }
9185 
9186 /* Convert a PangoAttrList and a location in that list to settings in a
9187  * charcell structure.  The cells array is assumed to contain enough items
9188  * so that all ranges in the attribute list can be mapped into the array, which
9189  * typically means that the cell array should have the same length as the
9190  * string (byte-wise) which the attributes describe. */
9191 static void
_vte_terminal_pango_attribute_destroy(gpointer attr,gpointer data)9192 _vte_terminal_pango_attribute_destroy(gpointer attr, gpointer data)
9193 {
9194 	pango_attribute_destroy(attr);
9195 }
9196 static void
_vte_terminal_translate_pango_cells(VteTerminal * terminal,PangoAttrList * attrs,VteCell * cells,guint n_cells)9197 _vte_terminal_translate_pango_cells(VteTerminal *terminal, PangoAttrList *attrs,
9198 				    VteCell *cells, guint n_cells)
9199 {
9200 	PangoAttribute *attr;
9201 	PangoAttrIterator *attriter;
9202 	GSList *list, *listiter;
9203 	guint i;
9204 
9205 	for (i = 0; i < n_cells; i++) {
9206                 cells[i] = terminal->pvt->fill_defaults;
9207 	}
9208 
9209 	attriter = pango_attr_list_get_iterator(attrs);
9210 	if (attriter != NULL) {
9211 		do {
9212 			list = pango_attr_iterator_get_attrs(attriter);
9213 			if (list != NULL) {
9214 				for (listiter = list;
9215 				     listiter != NULL;
9216 				     listiter = g_slist_next(listiter)) {
9217 					attr = listiter->data;
9218 					_vte_terminal_apply_pango_attr(terminal,
9219 								       attr,
9220 								       cells,
9221 								       n_cells);
9222 				}
9223 				attr = list->data;
9224 				_vte_terminal_fudge_pango_colors(terminal,
9225 								 list,
9226 								 cells +
9227 								 attr->start_index,
9228 								 attr->end_index -
9229 								 attr->start_index);
9230 				g_slist_foreach(list,
9231 						_vte_terminal_pango_attribute_destroy,
9232 						NULL);
9233 				g_slist_free(list);
9234 			}
9235 		} while (pango_attr_iterator_next(attriter) == TRUE);
9236 		pango_attr_iterator_destroy(attriter);
9237 	}
9238 }
9239 
9240 /* Draw the listed items using the given attributes.  Tricky because the
9241  * attribute string is indexed by byte in the UTF-8 representation of the string
9242  * of characters.  Because we draw a character at a time, this is slower. */
9243 static void
vte_terminal_draw_cells_with_attributes(VteTerminal * terminal,struct _vte_draw_text_request * items,gssize n,PangoAttrList * attrs,gboolean draw_default_bg,gint column_width,gint height)9244 vte_terminal_draw_cells_with_attributes(VteTerminal *terminal,
9245 					struct _vte_draw_text_request *items,
9246 					gssize n,
9247 					PangoAttrList *attrs,
9248 					gboolean draw_default_bg,
9249 					gint column_width, gint height)
9250 {
9251 	int i, j, cell_count;
9252 	VteCell *cells;
9253 	char scratch_buf[VTE_UTF8_BPC];
9254 	guint fore, back;
9255 
9256 	/* Note: since this function is only called with the pre-edit text,
9257 	 * all the items contain gunichar only, not vteunistr. */
9258 
9259 	for (i = 0, cell_count = 0; i < n; i++) {
9260 		cell_count += g_unichar_to_utf8(items[i].c, scratch_buf);
9261 	}
9262 	cells = g_new(VteCell, cell_count);
9263 	_vte_terminal_translate_pango_cells(terminal, attrs, cells, cell_count);
9264 	for (i = 0, j = 0; i < n; i++) {
9265 		vte_terminal_determine_colors(terminal, &cells[j], FALSE, &fore, &back);
9266 		vte_terminal_draw_cells(terminal, items + i, 1,
9267 					fore,
9268 					back,
9269 					TRUE, draw_default_bg,
9270 					cells[j].attr.bold,
9271 					cells[j].attr.italic,
9272 					cells[j].attr.underline,
9273 					cells[j].attr.strikethrough,
9274 					FALSE, FALSE, column_width, height);
9275 		j += g_unichar_to_utf8(items[i].c, scratch_buf);
9276 	}
9277 	g_free(cells);
9278 }
9279 
9280 
9281 /* Paint the contents of a given row at the given location.  Take advantage
9282  * of multiple-draw APIs by finding runs of characters with identical
9283  * attributes and bundling them together. */
9284 static void
vte_terminal_draw_rows(VteTerminal * terminal,VteScreen * screen,gint start_row,gint row_count,gint start_column,gint column_count,gint start_x,gint start_y,gint column_width,gint row_height)9285 vte_terminal_draw_rows(VteTerminal *terminal,
9286 		      VteScreen *screen,
9287 		      gint start_row, gint row_count,
9288 		      gint start_column, gint column_count,
9289 		      gint start_x, gint start_y,
9290 		      gint column_width, gint row_height)
9291 {
9292 	struct _vte_draw_text_request items[4*VTE_DRAW_MAX_LENGTH];
9293 	gint i, j, row, rows, x, y, end_column;
9294 	guint fore, nfore, back, nback;
9295 	gboolean underline, nunderline, bold, nbold, italic, nitalic, hilite, nhilite,
9296 		 selected, nselected, strikethrough, nstrikethrough;
9297 	guint item_count;
9298 	const VteCell *cell;
9299 	const VteRowData *row_data;
9300 
9301 	/* adjust for the absolute start of row */
9302 	start_x -= start_column * column_width;
9303 	end_column = start_column + column_count;
9304 
9305 	/* clear the background */
9306 	x = start_x + terminal->pvt->padding.left;
9307 	y = start_y + terminal->pvt->padding.top;
9308 	row = start_row;
9309 	rows = row_count;
9310 	do {
9311 		row_data = _vte_terminal_find_row_data(terminal, row);
9312 		/* Back up in case this is a multicolumn character,
9313 		 * making the drawing area a little wider. */
9314 		i = start_column;
9315 		if (row_data != NULL) {
9316 			cell = _vte_row_data_get (row_data, i);
9317 			if (cell != NULL) {
9318 				while (cell->attr.fragment && i > 0) {
9319 					cell = _vte_row_data_get (row_data, --i);
9320 				}
9321 			}
9322 			/* Walk the line. */
9323 			do {
9324 				/* Get the character cell's contents. */
9325 				cell = _vte_row_data_get (row_data, i);
9326 				/* Find the colors for this cell. */
9327 				selected = vte_cell_is_selected(terminal, i, row, NULL);
9328 				vte_terminal_determine_colors(terminal, cell, selected, &fore, &back);
9329 
9330 				bold = cell && cell->attr.bold;
9331 				j = i + (cell ? cell->attr.columns : 1);
9332 
9333 				while (j < end_column){
9334 					/* Retrieve the cell. */
9335 					cell = _vte_row_data_get (row_data, j);
9336 					/* Don't render fragments of multicolumn characters
9337 					 * which have the same attributes as the initial
9338 					 * portions. */
9339 					if (cell != NULL && cell->attr.fragment) {
9340 						j++;
9341 						continue;
9342 					}
9343 					/* Resolve attributes to colors where possible and
9344 					 * compare visual attributes to the first character
9345 					 * in this chunk. */
9346 					selected = vte_cell_is_selected(terminal, j, row, NULL);
9347 					vte_terminal_determine_colors(terminal, cell, selected, &nfore, &nback);
9348 					if (nback != back) {
9349 						break;
9350 					}
9351 					bold = cell && cell->attr.bold;
9352 					j += cell ? cell->attr.columns : 1;
9353 				}
9354 				if (back != VTE_DEFAULT_BG) {
9355 					PangoColor bg;
9356 					gint bold_offset = _vte_draw_has_bold(terminal->pvt->draw,
9357 											VTE_DRAW_BOLD) ? 0 : bold;
9358 					vte_terminal_get_rgb_from_index(terminal, back, &bg);
9359 					_vte_draw_fill_rectangle (
9360 							terminal->pvt->draw,
9361 							x + i * column_width,
9362 							y,
9363 							(j - i) * column_width + bold_offset,
9364 							row_height,
9365 							&bg, VTE_DRAW_OPAQUE);
9366 				}
9367 				/* We'll need to continue at the first cell which didn't
9368 				 * match the first one in this set. */
9369 				i = j;
9370 			} while (i < end_column);
9371 		} else {
9372 			do {
9373 				selected = vte_cell_is_selected(terminal, i, row, NULL);
9374 				j = i + 1;
9375 				while (j < end_column){
9376 					nselected = vte_cell_is_selected(terminal, j, row, NULL);
9377 					if (nselected != selected) {
9378 						break;
9379 					}
9380 					j++;
9381 				}
9382 				vte_terminal_determine_colors(terminal, NULL, selected, &fore, &back);
9383 				if (back != VTE_DEFAULT_BG) {
9384 					PangoColor bg;
9385 					vte_terminal_get_rgb_from_index(terminal, back, &bg);
9386 					_vte_draw_fill_rectangle (terminal->pvt->draw,
9387 								  x + i *column_width,
9388 								  y,
9389 								  (j - i)  * column_width,
9390 								  row_height,
9391 								  &bg, VTE_DRAW_OPAQUE);
9392 				}
9393 				i = j;
9394 			} while (i < end_column);
9395 		}
9396 		row++;
9397 		y += row_height;
9398 	} while (--rows);
9399 
9400 
9401 	/* render the text */
9402 	y = start_y;
9403 	row = start_row;
9404 	rows = row_count;
9405 	item_count = 1;
9406 	do {
9407 		row_data = _vte_terminal_find_row_data(terminal, row);
9408 		if (row_data == NULL) {
9409 			goto fg_skip_row;
9410 		}
9411 		/* Back up in case this is a multicolumn character,
9412 		 * making the drawing area a little wider. */
9413 		i = start_column;
9414 		cell = _vte_row_data_get (row_data, i);
9415 		if (cell == NULL) {
9416 			goto fg_skip_row;
9417 		}
9418 		while (cell->attr.fragment && i > 0)
9419 			cell = _vte_row_data_get (row_data, --i);
9420 
9421 		/* Walk the line. */
9422 		do {
9423 			/* Get the character cell's contents. */
9424 			cell = _vte_row_data_get (row_data, i);
9425 			if (cell == NULL) {
9426 				goto fg_skip_row;
9427 			}
9428 			while (cell->c == 0 || cell->attr.invisible ||
9429 					(cell->c == ' ' &&
9430 					 !cell->attr.underline &&
9431 					 !cell->attr.strikethrough) ||
9432 					cell->attr.fragment) {
9433 				if (++i >= end_column) {
9434 					goto fg_skip_row;
9435 				}
9436 				cell = _vte_row_data_get (row_data, i);
9437 				if (cell == NULL) {
9438 					goto fg_skip_row;
9439 				}
9440 			}
9441 			/* Find the colors for this cell. */
9442 			selected = vte_cell_is_selected(terminal, i, row, NULL);
9443 			vte_terminal_determine_colors(terminal, cell, selected, &fore, &back);
9444 			underline = cell->attr.underline;
9445 			strikethrough = cell->attr.strikethrough;
9446 			bold = cell->attr.bold;
9447 			italic = cell->attr.italic;
9448 			if (terminal->pvt->show_match) {
9449 				hilite = vte_cell_is_between(i, row,
9450 						terminal->pvt->match_start.col,
9451 						terminal->pvt->match_start.row,
9452 						terminal->pvt->match_end.col,
9453 						terminal->pvt->match_end.row,
9454 						TRUE);
9455 			} else {
9456 				hilite = FALSE;
9457 			}
9458 
9459 			items[0].c = cell->c;
9460 			items[0].columns = cell->attr.columns;
9461 			items[0].x = start_x + i * column_width;
9462 			items[0].y = y;
9463 			j = i + items[0].columns;
9464 
9465 			/* Now find out how many cells have the same attributes. */
9466 			do {
9467 				while (j < end_column &&
9468 						item_count < G_N_ELEMENTS(items)) {
9469 					/* Retrieve the cell. */
9470 					cell = _vte_row_data_get (row_data, j);
9471 					if (cell == NULL) {
9472 						goto fg_next_row;
9473 					}
9474 					/* Don't render blank cells or fragments of multicolumn characters
9475 					 * which have the same attributes as the initial
9476 					 * portions.  Don't render invisible cells */
9477 					if (cell->attr.fragment || cell->attr.invisible) {
9478 						j++;
9479 						continue;
9480 					}
9481 					if (cell->c == 0){
9482 						/* only break the run if we
9483 						 * are drawing attributes
9484 						 */
9485 						if (underline || strikethrough || hilite) {
9486 							break;
9487 						} else {
9488 							j++;
9489 							continue;
9490 						}
9491 					}
9492 					/* Resolve attributes to colors where possible and
9493 					 * compare visual attributes to the first character
9494 					 * in this chunk. */
9495 					selected = vte_cell_is_selected(terminal, j, row, NULL);
9496 					vte_terminal_determine_colors(terminal, cell, selected, &nfore, &nback);
9497 					if (nfore != fore) {
9498 						break;
9499 					}
9500 					nbold = cell->attr.bold;
9501 					if (nbold != bold) {
9502 						break;
9503 					}
9504 					nitalic = cell->attr.italic;
9505 					if (nitalic != italic) {
9506 						break;
9507 					}
9508 					/* Break up underlined/not-underlined text. */
9509 					nunderline = cell->attr.underline;
9510 					if (nunderline != underline) {
9511 						break;
9512 					}
9513 					nstrikethrough = cell->attr.strikethrough;
9514 					if (nstrikethrough != strikethrough) {
9515 						break;
9516 					}
9517 					/* Break up matched/not-matched text. */
9518 					nhilite = FALSE;
9519 					if (terminal->pvt->show_match) {
9520 						nhilite = vte_cell_is_between(j, row,
9521 								terminal->pvt->match_start.col,
9522 								terminal->pvt->match_start.row,
9523 								terminal->pvt->match_end.col,
9524 								terminal->pvt->match_end.row,
9525 								TRUE);
9526 					}
9527 					if (nhilite != hilite) {
9528 						break;
9529 					}
9530 					/* Add this cell to the draw list. */
9531 					items[item_count].c = cell->c;
9532 					items[item_count].columns = cell->attr.columns;
9533 					items[item_count].x = start_x + j * column_width;
9534 					items[item_count].y = y;
9535 					j +=  items[item_count].columns;
9536 					item_count++;
9537 				}
9538 				/* have we encountered a state change? */
9539 				if (j < end_column) {
9540 					break;
9541 				}
9542 fg_next_row:
9543 				/* is this the last column, on the last row? */
9544 				do {
9545 					do {
9546 						if (!--rows) {
9547 							goto fg_draw;
9548 						}
9549 
9550 						/* restart on the next row */
9551 						row++;
9552 						y += row_height;
9553 						row_data = _vte_terminal_find_row_data(terminal, row);
9554 					} while (row_data == NULL);
9555 
9556 					/* Back up in case this is a
9557 					 * multicolumn character, making the drawing
9558 					 * area a little wider. */
9559 					j = start_column;
9560 					cell = _vte_row_data_get (row_data, j);
9561 				} while (cell == NULL);
9562 				while (cell->attr.fragment && j > 0) {
9563 					cell = _vte_row_data_get (row_data, --j);
9564 				}
9565 			} while (TRUE);
9566 fg_draw:
9567 			/* Draw the cells. */
9568 			vte_terminal_draw_cells(terminal,
9569 					items,
9570 					item_count,
9571 					fore, back, FALSE, FALSE,
9572 					bold, italic, underline,
9573 					strikethrough, hilite, FALSE,
9574 					column_width, row_height);
9575 			item_count = 1;
9576 			/* We'll need to continue at the first cell which didn't
9577 			 * match the first one in this set. */
9578 			i = j;
9579 			if (!rows) {
9580 				goto fg_out;
9581 			}
9582 		} while (i < end_column);
9583 fg_skip_row:
9584 		row++;
9585 		y += row_height;
9586 	} while (--rows);
9587 fg_out:
9588 	return;
9589 }
9590 
9591 static void
vte_terminal_expand_region(VteTerminal * terminal,cairo_region_t * region,const GdkRectangle * area)9592 vte_terminal_expand_region (VteTerminal *terminal, cairo_region_t *region, const GdkRectangle *area)
9593 {
9594 	int width, height;
9595 	int row, col, row_stop, col_stop;
9596 	cairo_rectangle_int_t rect;
9597 
9598 	width = terminal->pvt->char_width;
9599 	height = terminal->pvt->char_height;
9600 
9601 	/* increase the paint by one pixel on all sides to force the
9602 	 * inclusion of neighbouring cells */
9603 	row = MAX(0, (area->y - terminal->pvt->padding.top - 1) / height);
9604 	row_stop = MIN(howmany(area->height + area->y - terminal->pvt->padding.top + 1, height),
9605 		       terminal->pvt->row_count);
9606 	if (row_stop <= row) {
9607 		return;
9608 	}
9609 	col = MAX(0, (area->x - terminal->pvt->padding.left - 1) / width);
9610 	col_stop = MIN(howmany(area->width + area->x - terminal->pvt->padding.left + 1, width),
9611 		       terminal->pvt->column_count);
9612 	if (col_stop <= col) {
9613 		return;
9614 	}
9615 
9616 	rect.x = col*width + terminal->pvt->padding.left;
9617 	rect.width = (col_stop - col) * width;
9618 
9619 	rect.y = row*height + terminal->pvt->padding.top;
9620 	rect.height = (row_stop - row)*height;
9621 
9622 	/* the rect must be cell aligned to avoid overlapping XY bands */
9623 	cairo_region_union_rectangle(region, &rect);
9624 
9625 	_vte_debug_print (VTE_DEBUG_UPDATES,
9626 			"vte_terminal_expand_region"
9627 			"	(%d,%d)x(%d,%d) pixels,"
9628 			" (%d,%d)x(%d,%d) cells"
9629 			" [(%d,%d)x(%d,%d) pixels]\n",
9630 			area->x, area->y, area->width, area->height,
9631 			col, row, col_stop - col, row_stop - row,
9632 			rect.x, rect.y, rect.width, rect.height);
9633 }
9634 
9635 static void
vte_terminal_paint_area(VteTerminal * terminal,const GdkRectangle * area)9636 vte_terminal_paint_area (VteTerminal *terminal, const GdkRectangle *area)
9637 {
9638 	VteScreen *screen;
9639 	int width, height, delta;
9640 	int row, col, row_stop, col_stop;
9641 
9642 	screen = terminal->pvt->screen;
9643 
9644 	width = terminal->pvt->char_width;
9645 	height = terminal->pvt->char_height;
9646 
9647 	row = MAX(0, (area->y - terminal->pvt->padding.top) / height);
9648 	row_stop = MIN((area->height + area->y - terminal->pvt->padding.top) / height,
9649 		       terminal->pvt->row_count);
9650 	if (row_stop <= row) {
9651 		return;
9652 	}
9653 	col = MAX(0, (area->x - terminal->pvt->padding.left) / width);
9654 	col_stop = MIN((area->width + area->x - terminal->pvt->padding.left) / width,
9655 		       terminal->pvt->column_count);
9656 	if (col_stop <= col) {
9657 		return;
9658 	}
9659 	_vte_debug_print (VTE_DEBUG_UPDATES,
9660 			"vte_terminal_paint_area"
9661 			"	(%d,%d)x(%d,%d) pixels,"
9662 			" (%d,%d)x(%d,%d) cells"
9663 			" [(%d,%d)x(%d,%d) pixels]\n",
9664 			area->x, area->y, area->width, area->height,
9665 			col, row, col_stop - col, row_stop - row,
9666 			col * width + terminal->pvt->padding.left,
9667 			row * height + terminal->pvt->padding.top,
9668 			(col_stop - col) * width,
9669 			(row_stop - row) * height);
9670 
9671 	/* Now we're ready to draw the text.  Iterate over the rows we
9672 	 * need to draw. */
9673 	delta = screen->scroll_delta;
9674 	vte_terminal_draw_rows(terminal,
9675 			      screen,
9676 			      row + delta, row_stop - row,
9677 			      col, col_stop - col,
9678 			      col * width,
9679 			      row * height,
9680 			      width,
9681 			      height);
9682 }
9683 
9684 static void
vte_terminal_paint_cursor(VteTerminal * terminal)9685 vte_terminal_paint_cursor(VteTerminal *terminal)
9686 {
9687 	VteScreen *screen;
9688 	const VteCell *cell;
9689 	struct _vte_draw_text_request item;
9690 	int row, drow, col;
9691 	long width, height, delta, cursor_width;
9692 	guint fore, back;
9693 	PangoColor bg;
9694 	int x, y;
9695 	gboolean blink, selected, focus;
9696 
9697 	if (!terminal->pvt->cursor_visible)
9698 		return;
9699 
9700 	screen = terminal->pvt->screen;
9701 	delta = screen->scroll_delta;
9702         col = terminal->pvt->cursor.col;
9703         drow = terminal->pvt->cursor.row;
9704 	row = drow - delta;
9705 	width = terminal->pvt->char_width;
9706 	height = terminal->pvt->char_height;
9707 
9708 	if ((CLAMP(col, 0, terminal->pvt->column_count - 1) != col) ||
9709 	    (CLAMP(row, 0, terminal->pvt->row_count    - 1) != row))
9710 		return;
9711 
9712 	focus = terminal->pvt->has_focus;
9713 	blink = terminal->pvt->cursor_blink_state;
9714 
9715 	if (focus && !blink)
9716 		return;
9717 
9718         /* Find the first cell of the character "under" the cursor.
9719          * This is for CJK.  For TAB, paint the cursor where it really is. */
9720 	cell = vte_terminal_find_charcell(terminal, col, drow);
9721         while (cell != NULL && cell->attr.fragment && cell->c != '\t' && col > 0) {
9722 		col--;
9723 		cell = vte_terminal_find_charcell(terminal, col, drow);
9724 	}
9725 
9726 	/* Draw the cursor. */
9727 	item.c = (cell && cell->c) ? cell->c : ' ';
9728 	item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns : 1;
9729 	item.x = col * width;
9730 	item.y = row * height;
9731 	cursor_width = item.columns * width;
9732 	if (cell && cell->c != 0) {
9733 		guint style;
9734 		gint cw;
9735 		style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
9736 		cw = _vte_draw_get_char_width (terminal->pvt->draw, cell->c,
9737 					cell->attr.columns, style);
9738 		cursor_width = MAX(cursor_width, cw);
9739 	}
9740 
9741 	selected = vte_cell_is_selected(terminal, col, drow, NULL);
9742 
9743 	vte_terminal_determine_cursor_colors(terminal, cell, selected, &fore, &back);
9744 	vte_terminal_get_rgb_from_index(terminal, back, &bg);
9745 
9746 	x = item.x;
9747 	y = item.y;
9748 
9749         switch (_vte_terminal_decscusr_cursor_shape(terminal)) {
9750 
9751 		case VTE_CURSOR_SHAPE_IBEAM: {
9752                         int stem_width;
9753 
9754                         stem_width = (int) (((float) height) * terminal->pvt->cursor_aspect_ratio + 0.5);
9755                         stem_width = CLAMP (stem_width, VTE_LINE_WIDTH, cursor_width);
9756 
9757 			vte_terminal_fill_rectangle(terminal, &bg,
9758 						     x, y, stem_width, height);
9759 			break;
9760                 }
9761 
9762 		case VTE_CURSOR_SHAPE_UNDERLINE: {
9763                         int line_height;
9764 
9765 			/* use height (not width) so underline and ibeam will
9766 			 * be equally visible */
9767                         line_height = (int) (((float) height) * terminal->pvt->cursor_aspect_ratio + 0.5);
9768                         line_height = CLAMP (line_height, VTE_LINE_WIDTH, height);
9769 
9770 			vte_terminal_fill_rectangle(terminal, &bg,
9771 						     x, y + height - line_height,
9772 						     cursor_width, line_height);
9773 			break;
9774                 }
9775 
9776 		case VTE_CURSOR_SHAPE_BLOCK:
9777 
9778 			if (focus) {
9779                                 gboolean hilite = FALSE;
9780 
9781 				/* just reverse the character under the cursor */
9782 				vte_terminal_fill_rectangle (terminal,
9783 							     &bg,
9784 							     x, y,
9785 							     cursor_width, height);
9786 
9787                                 if (cell && terminal->pvt->show_match) {
9788                                         hilite = vte_cell_is_between(col, row,
9789                                                         terminal->pvt->match_start.col,
9790                                                         terminal->pvt->match_start.row,
9791                                                         terminal->pvt->match_end.col,
9792                                                         terminal->pvt->match_end.row,
9793                                                         TRUE);
9794                                 }
9795                                 if (cell && cell->c != 0 && cell->c != ' ') {
9796                                         vte_terminal_draw_cells(terminal,
9797                                                         &item, 1,
9798                                                         fore, back, TRUE, FALSE,
9799                                                         cell->attr.bold,
9800                                                         cell->attr.italic,
9801                                                         cell->attr.underline,
9802                                                         cell->attr.strikethrough,
9803                                                         hilite,
9804                                                         FALSE,
9805                                                         width,
9806                                                         height);
9807 				}
9808 
9809 			} else {
9810 				/* draw a box around the character */
9811 				vte_terminal_draw_rectangle (terminal,
9812 							    &bg,
9813 							     x - VTE_LINE_WIDTH,
9814 							     y - VTE_LINE_WIDTH,
9815 							     cursor_width + 2*VTE_LINE_WIDTH,
9816 							     height + 2*VTE_LINE_WIDTH);
9817 			}
9818 
9819 			break;
9820 	}
9821 }
9822 
9823 static void
vte_terminal_paint_im_preedit_string(VteTerminal * terminal)9824 vte_terminal_paint_im_preedit_string(VteTerminal *terminal)
9825 {
9826 	VteScreen *screen;
9827 	int row, col, columns;
9828 	long width, height, delta;
9829 	int i, len;
9830 	guint fore, back;
9831 
9832 	if (!terminal->pvt->im_preedit)
9833 		return;
9834 
9835 	/* Get going. */
9836 	screen = terminal->pvt->screen;
9837 
9838 	/* Keep local copies of rendering information. */
9839 	width = terminal->pvt->char_width;
9840 	height = terminal->pvt->char_height;
9841 	delta = screen->scroll_delta;
9842 
9843         row = terminal->pvt->cursor.row - delta;
9844 
9845 	/* Find out how many columns the pre-edit string takes up. */
9846 	columns = vte_terminal_preedit_width(terminal, FALSE);
9847 	len = vte_terminal_preedit_length(terminal, FALSE);
9848 
9849 	/* If the pre-edit string won't fit on the screen if we start
9850 	 * drawing it at the cursor's position, move it left. */
9851         col = terminal->pvt->cursor.col;
9852 	if (col + columns > terminal->pvt->column_count) {
9853 		col = MAX(0, terminal->pvt->column_count - columns);
9854 	}
9855 
9856 	/* Draw the preedit string, boxed. */
9857 	if (len > 0) {
9858 		struct _vte_draw_text_request *items;
9859 		const char *preedit = terminal->pvt->im_preedit;
9860 		int preedit_cursor;
9861 
9862 		items = g_new(struct _vte_draw_text_request, len);
9863 		for (i = columns = 0; i < len; i++) {
9864 			items[i].c = g_utf8_get_char(preedit);
9865                         items[i].columns = _vte_unichar_width(items[i].c,
9866                                                               terminal->pvt->utf8_ambiguous_width);
9867 			items[i].x = (col + columns) * width;
9868 			items[i].y = row * height;
9869 			columns += items[i].columns;
9870 			preedit = g_utf8_next_char(preedit);
9871 		}
9872 		_vte_draw_clear(terminal->pvt->draw,
9873 				col * width + terminal->pvt->padding.left,
9874 				row * height + terminal->pvt->padding.top,
9875 				width * columns,
9876 				height);
9877                 fore = terminal->pvt->color_defaults.attr.fore;
9878                 back = terminal->pvt->color_defaults.attr.back;
9879 		vte_terminal_draw_cells_with_attributes(terminal,
9880 							items, len,
9881 							terminal->pvt->im_preedit_attrs,
9882 							TRUE,
9883 							width, height);
9884 		preedit_cursor = terminal->pvt->im_preedit_cursor;
9885 		if (preedit_cursor >= 0 && preedit_cursor < len) {
9886 			/* Cursored letter in reverse. */
9887 			vte_terminal_draw_cells(terminal,
9888 						&items[preedit_cursor], 1,
9889 						back, fore, TRUE, TRUE,
9890 						FALSE,
9891 						FALSE,
9892 						FALSE,
9893 						FALSE,
9894 						FALSE,
9895 						TRUE,
9896 						width, height);
9897 		}
9898 		g_free(items);
9899 	}
9900 }
9901 
9902 static gboolean
vte_terminal_draw(GtkWidget * widget,cairo_t * cr)9903 vte_terminal_draw(GtkWidget *widget,
9904                   cairo_t *cr)
9905 {
9906         VteTerminal *terminal = VTE_TERMINAL (widget);
9907         cairo_rectangle_int_t clip_rect;
9908         cairo_region_t *region;
9909         int allocated_width, allocated_height;
9910 
9911         if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
9912                 return FALSE;
9913 
9914         _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_draw()\n");
9915         _vte_debug_print (VTE_DEBUG_WORK, "+");
9916         _vte_debug_print (VTE_DEBUG_UPDATES, "Draw (%d,%d)x(%d,%d)\n",
9917                           clip_rect.x, clip_rect.y,
9918                           clip_rect.width, clip_rect.height);
9919 
9920         region = vte_cairo_get_clip_region (cr);
9921         if (region == NULL)
9922                 return FALSE;
9923 
9924         allocated_width = gtk_widget_get_allocated_width(widget);
9925         allocated_height = gtk_widget_get_allocated_height(widget);
9926 
9927 	/* Designate the start of the drawing operation and clear the area. */
9928 	_vte_draw_set_cairo(terminal->pvt->draw, cr);
9929 
9930 	_vte_draw_clear (terminal->pvt->draw, 0, 0,
9931 			 allocated_width, allocated_height);
9932 
9933 	/* Calculate the bounding rectangle. */
9934 	{
9935 		cairo_rectangle_int_t *rectangles;
9936 		gint n, n_rectangles;
9937 		n_rectangles = cairo_region_num_rectangles (region);
9938 		rectangles = g_new (cairo_rectangle_int_t, n_rectangles);
9939 		for (n = 0; n < n_rectangles; n++) {
9940 			cairo_region_get_rectangle (region, n, &rectangles[n]);
9941 		}
9942 
9943 		/* don't bother to enlarge an invalidate all */
9944 		if (!(n_rectangles == 1
9945 		      && rectangles[0].width == allocated_width
9946 		      && rectangles[0].height == allocated_height)) {
9947 			cairo_region_t *rr = cairo_region_create ();
9948 			/* convert pixels into whole cells */
9949 			for (n = 0; n < n_rectangles; n++) {
9950 				vte_terminal_expand_region (terminal, rr, rectangles + n);
9951 			}
9952 			g_free (rectangles);
9953 
9954 			n_rectangles = cairo_region_num_rectangles (rr);
9955 			rectangles = g_new (cairo_rectangle_int_t, n_rectangles);
9956 			for (n = 0; n < n_rectangles; n++) {
9957 				cairo_region_get_rectangle (rr, n, &rectangles[n]);
9958 			}
9959 			cairo_region_destroy (rr);
9960 		}
9961 
9962 		/* and now paint them */
9963 		for (n = 0; n < n_rectangles; n++) {
9964 			vte_terminal_paint_area (terminal, rectangles + n);
9965 		}
9966 		g_free (rectangles);
9967 	}
9968 
9969 	vte_terminal_paint_cursor(terminal);
9970 
9971 	vte_terminal_paint_im_preedit_string(terminal);
9972 
9973 	/* Done with various structures. */
9974 	_vte_draw_set_cairo(terminal->pvt->draw, NULL);
9975 
9976         cairo_region_destroy (region);
9977 
9978         terminal->pvt->invalidated_all = FALSE;
9979 
9980         return FALSE;
9981 }
9982 
9983 /* Handle an expose event by painting the exposed area. */
9984 static cairo_region_t *
vte_cairo_get_clip_region(cairo_t * cr)9985 vte_cairo_get_clip_region (cairo_t *cr)
9986 {
9987         cairo_rectangle_list_t *list;
9988         cairo_region_t *region;
9989         int i;
9990 
9991         list = cairo_copy_clip_rectangle_list (cr);
9992         if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) {
9993                 cairo_rectangle_int_t clip_rect;
9994 
9995                 cairo_rectangle_list_destroy (list);
9996 
9997                 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
9998                         return NULL;
9999                 return cairo_region_create_rectangle (&clip_rect);
10000         }
10001 
10002 
10003         region = cairo_region_create ();
10004         for (i = list->num_rectangles - 1; i >= 0; --i) {
10005                 cairo_rectangle_t *rect = &list->rectangles[i];
10006                 cairo_rectangle_int_t clip_rect;
10007 
10008                 clip_rect.x = floor (rect->x);
10009                 clip_rect.y = floor (rect->y);
10010                 clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x;
10011                 clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y;
10012 
10013                 if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) {
10014                         cairo_region_destroy (region);
10015                         region = NULL;
10016                         break;
10017                 }
10018         }
10019 
10020         cairo_rectangle_list_destroy (list);
10021         return region;
10022 }
10023 
10024 /* Handle a scroll event. */
10025 static gboolean
vte_terminal_scroll(GtkWidget * widget,GdkEventScroll * event)10026 vte_terminal_scroll(GtkWidget *widget, GdkEventScroll *event)
10027 {
10028 	GtkAdjustment *adj;
10029 	VteTerminal *terminal;
10030 	gdouble delta_x, delta_y;
10031 	gdouble v;
10032 	gint cnt, i;
10033 	int button;
10034 
10035 	terminal = VTE_TERMINAL(widget);
10036 
10037 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
10038 
10039 	switch (event->direction) {
10040 	case GDK_SCROLL_UP:
10041 		terminal->pvt->mouse_smooth_scroll_delta -= 1.;
10042 		_vte_debug_print(VTE_DEBUG_EVENTS, "Scroll up\n");
10043 		break;
10044 	case GDK_SCROLL_DOWN:
10045 		terminal->pvt->mouse_smooth_scroll_delta += 1.;
10046 		_vte_debug_print(VTE_DEBUG_EVENTS, "Scroll down\n");
10047 		break;
10048 #if GTK_CHECK_VERSION (3, 4, 0)
10049 	case GDK_SCROLL_SMOOTH:
10050 		gdk_event_get_scroll_deltas ((GdkEvent*) event, &delta_x, &delta_y);
10051 		terminal->pvt->mouse_smooth_scroll_delta += delta_y;
10052 		_vte_debug_print(VTE_DEBUG_EVENTS,
10053 				"Smooth scroll by %f, delta now at %f\n",
10054 				delta_y, terminal->pvt->mouse_smooth_scroll_delta);
10055 		break;
10056 #endif
10057 	default:
10058 		break;
10059 	}
10060 
10061 	/* If we're running a mouse-aware application, map the scroll event
10062 	 * to a button press on buttons four and five. */
10063 	if (terminal->pvt->mouse_tracking_mode) {
10064 		cnt = terminal->pvt->mouse_smooth_scroll_delta;
10065 		if (cnt == 0)
10066 			return TRUE;
10067 		terminal->pvt->mouse_smooth_scroll_delta -= cnt;
10068 		_vte_debug_print(VTE_DEBUG_EVENTS,
10069 				"Scroll application by %d lines, smooth scroll delta set back to %f\n",
10070 				cnt, terminal->pvt->mouse_smooth_scroll_delta);
10071 
10072 		button = cnt > 0 ? 5 : 4;
10073 		if (cnt < 0)
10074 			cnt = -cnt;
10075 		for (i = 0; i < cnt; i++) {
10076 			/* Encode the parameters and send them to the app. */
10077 			vte_terminal_send_mouse_button_internal(terminal,
10078 								button,
10079 								FALSE /* not release */,
10080 								event->x,
10081 								event->y);
10082 		}
10083 		return TRUE;
10084 	}
10085 
10086 	adj = terminal->pvt->vadjustment;
10087 	v = MAX (1., ceil (gtk_adjustment_get_page_increment (adj) / 10.));
10088 	_vte_debug_print(VTE_DEBUG_EVENTS,
10089 			"Scroll speed is %d lines per non-smooth scroll unit\n",
10090 			(int) v);
10091 	cnt = v * terminal->pvt->mouse_smooth_scroll_delta;
10092 	if (cnt == 0)
10093 		return TRUE;
10094 	terminal->pvt->mouse_smooth_scroll_delta -= cnt / v;
10095 	_vte_debug_print(VTE_DEBUG_EVENTS,
10096 			"Scroll by %d lines, smooth scroll delta set back to %f\n",
10097 			cnt, terminal->pvt->mouse_smooth_scroll_delta);
10098 
10099 	if (terminal->pvt->screen == &terminal->pvt->alternate_screen &&
10100             terminal->pvt->alternate_screen_scroll) {
10101 		char *normal;
10102 		gssize normal_length;
10103 
10104 		/* In the alternate screen there is no scrolling,
10105 		 * so fake a few cursor keystrokes. */
10106 
10107 		_vte_keymap_map (
10108 				cnt > 0 ? GDK_KEY_Down : GDK_KEY_Up,
10109 				terminal->pvt->modifiers,
10110 				terminal->pvt->cursor_mode == VTE_KEYMODE_APPLICATION,
10111 				terminal->pvt->keypad_mode == VTE_KEYMODE_APPLICATION,
10112 				&normal,
10113 				&normal_length);
10114 		if (cnt < 0)
10115 			cnt = -cnt;
10116 		for (i = 0; i < cnt; i++) {
10117 			vte_terminal_feed_child_using_modes (terminal,
10118 					normal, normal_length);
10119 		}
10120 		g_free (normal);
10121 	} else {
10122 		/* Perform a history scroll. */
10123 		cnt += terminal->pvt->screen->scroll_delta;
10124 		vte_terminal_queue_adjustment_value_changed_clamped (terminal, cnt);
10125 	}
10126 
10127 	return TRUE;
10128 }
10129 
10130 static void
vte_terminal_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)10131 vte_terminal_get_property (GObject *object,
10132                            guint prop_id,
10133                            GValue *value,
10134                            GParamSpec *pspec)
10135 {
10136         VteTerminal *terminal = VTE_TERMINAL (object);
10137         VteTerminalPrivate *pvt = terminal->pvt;
10138 
10139 	switch (prop_id)
10140 	{
10141                 case PROP_HADJUSTMENT:
10142                         g_value_set_object (value, pvt->hadjustment);
10143                         break;
10144                 case PROP_VADJUSTMENT:
10145                         g_value_set_object (value, terminal->pvt->vadjustment);
10146                         break;
10147                 case PROP_HSCROLL_POLICY:
10148                         g_value_set_enum (value, pvt->hscroll_policy);
10149                         break;
10150                 case PROP_VSCROLL_POLICY:
10151                         g_value_set_enum (value, pvt->vscroll_policy);
10152                         break;
10153                 case PROP_ALLOW_BOLD:
10154                         g_value_set_boolean (value, vte_terminal_get_allow_bold (terminal));
10155                         break;
10156                 case PROP_AUDIBLE_BELL:
10157                         g_value_set_boolean (value, vte_terminal_get_audible_bell (terminal));
10158                         break;
10159                 case PROP_BACKSPACE_BINDING:
10160                         g_value_set_enum (value, pvt->backspace_binding);
10161                         break;
10162                 case PROP_CJK_AMBIGUOUS_WIDTH:
10163                         g_value_set_int (value, vte_terminal_get_cjk_ambiguous_width (terminal));
10164                         break;
10165                 case PROP_CURSOR_BLINK_MODE:
10166                         g_value_set_enum (value, vte_terminal_get_cursor_blink_mode (terminal));
10167                         break;
10168                 case PROP_CURRENT_DIRECTORY_URI:
10169                         g_value_set_string (value, vte_terminal_get_current_directory_uri (terminal));
10170                         break;
10171                 case PROP_CURRENT_FILE_URI:
10172                         g_value_set_string (value, vte_terminal_get_current_file_uri (terminal));
10173                         break;
10174                 case PROP_CURSOR_SHAPE:
10175                         g_value_set_enum (value, vte_terminal_get_cursor_shape (terminal));
10176                         break;
10177                 case PROP_DELETE_BINDING:
10178                         g_value_set_enum (value, pvt->delete_binding);
10179                         break;
10180                 case PROP_ENCODING:
10181                         g_value_set_string (value, vte_terminal_get_encoding (terminal));
10182                         break;
10183                 case PROP_FONT_DESC:
10184                         g_value_set_boxed (value, vte_terminal_get_font (terminal));
10185                         break;
10186                 case PROP_FONT_SCALE:
10187                         g_value_set_double (value, vte_terminal_get_font_scale (terminal));
10188                         break;
10189                 case PROP_ICON_TITLE:
10190                         g_value_set_string (value, vte_terminal_get_icon_title (terminal));
10191                         break;
10192                 case PROP_INPUT_ENABLED:
10193                         g_value_set_boolean (value, vte_terminal_get_input_enabled (terminal));
10194                         break;
10195                 case PROP_MOUSE_POINTER_AUTOHIDE:
10196                         g_value_set_boolean (value, vte_terminal_get_mouse_autohide (terminal));
10197                         break;
10198                 case PROP_PTY:
10199                         g_value_set_object (value, vte_terminal_get_pty(terminal));
10200                         break;
10201                 case PROP_REWRAP_ON_RESIZE:
10202                         g_value_set_boolean (value, vte_terminal_get_rewrap_on_resize (terminal));
10203                         break;
10204                 case PROP_SCROLLBACK_LINES:
10205                         g_value_set_uint (value, pvt->scrollback_lines);
10206                         break;
10207                 case PROP_SCROLL_ON_KEYSTROKE:
10208                         g_value_set_boolean (value, pvt->scroll_on_keystroke);
10209                         break;
10210                 case PROP_SCROLL_ON_OUTPUT:
10211                         g_value_set_boolean (value, pvt->scroll_on_output);
10212                         break;
10213                 case PROP_WINDOW_TITLE:
10214                         g_value_set_string (value, vte_terminal_get_window_title (terminal));
10215                         break;
10216                 case PROP_WORD_CHAR_EXCEPTIONS:
10217                         g_value_set_string (value, vte_terminal_get_word_char_exceptions (terminal));
10218                         break;
10219 
10220 		default:
10221 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
10222 			return;
10223         }
10224 }
10225 
10226 static void
vte_terminal_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)10227 vte_terminal_set_property (GObject *object,
10228                            guint prop_id,
10229                            const GValue *value,
10230                            GParamSpec *pspec)
10231 {
10232         VteTerminal *terminal = VTE_TERMINAL (object);
10233         VteTerminalPrivate *pvt = terminal->pvt;
10234 
10235 	switch (prop_id)
10236 	{
10237                 case PROP_HADJUSTMENT:
10238                         vte_terminal_set_hadjustment (terminal, g_value_get_object (value));
10239                         break;
10240                 case PROP_VADJUSTMENT:
10241                         vte_terminal_set_vadjustment (terminal, g_value_get_object (value));
10242                         break;
10243                 case PROP_HSCROLL_POLICY:
10244                         pvt->hscroll_policy = g_value_get_enum (value);
10245                         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (terminal));
10246                         break;
10247                 case PROP_VSCROLL_POLICY:
10248                         pvt->vscroll_policy = g_value_get_enum (value);
10249                         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (terminal));
10250                         break;
10251                 case PROP_ALLOW_BOLD:
10252                         vte_terminal_set_allow_bold (terminal, g_value_get_boolean (value));
10253                         break;
10254                 case PROP_AUDIBLE_BELL:
10255                         vte_terminal_set_audible_bell (terminal, g_value_get_boolean (value));
10256                         break;
10257                 case PROP_BACKSPACE_BINDING:
10258                         vte_terminal_set_backspace_binding (terminal, g_value_get_enum (value));
10259                         break;
10260                 case PROP_CJK_AMBIGUOUS_WIDTH:
10261                         vte_terminal_set_cjk_ambiguous_width (terminal, g_value_get_int (value));
10262                         break;
10263                 case PROP_CURSOR_BLINK_MODE:
10264                         vte_terminal_set_cursor_blink_mode (terminal, g_value_get_enum (value));
10265                         break;
10266                 case PROP_CURSOR_SHAPE:
10267                         vte_terminal_set_cursor_shape (terminal, g_value_get_enum (value));
10268                         break;
10269                 case PROP_DELETE_BINDING:
10270                         vte_terminal_set_delete_binding (terminal, g_value_get_enum (value));
10271                         break;
10272                 case PROP_ENCODING:
10273                         vte_terminal_set_encoding (terminal, g_value_get_string (value), NULL);
10274                         break;
10275                 case PROP_FONT_DESC:
10276                         vte_terminal_set_font (terminal, g_value_get_boxed (value));
10277                         break;
10278                 case PROP_FONT_SCALE:
10279                         vte_terminal_set_font_scale (terminal, g_value_get_double (value));
10280                         break;
10281                 case PROP_INPUT_ENABLED:
10282                         vte_terminal_set_input_enabled (terminal, g_value_get_boolean (value));
10283                         break;
10284                 case PROP_MOUSE_POINTER_AUTOHIDE:
10285                         vte_terminal_set_mouse_autohide (terminal, g_value_get_boolean (value));
10286                         break;
10287                 case PROP_PTY:
10288                         vte_terminal_set_pty (terminal, g_value_get_object (value));
10289                         break;
10290                 case PROP_REWRAP_ON_RESIZE:
10291                         vte_terminal_set_rewrap_on_resize (terminal, g_value_get_boolean (value));
10292                         break;
10293                 case PROP_SCROLLBACK_LINES:
10294                         vte_terminal_set_scrollback_lines (terminal, g_value_get_uint (value));
10295                         break;
10296                 case PROP_SCROLL_ON_KEYSTROKE:
10297                         vte_terminal_set_scroll_on_keystroke(terminal, g_value_get_boolean (value));
10298                         break;
10299                 case PROP_SCROLL_ON_OUTPUT:
10300                         vte_terminal_set_scroll_on_output (terminal, g_value_get_boolean (value));
10301                         break;
10302                 case PROP_WORD_CHAR_EXCEPTIONS:
10303                         vte_terminal_set_word_char_exceptions (terminal, g_value_get_string (value));
10304                         break;
10305 
10306                 /* Not writable */
10307                 case PROP_CURRENT_DIRECTORY_URI:
10308                 case PROP_CURRENT_FILE_URI:
10309                 case PROP_ICON_TITLE:
10310                 case PROP_WINDOW_TITLE:
10311                         g_assert_not_reached ();
10312                         break;
10313 
10314 		default:
10315 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
10316 			return;
10317 	}
10318 }
10319 
10320 /* Initialize methods. */
10321 static void
vte_terminal_class_init(VteTerminalClass * klass)10322 vte_terminal_class_init(VteTerminalClass *klass)
10323 {
10324 	GObjectClass *gobject_class;
10325 	GtkWidgetClass *widget_class;
10326 	GtkBindingSet  *binding_set;
10327 
10328 #ifdef VTE_DEBUG
10329 	{
10330                 _vte_debug_init();
10331 		_vte_debug_print(VTE_DEBUG_LIFECYCLE,
10332 				"vte_terminal_class_init()\n");
10333 		/* print out the legend */
10334 		_vte_debug_print(VTE_DEBUG_WORK,
10335 			"Debugging work flow (top input to bottom output):\n"
10336 					"  .  _vte_terminal_process_incoming\n"
10337 					"  <  start process_timeout\n"
10338 					"  {[ start update_timeout  [ => rate limited\n"
10339 					"  T  start of terminal in update_timeout\n"
10340 					"  (  start _vte_terminal_process_incoming\n"
10341 					"  ?  _vte_invalidate_cells (call)\n"
10342 					"  !  _vte_invalidate_cells (dirty)\n"
10343 					"  *  _vte_invalidate_all\n"
10344 					"  )  end _vte_terminal_process_incoming\n"
10345 					"  -  gdk_window_process_updates\n"
10346 					"  =  vte_terminal_paint\n"
10347 					"  ]} end update_timeout\n"
10348 					"  >  end process_timeout\n");
10349 	}
10350 #endif
10351 
10352 	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
10353 #ifdef HAVE_DECL_BIND_TEXTDOMAIN_CODESET
10354 	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
10355 #endif
10356 
10357 	g_type_class_add_private(klass, sizeof (VteTerminalPrivate));
10358 
10359 	gobject_class = G_OBJECT_CLASS(klass);
10360 	widget_class = GTK_WIDGET_CLASS(klass);
10361 
10362 	/* Override some of the default handlers. */
10363 	gobject_class->finalize = vte_terminal_finalize;
10364         gobject_class->get_property = vte_terminal_get_property;
10365         gobject_class->set_property = vte_terminal_set_property;
10366 	widget_class->realize = vte_terminal_realize;
10367 	widget_class->scroll_event = vte_terminal_scroll;
10368         widget_class->draw = vte_terminal_draw;
10369 	widget_class->key_press_event = vte_terminal_key_press;
10370 	widget_class->key_release_event = vte_terminal_key_release;
10371 	widget_class->button_press_event = vte_terminal_button_press;
10372 	widget_class->button_release_event = vte_terminal_button_release;
10373 	widget_class->motion_notify_event = vte_terminal_motion_notify;
10374 	widget_class->enter_notify_event = vte_terminal_enter;
10375 	widget_class->leave_notify_event = vte_terminal_leave;
10376 	widget_class->focus_in_event = vte_terminal_focus_in;
10377 	widget_class->focus_out_event = vte_terminal_focus_out;
10378 	widget_class->visibility_notify_event = vte_terminal_visibility_notify;
10379 	widget_class->unrealize = vte_terminal_unrealize;
10380 	widget_class->style_updated = vte_terminal_style_updated;
10381 	widget_class->get_preferred_width = vte_terminal_get_preferred_width;
10382 	widget_class->get_preferred_height = vte_terminal_get_preferred_height;
10383 	widget_class->size_allocate = vte_terminal_size_allocate;
10384         widget_class->screen_changed = vte_terminal_screen_changed;
10385 
10386 	/* Initialize default handlers. */
10387 	klass->eof = NULL;
10388 	klass->child_exited = NULL;
10389 	klass->encoding_changed = NULL;
10390 	klass->char_size_changed = NULL;
10391 	klass->window_title_changed = NULL;
10392 	klass->icon_title_changed = NULL;
10393 	klass->selection_changed = NULL;
10394 	klass->contents_changed = NULL;
10395 	klass->cursor_moved = NULL;
10396 	klass->commit = NULL;
10397 
10398 	klass->deiconify_window = NULL;
10399 	klass->iconify_window = NULL;
10400 	klass->raise_window = NULL;
10401 	klass->lower_window = NULL;
10402 	klass->refresh_window = NULL;
10403 	klass->restore_window = NULL;
10404 	klass->maximize_window = NULL;
10405 	klass->resize_window = NULL;
10406 	klass->move_window = NULL;
10407 
10408 	klass->increase_font_size = NULL;
10409 	klass->decrease_font_size = NULL;
10410 
10411 	klass->text_modified = NULL;
10412 	klass->text_inserted = NULL;
10413 	klass->text_deleted = NULL;
10414 	klass->text_scrolled = NULL;
10415 
10416 	klass->copy_clipboard = vte_terminal_real_copy_clipboard;
10417 	klass->paste_clipboard = vte_terminal_real_paste_clipboard;
10418 
10419         klass->bell = NULL;
10420 
10421         /* GtkScrollable interface properties */
10422         g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
10423         g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
10424         g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
10425         g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
10426 
10427 	/* Register some signals of our own. */
10428 
10429         /**
10430          * VteTerminal::eof:
10431          * @vteterminal: the object which received the signal
10432          *
10433          * Emitted when the terminal receives an end-of-file from a child which
10434          * is running in the terminal.  This signal is frequently (but not
10435          * always) emitted with a #VteTerminal::child-exited signal.
10436          */
10437 	g_signal_new(I_("eof"),
10438 		     G_OBJECT_CLASS_TYPE(klass),
10439 		     G_SIGNAL_RUN_LAST,
10440 		     G_STRUCT_OFFSET(VteTerminalClass, eof),
10441 		     NULL,
10442 		     NULL,
10443 		     g_cclosure_marshal_VOID__VOID,
10444 		     G_TYPE_NONE, 0);
10445 
10446         /**
10447          * VteTerminal::child-exited:
10448          * @vteterminal: the object which received the signal
10449          * @status: the child's exit status
10450          *
10451          * This signal is emitted when the terminal detects that a child
10452          * watched using vte_terminal_watch_child() has exited.
10453          */
10454 	g_signal_new(I_("child-exited"),
10455 		     G_OBJECT_CLASS_TYPE(klass),
10456 		     G_SIGNAL_RUN_LAST,
10457 		     G_STRUCT_OFFSET(VteTerminalClass, child_exited),
10458 		     NULL,
10459 		     NULL,
10460                      g_cclosure_marshal_VOID__INT,
10461                      G_TYPE_NONE,
10462                      1, G_TYPE_INT);
10463 
10464         /**
10465          * VteTerminal::window-title-changed:
10466          * @vteterminal: the object which received the signal
10467          *
10468          * Emitted when the terminal's %window_title field is modified.
10469          */
10470 	g_signal_new(I_("window-title-changed"),
10471 		     G_OBJECT_CLASS_TYPE(klass),
10472 		     G_SIGNAL_RUN_LAST,
10473 		     G_STRUCT_OFFSET(VteTerminalClass, window_title_changed),
10474 		     NULL,
10475 		     NULL,
10476 		     g_cclosure_marshal_VOID__VOID,
10477 		     G_TYPE_NONE, 0);
10478 
10479         /**
10480          * VteTerminal::icon-title-changed:
10481          * @vteterminal: the object which received the signal
10482          *
10483          * Emitted when the terminal's %icon_title field is modified.
10484          */
10485 	g_signal_new(I_("icon-title-changed"),
10486 		     G_OBJECT_CLASS_TYPE(klass),
10487 		     G_SIGNAL_RUN_LAST,
10488 		     G_STRUCT_OFFSET(VteTerminalClass, icon_title_changed),
10489 		     NULL,
10490 		     NULL,
10491 		     g_cclosure_marshal_VOID__VOID,
10492 		     G_TYPE_NONE, 0);
10493 
10494         /**
10495          * VteTerminal::current-directory-uri-changed:
10496          * @vteterminal: the object which received the signal
10497          *
10498          * Emitted when the current directory URI is modified.
10499          */
10500 	g_signal_new(I_("current-directory-uri-changed"),
10501 		     G_OBJECT_CLASS_TYPE(klass),
10502 		     G_SIGNAL_RUN_LAST,
10503 		     0,
10504 		     NULL,
10505 		     NULL,
10506 		     g_cclosure_marshal_VOID__VOID,
10507 		     G_TYPE_NONE, 0);
10508 
10509         /**
10510          * VteTerminal::current-file-uri-changed:
10511          * @vteterminal: the object which received the signal
10512          *
10513          * Emitted when the current file URI is modified.
10514          */
10515 	g_signal_new(I_("current-file-uri-changed"),
10516 		     G_OBJECT_CLASS_TYPE(klass),
10517 		     G_SIGNAL_RUN_LAST,
10518 		     0,
10519 		     NULL,
10520 		     NULL,
10521 		     g_cclosure_marshal_VOID__VOID,
10522 		     G_TYPE_NONE, 0);
10523 
10524         /**
10525          * VteTerminal::encoding-changed:
10526          * @vteterminal: the object which received the signal
10527          *
10528          * Emitted whenever the terminal's current encoding has changed, either
10529          * as a result of receiving a control sequence which toggled between the
10530          * local and UTF-8 encodings, or at the parent application's request.
10531          */
10532 	g_signal_new(I_("encoding-changed"),
10533 		     G_OBJECT_CLASS_TYPE(klass),
10534 		     G_SIGNAL_RUN_LAST,
10535 		     G_STRUCT_OFFSET(VteTerminalClass, encoding_changed),
10536 		     NULL,
10537 		     NULL,
10538 		     g_cclosure_marshal_VOID__VOID,
10539 		     G_TYPE_NONE, 0);
10540 
10541         /**
10542          * VteTerminal::commit:
10543          * @vteterminal: the object which received the signal
10544          * @text: a string of text
10545          * @size: the length of that string of text
10546          *
10547          * Emitted whenever the terminal receives input from the user and
10548          * prepares to send it to the child process.  The signal is emitted even
10549          * when there is no child process.
10550          */
10551 	g_signal_new(I_("commit"),
10552 		     G_OBJECT_CLASS_TYPE(klass),
10553 		     G_SIGNAL_RUN_LAST,
10554 		     G_STRUCT_OFFSET(VteTerminalClass, commit),
10555 		     NULL,
10556 		     NULL,
10557 		     _vte_marshal_VOID__STRING_UINT,
10558 		     G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT);
10559 
10560         /**
10561          * VteTerminal::char-size-changed:
10562          * @vteterminal: the object which received the signal
10563          * @width: the new character cell width
10564          * @height: the new character cell height
10565          *
10566          * Emitted whenever selection of a new font causes the values of the
10567          * %char_width or %char_height fields to change.
10568          */
10569 	g_signal_new(I_("char-size-changed"),
10570 		     G_OBJECT_CLASS_TYPE(klass),
10571 		     G_SIGNAL_RUN_LAST,
10572 		     G_STRUCT_OFFSET(VteTerminalClass, char_size_changed),
10573 		     NULL,
10574 		     NULL,
10575 		     _vte_marshal_VOID__UINT_UINT,
10576 		     G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
10577 
10578         /**
10579          * VteTerminal::selection-changed:
10580          * @vteterminal: the object which received the signal
10581          *
10582          * Emitted whenever the contents of terminal's selection changes.
10583          */
10584 	g_signal_new (I_("selection-changed"),
10585 		      G_OBJECT_CLASS_TYPE(klass),
10586 		      G_SIGNAL_RUN_LAST,
10587 		      G_STRUCT_OFFSET(VteTerminalClass, selection_changed),
10588 		      NULL,
10589 		      NULL,
10590 		      g_cclosure_marshal_VOID__VOID,
10591 		      G_TYPE_NONE, 0);
10592 
10593         /**
10594          * VteTerminal::contents-changed:
10595          * @vteterminal: the object which received the signal
10596          *
10597          * Emitted whenever the visible appearance of the terminal has changed.
10598          * Used primarily by #VteTerminalAccessible.
10599          */
10600 	g_signal_new(I_("contents-changed"),
10601 		     G_OBJECT_CLASS_TYPE(klass),
10602 		     G_SIGNAL_RUN_LAST,
10603 		     G_STRUCT_OFFSET(VteTerminalClass, contents_changed),
10604 		     NULL,
10605 		     NULL,
10606 		     g_cclosure_marshal_VOID__VOID,
10607 		     G_TYPE_NONE, 0);
10608 
10609         /**
10610          * VteTerminal::cursor-moved:
10611          * @vteterminal: the object which received the signal
10612          *
10613          * Emitted whenever the cursor moves to a new character cell.  Used
10614          * primarily by #VteTerminalAccessible.
10615          */
10616 	g_signal_new(I_("cursor-moved"),
10617 		     G_OBJECT_CLASS_TYPE(klass),
10618 		     G_SIGNAL_RUN_LAST,
10619 		     G_STRUCT_OFFSET(VteTerminalClass, cursor_moved),
10620 		     NULL,
10621 		     NULL,
10622 		     g_cclosure_marshal_VOID__VOID,
10623 		     G_TYPE_NONE, 0);
10624 
10625         /**
10626          * VteTerminal::deiconify-window:
10627          * @vteterminal: the object which received the signal
10628          *
10629          * Emitted at the child application's request.
10630          */
10631 	g_signal_new(I_("deiconify-window"),
10632 		     G_OBJECT_CLASS_TYPE(klass),
10633 		     G_SIGNAL_RUN_LAST,
10634 		     G_STRUCT_OFFSET(VteTerminalClass, deiconify_window),
10635 		     NULL,
10636 		     NULL,
10637 		     g_cclosure_marshal_VOID__VOID,
10638 		     G_TYPE_NONE, 0);
10639 
10640         /**
10641          * VteTerminal::iconify-window:
10642          * @vteterminal: the object which received the signal
10643          *
10644          * Emitted at the child application's request.
10645          */
10646 	g_signal_new(I_("iconify-window"),
10647 		     G_OBJECT_CLASS_TYPE(klass),
10648 		     G_SIGNAL_RUN_LAST,
10649 		     G_STRUCT_OFFSET(VteTerminalClass, iconify_window),
10650 		     NULL,
10651 		     NULL,
10652 		     g_cclosure_marshal_VOID__VOID,
10653 		     G_TYPE_NONE, 0);
10654 
10655         /**
10656          * VteTerminal::raise-window:
10657          * @vteterminal: the object which received the signal
10658          *
10659          * Emitted at the child application's request.
10660          */
10661 	g_signal_new(I_("raise-window"),
10662 		     G_OBJECT_CLASS_TYPE(klass),
10663 		     G_SIGNAL_RUN_LAST,
10664 		     G_STRUCT_OFFSET(VteTerminalClass, raise_window),
10665 		     NULL,
10666 		     NULL,
10667 		     g_cclosure_marshal_VOID__VOID,
10668 		     G_TYPE_NONE, 0);
10669 
10670         /**
10671          * VteTerminal::lower-window:
10672          * @vteterminal: the object which received the signal
10673          *
10674          * Emitted at the child application's request.
10675          */
10676 	g_signal_new(I_("lower-window"),
10677 		     G_OBJECT_CLASS_TYPE(klass),
10678 		     G_SIGNAL_RUN_LAST,
10679 		     G_STRUCT_OFFSET(VteTerminalClass, lower_window),
10680 		     NULL,
10681 		     NULL,
10682 		     g_cclosure_marshal_VOID__VOID,
10683 		     G_TYPE_NONE, 0);
10684 
10685         /**
10686          * VteTerminal::refresh-window:
10687          * @vteterminal: the object which received the signal
10688          *
10689          * Emitted at the child application's request.
10690          */
10691 	g_signal_new(I_("refresh-window"),
10692 		     G_OBJECT_CLASS_TYPE(klass),
10693 		     G_SIGNAL_RUN_LAST,
10694 		     G_STRUCT_OFFSET(VteTerminalClass, refresh_window),
10695 		     NULL,
10696 		     NULL,
10697 		     g_cclosure_marshal_VOID__VOID,
10698 		     G_TYPE_NONE, 0);
10699 
10700         /**
10701          * VteTerminal::restore-window:
10702          * @vteterminal: the object which received the signal
10703          *
10704          * Emitted at the child application's request.
10705          */
10706 	g_signal_new(I_("restore-window"),
10707 		     G_OBJECT_CLASS_TYPE(klass),
10708 		     G_SIGNAL_RUN_LAST,
10709 		     G_STRUCT_OFFSET(VteTerminalClass, restore_window),
10710 		     NULL,
10711 		     NULL,
10712 		     g_cclosure_marshal_VOID__VOID,
10713 		     G_TYPE_NONE, 0);
10714 
10715         /**
10716          * VteTerminal::maximize-window:
10717          * @vteterminal: the object which received the signal
10718          *
10719          * Emitted at the child application's request.
10720          */
10721 	g_signal_new(I_("maximize-window"),
10722 		     G_OBJECT_CLASS_TYPE(klass),
10723 		     G_SIGNAL_RUN_LAST,
10724 		     G_STRUCT_OFFSET(VteTerminalClass, maximize_window),
10725 		     NULL,
10726 		     NULL,
10727 		     g_cclosure_marshal_VOID__VOID,
10728 		     G_TYPE_NONE, 0);
10729 
10730         /**
10731          * VteTerminal::resize-window:
10732          * @vteterminal: the object which received the signal
10733          * @width: the desired number of columns
10734          * @height: the desired number of rows
10735          *
10736          * Emitted at the child application's request.
10737          */
10738 	g_signal_new(I_("resize-window"),
10739 		     G_OBJECT_CLASS_TYPE(klass),
10740 		     G_SIGNAL_RUN_LAST,
10741 		     G_STRUCT_OFFSET(VteTerminalClass, resize_window),
10742 		     NULL,
10743 		     NULL,
10744 		     _vte_marshal_VOID__UINT_UINT,
10745 		     G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
10746 
10747         /**
10748          * VteTerminal::move-window:
10749          * @vteterminal: the object which received the signal
10750          * @x: the terminal's desired location, X coordinate
10751          * @y: the terminal's desired location, Y coordinate
10752          *
10753          * Emitted at the child application's request.
10754          */
10755 	g_signal_new(I_("move-window"),
10756 		     G_OBJECT_CLASS_TYPE(klass),
10757 		     G_SIGNAL_RUN_LAST,
10758 		     G_STRUCT_OFFSET(VteTerminalClass, move_window),
10759 		     NULL,
10760 		     NULL,
10761 		     _vte_marshal_VOID__UINT_UINT,
10762 		     G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
10763 
10764         /**
10765          * VteTerminal::increase-font-size:
10766          * @vteterminal: the object which received the signal
10767          *
10768          * Emitted when the user hits the '+' key while holding the Control key.
10769          */
10770 	g_signal_new(I_("increase-font-size"),
10771 		     G_OBJECT_CLASS_TYPE(klass),
10772 		     G_SIGNAL_RUN_LAST,
10773 		     G_STRUCT_OFFSET(VteTerminalClass, increase_font_size),
10774 		     NULL,
10775 		     NULL,
10776 		     g_cclosure_marshal_VOID__VOID,
10777 		     G_TYPE_NONE, 0);
10778 
10779         /**
10780          * VteTerminal::decrease-font-size:
10781          * @vteterminal: the object which received the signal
10782          *
10783          * Emitted when the user hits the '-' key while holding the Control key.
10784          */
10785 	g_signal_new(I_("decrease-font-size"),
10786 		     G_OBJECT_CLASS_TYPE(klass),
10787 		     G_SIGNAL_RUN_LAST,
10788 		     G_STRUCT_OFFSET(VteTerminalClass, decrease_font_size),
10789 		     NULL,
10790 		     NULL,
10791 		     g_cclosure_marshal_VOID__VOID,
10792 		     G_TYPE_NONE, 0);
10793 
10794         /**
10795          * VteTerminal::text-modified:
10796          * @vteterminal: the object which received the signal
10797          *
10798          * An internal signal used for communication between the terminal and
10799          * its accessibility peer. May not be emitted under certain
10800          * circumstances.
10801          */
10802 	g_signal_new(I_("text-modified"),
10803 		     G_OBJECT_CLASS_TYPE(klass),
10804 		     G_SIGNAL_RUN_LAST,
10805 		     G_STRUCT_OFFSET(VteTerminalClass, text_modified),
10806 		     NULL,
10807 		     NULL,
10808 		     g_cclosure_marshal_VOID__VOID,
10809 		     G_TYPE_NONE, 0);
10810 
10811         /**
10812          * VteTerminal::text-inserted:
10813          * @vteterminal: the object which received the signal
10814          *
10815          * An internal signal used for communication between the terminal and
10816          * its accessibility peer. May not be emitted under certain
10817          * circumstances.
10818          */
10819 	g_signal_new(I_("text-inserted"),
10820 		     G_OBJECT_CLASS_TYPE(klass),
10821 		     G_SIGNAL_RUN_LAST,
10822 		     G_STRUCT_OFFSET(VteTerminalClass, text_inserted),
10823 		     NULL,
10824 		     NULL,
10825 		     g_cclosure_marshal_VOID__VOID,
10826 		     G_TYPE_NONE, 0);
10827 
10828         /**
10829          * VteTerminal::text-deleted:
10830          * @vteterminal: the object which received the signal
10831          *
10832          * An internal signal used for communication between the terminal and
10833          * its accessibility peer. May not be emitted under certain
10834          * circumstances.
10835          */
10836 	g_signal_new(I_("text-deleted"),
10837 		     G_OBJECT_CLASS_TYPE(klass),
10838 		     G_SIGNAL_RUN_LAST,
10839 		     G_STRUCT_OFFSET(VteTerminalClass, text_deleted),
10840 		     NULL,
10841 		     NULL,
10842 		     g_cclosure_marshal_VOID__VOID,
10843 		     G_TYPE_NONE, 0);
10844 
10845         /**
10846          * VteTerminal::text-scrolled:
10847          * @vteterminal: the object which received the signal
10848          * @delta: the number of lines scrolled
10849          *
10850          * An internal signal used for communication between the terminal and
10851          * its accessibility peer. May not be emitted under certain
10852          * circumstances.
10853          */
10854 	g_signal_new(I_("text-scrolled"),
10855 		     G_OBJECT_CLASS_TYPE(klass),
10856 		     G_SIGNAL_RUN_LAST,
10857 		     G_STRUCT_OFFSET(VteTerminalClass, text_scrolled),
10858 		     NULL,
10859 		     NULL,
10860 		     g_cclosure_marshal_VOID__INT,
10861 		     G_TYPE_NONE, 1, G_TYPE_INT);
10862 
10863         /**
10864          * VteTerminal::copy-clipboard:
10865          * @vteterminal: the object which received the signal
10866          *
10867          * Emitted whenever vte_terminal_copy_clipboard() is called.
10868          */
10869 	signals[COPY_CLIPBOARD] =
10870                 g_signal_new(I_("copy-clipboard"),
10871 			     G_OBJECT_CLASS_TYPE(klass),
10872 			     G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
10873 			     G_STRUCT_OFFSET(VteTerminalClass, copy_clipboard),
10874 			     NULL,
10875 			     NULL,
10876                              g_cclosure_marshal_VOID__VOID,
10877 			     G_TYPE_NONE, 0);
10878 
10879         /**
10880          * VteTerminal::paste-clipboard:
10881          * @vteterminal: the object which received the signal
10882          *
10883          * Emitted whenever vte_terminal_paste_clipboard() is called.
10884          */
10885 	signals[PASTE_CLIPBOARD] =
10886                 g_signal_new(I_("paste-clipboard"),
10887 			     G_OBJECT_CLASS_TYPE(klass),
10888 			     G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
10889 			     G_STRUCT_OFFSET(VteTerminalClass, paste_clipboard),
10890 			     NULL,
10891 			     NULL,
10892                              g_cclosure_marshal_VOID__VOID,
10893 			     G_TYPE_NONE, 0);
10894 
10895         /**
10896          * VteTerminal::bell:
10897          * @vteterminal: the object which received the signal
10898          *
10899          * This signal is emitted when the a child sends a bell request to the
10900          * terminal.
10901          */
10902         g_signal_new(I_("bell"),
10903 			     G_OBJECT_CLASS_TYPE(klass),
10904 			     G_SIGNAL_RUN_LAST,
10905 			     G_STRUCT_OFFSET(VteTerminalClass, bell),
10906 			     NULL,
10907 			     NULL,
10908                              g_cclosure_marshal_VOID__VOID,
10909 			     G_TYPE_NONE, 0);
10910 
10911         /**
10912          * VteTerminal:allow-bold:
10913          *
10914          * Controls whether or not the terminal will attempt to draw bold text.
10915          * This may happen either by using a bold font variant, or by
10916          * repainting text with a different offset.
10917          */
10918         g_object_class_install_property
10919                 (gobject_class,
10920                  PROP_ALLOW_BOLD,
10921                  g_param_spec_boolean ("allow-bold", NULL, NULL,
10922                                        TRUE,
10923                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10924 
10925         /**
10926          * VteTerminal:audible-bell:
10927          *
10928          * Controls whether or not the terminal will beep when the child outputs the
10929          * "bl" sequence.
10930          */
10931         g_object_class_install_property
10932                 (gobject_class,
10933                  PROP_AUDIBLE_BELL,
10934                  g_param_spec_boolean ("audible-bell", NULL, NULL,
10935                                        TRUE,
10936                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10937 
10938         /**
10939          * VteTerminal:backspace-binding:
10940          *
10941          * *Controls what string or control sequence the terminal sends to its child
10942          * when the user presses the backspace key.
10943          */
10944         g_object_class_install_property
10945                 (gobject_class,
10946                  PROP_BACKSPACE_BINDING,
10947                  g_param_spec_enum ("backspace-binding", NULL, NULL,
10948                                     VTE_TYPE_ERASE_BINDING,
10949                                     VTE_ERASE_AUTO,
10950                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10951 
10952         /**
10953          * VteTerminal:cjk-ambiguous-width:
10954          *
10955          * This setting controls whether ambiguous-width characters are narrow or wide
10956          * when using the UTF-8 encoding (vte_terminal_set_encoding()). In all other encodings,
10957          * the width of ambiguous-width characters is fixed.
10958          *
10959          * This setting only takes effect the next time the terminal is reset, either
10960          * via escape sequence or with vte_terminal_reset().
10961          */
10962         g_object_class_install_property
10963                 (gobject_class,
10964                  PROP_CJK_AMBIGUOUS_WIDTH,
10965                  g_param_spec_int ("cjk-ambiguous-width", NULL, NULL,
10966                                    1, 2, VTE_DEFAULT_UTF8_AMBIGUOUS_WIDTH,
10967                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10968 
10969         /**
10970          * VteTerminal:cursor-blink-mode:
10971          *
10972          * Sets whether or not the cursor will blink. Using %VTE_CURSOR_BLINK_SYSTEM
10973          * will use the #GtkSettings::gtk-cursor-blink setting.
10974          */
10975         g_object_class_install_property
10976                 (gobject_class,
10977                  PROP_CURSOR_BLINK_MODE,
10978                  g_param_spec_enum ("cursor-blink-mode", NULL, NULL,
10979                                     VTE_TYPE_CURSOR_BLINK_MODE,
10980                                     VTE_CURSOR_BLINK_SYSTEM,
10981                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10982 
10983         /**
10984          * VteTerminal:cursor-shape:
10985          *
10986          * Controls the shape of the cursor.
10987          */
10988         g_object_class_install_property
10989                 (gobject_class,
10990                  PROP_CURSOR_SHAPE,
10991                  g_param_spec_enum ("cursor-shape", NULL, NULL,
10992                                     VTE_TYPE_CURSOR_SHAPE,
10993                                     VTE_CURSOR_SHAPE_BLOCK,
10994                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
10995 
10996         /**
10997          * VteTerminal:delete-binding:
10998          *
10999          * Controls what string or control sequence the terminal sends to its child
11000          * when the user presses the delete key.
11001          */
11002         g_object_class_install_property
11003                 (gobject_class,
11004                  PROP_DELETE_BINDING,
11005                  g_param_spec_enum ("delete-binding", NULL, NULL,
11006                                     VTE_TYPE_ERASE_BINDING,
11007                                     VTE_ERASE_AUTO,
11008                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11009 
11010         /**
11011          * VteTerminal:font-scale:
11012          *
11013          * The terminal's font scale.
11014          */
11015         g_object_class_install_property
11016                 (gobject_class,
11017                  PROP_FONT_SCALE,
11018                  g_param_spec_double ("font-scale", NULL, NULL,
11019                                       VTE_FONT_SCALE_MIN,
11020                                       VTE_FONT_SCALE_MAX,
11021                                       1.,
11022                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11023 
11024         /**
11025          * VteTerminal:encoding:
11026          *
11027          * Controls the encoding the terminal will expect data from the child to
11028          * be encoded with.  For certain terminal types, applications executing in the
11029          * terminal can change the encoding.  The default is defined by the
11030          * application's locale settings.
11031          */
11032         g_object_class_install_property
11033                 (gobject_class,
11034                  PROP_ENCODING,
11035                  g_param_spec_string ("encoding", NULL, NULL,
11036                                       NULL,
11037                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11038 
11039         /**
11040          * VteTerminal:font-desc:
11041          *
11042          * Specifies the font used for rendering all text displayed by the terminal,
11043          * overriding any fonts set using gtk_widget_modify_font().  The terminal
11044          * will immediately attempt to load the desired font, retrieve its
11045          * metrics, and attempt to resize itself to keep the same number of rows
11046          * and columns.
11047          */
11048         g_object_class_install_property
11049                 (gobject_class,
11050                  PROP_FONT_DESC,
11051                  g_param_spec_boxed ("font-desc", NULL, NULL,
11052                                      PANGO_TYPE_FONT_DESCRIPTION,
11053                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11054 
11055         /**
11056          * VteTerminal:icon-title:
11057          *
11058          * The terminal's so-called icon title, or %NULL if no icon title has been set.
11059          */
11060         g_object_class_install_property
11061                 (gobject_class,
11062                  PROP_ICON_TITLE,
11063                  g_param_spec_string ("icon-title", NULL, NULL,
11064                                       NULL,
11065                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11066 
11067         /**
11068          * VteTerminal:input-enabled:
11069          *
11070          * Controls whether the terminal allows user input. When user input is disabled,
11071          * key press and mouse button press and motion events are not sent to the
11072          * terminal's child.
11073          */
11074         g_object_class_install_property
11075                 (gobject_class,
11076                  PROP_INPUT_ENABLED,
11077                  g_param_spec_boolean ("input-enabled", NULL, NULL,
11078                                        TRUE,
11079                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11080 
11081         /**
11082          * VteTerminal:pointer-autohide:
11083          *
11084          * Controls the value of the terminal's mouse autohide setting.  When autohiding
11085          * is enabled, the mouse cursor will be hidden when the user presses a key and
11086          * shown when the user moves the mouse.
11087          */
11088         g_object_class_install_property
11089                 (gobject_class,
11090                  PROP_MOUSE_POINTER_AUTOHIDE,
11091                  g_param_spec_boolean ("pointer-autohide", NULL, NULL,
11092                                        FALSE,
11093                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11094 
11095         /**
11096          * VteTerminal:pty:
11097          *
11098          * The PTY object for the terminal.
11099          */
11100         g_object_class_install_property
11101                 (gobject_class,
11102                  PROP_PTY,
11103                  g_param_spec_object ("pty", NULL, NULL,
11104                                       VTE_TYPE_PTY,
11105                                       G_PARAM_READWRITE |
11106                                       G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11107 
11108         /**
11109          * VteTerminal:rewrap-on-resize:
11110          *
11111          * Controls whether or not the terminal will rewrap its contents, including
11112          * the scrollback buffer, whenever the terminal's width changes.
11113          */
11114         g_object_class_install_property
11115                 (gobject_class,
11116                  PROP_REWRAP_ON_RESIZE,
11117                  g_param_spec_boolean ("rewrap-on-resize", NULL, NULL,
11118                                        TRUE,
11119                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11120 
11121         /**
11122          * VteTerminal:scrollback-lines:
11123          *
11124          * The length of the scrollback buffer used by the terminal.  The size of
11125          * the scrollback buffer will be set to the larger of this value and the number
11126          * of visible rows the widget can display, so 0 can safely be used to disable
11127          * scrollback.  Note that this setting only affects the normal screen buffer.
11128          * For terminal types which have an alternate screen buffer, no scrollback is
11129          * allowed on the alternate screen buffer.
11130          */
11131         g_object_class_install_property
11132                 (gobject_class,
11133                  PROP_SCROLLBACK_LINES,
11134                  g_param_spec_uint ("scrollback-lines", NULL, NULL,
11135                                     0, G_MAXUINT,
11136                                     VTE_SCROLLBACK_INIT,
11137                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11138 
11139         /**
11140          * VteTerminal:scroll-on-keystroke:
11141          *
11142          * Controls whether or not the terminal will forcibly scroll to the bottom of
11143          * the viewable history when the user presses a key.  Modifier keys do not
11144          * trigger this behavior.
11145          */
11146         g_object_class_install_property
11147                 (gobject_class,
11148                  PROP_SCROLL_ON_KEYSTROKE,
11149                  g_param_spec_boolean ("scroll-on-keystroke", NULL, NULL,
11150                                        FALSE,
11151                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11152 
11153         /**
11154          * VteTerminal:scroll-on-output:
11155          *
11156          * Controls whether or not the terminal will forcibly scroll to the bottom of
11157          * the viewable history when the new data is received from the child.
11158          */
11159         g_object_class_install_property
11160                 (gobject_class,
11161                  PROP_SCROLL_ON_OUTPUT,
11162                  g_param_spec_boolean ("scroll-on-output", NULL, NULL,
11163                                        TRUE,
11164                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11165 
11166         /**
11167          * VteTerminal:window-title:
11168          *
11169          * The terminal's title.
11170          */
11171         g_object_class_install_property
11172                 (gobject_class,
11173                  PROP_WINDOW_TITLE,
11174                  g_param_spec_string ("window-title", NULL, NULL,
11175                                       NULL,
11176                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11177 
11178         /**
11179          * VteTerminal:current-directory-uri:
11180          *
11181          * The current directory URI, or %NULL if unset.
11182          */
11183         g_object_class_install_property
11184                 (gobject_class,
11185                  PROP_CURRENT_DIRECTORY_URI,
11186                  g_param_spec_string ("current-directory-uri", NULL, NULL,
11187                                       NULL,
11188                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11189 
11190         /**
11191          * VteTerminal:current-file-uri:
11192          *
11193          * The current file URI, or %NULL if unset.
11194          */
11195         g_object_class_install_property
11196                 (gobject_class,
11197                  PROP_CURRENT_FILE_URI,
11198                  g_param_spec_string ("current-file-uri", NULL, NULL,
11199                                       NULL,
11200                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11201 
11202         /**
11203          * VteTerminal:word-char-exceptions:
11204          *
11205          * The set of characters which will be considered parts of a word
11206          * when doing word-wise selection, in addition to the default which only
11207          * considers alphanumeric characters part of a word.
11208          *
11209          * If %NULL, a built-in set is used.
11210          *
11211          * Since: 0.40
11212          */
11213         g_object_class_install_property
11214                 (gobject_class,
11215                  PROP_WORD_CHAR_EXCEPTIONS,
11216                  g_param_spec_string ("word-char-exceptions", NULL, NULL,
11217                                       NULL,
11218                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
11219 
11220 	/* Disable GtkWidget's keybindings except for Shift-F10 and MenuKey
11221          * which pop up the context menu.
11222          */
11223 	binding_set = gtk_binding_set_by_class(vte_terminal_parent_class);
11224 	gtk_binding_entry_skip(binding_set, GDK_KEY_F1, GDK_CONTROL_MASK);
11225 	gtk_binding_entry_skip(binding_set, GDK_KEY_F1, GDK_SHIFT_MASK);
11226 	gtk_binding_entry_skip(binding_set, GDK_KEY_KP_F1, GDK_CONTROL_MASK);
11227 	gtk_binding_entry_skip(binding_set, GDK_KEY_KP_F1, GDK_SHIFT_MASK);
11228 
11229 
11230 	process_timer = g_timer_new ();
11231 
11232         klass->priv = G_TYPE_CLASS_GET_PRIVATE (klass, VTE_TYPE_TERMINAL, VteTerminalClassPrivate);
11233 
11234         klass->priv->style_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
11235         gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (klass->priv->style_provider),
11236                                          "VteTerminal {\n"
11237                                            "padding: 1px 1px 1px 1px;\n"
11238                                            "background-color: @theme_base_color;\n"
11239                                            "color: @theme_fg_color;\n"
11240                                          "}\n",
11241                                          -1, NULL);
11242 
11243         /* a11y */
11244         gtk_widget_class_set_accessible_type(widget_class, VTE_TYPE_TERMINAL_ACCESSIBLE);
11245 }
11246 
11247 /**
11248  * vte_terminal_set_audible_bell:
11249  * @terminal: a #VteTerminal
11250  * @is_audible: %TRUE if the terminal should beep
11251  *
11252  * Controls whether or not the terminal will beep when the child outputs the
11253  * "bl" sequence.
11254  */
11255 void
vte_terminal_set_audible_bell(VteTerminal * terminal,gboolean is_audible)11256 vte_terminal_set_audible_bell(VteTerminal *terminal, gboolean is_audible)
11257 {
11258         VteTerminalPrivate *pvt;
11259 
11260 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11261 
11262         pvt = terminal->pvt;
11263 
11264         is_audible = is_audible != FALSE;
11265         if (is_audible == pvt->audible_bell)
11266                 return;
11267 
11268 	pvt->audible_bell = is_audible;
11269 
11270         g_object_notify (G_OBJECT (terminal), "audible-bell");
11271 }
11272 
11273 /**
11274  * vte_terminal_get_audible_bell:
11275  * @terminal: a #VteTerminal
11276  *
11277  * Checks whether or not the terminal will beep when the child outputs the
11278  * "bl" sequence.
11279  *
11280  * Returns: %TRUE if audible bell is enabled, %FALSE if not
11281  */
11282 gboolean
vte_terminal_get_audible_bell(VteTerminal * terminal)11283 vte_terminal_get_audible_bell(VteTerminal *terminal)
11284 {
11285 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
11286 	return terminal->pvt->audible_bell;
11287 }
11288 
11289 /**
11290  * vte_terminal_set_allow_bold:
11291  * @terminal: a #VteTerminal
11292  * @allow_bold: %TRUE if the terminal should attempt to draw bold text
11293  *
11294  * Controls whether or not the terminal will attempt to draw bold text,
11295  * either by using a bold font variant or by repainting text with a different
11296  * offset.
11297  *
11298  */
11299 void
vte_terminal_set_allow_bold(VteTerminal * terminal,gboolean allow_bold)11300 vte_terminal_set_allow_bold(VteTerminal *terminal, gboolean allow_bold)
11301 {
11302         VteTerminalPrivate *pvt;
11303 
11304 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11305 
11306         pvt = terminal->pvt;
11307 
11308         allow_bold = allow_bold != FALSE;
11309         if (allow_bold == pvt->allow_bold)
11310                 return;
11311 
11312 	pvt->allow_bold = allow_bold;
11313         g_object_notify (G_OBJECT (terminal), "allow-bold");
11314 
11315 	_vte_invalidate_all (terminal);
11316 }
11317 
11318 /**
11319  * vte_terminal_get_allow_bold:
11320  * @terminal: a #VteTerminal
11321  *
11322  * Checks whether or not the terminal will attempt to draw bold text by
11323  * repainting text with a one-pixel offset.
11324  *
11325  * Returns: %TRUE if bolding is enabled, %FALSE if not
11326  */
11327 gboolean
vte_terminal_get_allow_bold(VteTerminal * terminal)11328 vte_terminal_get_allow_bold(VteTerminal *terminal)
11329 {
11330 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
11331 	return terminal->pvt->allow_bold;
11332 }
11333 
11334 /**
11335  * vte_terminal_set_scroll_on_output:
11336  * @terminal: a #VteTerminal
11337  * @scroll: whether the terminal should scroll on output
11338  *
11339  * Controls whether or not the terminal will forcibly scroll to the bottom of
11340  * the viewable history when the new data is received from the child.
11341  */
11342 void
vte_terminal_set_scroll_on_output(VteTerminal * terminal,gboolean scroll)11343 vte_terminal_set_scroll_on_output(VteTerminal *terminal, gboolean scroll)
11344 {
11345 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11346 	terminal->pvt->scroll_on_output = scroll;
11347 }
11348 
11349 /**
11350  * vte_terminal_set_scroll_on_keystroke:
11351  * @terminal: a #VteTerminal
11352  * @scroll: whether the terminal should scroll on keystrokes
11353  *
11354  * Controls whether or not the terminal will forcibly scroll to the bottom of
11355  * the viewable history when the user presses a key.  Modifier keys do not
11356  * trigger this behavior.
11357  */
11358 void
vte_terminal_set_scroll_on_keystroke(VteTerminal * terminal,gboolean scroll)11359 vte_terminal_set_scroll_on_keystroke(VteTerminal *terminal, gboolean scroll)
11360 {
11361         VteTerminalPrivate *pvt;
11362 
11363 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11364 
11365         pvt = terminal->pvt;
11366 
11367         scroll = scroll != FALSE;
11368         if (scroll == pvt->scroll_on_keystroke)
11369                 return;
11370 
11371 	pvt->scroll_on_keystroke = scroll;
11372 
11373         g_object_notify (G_OBJECT (terminal), "scroll-on-keystroke");
11374 }
11375 
11376 /**
11377  * vte_terminal_set_rewrap_on_resize:
11378  * @terminal: a #VteTerminal
11379  * @rewrap: %TRUE if the terminal should rewrap on resize
11380  *
11381  * Controls whether or not the terminal will rewrap its contents, including
11382  * the scrollback history, whenever the terminal's width changes.
11383  */
11384 void
vte_terminal_set_rewrap_on_resize(VteTerminal * terminal,gboolean rewrap)11385 vte_terminal_set_rewrap_on_resize(VteTerminal *terminal, gboolean rewrap)
11386 {
11387         VteTerminalPrivate *pvt;
11388 
11389         g_return_if_fail(VTE_IS_TERMINAL(terminal));
11390 
11391         pvt = terminal->pvt;
11392 
11393         if (rewrap == pvt->rewrap_on_resize)
11394                 return;
11395 
11396         pvt->rewrap_on_resize = rewrap;
11397         g_object_notify (G_OBJECT (terminal), "rewrap-on-resize");
11398 }
11399 
11400 /**
11401  * vte_terminal_get_rewrap_on_resize:
11402  * @terminal: a #VteTerminal
11403  *
11404  * Checks whether or not the terminal will rewrap its contents upon resize.
11405  *
11406  * Returns: %TRUE if rewrapping is enabled, %FALSE if not
11407  */
11408 gboolean
vte_terminal_get_rewrap_on_resize(VteTerminal * terminal)11409 vte_terminal_get_rewrap_on_resize(VteTerminal *terminal)
11410 {
11411 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
11412 	return terminal->pvt->rewrap_on_resize;
11413 }
11414 
11415 static void
vte_terminal_real_copy_clipboard(VteTerminal * terminal)11416 vte_terminal_real_copy_clipboard(VteTerminal *terminal)
11417 {
11418 	_vte_debug_print(VTE_DEBUG_SELECTION, "Copying to CLIPBOARD.\n");
11419 	if (terminal->pvt->selection != NULL) {
11420 		GtkClipboard *clipboard;
11421 		clipboard = vte_terminal_clipboard_get(terminal,
11422 						       GDK_SELECTION_CLIPBOARD);
11423 		gtk_clipboard_set_text(clipboard, terminal->pvt->selection, -1);
11424 	}
11425 }
11426 
11427 /**
11428  * vte_terminal_copy_clipboard:
11429  * @terminal: a #VteTerminal
11430  *
11431  * Places the selected text in the terminal in the #GDK_SELECTION_CLIPBOARD
11432  * selection.
11433  */
11434 void
vte_terminal_copy_clipboard(VteTerminal * terminal)11435 vte_terminal_copy_clipboard(VteTerminal *terminal)
11436 {
11437 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11438 	g_signal_emit (terminal, signals[COPY_CLIPBOARD], 0);
11439 }
11440 
11441 static void
vte_terminal_real_paste_clipboard(VteTerminal * terminal)11442 vte_terminal_real_paste_clipboard(VteTerminal *terminal)
11443 {
11444 	_vte_debug_print(VTE_DEBUG_SELECTION, "Pasting CLIPBOARD.\n");
11445 	vte_terminal_paste(terminal, GDK_SELECTION_CLIPBOARD);
11446 }
11447 
11448 /**
11449  * vte_terminal_paste_clipboard:
11450  * @terminal: a #VteTerminal
11451  *
11452  * Sends the contents of the #GDK_SELECTION_CLIPBOARD selection to the
11453  * terminal's child.  If necessary, the data is converted from UTF-8 to the
11454  * terminal's current encoding. It's called on paste menu item, or when
11455  * user presses Shift+Insert.
11456  */
11457 void
vte_terminal_paste_clipboard(VteTerminal * terminal)11458 vte_terminal_paste_clipboard(VteTerminal *terminal)
11459 {
11460 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11461 	g_signal_emit (terminal, signals[PASTE_CLIPBOARD], 0);
11462 }
11463 
11464 /**
11465  * vte_terminal_copy_primary:
11466  * @terminal: a #VteTerminal
11467  *
11468  * Places the selected text in the terminal in the #GDK_SELECTION_PRIMARY
11469  * selection.
11470  */
11471 void
vte_terminal_copy_primary(VteTerminal * terminal)11472 vte_terminal_copy_primary(VteTerminal *terminal)
11473 {
11474 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11475 	_vte_debug_print(VTE_DEBUG_SELECTION, "Copying to PRIMARY.\n");
11476 	vte_terminal_copy(terminal, GDK_SELECTION_PRIMARY);
11477 }
11478 
11479 /**
11480  * vte_terminal_paste_primary:
11481  * @terminal: a #VteTerminal
11482  *
11483  * Sends the contents of the #GDK_SELECTION_PRIMARY selection to the terminal's
11484  * child.  If necessary, the data is converted from UTF-8 to the terminal's
11485  * current encoding.  The terminal will call also paste the
11486  * #GDK_SELECTION_PRIMARY selection when the user clicks with the the second
11487  * mouse button.
11488  */
11489 void
vte_terminal_paste_primary(VteTerminal * terminal)11490 vte_terminal_paste_primary(VteTerminal *terminal)
11491 {
11492 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11493 	_vte_debug_print(VTE_DEBUG_SELECTION, "Pasting PRIMARY.\n");
11494 	vte_terminal_paste(terminal, GDK_SELECTION_PRIMARY);
11495 }
11496 
11497 /* Set up whatever background we wanted. */
11498 static void
vte_terminal_background_update(VteTerminal * terminal)11499 vte_terminal_background_update(VteTerminal *terminal)
11500 {
11501 	const PangoColor *entry;
11502 	GdkRGBA color;
11503 
11504 	/* If we're not realized yet, don't worry about it, because we get
11505 	 * called when we realize. */
11506 	if (! gtk_widget_get_realized (&terminal->widget)) {
11507 		return;
11508 	}
11509 
11510 	_vte_debug_print(VTE_DEBUG_MISC|VTE_DEBUG_EVENTS,
11511 			"Updating background color.\n");
11512 
11513 	entry = _vte_terminal_get_color(terminal, VTE_DEFAULT_BG);
11514 	_vte_debug_print(VTE_DEBUG_STYLE,
11515 			 "Setting background color to (%d, %d, %d, %.3f).\n",
11516 			 entry->red, entry->green, entry->blue,
11517 			 terminal->pvt->background_alpha);
11518 
11519 	color.red = entry->red / 65535.;
11520 	color.green = entry->green / 65535.;
11521 	color.blue = entry->blue / 65535.;
11522         color.alpha = terminal->pvt->background_alpha;
11523 
11524         _vte_draw_set_background_solid (terminal->pvt->draw, &color);
11525 
11526 	/* Force a redraw for everything. */
11527 	_vte_invalidate_all (terminal);
11528 }
11529 
11530 /**
11531  * vte_terminal_get_has_selection:
11532  * @terminal: a #VteTerminal
11533  *
11534  * Checks if the terminal currently contains selected text.  Note that this
11535  * is different from determining if the terminal is the owner of any
11536  * #GtkClipboard items.
11537  *
11538  * Returns: %TRUE if part of the text in the terminal is selected.
11539  */
11540 gboolean
vte_terminal_get_has_selection(VteTerminal * terminal)11541 vte_terminal_get_has_selection(VteTerminal *terminal)
11542 {
11543 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
11544 	return terminal->pvt->has_selection;
11545 }
11546 
11547 static void
vte_terminal_update_cursor_blinks_internal(VteTerminal * terminal)11548 vte_terminal_update_cursor_blinks_internal(VteTerminal *terminal)
11549 {
11550         VteTerminalPrivate *pvt = terminal->pvt;
11551         gboolean blink = FALSE;
11552 
11553         switch (_vte_terminal_decscusr_cursor_blink(terminal)) {
11554         case VTE_CURSOR_BLINK_SYSTEM:
11555                 g_object_get(gtk_widget_get_settings(GTK_WIDGET(terminal)),
11556                                                      "gtk-cursor-blink",
11557                                                      &blink, NULL);
11558                 break;
11559         case VTE_CURSOR_BLINK_ON:
11560                 blink = TRUE;
11561                 break;
11562         case VTE_CURSOR_BLINK_OFF:
11563                 blink = FALSE;
11564                 break;
11565         }
11566 
11567 	if (pvt->cursor_blinks == blink)
11568 		return;
11569 
11570 	pvt->cursor_blinks = blink;
11571 	_vte_check_cursor_blink (terminal);
11572 }
11573 
11574 /**
11575  * vte_terminal_set_cursor_blink_mode:
11576  * @terminal: a #VteTerminal
11577  * @mode: the #VteCursorBlinkMode to use
11578  *
11579  * Sets whether or not the cursor will blink. Using %VTE_CURSOR_BLINK_SYSTEM
11580  * will use the #GtkSettings::gtk-cursor-blink setting.
11581  */
11582 void
vte_terminal_set_cursor_blink_mode(VteTerminal * terminal,VteCursorBlinkMode mode)11583 vte_terminal_set_cursor_blink_mode(VteTerminal *terminal, VteCursorBlinkMode mode)
11584 {
11585         VteTerminalPrivate *pvt;
11586 
11587 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11588         pvt = terminal->pvt;
11589 
11590         if (pvt->cursor_blink_mode == mode)
11591                 return;
11592 
11593         pvt->cursor_blink_mode = mode;
11594 
11595         vte_terminal_update_cursor_blinks_internal(terminal);
11596 
11597         g_object_notify(G_OBJECT(terminal), "cursor-blink-mode");
11598 }
11599 
11600 /**
11601  * vte_terminal_get_cursor_blink_mode:
11602  * @terminal: a #VteTerminal
11603  *
11604  * Returns the currently set cursor blink mode.
11605  *
11606  * Return value: cursor blink mode.
11607  */
11608 VteCursorBlinkMode
vte_terminal_get_cursor_blink_mode(VteTerminal * terminal)11609 vte_terminal_get_cursor_blink_mode(VteTerminal *terminal)
11610 {
11611         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), VTE_CURSOR_BLINK_SYSTEM);
11612 
11613         return terminal->pvt->cursor_blink_mode;
11614 }
11615 
11616 /**
11617  * vte_terminal_set_cursor_shape:
11618  * @terminal: a #VteTerminal
11619  * @shape: the #VteCursorShape to use
11620  *
11621  * Sets the shape of the cursor drawn.
11622  */
11623 void
vte_terminal_set_cursor_shape(VteTerminal * terminal,VteCursorShape shape)11624 vte_terminal_set_cursor_shape(VteTerminal *terminal, VteCursorShape shape)
11625 {
11626         VteTerminalPrivate *pvt;
11627 
11628 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11629         pvt = terminal->pvt;
11630 
11631         if (pvt->cursor_shape == shape)
11632                 return;
11633 
11634         pvt->cursor_shape = shape;
11635 	_vte_invalidate_cursor_once(terminal, FALSE);
11636 
11637         g_object_notify(G_OBJECT(terminal), "cursor-shape");
11638 }
11639 
11640 /**
11641  * vte_terminal_get_cursor_shape:
11642  * @terminal: a #VteTerminal
11643  *
11644  * Returns the currently set cursor shape.
11645  *
11646  * Return value: cursor shape.
11647  */
11648 VteCursorShape
vte_terminal_get_cursor_shape(VteTerminal * terminal)11649 vte_terminal_get_cursor_shape(VteTerminal *terminal)
11650 {
11651         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), VTE_CURSOR_SHAPE_BLOCK);
11652 
11653         return terminal->pvt->cursor_shape;
11654 }
11655 
11656 /* DECSCUSR set cursor style */
11657 void
_vte_terminal_set_cursor_style(VteTerminal * terminal,VteCursorStyle style)11658 _vte_terminal_set_cursor_style(VteTerminal *terminal, VteCursorStyle style)
11659 {
11660         VteTerminalPrivate *pvt;
11661 
11662         g_return_if_fail(VTE_IS_TERMINAL(terminal));
11663         pvt = terminal->pvt;
11664 
11665         if (pvt->cursor_style == style)
11666                 return;
11667 
11668         pvt->cursor_style = style;
11669 
11670         vte_terminal_update_cursor_blinks_internal(terminal);
11671 
11672         /* and this will also make cursor shape match the DECSCUSR style */
11673         _vte_invalidate_cursor_once(terminal, FALSE);
11674 }
11675 
11676 /*
11677  * _vte_terminal_decscusr_cursor_blink:
11678  * @terminal: a #VteTerminal
11679  *
11680  * Returns the cursor blink mode set by DECSCUSR. If DECSCUSR was never
11681  * called, or it set the blink mode to terminal default, this returns the
11682  * value set via API or in dconf. Internal use only.
11683  *
11684  * Return value: cursor blink mode
11685  */
11686 static VteCursorBlinkMode
_vte_terminal_decscusr_cursor_blink(VteTerminal * terminal)11687 _vte_terminal_decscusr_cursor_blink(VteTerminal *terminal)
11688 {
11689         switch (terminal->pvt->cursor_style) {
11690         default:
11691         case VTE_CURSOR_STYLE_TERMINAL_DEFAULT:
11692                 return terminal->pvt->cursor_blink_mode;
11693         case VTE_CURSOR_STYLE_BLINK_BLOCK:
11694         case VTE_CURSOR_STYLE_BLINK_UNDERLINE:
11695         case VTE_CURSOR_STYLE_BLINK_IBEAM:
11696                 return VTE_CURSOR_BLINK_ON;
11697         case VTE_CURSOR_STYLE_STEADY_BLOCK:
11698         case VTE_CURSOR_STYLE_STEADY_UNDERLINE:
11699         case VTE_CURSOR_STYLE_STEADY_IBEAM:
11700                 return VTE_CURSOR_BLINK_OFF;
11701         }
11702 }
11703 
11704 /*
11705  * _vte_terminal_decscusr_cursor_shape:
11706  * @terminal: a #VteTerminal
11707  *
11708  * Returns the cursor shape set by DECSCUSR. If DECSCUSR was never called,
11709  * or it set the cursor shape to terminal default, this returns the value
11710  * set via API. Internal use only.
11711  *
11712  * Return value: cursor shape
11713  */
11714 static VteCursorShape
_vte_terminal_decscusr_cursor_shape(VteTerminal * terminal)11715 _vte_terminal_decscusr_cursor_shape(VteTerminal *terminal)
11716 {
11717         switch (terminal->pvt->cursor_style) {
11718         default:
11719         case VTE_CURSOR_STYLE_TERMINAL_DEFAULT:
11720                 return terminal->pvt->cursor_shape;
11721         case VTE_CURSOR_STYLE_BLINK_BLOCK:
11722         case VTE_CURSOR_STYLE_STEADY_BLOCK:
11723                 return VTE_CURSOR_SHAPE_BLOCK;
11724         case VTE_CURSOR_STYLE_BLINK_UNDERLINE:
11725         case VTE_CURSOR_STYLE_STEADY_UNDERLINE:
11726                 return VTE_CURSOR_SHAPE_UNDERLINE;
11727         case VTE_CURSOR_STYLE_BLINK_IBEAM:
11728         case VTE_CURSOR_STYLE_STEADY_IBEAM:
11729                 return VTE_CURSOR_SHAPE_IBEAM;
11730         }
11731 }
11732 
11733 /**
11734  * vte_terminal_set_scrollback_lines:
11735  * @terminal: a #VteTerminal
11736  * @lines: the length of the history buffer
11737  *
11738  * Sets the length of the scrollback buffer used by the terminal.  The size of
11739  * the scrollback buffer will be set to the larger of this value and the number
11740  * of visible rows the widget can display, so 0 can safely be used to disable
11741  * scrollback.
11742  *
11743  * A negative value means "infinite scrollback".
11744  *
11745  * Note that this setting only affects the normal screen buffer.
11746  * For terminal types which have an alternate screen buffer, no scrollback is
11747  * allowed on the alternate screen buffer.
11748  */
11749 void
vte_terminal_set_scrollback_lines(VteTerminal * terminal,glong lines)11750 vte_terminal_set_scrollback_lines(VteTerminal *terminal, glong lines)
11751 {
11752         VteTerminalPrivate *pvt;
11753         GObject *object;
11754         glong scroll_delta, low, high, next;
11755 	VteScreen *screen;
11756 
11757 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11758 
11759 	if (lines < 0)
11760 		lines = G_MAXLONG;
11761 
11762         object = G_OBJECT(terminal);
11763         pvt = terminal->pvt;
11764 
11765 #if 0
11766         /* FIXME: this breaks the scrollbar range, bug #562511 */
11767         if (lines == pvt->scrollback_lines)
11768                 return;
11769 #endif
11770 
11771         g_object_freeze_notify(object);
11772 
11773 	_vte_debug_print (VTE_DEBUG_MISC,
11774 			"Setting scrollback lines to %ld\n", lines);
11775 
11776 	pvt->scrollback_lines = lines;
11777 
11778         /* The main screen gets the full scrollback buffer. */
11779         screen = &terminal->pvt->normal_screen;
11780         lines = MAX (lines, terminal->pvt->row_count);
11781         next = MAX (terminal->pvt->cursor.row + 1,
11782                     _vte_ring_next (screen->row_data));
11783         _vte_ring_resize (screen->row_data, lines);
11784         low = _vte_ring_delta (screen->row_data);
11785         high = lines + MIN (G_MAXLONG - lines, low - terminal->pvt->row_count + 1);
11786         screen->insert_delta = CLAMP (screen->insert_delta, low, high);
11787         screen->scroll_delta = CLAMP (screen->scroll_delta, low, screen->insert_delta);
11788         next = MIN (next, screen->insert_delta + terminal->pvt->row_count);
11789         if (_vte_ring_next (screen->row_data) > next){
11790                 _vte_ring_shrink (screen->row_data, next - low);
11791         }
11792 
11793         /* The alternate screen isn't allowed to scroll at all. */
11794         screen = &terminal->pvt->alternate_screen;
11795         _vte_ring_resize (screen->row_data, terminal->pvt->row_count);
11796         screen->scroll_delta = _vte_ring_delta (screen->row_data);
11797         screen->insert_delta = _vte_ring_delta (screen->row_data);
11798         if (_vte_ring_next (screen->row_data) > screen->insert_delta + terminal->pvt->row_count){
11799                 _vte_ring_shrink (screen->row_data, terminal->pvt->row_count);
11800         }
11801 
11802 	/* Adjust the scrollbar to the new location. */
11803 	/* Hack: force a change in scroll_delta even if the value remains, so that
11804 	   vte_term_q_adj_val_changed() doesn't shortcut to no-op, see bug 676075. */
11805         scroll_delta = terminal->pvt->screen->scroll_delta;
11806 	terminal->pvt->screen->scroll_delta = -1;
11807 	vte_terminal_queue_adjustment_value_changed (terminal, scroll_delta);
11808 	_vte_terminal_adjust_adjustments_full (terminal);
11809 
11810         g_object_notify(object, "scrollback-lines");
11811 
11812         g_object_thaw_notify(object);
11813 }
11814 
11815 /**
11816  * vte_terminal_set_backspace_binding:
11817  * @terminal: a #VteTerminal
11818  * @binding: a #VteEraseBinding for the backspace key
11819  *
11820  * Modifies the terminal's backspace key binding, which controls what
11821  * string or control sequence the terminal sends to its child when the user
11822  * presses the backspace key.
11823  */
11824 void
vte_terminal_set_backspace_binding(VteTerminal * terminal,VteEraseBinding binding)11825 vte_terminal_set_backspace_binding(VteTerminal *terminal,
11826 				   VteEraseBinding binding)
11827 {
11828         VteTerminalPrivate *pvt;
11829 
11830 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11831 
11832         pvt = terminal->pvt;
11833 
11834         if (binding == pvt->backspace_binding)
11835                 return;
11836 
11837 	/* FIXME: should we set the pty mode to match? */
11838 	pvt->backspace_binding = binding;
11839 
11840         g_object_notify(G_OBJECT(terminal), "backspace-binding");
11841 }
11842 
11843 /**
11844  * vte_terminal_set_delete_binding:
11845  * @terminal: a #VteTerminal
11846  * @binding: a #VteEraseBinding for the delete key
11847  *
11848  * Modifies the terminal's delete key binding, which controls what
11849  * string or control sequence the terminal sends to its child when the user
11850  * presses the delete key.
11851  */
11852 void
vte_terminal_set_delete_binding(VteTerminal * terminal,VteEraseBinding binding)11853 vte_terminal_set_delete_binding(VteTerminal *terminal,
11854 				VteEraseBinding binding)
11855 {
11856         VteTerminalPrivate *pvt;
11857 
11858 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11859 
11860         pvt = terminal->pvt;
11861 
11862         if (binding == pvt->delete_binding)
11863                 return;
11864 
11865 	pvt->delete_binding = binding;
11866 
11867         g_object_notify(G_OBJECT(terminal), "delete-binding");
11868 }
11869 
11870 /**
11871  * vte_terminal_set_mouse_autohide:
11872  * @terminal: a #VteTerminal
11873  * @setting: whether the mouse pointer should autohide
11874  *
11875  * Changes the value of the terminal's mouse autohide setting.  When autohiding
11876  * is enabled, the mouse cursor will be hidden when the user presses a key and
11877  * shown when the user moves the mouse.  This setting can be read using
11878  * vte_terminal_get_mouse_autohide().
11879  */
11880 void
vte_terminal_set_mouse_autohide(VteTerminal * terminal,gboolean setting)11881 vte_terminal_set_mouse_autohide(VteTerminal *terminal, gboolean setting)
11882 {
11883         VteTerminalPrivate *pvt;
11884 
11885 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11886 
11887         pvt = terminal->pvt;
11888 
11889         setting = setting != FALSE;
11890         if (setting == pvt->mouse_autohide)
11891                 return;
11892 
11893 	pvt->mouse_autohide = setting;
11894 
11895         g_object_notify(G_OBJECT(terminal), "pointer-autohide");
11896 }
11897 
11898 /**
11899  * vte_terminal_get_mouse_autohide:
11900  * @terminal: a #VteTerminal
11901  *
11902  * Determines the value of the terminal's mouse autohide setting.  When
11903  * autohiding is enabled, the mouse cursor will be hidden when the user presses
11904  * a key and shown when the user moves the mouse.  This setting can be changed
11905  * using vte_terminal_set_mouse_autohide().
11906  *
11907  * Returns: %TRUE if autohiding is enabled, %FALSE if not
11908  */
11909 gboolean
vte_terminal_get_mouse_autohide(VteTerminal * terminal)11910 vte_terminal_get_mouse_autohide(VteTerminal *terminal)
11911 {
11912 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
11913 	return terminal->pvt->mouse_autohide;
11914 }
11915 
11916 /**
11917  * vte_terminal_reset:
11918  * @terminal: a #VteTerminal
11919  * @clear_tabstops: whether to reset tabstops
11920  * @clear_history: whether to empty the terminal's scrollback buffer
11921  *
11922  * Resets as much of the terminal's internal state as possible, discarding any
11923  * unprocessed input data, resetting character attributes, cursor state,
11924  * national character set state, status line, terminal modes (insert/delete),
11925  * selection state, and encoding.
11926  *
11927  */
11928 void
vte_terminal_reset(VteTerminal * terminal,gboolean clear_tabstops,gboolean clear_history)11929 vte_terminal_reset(VteTerminal *terminal,
11930                    gboolean clear_tabstops,
11931                    gboolean clear_history)
11932 {
11933         VteTerminalPrivate *pvt;
11934         int i;
11935 
11936 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
11937 
11938         pvt = terminal->pvt;
11939 
11940         g_object_freeze_notify(G_OBJECT(terminal));
11941 
11942 	/* Clear the output buffer. */
11943 	_vte_byte_array_clear(pvt->outgoing);
11944 	/* Reset charset substitution state. */
11945 	_vte_iso2022_state_free(pvt->iso2022);
11946         pvt->iso2022 = _vte_iso2022_state_new(NULL);
11947 	_vte_iso2022_state_set_codeset(pvt->iso2022,
11948 				       pvt->encoding);
11949 	/* Reset keypad/cursor key modes. */
11950 	pvt->keypad_mode = VTE_KEYMODE_NORMAL;
11951 	pvt->cursor_mode = VTE_KEYMODE_NORMAL;
11952         /* Enable autowrap. */
11953         pvt->autowrap = TRUE;
11954 	/* Enable meta-sends-escape. */
11955 	pvt->meta_sends_escape = TRUE;
11956 	/* Disable margin bell. */
11957 	pvt->margin_bell = FALSE;
11958         /* Disable DECCOLM mode. */
11959         pvt->deccolm_mode = FALSE;
11960 	/* Reset saved settings. */
11961 	if (pvt->dec_saved != NULL) {
11962 		g_hash_table_destroy(pvt->dec_saved);
11963 		pvt->dec_saved = g_hash_table_new(NULL, NULL);
11964 	}
11965 	/* Reset the color palette. Only the 256 indexed colors, not the special ones, as per xterm. */
11966 	for (i = 0; i < 256; i++)
11967 		terminal->pvt->palette[i].sources[VTE_COLOR_SOURCE_ESCAPE].is_set = FALSE;
11968 	/* Reset the default attributes.  Reset the alternate attribute because
11969 	 * it's not a real attribute, but we need to treat it as one here. */
11970 	_vte_terminal_set_default_attributes(terminal);
11971         /* Reset charset modes. */
11972         pvt->character_replacements[0] = VTE_CHARACTER_REPLACEMENT_NONE;
11973         pvt->character_replacements[1] = VTE_CHARACTER_REPLACEMENT_NONE;
11974         pvt->character_replacement = &pvt->character_replacements[0];
11975 	/* Clear the scrollback buffers and reset the cursors. Switch to normal screen. */
11976 	if (clear_history) {
11977                 pvt->screen = &pvt->normal_screen;
11978                 pvt->normal_screen.scroll_delta = pvt->normal_screen.insert_delta =
11979                         _vte_ring_reset(pvt->normal_screen.row_data);
11980                 pvt->alternate_screen.scroll_delta = pvt->alternate_screen.insert_delta =
11981                         _vte_ring_reset(pvt->alternate_screen.row_data);
11982                 pvt->cursor.row = pvt->screen->insert_delta;
11983                 pvt->cursor.col = 0;
11984                 /* Adjust the scrollbar to the new location. */
11985                 /* Hack: force a change in scroll_delta even if the value remains, so that
11986                    vte_term_q_adj_val_changed() doesn't shortcut to no-op, see bug 730599. */
11987                 pvt->screen->scroll_delta = -1;
11988                 vte_terminal_queue_adjustment_value_changed (terminal, pvt->screen->insert_delta);
11989 		_vte_terminal_adjust_adjustments_full (terminal);
11990 	}
11991         /* DECSCUSR cursor style */
11992         pvt->cursor_style = VTE_CURSOR_STYLE_TERMINAL_DEFAULT;
11993 	/* Do more stuff we refer to as a "full" reset. */
11994 	if (clear_tabstops) {
11995 		vte_terminal_set_default_tabstops(terminal);
11996 	}
11997 	/* Reset restricted scrolling regions, leave insert mode, make
11998 	 * the cursor visible again. */
11999         pvt->scrolling_restricted = FALSE;
12000         pvt->sendrecv_mode = TRUE;
12001         pvt->insert_mode = FALSE;
12002         pvt->linefeed_mode = FALSE;
12003         pvt->origin_mode = FALSE;
12004         pvt->reverse_mode = FALSE;
12005 	pvt->cursor_visible = TRUE;
12006         /* For some reason, xterm doesn't reset alternateScroll, but we do. */
12007         pvt->alternate_screen_scroll = TRUE;
12008 	/* Reset the encoding. */
12009 	vte_terminal_set_encoding(terminal, NULL /* UTF-8 */, NULL);
12010 	g_assert_cmpstr(pvt->encoding, ==, "UTF-8");
12011 	/* Reset selection. */
12012 	vte_terminal_deselect_all(terminal);
12013 	pvt->has_selection = FALSE;
12014 	pvt->selecting = FALSE;
12015 	pvt->selecting_restart = FALSE;
12016 	pvt->selecting_had_delta = FALSE;
12017 	if (pvt->selection != NULL) {
12018 		g_free(pvt->selection);
12019 		pvt->selection = NULL;
12020 		memset(&pvt->selection_origin, 0,
12021 		       sizeof(pvt->selection_origin));
12022 		memset(&pvt->selection_last, 0,
12023 		       sizeof(pvt->selection_last));
12024 		memset(&pvt->selection_start, 0,
12025 		       sizeof(pvt->selection_start));
12026 		memset(&pvt->selection_end, 0,
12027 		       sizeof(pvt->selection_end));
12028 	}
12029 	/* Reset mouse motion events. */
12030 	pvt->mouse_tracking_mode = MOUSE_TRACKING_NONE;
12031         pvt->mouse_pressed_buttons = 0;
12032         pvt->mouse_handled_buttons = 0;
12033 	pvt->mouse_last_x = 0;
12034 	pvt->mouse_last_y = 0;
12035 	pvt->mouse_xterm_extension = FALSE;
12036 	pvt->mouse_urxvt_extension = FALSE;
12037 	pvt->mouse_smooth_scroll_delta = 0.;
12038 	/* Clear modifiers. */
12039 	pvt->modifiers = 0;
12040 	/* Reset miscellaneous stuff. */
12041 	pvt->bracketed_paste_mode = FALSE;
12042         /* Reset the saved cursor. */
12043         _vte_terminal_save_cursor(terminal, &terminal->pvt->normal_screen);
12044         _vte_terminal_save_cursor(terminal, &terminal->pvt->alternate_screen);
12045 	/* Cause everything to be redrawn (or cleared). */
12046 	vte_terminal_maybe_scroll_to_bottom(terminal);
12047 	_vte_invalidate_all(terminal);
12048 
12049         g_object_thaw_notify(G_OBJECT(terminal));
12050 }
12051 
12052 /**
12053  * vte_terminal_get_char_width:
12054  * @terminal: a #VteTerminal
12055  *
12056  * Returns: the width of a character cell
12057  */
12058 glong
vte_terminal_get_char_width(VteTerminal * terminal)12059 vte_terminal_get_char_width(VteTerminal *terminal)
12060 {
12061 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
12062 	vte_terminal_ensure_font (terminal);
12063 	return terminal->pvt->char_width;
12064 }
12065 
12066 /**
12067  * vte_terminal_get_char_height:
12068  * @terminal: a #VteTerminal
12069  *
12070  * Returns: the height of a character cell
12071  */
12072 glong
vte_terminal_get_char_height(VteTerminal * terminal)12073 vte_terminal_get_char_height(VteTerminal *terminal)
12074 {
12075 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
12076 	vte_terminal_ensure_font (terminal);
12077 	return terminal->pvt->char_height;
12078 }
12079 
12080 /**
12081  * vte_terminal_get_row_count:
12082  * @terminal: a #VteTerminal
12083  *
12084  *
12085  * Returns: the number of rows
12086  */
12087 glong
vte_terminal_get_row_count(VteTerminal * terminal)12088 vte_terminal_get_row_count(VteTerminal *terminal)
12089 {
12090 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
12091 	return terminal->pvt->row_count;
12092 }
12093 
12094 /**
12095  * vte_terminal_get_column_count:
12096  * @terminal: a #VteTerminal
12097  *
12098  * Returns: the number of columns
12099  */
12100 glong
vte_terminal_get_column_count(VteTerminal * terminal)12101 vte_terminal_get_column_count(VteTerminal *terminal)
12102 {
12103 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
12104 	return terminal->pvt->column_count;
12105 }
12106 
12107 /**
12108  * vte_terminal_get_window_title:
12109  * @terminal: a #VteTerminal
12110  *
12111  * Returns: (transfer none): the window title
12112  */
12113 const char *
vte_terminal_get_window_title(VteTerminal * terminal)12114 vte_terminal_get_window_title(VteTerminal *terminal)
12115 {
12116 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), "");
12117 	return terminal->pvt->window_title;
12118 }
12119 
12120 /**
12121  * vte_terminal_get_icon_title:
12122  * @terminal: a #VteTerminal
12123  *
12124  * Returns: (transfer none): the icon title
12125  */
12126 const char *
vte_terminal_get_icon_title(VteTerminal * terminal)12127 vte_terminal_get_icon_title(VteTerminal *terminal)
12128 {
12129 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), "");
12130 	return terminal->pvt->icon_title;
12131 }
12132 
12133 /**
12134  * vte_terminal_get_current_directory_uri:
12135  * @terminal: a #VteTerminal
12136  *
12137  * Returns: (transfer none): the URI of the current directory of the
12138  *   process running in the terminal, or %NULL
12139  */
12140 const char *
vte_terminal_get_current_directory_uri(VteTerminal * terminal)12141 vte_terminal_get_current_directory_uri(VteTerminal *terminal)
12142 {
12143         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
12144         return terminal->pvt->current_directory_uri;
12145 }
12146 
12147 /**
12148  * vte_terminal_get_current_file_uri:
12149  * @terminal: a #VteTerminal
12150  *
12151  * Returns: (transfer none): the URI of the current file the
12152  *   process running in the terminal is operating on, or %NULL if
12153  *   not set
12154  */
12155 const char *
vte_terminal_get_current_file_uri(VteTerminal * terminal)12156 vte_terminal_get_current_file_uri(VteTerminal *terminal)
12157 {
12158         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
12159         return terminal->pvt->current_file_uri;
12160 }
12161 
12162 /**
12163  * vte_terminal_set_pty:
12164  * @terminal: a #VteTerminal
12165  * @pty: (allow-none): a #VtePty, or %NULL
12166  *
12167  * Sets @pty as the PTY to use in @terminal.
12168  * Use %NULL to unset the PTY.
12169  */
12170 void
vte_terminal_set_pty(VteTerminal * terminal,VtePty * pty)12171 vte_terminal_set_pty(VteTerminal *terminal,
12172                             VtePty *pty)
12173 {
12174         VteTerminalPrivate *pvt;
12175         GObject *object;
12176         long flags;
12177         int pty_master;
12178 
12179         g_return_if_fail(VTE_IS_TERMINAL(terminal));
12180         g_return_if_fail(pty == NULL || VTE_IS_PTY(pty));
12181 
12182         pvt = terminal->pvt;
12183         if (pvt->pty == pty)
12184                 return;
12185 
12186         object = G_OBJECT(terminal);
12187 
12188         g_object_freeze_notify(object);
12189 
12190         if (pvt->pty != NULL) {
12191                 _vte_terminal_disconnect_pty_read(terminal);
12192                 _vte_terminal_disconnect_pty_write(terminal);
12193 
12194                 if (terminal->pvt->pty_channel != NULL) {
12195                         g_io_channel_unref (terminal->pvt->pty_channel);
12196                         pvt->pty_channel = NULL;
12197                 }
12198 
12199 		/* Take one last shot at processing whatever data is pending,
12200 		 * then flush the buffers in case we're about to run a new
12201 		 * command, disconnecting the timeout. */
12202 		if (terminal->pvt->incoming != NULL) {
12203 			vte_terminal_process_incoming(terminal);
12204 			_vte_incoming_chunks_release (terminal->pvt->incoming);
12205 			terminal->pvt->incoming = NULL;
12206 			terminal->pvt->input_bytes = 0;
12207 		}
12208 		g_array_set_size(terminal->pvt->pending, 0);
12209 		vte_terminal_stop_processing (terminal);
12210 
12211 		/* Clear the outgoing buffer as well. */
12212 		_vte_byte_array_clear(terminal->pvt->outgoing);
12213 
12214                 vte_pty_close(pvt->pty);
12215                 g_object_unref(pvt->pty);
12216                 pvt->pty = NULL;
12217         }
12218 
12219         if (pty == NULL) {
12220                 pvt->pty = NULL;
12221                 g_object_notify(object, "pty");
12222                 g_object_thaw_notify(object);
12223                 return;
12224         }
12225 
12226         pvt->pty = g_object_ref(pty);
12227         pty_master = vte_pty_get_fd(pvt->pty);
12228 
12229         pvt->pty_channel = g_io_channel_unix_new (pty_master);
12230         g_io_channel_set_close_on_unref (pvt->pty_channel, FALSE);
12231 
12232         /* FIXMEchpe: vte_pty_open_unix98 does the inverse ... */
12233         /* Set the pty to be non-blocking. */
12234         flags = fcntl(pty_master, F_GETFL);
12235         if ((flags & O_NONBLOCK) == 0) {
12236                 fcntl(pty_master, F_SETFL, flags | O_NONBLOCK);
12237         }
12238 
12239         vte_terminal_set_size(terminal,
12240                               terminal->pvt->column_count,
12241                               terminal->pvt->row_count);
12242 
12243         _vte_terminal_setup_utf8 (terminal);
12244 
12245         /* Open channels to listen for input on. */
12246         _vte_terminal_connect_pty_read (terminal);
12247 
12248         g_object_notify(object, "pty");
12249 
12250         g_object_thaw_notify(object);
12251 }
12252 
12253 /**
12254  * vte_terminal_get_pty:
12255  * @terminal: a #VteTerminal
12256  *
12257  * Returns the #VtePty of @terminal.
12258  *
12259  * Returns: (transfer none): a #VtePty, or %NULL
12260  */
12261 VtePty *
vte_terminal_get_pty(VteTerminal * terminal)12262 vte_terminal_get_pty(VteTerminal *terminal)
12263 {
12264         g_return_val_if_fail (VTE_IS_TERMINAL (terminal), NULL);
12265 
12266         return terminal->pvt->pty;
12267 }
12268 
12269 /* We need this bit of glue to ensure that accessible objects will always
12270  * get signals. */
12271 void
_vte_terminal_accessible_ref(VteTerminal * terminal)12272 _vte_terminal_accessible_ref(VteTerminal *terminal)
12273 {
12274 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
12275 	terminal->pvt->accessible_emit = TRUE;
12276 }
12277 
12278 char *
_vte_terminal_get_selection(VteTerminal * terminal)12279 _vte_terminal_get_selection(VteTerminal *terminal)
12280 {
12281 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
12282 
12283 	return g_strdup (terminal->pvt->selection);
12284 }
12285 
12286 void
_vte_terminal_get_start_selection(VteTerminal * terminal,long * col,long * row)12287 _vte_terminal_get_start_selection(VteTerminal *terminal, long *col, long *row)
12288 {
12289 	VteVisualPosition ss;
12290 
12291 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
12292 
12293 	ss = terminal->pvt->selection_start;
12294 
12295 	if (col) {
12296 		*col = ss.col;
12297 	}
12298 
12299 	if (row) {
12300 		*row = ss.row;
12301 	}
12302 }
12303 
12304 void
_vte_terminal_get_end_selection(VteTerminal * terminal,long * col,long * row)12305 _vte_terminal_get_end_selection(VteTerminal *terminal, long *col, long *row)
12306 {
12307 	VteVisualPosition se;
12308 
12309 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
12310 
12311 	se = terminal->pvt->selection_end;
12312 
12313 	if (col) {
12314 		*col = se.col;
12315 	}
12316 
12317 	if (row) {
12318 		*row = se.row;
12319 	}
12320 }
12321 
12322 void
_vte_terminal_select_text(VteTerminal * terminal,long start_col,long start_row,long end_col,long end_row,int start_offset,int end_offset)12323 _vte_terminal_select_text(VteTerminal *terminal,
12324 			  long start_col, long start_row,
12325 			  long end_col, long end_row,
12326 			  int start_offset, int end_offset)
12327 {
12328 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
12329 
12330 	vte_terminal_deselect_all (terminal);
12331 
12332 	terminal->pvt->selection_type = selection_type_char;
12333 	terminal->pvt->selecting_had_delta = TRUE;
12334 	terminal->pvt->selection_start.col = start_col;
12335 	terminal->pvt->selection_start.row = start_row;
12336 	terminal->pvt->selection_end.col = end_col;
12337 	terminal->pvt->selection_end.row = end_row;
12338 	vte_terminal_copy_primary(terminal);
12339 	vte_terminal_emit_selection_changed(terminal);
12340 
12341 	_vte_invalidate_region (terminal,
12342 			MIN (start_col, end_col), MAX (start_col, end_col),
12343 			MIN (start_row, end_row), MAX (start_row, end_row),
12344 			FALSE);
12345 
12346 }
12347 
12348 void
_vte_terminal_remove_selection(VteTerminal * terminal)12349 _vte_terminal_remove_selection(VteTerminal *terminal)
12350 {
12351 	vte_terminal_deselect_all (terminal);
12352 }
12353 
12354 static void
_vte_terminal_select_empty_at(VteTerminal * terminal,long col,long row)12355 _vte_terminal_select_empty_at(VteTerminal *terminal,
12356 			      long col, long row)
12357 {
12358 	_vte_terminal_select_text(terminal, col, row, col - 1, row, 0, 0);
12359 }
12360 
12361 static void
add_update_timeout(VteTerminal * terminal)12362 add_update_timeout (VteTerminal *terminal)
12363 {
12364 	if (update_timeout_tag == 0) {
12365 		_vte_debug_print (VTE_DEBUG_TIMEOUT,
12366 				"Starting update timeout\n");
12367 		update_timeout_tag =
12368 			g_timeout_add_full (GDK_PRIORITY_REDRAW,
12369 					VTE_UPDATE_TIMEOUT,
12370 					update_timeout, NULL,
12371 					NULL);
12372 	}
12373 	if (in_process_timeout == FALSE &&
12374 			process_timeout_tag != 0) {
12375 		_vte_debug_print (VTE_DEBUG_TIMEOUT,
12376 				"Removing process timeout\n");
12377 		g_source_remove (process_timeout_tag);
12378 		process_timeout_tag = 0;
12379 	}
12380 	if (terminal->pvt->active == NULL) {
12381 		_vte_debug_print (VTE_DEBUG_TIMEOUT,
12382 				"Adding terminal to active list\n");
12383 		terminal->pvt->active = active_terminals =
12384 			g_list_prepend (active_terminals, terminal);
12385 	}
12386 
12387 }
12388 static void
reset_update_regions(VteTerminal * terminal)12389 reset_update_regions (VteTerminal *terminal)
12390 {
12391 	if (terminal->pvt->update_regions != NULL) {
12392 		g_slist_foreach (terminal->pvt->update_regions,
12393 				(GFunc)cairo_region_destroy, NULL);
12394 		g_slist_free (terminal->pvt->update_regions);
12395 		terminal->pvt->update_regions = NULL;
12396 	}
12397 	/* the invalidated_all flag also marks whether to skip processing
12398 	 * due to the widget being invisible */
12399 	terminal->pvt->invalidated_all =
12400 		terminal->pvt->visibility_state==GDK_VISIBILITY_FULLY_OBSCURED;
12401 }
12402 
12403 static void
remove_from_active_list(VteTerminal * terminal)12404 remove_from_active_list (VteTerminal *terminal)
12405 {
12406 	if (terminal->pvt->active != NULL
12407 			&& terminal->pvt->update_regions == NULL) {
12408 		_vte_debug_print(VTE_DEBUG_TIMEOUT,
12409 			"Removing terminal from active list\n");
12410 		active_terminals = g_list_delete_link (active_terminals,
12411 				terminal->pvt->active);
12412 		terminal->pvt->active = NULL;
12413 
12414 		if (active_terminals == NULL) {
12415 			if (in_process_timeout == FALSE &&
12416 					process_timeout_tag != 0) {
12417 				_vte_debug_print(VTE_DEBUG_TIMEOUT,
12418 						"Removing process timeout\n");
12419 				g_source_remove (process_timeout_tag);
12420 				process_timeout_tag = 0;
12421 			}
12422 			if (in_update_timeout == FALSE &&
12423 					update_timeout_tag != 0) {
12424 				_vte_debug_print(VTE_DEBUG_TIMEOUT,
12425 						"Removing update timeout\n");
12426 				g_source_remove (update_timeout_tag);
12427 				update_timeout_tag = 0;
12428 			}
12429 		}
12430 	}
12431 }
12432 static void
remove_update_timeout(VteTerminal * terminal)12433 remove_update_timeout (VteTerminal *terminal)
12434 {
12435 	reset_update_regions (terminal);
12436 	remove_from_active_list (terminal);
12437 }
12438 
12439 static void
vte_terminal_add_process_timeout(VteTerminal * terminal)12440 vte_terminal_add_process_timeout (VteTerminal *terminal)
12441 {
12442 	_vte_debug_print(VTE_DEBUG_TIMEOUT,
12443 			"Adding terminal to active list\n");
12444 	terminal->pvt->active = active_terminals =
12445 		g_list_prepend (active_terminals, terminal);
12446 	if (update_timeout_tag == 0 &&
12447 			process_timeout_tag == 0) {
12448 		_vte_debug_print(VTE_DEBUG_TIMEOUT,
12449 				"Starting process timeout\n");
12450 		process_timeout_tag =
12451 			g_timeout_add (VTE_DISPLAY_TIMEOUT,
12452 					process_timeout, NULL);
12453 	}
12454 }
12455 static inline gboolean
vte_terminal_is_processing(VteTerminal * terminal)12456 vte_terminal_is_processing (VteTerminal *terminal)
12457 {
12458 	return terminal->pvt->active != NULL;
12459 }
12460 static inline void
vte_terminal_start_processing(VteTerminal * terminal)12461 vte_terminal_start_processing (VteTerminal *terminal)
12462 {
12463 	if (!vte_terminal_is_processing (terminal)) {
12464 		vte_terminal_add_process_timeout (terminal);
12465 	}
12466 }
12467 
12468 static void
vte_terminal_stop_processing(VteTerminal * terminal)12469 vte_terminal_stop_processing (VteTerminal *terminal)
12470 {
12471 	remove_from_active_list (terminal);
12472 }
12473 
12474 static inline gboolean
need_processing(VteTerminal * terminal)12475 need_processing (VteTerminal *terminal)
12476 {
12477 	return _vte_incoming_chunks_length (terminal->pvt->incoming) != 0;
12478 }
12479 
12480 /* Emit an "icon-title-changed" signal. */
12481 static void
vte_terminal_emit_icon_title_changed(VteTerminal * terminal)12482 vte_terminal_emit_icon_title_changed(VteTerminal *terminal)
12483 {
12484 	_vte_debug_print(VTE_DEBUG_SIGNALS,
12485 			"Emitting `icon-title-changed'.\n");
12486 	g_signal_emit_by_name(terminal, "icon-title-changed");
12487 }
12488 
12489 /* Emit a "window-title-changed" signal. */
12490 static void
vte_terminal_emit_window_title_changed(VteTerminal * terminal)12491 vte_terminal_emit_window_title_changed(VteTerminal *terminal)
12492 {
12493 	_vte_debug_print(VTE_DEBUG_SIGNALS,
12494 			"Emitting `window-title-changed'.\n");
12495 	g_signal_emit_by_name(terminal, "window-title-changed");
12496 }
12497 
12498 static void
vte_terminal_emit_current_directory_uri_changed(VteTerminal * terminal)12499 vte_terminal_emit_current_directory_uri_changed(VteTerminal *terminal)
12500 {
12501         _vte_debug_print(VTE_DEBUG_SIGNALS,
12502                         "Emitting `current-directory-uri-changed'.\n");
12503         g_signal_emit_by_name(terminal, "current-directory-uri-changed");
12504 }
12505 
12506 static void
vte_terminal_emit_current_file_uri_changed(VteTerminal * terminal)12507 vte_terminal_emit_current_file_uri_changed(VteTerminal *terminal)
12508 {
12509         _vte_debug_print(VTE_DEBUG_SIGNALS,
12510                         "Emitting `current-file-uri-changed'.\n");
12511         g_signal_emit_by_name(terminal, "current-file-uri-changed");
12512 }
12513 
12514 static void
vte_terminal_emit_pending_signals(VteTerminal * terminal)12515 vte_terminal_emit_pending_signals(VteTerminal *terminal)
12516 {
12517         GObject *object;
12518 	GdkWindow *window;
12519 
12520 	object = G_OBJECT (terminal);
12521 	window = gtk_widget_get_window (&terminal->widget);
12522 
12523         g_object_freeze_notify(object);
12524 
12525 	vte_terminal_emit_adjustment_changed (terminal);
12526 
12527 	if (terminal->pvt->window_title_changed) {
12528 		g_free (terminal->pvt->window_title);
12529 		terminal->pvt->window_title = terminal->pvt->window_title_changed;
12530 		terminal->pvt->window_title_changed = NULL;
12531 
12532 		if (window)
12533 			gdk_window_set_title (window, terminal->pvt->window_title);
12534 		vte_terminal_emit_window_title_changed(terminal);
12535                 g_object_notify(object, "window-title");
12536 	}
12537 
12538 	if (terminal->pvt->icon_title_changed) {
12539 		g_free (terminal->pvt->icon_title);
12540 		terminal->pvt->icon_title = terminal->pvt->icon_title_changed;
12541 		terminal->pvt->icon_title_changed = NULL;
12542 
12543 		if (window)
12544 			gdk_window_set_icon_name (window, terminal->pvt->icon_title);
12545 		vte_terminal_emit_icon_title_changed(terminal);
12546                 g_object_notify(object, "icon-title");
12547 	}
12548 
12549 	if (terminal->pvt->current_directory_uri_changed) {
12550                 g_free (terminal->pvt->current_directory_uri);
12551                 terminal->pvt->current_directory_uri = terminal->pvt->current_directory_uri_changed;
12552                 terminal->pvt->current_directory_uri_changed = NULL;
12553 
12554                 vte_terminal_emit_current_directory_uri_changed(terminal);
12555                 g_object_notify(object, "current-directory-uri");
12556         }
12557 
12558         if (terminal->pvt->current_file_uri_changed) {
12559                 g_free (terminal->pvt->current_file_uri);
12560                 terminal->pvt->current_file_uri = terminal->pvt->current_file_uri_changed;
12561                 terminal->pvt->current_file_uri_changed = NULL;
12562 
12563                 vte_terminal_emit_current_file_uri_changed(terminal);
12564                 g_object_notify(object, "current-file-uri");
12565         }
12566 
12567 	/* Flush any pending "inserted" signals. */
12568 	vte_terminal_emit_cursor_moved(terminal);
12569 	vte_terminal_emit_pending_text_signals(terminal);
12570 	vte_terminal_emit_contents_changed (terminal);
12571 
12572         g_object_thaw_notify(object);
12573 }
12574 
time_process_incoming(VteTerminal * terminal)12575 static void time_process_incoming (VteTerminal *terminal)
12576 {
12577 	gdouble elapsed;
12578 	glong target;
12579 	g_timer_reset (process_timer);
12580 	vte_terminal_process_incoming (terminal);
12581 	elapsed = g_timer_elapsed (process_timer, NULL) * 1000;
12582 	target = VTE_MAX_PROCESS_TIME / elapsed * terminal->pvt->input_bytes;
12583 	terminal->pvt->max_input_bytes =
12584 		(terminal->pvt->max_input_bytes + target) / 2;
12585 }
12586 
12587 
12588 /* This function is called after DISPLAY_TIMEOUT ms.
12589  * It makes sure initial output is never delayed by more than DISPLAY_TIMEOUT
12590  */
12591 static gboolean
process_timeout(gpointer data)12592 process_timeout (gpointer data)
12593 {
12594 	GList *l, *next;
12595 	gboolean again;
12596 
12597         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12598 	gdk_threads_enter();
12599         G_GNUC_END_IGNORE_DEPRECATIONS;
12600 
12601 	in_process_timeout = TRUE;
12602 
12603 	_vte_debug_print (VTE_DEBUG_WORK, "<");
12604 	_vte_debug_print (VTE_DEBUG_TIMEOUT,
12605 			"Process timeout:  %d active\n",
12606 			g_list_length (active_terminals));
12607 
12608 	for (l = active_terminals; l != NULL; l = next) {
12609 		VteTerminal *terminal = l->data;
12610 		gboolean active = FALSE;
12611 
12612 		next = g_list_next (l);
12613 
12614 		if (l != active_terminals) {
12615 			_vte_debug_print (VTE_DEBUG_WORK, "T");
12616 		}
12617 		if (terminal->pvt->pty_channel != NULL) {
12618 			if (terminal->pvt->pty_input_active ||
12619 					terminal->pvt->pty_input_source == 0) {
12620 				terminal->pvt->pty_input_active = FALSE;
12621 				vte_terminal_io_read (terminal->pvt->pty_channel,
12622 						G_IO_IN, terminal);
12623 			}
12624 			_vte_terminal_enable_input_source (terminal);
12625 		}
12626 		if (need_processing (terminal)) {
12627 			active = TRUE;
12628 			if (VTE_MAX_PROCESS_TIME) {
12629 				time_process_incoming (terminal);
12630 			} else {
12631 				vte_terminal_process_incoming(terminal);
12632 			}
12633 			terminal->pvt->input_bytes = 0;
12634 		} else
12635 			vte_terminal_emit_pending_signals (terminal);
12636 		if (!active && terminal->pvt->update_regions == NULL) {
12637 			if (terminal->pvt->active != NULL) {
12638 				_vte_debug_print(VTE_DEBUG_TIMEOUT,
12639 						"Removing terminal from active list [process]\n");
12640 				active_terminals = g_list_delete_link (
12641 						active_terminals,
12642 						terminal->pvt->active);
12643 				terminal->pvt->active = NULL;
12644 			}
12645 		}
12646 	}
12647 
12648 	_vte_debug_print (VTE_DEBUG_WORK, ">");
12649 
12650 	if (active_terminals && update_timeout_tag == 0) {
12651 		again = TRUE;
12652 	} else {
12653 		_vte_debug_print(VTE_DEBUG_TIMEOUT,
12654 				"Stoping process timeout\n");
12655 		process_timeout_tag = 0;
12656 		again = FALSE;
12657 	}
12658 
12659 	in_process_timeout = FALSE;
12660 
12661         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12662 	gdk_threads_leave();
12663         G_GNUC_END_IGNORE_DEPRECATIONS;
12664 
12665 	if (again) {
12666 		/* Force us to relinquish the CPU as the child is running
12667 		 * at full tilt and making us run to keep up...
12668 		 */
12669 		g_usleep (0);
12670 	} else if (update_timeout_tag == 0) {
12671 		/* otherwise free up memory used to capture incoming data */
12672 		prune_chunks (10);
12673 	}
12674 
12675 	return again;
12676 }
12677 
12678 
12679 static gboolean
update_regions(VteTerminal * terminal)12680 update_regions (VteTerminal *terminal)
12681 {
12682 	GSList *l;
12683 	cairo_region_t *region;
12684 	GdkWindow *window;
12685 
12686         if (G_UNLIKELY(!gtk_widget_get_realized(&terminal->widget)))
12687                 return FALSE;
12688 	if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
12689 		reset_update_regions (terminal);
12690 		return FALSE;
12691 	}
12692 
12693 	if (G_UNLIKELY (!terminal->pvt->update_regions))
12694 		return FALSE;
12695 
12696 
12697 	l = terminal->pvt->update_regions;
12698 	if (g_slist_next (l) != NULL) {
12699 		/* amalgamate into one super-region */
12700 		region = cairo_region_create ();
12701 		do {
12702 			cairo_region_union (region, l->data);
12703 			cairo_region_destroy (l->data);
12704 		} while ((l = g_slist_next (l)) != NULL);
12705 	} else {
12706 		region = l->data;
12707 	}
12708 	g_slist_free (terminal->pvt->update_regions);
12709 	terminal->pvt->update_regions = NULL;
12710 	terminal->pvt->invalidated_all = FALSE;
12711 
12712 	/* and perform the merge with the window visible area */
12713 	window = gtk_widget_get_window (&terminal->widget);
12714 	gdk_window_invalidate_region (window, region, FALSE);
12715 	gdk_window_process_updates (window, FALSE);
12716 	cairo_region_destroy (region);
12717 
12718 	_vte_debug_print (VTE_DEBUG_WORK, "-");
12719 
12720 	return TRUE;
12721 }
12722 
12723 static gboolean
update_repeat_timeout(gpointer data)12724 update_repeat_timeout (gpointer data)
12725 {
12726 	GList *l, *next;
12727 	gboolean again;
12728 
12729         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12730 	gdk_threads_enter();
12731         G_GNUC_END_IGNORE_DEPRECATIONS;
12732 
12733 	in_update_timeout = TRUE;
12734 
12735 	_vte_debug_print (VTE_DEBUG_WORK, "[");
12736 	_vte_debug_print (VTE_DEBUG_TIMEOUT,
12737 			"Repeat timeout:  %d active\n",
12738 			g_list_length (active_terminals));
12739 
12740 	for (l = active_terminals; l != NULL; l = next) {
12741 		VteTerminal *terminal = l->data;
12742 
12743 		next = g_list_next (l);
12744 
12745 		if (l != active_terminals) {
12746 			_vte_debug_print (VTE_DEBUG_WORK, "T");
12747 		}
12748 		if (terminal->pvt->pty_channel != NULL) {
12749 			if (terminal->pvt->pty_input_active ||
12750 					terminal->pvt->pty_input_source == 0) {
12751 				terminal->pvt->pty_input_active = FALSE;
12752 				vte_terminal_io_read (terminal->pvt->pty_channel,
12753 						G_IO_IN, terminal);
12754 			}
12755 			_vte_terminal_enable_input_source (terminal);
12756 		}
12757 		vte_terminal_emit_adjustment_changed (terminal);
12758 		if (need_processing (terminal)) {
12759 			if (VTE_MAX_PROCESS_TIME) {
12760 				time_process_incoming (terminal);
12761 			} else {
12762 				vte_terminal_process_incoming (terminal);
12763 			}
12764 			terminal->pvt->input_bytes = 0;
12765 		} else
12766 			vte_terminal_emit_pending_signals (terminal);
12767 
12768 		again = update_regions (terminal);
12769 		if (!again) {
12770 			if (terminal->pvt->active != NULL) {
12771 				_vte_debug_print(VTE_DEBUG_TIMEOUT,
12772 						"Removing terminal from active list [update]\n");
12773 				active_terminals = g_list_delete_link (
12774 						active_terminals,
12775 						terminal->pvt->active);
12776 				terminal->pvt->active = NULL;
12777 			}
12778 		}
12779 	}
12780 
12781 
12782 	if (active_terminals != NULL) {
12783 		/* remove the idle source, and draw non-Terminals
12784 		 * (except for gdk/{directfb,quartz}!)
12785 		 */
12786 		gdk_window_process_all_updates ();
12787 	}
12788 
12789 	_vte_debug_print (VTE_DEBUG_WORK, "]");
12790 
12791 	/* We only stop the timer if no update request was received in this
12792          * past cycle.  Technically, always stop this timer object and maybe
12793          * reinstall a new one because we need to delay by the amount of time
12794          * it took to repaint the screen: bug 730732.
12795 	 */
12796 	if (active_terminals == NULL) {
12797 		_vte_debug_print(VTE_DEBUG_TIMEOUT,
12798 				"Stoping update timeout\n");
12799 		update_timeout_tag = 0;
12800 		again = FALSE;
12801         } else {
12802                 update_timeout_tag =
12803                         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
12804                                             VTE_UPDATE_REPEAT_TIMEOUT,
12805                                             update_repeat_timeout, NULL,
12806                                             NULL);
12807                 again = TRUE;
12808 	}
12809 
12810 	in_update_timeout = FALSE;
12811 
12812         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12813 	gdk_threads_leave();
12814         G_GNUC_END_IGNORE_DEPRECATIONS;
12815 
12816 	if (again) {
12817 		/* Force us to relinquish the CPU as the child is running
12818 		 * at full tilt and making us run to keep up...
12819 		 */
12820 		g_usleep (0);
12821 	} else {
12822 		/* otherwise free up memory used to capture incoming data */
12823 		prune_chunks (10);
12824 	}
12825 
12826         return FALSE;  /* If we need to go again, we already have a new timer for that. */
12827 }
12828 
12829 static gboolean
update_timeout(gpointer data)12830 update_timeout (gpointer data)
12831 {
12832 	GList *l, *next;
12833 	gboolean redraw = FALSE;
12834 
12835         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12836 	gdk_threads_enter();
12837         G_GNUC_END_IGNORE_DEPRECATIONS;
12838 
12839 	in_update_timeout = TRUE;
12840 
12841 	_vte_debug_print (VTE_DEBUG_WORK, "{");
12842 	_vte_debug_print (VTE_DEBUG_TIMEOUT,
12843 			"Update timeout:  %d active\n",
12844 			g_list_length (active_terminals));
12845 
12846 	if (process_timeout_tag != 0) {
12847 		_vte_debug_print(VTE_DEBUG_TIMEOUT,
12848 				"Removing process timeout\n");
12849 		g_source_remove (process_timeout_tag);
12850 		process_timeout_tag = 0;
12851 	}
12852 
12853 	for (l = active_terminals; l != NULL; l = next) {
12854 		VteTerminal *terminal = l->data;
12855 
12856 		next = g_list_next (l);
12857 
12858 		if (l != active_terminals) {
12859 			_vte_debug_print (VTE_DEBUG_WORK, "T");
12860 		}
12861 		if (terminal->pvt->pty_channel != NULL) {
12862 			if (terminal->pvt->pty_input_active ||
12863 					terminal->pvt->pty_input_source == 0) {
12864 				terminal->pvt->pty_input_active = FALSE;
12865 				vte_terminal_io_read (terminal->pvt->pty_channel,
12866 						G_IO_IN, terminal);
12867 			}
12868 			_vte_terminal_enable_input_source (terminal);
12869 		}
12870 		vte_terminal_emit_adjustment_changed (terminal);
12871 		if (need_processing (terminal)) {
12872 			if (VTE_MAX_PROCESS_TIME) {
12873 				time_process_incoming (terminal);
12874 			} else {
12875 				vte_terminal_process_incoming (terminal);
12876 			}
12877 			terminal->pvt->input_bytes = 0;
12878 		} else
12879 			vte_terminal_emit_pending_signals (terminal);
12880 
12881 		redraw |= update_regions (terminal);
12882 	}
12883 
12884 	if (redraw) {
12885 		/* remove the idle source, and draw non-Terminals
12886 		 * (except for gdk/{directfb,quartz}!)
12887 		 */
12888 		gdk_window_process_all_updates ();
12889 	}
12890 
12891 	_vte_debug_print (VTE_DEBUG_WORK, "}");
12892 
12893 	/* Set a timer such that we do not invalidate for a while. */
12894 	/* This limits the number of times we draw to ~40fps. */
12895 	update_timeout_tag =
12896 		g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
12897 				    VTE_UPDATE_REPEAT_TIMEOUT,
12898 				    update_repeat_timeout, NULL,
12899 				    NULL);
12900 	in_update_timeout = FALSE;
12901 
12902         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
12903 	gdk_threads_leave();
12904         G_GNUC_END_IGNORE_DEPRECATIONS;
12905 
12906 	return FALSE;
12907 }
12908 
12909 /**
12910  * vte_terminal_write_contents_sync:
12911  * @terminal: a #VteTerminal
12912  * @stream: a #GOutputStream to write to
12913  * @flags: a set of #VteWriteFlags
12914  * @cancellable: (allow-none): a #GCancellable object, or %NULL
12915  * @error: (allow-none): a #GError location to store the error occuring, or %NULL
12916  *
12917  * Write contents of the current contents of @terminal (including any
12918  * scrollback history) to @stream according to @flags.
12919  *
12920  * If @cancellable is not %NULL, then the operation can be cancelled by triggering
12921  * the cancellable object from another thread. If the operation was cancelled,
12922  * the error %G_IO_ERROR_CANCELLED will be returned in @error.
12923  *
12924  * This is a synchronous operation and will make the widget (and input
12925  * processing) during the write operation, which may take a long time
12926  * depending on scrollback history and @stream availability for writing.
12927  *
12928  * Returns: %TRUE on success, %FALSE if there was an error
12929  */
12930 gboolean
vte_terminal_write_contents_sync(VteTerminal * terminal,GOutputStream * stream,VteWriteFlags flags,GCancellable * cancellable,GError ** error)12931 vte_terminal_write_contents_sync (VteTerminal *terminal,
12932                                   GOutputStream *stream,
12933                                   VteWriteFlags flags,
12934                                   GCancellable *cancellable,
12935                                   GError **error)
12936 {
12937         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
12938         g_return_val_if_fail(G_IS_OUTPUT_STREAM(stream), FALSE);
12939 	return _vte_ring_write_contents (terminal->pvt->screen->row_data,
12940 					 stream, flags,
12941 					 cancellable, error);
12942 }
12943 
12944 
12945 /*
12946  * Buffer search
12947  */
12948 
12949 /* TODO Add properties & signals */
12950 
12951 /**
12952  * vte_terminal_search_set_gregex:
12953  * @terminal: a #VteTerminal
12954  * @regex: (allow-none): a #GRegex, or %NULL
12955  * @flags: flags from #GRegexMatchFlags
12956  *
12957  * Sets the #GRegex regex to search for. Unsets the search regex when passed %NULL.
12958  */
12959 void
vte_terminal_search_set_gregex(VteTerminal * terminal,GRegex * regex,GRegexMatchFlags flags)12960 vte_terminal_search_set_gregex (VteTerminal *terminal,
12961 				GRegex      *regex,
12962                                 GRegexMatchFlags flags)
12963 {
12964         g_return_if_fail(VTE_IS_TERMINAL(terminal));
12965 
12966 	if (terminal->pvt->search_regex == regex)
12967 		return;
12968 
12969 	if (terminal->pvt->search_regex) {
12970 		g_regex_unref (terminal->pvt->search_regex);
12971 		terminal->pvt->search_regex = NULL;
12972 	}
12973 
12974 	if (regex)
12975 		terminal->pvt->search_regex = g_regex_ref (regex);
12976         terminal->pvt->search_match_flags = flags;
12977 
12978 	_vte_invalidate_all (terminal);
12979 }
12980 
12981 /**
12982  * vte_terminal_search_get_gregex:
12983  * @terminal: a #VteTerminal
12984  *
12985  * Returns: (transfer none): the search #GRegex regex set in @terminal, or %NULL
12986  */
12987 GRegex *
vte_terminal_search_get_gregex(VteTerminal * terminal)12988 vte_terminal_search_get_gregex (VteTerminal *terminal)
12989 {
12990 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
12991 
12992 	return terminal->pvt->search_regex;
12993 }
12994 
12995 /**
12996  * vte_terminal_search_set_wrap_around:
12997  * @terminal: a #VteTerminal
12998  * @wrap_around: whether search should wrap
12999  *
13000  * Sets whether search should wrap around to the beginning of the
13001  * terminal content when reaching its end.
13002  */
13003 void
vte_terminal_search_set_wrap_around(VteTerminal * terminal,gboolean wrap_around)13004 vte_terminal_search_set_wrap_around (VteTerminal *terminal,
13005 				     gboolean     wrap_around)
13006 {
13007 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
13008 
13009 	terminal->pvt->search_wrap_around = !!wrap_around;
13010 }
13011 
13012 /**
13013  * vte_terminal_search_get_wrap_around:
13014  * @terminal: a #VteTerminal
13015  *
13016  * Returns: whether searching will wrap around
13017  */
13018 gboolean
vte_terminal_search_get_wrap_around(VteTerminal * terminal)13019 vte_terminal_search_get_wrap_around (VteTerminal *terminal)
13020 {
13021 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
13022 
13023 	return terminal->pvt->search_wrap_around;
13024 }
13025 
13026 static gboolean
vte_terminal_search_rows(VteTerminal * terminal,long start_row,long end_row,gboolean backward)13027 vte_terminal_search_rows (VteTerminal *terminal,
13028 			  long start_row,
13029 			  long end_row,
13030 			  gboolean backward)
13031 {
13032         VteTerminalPrivate *pvt;
13033 	char *row_text;
13034 	GMatchInfo *match_info;
13035 	GError *error = NULL;
13036 	int start, end;
13037 	long start_col, end_col;
13038 	gchar *word;
13039 	VteCharAttributes *ca;
13040 	GArray *attrs;
13041 	gdouble value, page_size;
13042 
13043 	pvt = terminal->pvt;
13044 
13045 	row_text = vte_terminal_get_text_range (terminal, start_row, 0, end_row, -1, NULL, NULL, NULL);
13046 
13047 	g_regex_match_full (pvt->search_regex, row_text, -1, 0,
13048                             pvt->search_match_flags | G_REGEX_MATCH_NOTEMPTY,
13049                             &match_info, &error);
13050 	if (error) {
13051 		g_printerr ("Error while matching: %s\n", error->message);
13052 		g_error_free (error);
13053 		g_match_info_free (match_info);
13054 		g_free (row_text);
13055 		return TRUE;
13056 	}
13057 
13058 	if (!g_match_info_matches (match_info)) {
13059 		g_match_info_free (match_info);
13060 		g_free (row_text);
13061 		return FALSE;
13062 	}
13063 
13064 	word = g_match_info_fetch (match_info, 0);
13065 
13066 	/* Fetch text again, with attributes */
13067 	g_free (row_text);
13068 	if (!pvt->search_attrs)
13069 		pvt->search_attrs = g_array_new (FALSE, TRUE, sizeof (VteCharAttributes));
13070 	attrs = pvt->search_attrs;
13071 	row_text = vte_terminal_get_text_range (terminal, start_row, 0, end_row, -1, NULL, NULL, attrs);
13072 
13073 	/* This gives us the offset in the buffer */
13074 	g_match_info_fetch_pos (match_info, 0, &start, &end);
13075 
13076 	ca = &g_array_index (attrs, VteCharAttributes, start);
13077 	start_row = ca->row;
13078 	start_col = ca->column;
13079 	ca = &g_array_index (attrs, VteCharAttributes, end - 1);
13080 	end_row = ca->row;
13081 	end_col = ca->column;
13082 
13083 	g_free (word);
13084 	g_free (row_text);
13085 	g_match_info_free (match_info);
13086 
13087 	_vte_terminal_select_text (terminal, start_col, start_row, end_col, end_row, 0, 0);
13088 	/* Quite possibly the math here should not access adjustment directly... */
13089 	value = gtk_adjustment_get_value(terminal->pvt->vadjustment);
13090 	page_size = gtk_adjustment_get_page_size(terminal->pvt->vadjustment);
13091 	if (backward) {
13092 		if (end_row < value || end_row >= value + page_size)
13093 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, end_row - page_size + 1);
13094 	} else {
13095 		if (start_row < value || start_row >= value + page_size)
13096 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, start_row);
13097 	}
13098 
13099 	return TRUE;
13100 }
13101 
13102 static gboolean
vte_terminal_search_rows_iter(VteTerminal * terminal,long start_row,long end_row,gboolean backward)13103 vte_terminal_search_rows_iter (VteTerminal *terminal,
13104 			       long start_row,
13105 			       long end_row,
13106 			       gboolean backward)
13107 {
13108 	const VteRowData *row;
13109 	long iter_start_row, iter_end_row;
13110 
13111 	if (backward) {
13112 		iter_start_row = end_row;
13113 		while (iter_start_row > start_row) {
13114 			iter_end_row = iter_start_row;
13115 
13116 			do {
13117 				iter_start_row--;
13118 				row = _vte_terminal_find_row_data (terminal, iter_start_row);
13119 			} while (row && row->attr.soft_wrapped);
13120 
13121 			if (vte_terminal_search_rows (terminal, iter_start_row, iter_end_row, backward))
13122 				return TRUE;
13123 		}
13124 	} else {
13125 		iter_end_row = start_row;
13126 		while (iter_end_row < end_row) {
13127 			iter_start_row = iter_end_row;
13128 
13129 			do {
13130 				row = _vte_terminal_find_row_data (terminal, iter_end_row);
13131 				iter_end_row++;
13132 			} while (row && row->attr.soft_wrapped);
13133 
13134 			if (vte_terminal_search_rows (terminal, iter_start_row, iter_end_row, backward))
13135 				return TRUE;
13136 		}
13137 	}
13138 
13139 	return FALSE;
13140 }
13141 
13142 static gboolean
vte_terminal_search_find(VteTerminal * terminal,gboolean backward)13143 vte_terminal_search_find (VteTerminal *terminal,
13144 			  gboolean     backward)
13145 {
13146         VteTerminalPrivate *pvt;
13147 	long buffer_start_row, buffer_end_row;
13148 	long last_start_row, last_end_row;
13149 
13150 	g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
13151 
13152 	pvt = terminal->pvt;
13153 	if (!pvt->search_regex)
13154 		return FALSE;
13155 
13156 	/* TODO
13157 	 * Currently We only find one result per extended line, and ignore columns
13158 	 * Moreover, the whole search thing is implemented very inefficiently.
13159 	 */
13160 
13161 	buffer_start_row = _vte_ring_delta (terminal->pvt->screen->row_data);
13162 	buffer_end_row = _vte_ring_next (terminal->pvt->screen->row_data);
13163 
13164 	if (pvt->has_selection) {
13165 		last_start_row = pvt->selection_start.row;
13166 		last_end_row = pvt->selection_end.row + 1;
13167 	} else {
13168 		last_start_row = pvt->screen->scroll_delta + terminal->pvt->row_count;
13169 		last_end_row = pvt->screen->scroll_delta;
13170 	}
13171 	last_start_row = MAX (buffer_start_row, last_start_row);
13172 	last_end_row = MIN (buffer_end_row, last_end_row);
13173 
13174 	/* If search fails, we make an empty selection at the last searched
13175 	 * position... */
13176 	if (backward) {
13177 		if (vte_terminal_search_rows_iter (terminal, buffer_start_row, last_start_row, backward))
13178 			return TRUE;
13179 		if (pvt->search_wrap_around &&
13180 		    vte_terminal_search_rows_iter (terminal, last_end_row, buffer_end_row, backward))
13181 			return TRUE;
13182 		if (pvt->has_selection) {
13183 			if (pvt->search_wrap_around)
13184 			    _vte_terminal_select_empty_at (terminal,
13185 							   pvt->selection_start.col,
13186 							   pvt->selection_start.row);
13187 			else
13188 			    _vte_terminal_select_empty_at (terminal,
13189 							   -1,
13190 							   buffer_start_row - 1);
13191 		}
13192 	} else {
13193 		if (vte_terminal_search_rows_iter (terminal, last_end_row, buffer_end_row, backward))
13194 			return TRUE;
13195 		if (pvt->search_wrap_around &&
13196 		    vte_terminal_search_rows_iter (terminal, buffer_start_row, last_start_row, backward))
13197 			return TRUE;
13198 		if (pvt->has_selection) {
13199 			if (pvt->search_wrap_around)
13200 			    _vte_terminal_select_empty_at (terminal,
13201 							   pvt->selection_end.col + 1,
13202 							   pvt->selection_end.row);
13203 			else
13204 			    _vte_terminal_select_empty_at (terminal,
13205 							   -1,
13206 							   buffer_end_row);
13207 		}
13208 	}
13209 
13210 	return FALSE;
13211 }
13212 
13213 /**
13214  * vte_terminal_search_find_previous:
13215  * @terminal: a #VteTerminal
13216  *
13217  * Searches the previous string matching the search regex set with
13218  * vte_terminal_search_set_gregex().
13219  *
13220  * Returns: %TRUE if a match was found
13221  */
13222 gboolean
vte_terminal_search_find_previous(VteTerminal * terminal)13223 vte_terminal_search_find_previous (VteTerminal *terminal)
13224 {
13225 	return vte_terminal_search_find (terminal, TRUE);
13226 }
13227 
13228 /**
13229  * vte_terminal_search_find_next:
13230  * @terminal: a #VteTerminal
13231  *
13232  * Searches the next string matching the search regex set with
13233  * vte_terminal_search_set_gregex().
13234  *
13235  * Returns: %TRUE if a match was found
13236  */
13237 gboolean
vte_terminal_search_find_next(VteTerminal * terminal)13238 vte_terminal_search_find_next (VteTerminal *terminal)
13239 {
13240 	return vte_terminal_search_find (terminal, FALSE);
13241 }
13242 
13243 /* Just some arbitrary minimum values */
13244 #define MIN_COLUMNS (16)
13245 #define MIN_ROWS    (2)
13246 
13247 /**
13248  * vte_terminal_get_geometry_hints:
13249  * @terminal: a #VteTerminal
13250  * @hints: (out caller-allocates): a #GdkGeometry to fill in
13251  * @min_rows: the minimum number of rows to request
13252  * @min_columns: the minimum number of columns to request
13253  *
13254  * Fills in some @hints from @terminal's geometry. The hints
13255  * filled are those covered by the %GDK_HINT_RESIZE_INC,
13256  * %GDK_HINT_MIN_SIZE and %GDK_HINT_BASE_SIZE flags.
13257  *
13258  * See gtk_window_set_geometry_hints() for more information.
13259  *
13260  * @terminal must be realized (see gtk_widget_get_realized()).
13261  */
13262 void
vte_terminal_get_geometry_hints(VteTerminal * terminal,GdkGeometry * hints,int min_rows,int min_columns)13263 vte_terminal_get_geometry_hints(VteTerminal *terminal,
13264                                 GdkGeometry *hints,
13265                                 int min_rows,
13266                                 int min_columns)
13267 {
13268         VteTerminalPrivate *pvt;
13269         GtkWidget *widget;
13270         GtkBorder padding;
13271 
13272         g_return_if_fail(VTE_IS_TERMINAL(terminal));
13273         g_return_if_fail(hints != NULL);
13274         widget = &terminal->widget;
13275         g_return_if_fail(gtk_widget_get_realized(widget));
13276 
13277         pvt = terminal->pvt;
13278 
13279         gtk_style_context_get_padding(gtk_widget_get_style_context(widget),
13280                                       gtk_widget_get_state_flags(widget),
13281                                       &padding);
13282 
13283         hints->base_width  = padding.left + padding.right;
13284         hints->base_height = padding.top  + padding.bottom;
13285         hints->width_inc   = pvt->char_width;
13286         hints->height_inc  = pvt->char_height;
13287         hints->min_width   = hints->base_width  + hints->width_inc  * min_columns;
13288         hints->min_height  = hints->base_height + hints->height_inc * min_rows;
13289 }
13290 
13291 /**
13292  * vte_terminal_set_geometry_hints_for_window:
13293  * @terminal: a #VteTerminal
13294  * @window: a #GtkWindow
13295  *
13296  * Sets @terminal as @window's geometry widget. See
13297  * gtk_window_set_geometry_hints() for more information.
13298  *
13299  * @terminal must be realized (see gtk_widget_get_realized()).
13300  */
13301 void
vte_terminal_set_geometry_hints_for_window(VteTerminal * terminal,GtkWindow * window)13302 vte_terminal_set_geometry_hints_for_window(VteTerminal *terminal,
13303                                            GtkWindow *window)
13304 {
13305         GdkGeometry hints;
13306 
13307         g_return_if_fail(VTE_IS_TERMINAL(terminal));
13308         g_return_if_fail(gtk_widget_get_realized(&terminal->widget));
13309 
13310         vte_terminal_get_geometry_hints(terminal, &hints, MIN_ROWS, MIN_COLUMNS);
13311         gtk_window_set_geometry_hints(window,
13312                                       &terminal->widget,
13313                                       &hints,
13314                                       GDK_HINT_RESIZE_INC |
13315                                       GDK_HINT_MIN_SIZE |
13316                                       GDK_HINT_BASE_SIZE);
13317 }
13318 
13319 /**
13320  * vte_terminal_set_input_enabled:
13321  * @terminal: a #VteTerminal
13322  * @enabled: whether to enable user input
13323  *
13324  * Enables or disables user input. When user input is disabled,
13325  * the terminal's child will not receive any key press, or mouse button
13326  * press or motion events sent to it.
13327  */
13328 void
vte_terminal_set_input_enabled(VteTerminal * terminal,gboolean enabled)13329 vte_terminal_set_input_enabled (VteTerminal *terminal,
13330                                 gboolean enabled)
13331 {
13332         VteTerminalPrivate *pvt;
13333         GtkWidget *widget;
13334         GtkStyleContext *context;
13335 
13336         g_return_if_fail(VTE_IS_TERMINAL(terminal));
13337 
13338         pvt = terminal->pvt;
13339         widget = &terminal->widget;
13340 
13341         enabled = enabled != FALSE;
13342         if (enabled == terminal->pvt->input_enabled)
13343                 return;
13344 
13345         pvt->input_enabled = enabled;
13346 
13347         context = gtk_widget_get_style_context (widget);
13348 
13349         /* FIXME: maybe hide cursor when input disabled, too? */
13350 
13351         if (enabled) {
13352                 if (gtk_widget_has_focus(widget))
13353                         gtk_im_context_focus_in(pvt->im_context);
13354 
13355                 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_READ_ONLY);
13356         } else {
13357                 vte_terminal_im_reset(terminal);
13358                 if (gtk_widget_has_focus(widget))
13359                         gtk_im_context_focus_out(pvt->im_context);
13360 
13361                 _vte_terminal_disconnect_pty_write(terminal);
13362                 _vte_byte_array_clear(pvt->outgoing);
13363 
13364                 gtk_style_context_add_class (context, GTK_STYLE_CLASS_READ_ONLY);
13365         }
13366 
13367         g_object_notify(G_OBJECT(terminal), "input-enabled");
13368 }
13369 
13370 /**
13371  * vte_terminal_get_input_enabled:
13372  * @terminal: a #VteTerminal
13373  *
13374  * Returns whether the terminal allow user input.
13375  */
13376 gboolean
vte_terminal_get_input_enabled(VteTerminal * terminal)13377 vte_terminal_get_input_enabled (VteTerminal *terminal)
13378 {
13379         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
13380 
13381         return terminal->pvt->input_enabled;
13382 }
13383 
13384 static gboolean
process_word_char_exceptions(const char * str,gunichar ** arrayp,gsize * lenp)13385 process_word_char_exceptions(const char *str,
13386                              gunichar **arrayp,
13387                              gsize *lenp)
13388 {
13389         const char *p;
13390         gunichar *array, c;
13391         gsize len, i;
13392 
13393         if (str == NULL)
13394                 str = WORD_CHAR_EXCEPTIONS_DEFAULT;
13395 
13396         len = g_utf8_strlen(str, -1);
13397         array = g_new(gunichar, len);
13398         i = 0;
13399 
13400         for (p = str; *p; p = g_utf8_next_char(p)) {
13401                 c = g_utf8_get_char(p);
13402 
13403                 /* For forward compatibility reasons, we skip
13404                  * characters that aren't supposed to be here,
13405                  * instead of erroring out.
13406                  */
13407                 /* '-' must only be used*  at the start of the string */
13408                 if (c == (gunichar)'-' && p != str)
13409                         continue;
13410                 if (!g_unichar_isgraph(c))
13411                         continue;
13412                 if (g_unichar_isspace(c))
13413                         continue;
13414                 if (g_unichar_isalnum(c))
13415                         continue;
13416 
13417                 array[i++] = g_utf8_get_char(p);
13418         }
13419 
13420         g_assert(i <= len);
13421         len = i; /* we may have skipped some characters */
13422 
13423         /* Sort the result since we want to use bsearch on it */
13424         qsort(array, len, sizeof(gunichar), compare_unichar_p);
13425 
13426         /* Check that no character occurs twice */
13427         for (i = 1; i < len; i++) {
13428                 if (array[i-1] != array[i])
13429                         continue;
13430 
13431                 g_free(array);
13432                 return FALSE;
13433         }
13434 
13435 #if 0
13436         /* Debug */
13437         for (i = 0; i < len; i++) {
13438                 char utf[7];
13439                 c = array[i];
13440                 utf[g_unichar_to_utf8(c, utf)] = '\0';
13441                 g_printerr("Word char exception: U+%04X %s\n", c, utf);
13442         }
13443 #endif
13444 
13445         *lenp = len;
13446         *arrayp = array;
13447         return TRUE;
13448 }
13449 
13450 /**
13451  * vte_terminal_set_word_char_exceptions:
13452  * @terminal: a #VteTerminal
13453  * @exceptions: a string of ASCII punctuation characters, or %NULL
13454  *
13455  * With this function you can provide a set of characters which will
13456  * be considered parts of a word when doing word-wise selection, in
13457  * addition to the default which only considers alphanumeric characters
13458  * part of a word.
13459  *
13460  * The characters in @exceptions must be non-alphanumeric, each character
13461  * must occur only once, and if @exceptions contains the character
13462  * U+002D HYPHEN-MINUS, it must be at the start of the string.
13463  *
13464  * Use %NULL to reset the set of exception characters to the default.
13465  *
13466  * Since: 0.40
13467  */
13468 void
vte_terminal_set_word_char_exceptions(VteTerminal * terminal,const char * exceptions)13469 vte_terminal_set_word_char_exceptions(VteTerminal *terminal,
13470                                       const char *exceptions)
13471 {
13472         gunichar *array;
13473         gsize len;
13474 
13475         g_return_if_fail(VTE_IS_TERMINAL(terminal));
13476 
13477         if (g_strcmp0(exceptions, terminal->pvt->word_char_exceptions_string) == 0)
13478                 return;
13479 
13480         if (!process_word_char_exceptions(exceptions, &array, &len))
13481                 return;
13482 
13483         g_free(terminal->pvt->word_char_exceptions_string);
13484         terminal->pvt->word_char_exceptions_string = g_strdup(exceptions);
13485 
13486         g_free(terminal->pvt->word_char_exceptions);
13487         terminal->pvt->word_char_exceptions = array;
13488         terminal->pvt->word_char_exceptions_len = len;
13489 
13490         g_object_notify(G_OBJECT(terminal), "word-char-exceptions");
13491 }
13492 
13493 /**
13494  * vte_terminal_get_word_char_exceptions:
13495  * @terminal: a #VteTerminal
13496  *
13497  * Returns the set of characters which will be considered parts of a word
13498  * when doing word-wise selection, in addition to the default which only
13499  * considers alphanumeric characters part of a word.
13500  *
13501  * If %NULL, a built-in set is used.
13502  *
13503  * Returns: (transfer none): a string, or %NULL
13504  *
13505  * Since: 0.40
13506  */
13507 const char *
vte_terminal_get_word_char_exceptions(VteTerminal * terminal)13508 vte_terminal_get_word_char_exceptions(VteTerminal *terminal)
13509 {
13510         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
13511 
13512         return terminal->pvt->word_char_exceptions_string;
13513 }
13514 
13515 /**
13516  * vte_get_major_version:
13517  *
13518  * Returns the major version of the VTE library at runtime.
13519  * Contrast this with %VTE_MAJOR_VERSION which represents
13520  * the version of the VTE library that the code was compiled
13521  * with.
13522  *
13523  * Returns: the major version
13524  *
13525  * Since: 0.40
13526  */
13527 guint
vte_get_major_version(void)13528 vte_get_major_version (void)
13529 {
13530         return VTE_MAJOR_VERSION;
13531 }
13532 
13533 /**
13534  * vte_get_minor_version:
13535  *
13536  * Returns the minor version of the VTE library at runtime.
13537  * Contrast this with %VTE_MINOR_VERSION which represents
13538  * the version of the VTE library that the code was compiled
13539  * with.
13540  *
13541  * Returns: the minor version
13542  *
13543  * Since: 0.40
13544  */
13545 guint
vte_get_minor_version(void)13546 vte_get_minor_version (void)
13547 {
13548         return VTE_MINOR_VERSION;
13549 }
13550 
13551 /**
13552  * vte_get_micro_version:
13553  *
13554  * Returns the micro version of the VTE library at runtime.
13555  * Contrast this with %VTE_MICRO_VERSION which represents
13556  * the version of the VTE library that the code was compiled
13557  * with.
13558  *
13559  * Returns: the micro version
13560  *
13561  * Since: 0.40
13562  */
13563 guint
vte_get_micro_version(void)13564 vte_get_micro_version (void)
13565 {
13566         return VTE_MICRO_VERSION;
13567 }
13568