493fd066a29b8ca28fbc8e97cc323ac1e4e1ac7c
[vte.git] / src / vte.c
1 /*
2  * Copyright (C) 2001,2002,2003 Red Hat, Inc.
3  *
4  * This is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Library General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ident "$Id$"
20
21 #include "../config.h"
22
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <math.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #ifdef HAVE_TERMIOS_H
37 #include <termios.h>
38 #endif
39 #include <unistd.h>
40 #ifdef HAVE_WCHAR_H
41 #include <wchar.h>
42 #endif
43 #include <glib.h>
44 #include <glib-object.h>
45 #include <gdk/gdk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <gtk/gtk.h>
48 #include <pango/pango.h>
49 #include "buffer.h"
50 #include "caps.h"
51 #include "debug.h"
52 #include "iso2022.h"
53 #include "keymap.h"
54 #include "marshal.h"
55 #include "matcher.h"
56 #include "pty.h"
57 #include "reaper.h"
58 #include "ring.h"
59 #include "vte.h"
60 #include "vteaccess.h"
61 #include "vteconv.h"
62 #include "vtedraw.h"
63 #include "vteint.h"
64 #include "vteregex.h"
65 #include "vtetc.h"
66 #include <fontconfig/fontconfig.h>
67
68 #ifdef HAVE_LOCALE_H
69 #include <locale.h>
70 #endif
71
72 #ifndef HAVE_WINT_T
73 typedef gunichar wint_t;
74 #endif
75
76 #ifdef ENABLE_NLS
77 #include <libintl.h>
78 #define _(String) dgettext(PACKAGE, String)
79 #else
80 #define _(String) String
81 #define bindtextdomain(package,dir)
82 #endif
83
84 #define VTE_PAD_WIDTH                   1
85 #define VTE_TAB_WIDTH                   8
86 #define VTE_LINE_WIDTH                  1
87 #define VTE_COLOR_SET_SIZE              8
88 #define VTE_COLOR_PLAIN_OFFSET          0
89 #define VTE_COLOR_BRIGHT_OFFSET         8
90 #define VTE_COLOR_DIM_OFFSET            16
91 #define VTE_DEF_FG                      24
92 #define VTE_DEF_BG                      25
93 #define VTE_BOLD_FG                     26
94 #define VTE_DIM_FG                      27
95 #define VTE_DEF_HL                      28
96 #define VTE_CUR_BG                      29
97 #define VTE_SATURATION_MAX              10000
98 #define VTE_SCROLLBACK_MIN              100
99 #define VTE_DEFAULT_EMULATION           "xterm"
100 #define VTE_DEFAULT_CURSOR              GDK_XTERM
101 #define VTE_MOUSING_CURSOR              GDK_LEFT_PTR
102 #define VTE_TAB_MAX                     999
103 #define VTE_ADJUSTMENT_PRIORITY         G_PRIORITY_DEFAULT_IDLE
104 #define VTE_INPUT_RETRY_PRIORITY        G_PRIORITY_HIGH
105 #define VTE_INPUT_PRIORITY              G_PRIORITY_DEFAULT_IDLE
106 #define VTE_CHILD_INPUT_PRIORITY        G_PRIORITY_DEFAULT_IDLE
107 #define VTE_CHILD_OUTPUT_PRIORITY       G_PRIORITY_HIGH
108 #define VTE_FX_PRIORITY                 G_PRIORITY_DEFAULT_IDLE
109 #define VTE_REGCOMP_FLAGS               REG_EXTENDED
110 #define VTE_REGEXEC_FLAGS               0
111 #define VTE_INPUT_CHUNK_SIZE            0x1000
112 #define VTE_INVALID_SOURCE              -1
113 #define VTE_INVALID_BYTE                '?'
114 #define VTE_COALESCE_TIMEOUT            2
115
116 /* The structure we use to hold characters we're supposed to display -- this
117  * includes any supported visible attributes. */
118 struct vte_charcell {
119         gunichar c;             /* The Unicode character. */
120         guint32 columns: 11;    /* Number of visible columns (as determined
121                                    by g_unicode_iswide(c)).  Use as many bits
122                                    as possible without making this structure
123                                    grow any larger. */
124         guint32 fragment: 1;    /* The nth fragment of a wide character. */
125         guint32 fore: 5;        /* Indices in the color palette for the */
126         guint32 back: 5;        /* foreground and background of the cell. */
127         guint32 standout: 1;    /* Single-bit attributes. */
128         guint32 underline: 1;
129         guint32 strikethrough: 1;
130         guint32 reverse: 1;
131         guint32 blink: 1;
132         guint32 half: 1;
133         guint32 bold: 1;
134         guint32 invisible: 1;
135         guint32 protect: 1;
136         guint32 alternate: 1;
137 };
138
139 /* A match regex, with a tag. */
140 struct vte_match_regex {
141         struct _vte_regex *reg;
142         gint tag;
143         GdkCursor *cursor;
144 };
145
146 /* The terminal's keypad/cursor state.  A terminal can either be using the
147  * normal keypad, or the "application" keypad. */
148 typedef enum _VteKeymode {
149         VTE_KEYMODE_NORMAL,
150         VTE_KEYMODE_APPLICATION
151 } VteKeymode;
152
153 typedef struct _VteScreen VteScreen;
154
155 typedef struct _VteWordCharRange {
156         gunichar start, end;
157 } VteWordCharRange;
158
159 typedef struct _VteRowData {
160         GArray *cells;
161         guchar soft_wrapped: 1;
162 } VteRowData;
163
164 /* Terminal private data. */
165 struct _VteTerminalPrivate {
166         /* Emulation setup data. */
167         struct _vte_termcap *termcap;   /* termcap storage */
168         struct _vte_matcher *matcher;   /* control sequence matcher */
169         const char *termcap_path;       /* path to termcap file */
170         const char *emulation;          /* terminal type to emulate */
171         GTree *sequences;               /* sequence handlers, keyed by GQuark
172                                            based on the sequence name */
173         struct vte_terminal_flags {     /* boolean termcap flags */
174                 gboolean am;
175                 gboolean bw;
176                 gboolean ul;
177         } flags;
178         int keypad_mode, cursor_mode;   /* these would be VteKeymodes, but we
179                                            need to guarantee its type */
180         gboolean sun_fkey_mode;
181         gboolean hp_fkey_mode;
182         gboolean legacy_fkey_mode;
183         gboolean vt220_fkey_mode;
184         int fkey;                       /* this would be a VteFKey, but we
185                                            need to guarantee its type */
186         GHashTable *dec_saved;
187         int default_column_count, default_row_count;    /* default sizes */
188
189         /* PTY handling data. */
190         const char *shell;              /* shell we started */
191         int pty_master;                 /* pty master descriptor */
192         GIOChannel *pty_input;          /* master input watch */
193         guint pty_input_source;
194         GIOChannel *pty_output;         /* master output watch */
195         guint pty_output_source;
196         pid_t pty_pid;                  /* pid of child using pty slave */
197         VteReaper *pty_reaper;
198
199         /* Input data queues. */
200         const char *encoding;           /* the pty's encoding */
201         struct _vte_iso2022_state *iso2022;
202         struct _vte_buffer *incoming;   /* pending bytestream */
203         GArray *pending;                /* pending characters */
204         gboolean processing;
205         gint processing_tag;
206
207         /* Output data queue. */
208         struct _vte_buffer *outgoing;   /* pending input characters */
209         VteConv outgoing_conv;
210
211         /* IConv buffer. */
212         struct _vte_buffer *conv_buffer;
213
214         /* Screen data.  We support the normal screen, and an alternate
215          * screen, which seems to be a DEC-specific feature. */
216         struct _VteScreen {
217                 VteRing *row_data;      /* row data, arranged as a GArray of
218                                            vte_charcell structures */
219                 struct vte_cursor_position {
220                         long row, col;
221                 } cursor_current, cursor_saved;
222                                         /* the current and saved positions of
223                                            the [insertion] cursor -- current is
224                                            absolute, saved is relative to the
225                                            insertion delta */
226                 gboolean reverse_mode;  /* reverse mode */
227                 gboolean origin_mode;   /* origin mode */
228                 gboolean sendrecv_mode; /* sendrecv mode */
229                 gboolean insert_mode;   /* insert mode */
230                 gboolean linefeed_mode; /* linefeed mode */
231                 struct vte_scrolling_region {
232                         int start, end;
233                 } scrolling_region;     /* the region we scroll in */
234                 gboolean scrolling_restricted;
235                 long scroll_delta;      /* scroll offset */
236                 long insert_delta;      /* insertion offset */
237                 struct vte_charcell defaults;   /* default characteristics
238                                                    for insertion of any new
239                                                    characters */
240                 struct vte_charcell color_defaults;     /* original defaults
241                                                            plus the current
242                                                            fore/back */
243                 struct vte_charcell fill_defaults;      /* original defaults
244                                                            plus the current
245                                                            fore/back with no
246                                                            character data */
247                 struct vte_charcell basic_defaults;     /* original defaults */
248                 gboolean status_line;
249                 GString *status_line_contents;
250         } normal_screen, alternate_screen, *screen;
251
252         /* Selection information. */
253         GArray *word_chars;
254         gboolean has_selection;
255         gboolean selecting;
256         gboolean selecting_restart;
257         gboolean selecting_had_delta;
258         char *selection;
259         enum vte_selection_type {
260                 selection_type_char,
261                 selection_type_word,
262                 selection_type_line
263         } selection_type;
264         struct selection_event_coords {
265                 double x, y;
266         } selection_origin, selection_last, selection_restart_origin;
267         struct selection_cell_coords {
268                 long x, y;
269         } selection_start, selection_end;
270
271         /* Miscellaneous options. */
272         VteTerminalEraseBinding backspace_binding, delete_binding;
273         gboolean meta_sends_escape;
274         gboolean audible_bell;
275         gboolean visible_bell;
276         gboolean margin_bell;
277         guint bell_margin;
278         gboolean allow_bold;
279         gboolean nrc_mode;
280         gboolean smooth_scroll;
281         GHashTable *tabstops;
282         gboolean text_modified_flag;
283         glong text_inserted_count;
284         glong text_deleted_count;
285
286         /* Scrolling options. */
287         gboolean scroll_background;
288         long scroll_lock_count;
289         gboolean scroll_on_output;
290         gboolean scroll_on_keystroke;
291         long scrollback_lines;
292
293         /* Cursor blinking. */
294         int cursor_force_fg;
295         gboolean cursor_blinks;
296         gint cursor_blink_tag;
297         gint cursor_blink_timeout;
298         gboolean cursor_visible;
299
300         /* Input device options. */
301         time_t last_keypress_time;
302         gboolean mouse_send_xy_on_click;
303         gboolean mouse_send_xy_on_button;
304         gboolean mouse_hilite_tracking;
305         gboolean mouse_cell_motion_tracking;
306         gboolean mouse_all_motion_tracking;
307         guint mouse_last_button;
308         gdouble mouse_last_x, mouse_last_y;
309         gboolean mouse_autohide;
310         guint mouse_autoscroll_tag;
311
312         /* State variables for handling match checks. */
313         char *match_contents;
314         GArray *match_attributes;
315         GArray *match_regexes;
316         int match_previous;
317         struct {
318                 long row, column;
319         } match_start, match_end;
320
321         /* Data used when rendering the text which does not require server
322          * resources and which can be kept after unrealizing. */
323         PangoFontDescription *fontdesc;
324         GtkSettings *connected_settings;
325
326         /* Data used when rendering the text which reflects server resources
327          * and data, which should be dropped when unrealizing and (re)created
328          * when realizing. */
329         struct _vte_draw *draw;
330
331         gboolean palette_initialized;
332         gboolean highlight_color_set;
333         gboolean cursor_color_set;
334         struct vte_palette_entry {
335                 guint16 red, green, blue;
336         } palette[VTE_CUR_BG + 1];
337
338         /* Mouse cursors. */
339         gboolean mouse_cursor_visible;
340         GdkCursor *mouse_default_cursor,
341                   *mouse_mousing_cursor,
342                   *mouse_inviso_cursor;
343
344         /* Input method support. */
345         GtkIMContext *im_context;
346         gboolean im_preedit_active;
347         char *im_preedit;
348         PangoAttrList *im_preedit_attrs;
349         int im_preedit_cursor;
350
351         /* Our accessible peer. */
352         gpointer accessible;
353         gboolean accessible_emit;
354
355         /* Adjustment updates pending. */
356         gboolean adjustment_changed_tag;
357
358         /* Background images/"transparency". */
359         gboolean bg_update_pending;
360         gboolean bg_transparent;
361         GdkPixbuf *bg_pixbuf;
362         char *bg_file;
363         guint bg_update_tag;
364         GdkColor bg_tint_color;
365         long bg_saturation;     /* out of VTE_SATURATION_MAX */
366
367         /* Key modifiers. */
368         GdkModifierType modifiers;
369
370         /* Obscured? state. */
371         GdkVisibilityState visibility_state;
372 };
373
374 /* A function which can handle a terminal control sequence.  Returns TRUE only
375  * if something happened (usually a signal emission) to which the controlling
376  * application must have an immediate opportunity to respond. */
377 typedef gboolean (*VteTerminalSequenceHandler)(VteTerminal *terminal,
378                                                const char *match,
379                                                GQuark match_quark,
380                                                GValueArray *params);
381 static void vte_terminal_set_termcap(VteTerminal *terminal, const char *path,
382                                      gboolean reset);
383 static void vte_terminal_ensure_cursor(VteTerminal *terminal, gboolean current);
384 static void vte_terminal_paste(VteTerminal *terminal, GdkAtom board);
385 static void vte_terminal_insert_char(VteTerminal *terminal, gunichar c,
386                                      gboolean force_insert_mode,
387                                      gboolean invalidate_cells,
388                                      gboolean paint_cells,
389                                      gboolean ensure_after,
390                                      gint forced_width);
391 static gboolean vte_sequence_handler_clear_screen(VteTerminal *terminal,
392                                                   const char *match,
393                                                   GQuark match_quark,
394                                                   GValueArray *params);
395 static gboolean vte_sequence_handler_do(VteTerminal *terminal,
396                                         const char *match,
397                                         GQuark match_quark,
398                                         GValueArray *params);
399 static gboolean vte_sequence_handler_DO(VteTerminal *terminal,
400                                         const char *match,
401                                         GQuark match_quark,
402                                         GValueArray *params);
403 static gboolean vte_sequence_handler_ho(VteTerminal *terminal,
404                                         const char *match,
405                                         GQuark match_quark,
406                                         GValueArray *params);
407 static gboolean vte_sequence_handler_index(VteTerminal *terminal,
408                                            const char *match,
409                                            GQuark match_quark,
410                                            GValueArray *params);
411 static gboolean vte_sequence_handler_le(VteTerminal *terminal,
412                                         const char *match,
413                                         GQuark match_quark,
414                                         GValueArray *params);
415 static gboolean vte_sequence_handler_LE(VteTerminal *terminal,
416                                             const char *match,
417                                             GQuark match_quark,
418                                             GValueArray *params);
419 static gboolean vte_sequence_handler_nd(VteTerminal *terminal,
420                                         const char *match,
421                                         GQuark match_quark,
422                                         GValueArray *params);
423 static gboolean vte_sequence_handler_sf(VteTerminal *terminal,
424                                         const char *match,
425                                         GQuark match_quark,
426                                         GValueArray *params);
427 static gboolean vte_sequence_handler_sr(VteTerminal *terminal,
428                                         const char *match,
429                                         GQuark match_quark,
430                                         GValueArray *params);
431 static gboolean vte_sequence_handler_ue(VteTerminal *terminal,
432                                         const char *match,
433                                         GQuark match_quark,
434                                         GValueArray *params);
435 static gboolean vte_sequence_handler_up(VteTerminal *terminal,
436                                         const char *match,
437                                         GQuark match_quark,
438                                         GValueArray *params);
439 static gboolean vte_sequence_handler_UP(VteTerminal *terminal,
440                                         const char *match,
441                                         GQuark match_quark,
442                                         GValueArray *params);
443 static gboolean vte_sequence_handler_us(VteTerminal *terminal,
444                                         const char *match,
445                                         GQuark match_quark,
446                                         GValueArray *params);
447 static gboolean vte_sequence_handler_vb(VteTerminal *terminal,
448                                         const char *match,
449                                         GQuark match_quark,
450                                         GValueArray *params);
451 static gboolean vte_terminal_io_read(GIOChannel *channel,
452                                      GdkInputCondition condition,
453                                      gpointer data);
454 static gboolean vte_terminal_io_write(GIOChannel *channel,
455                                       GdkInputCondition condition,
456                                       gpointer data);
457 static void vte_terminal_match_hilite_clear(VteTerminal *terminal);
458 static gboolean vte_terminal_background_update(gpointer data);
459 static void vte_terminal_queue_background_update(VteTerminal *terminal);
460 static void vte_terminal_queue_adjustment_changed(VteTerminal *terminal);
461 static gboolean vte_terminal_process_incoming(gpointer data);
462 static char *vte_terminal_get_text_range_maybe_wrapped(VteTerminal *terminal,
463                                                        glong start_row,
464                                                        glong start_col,
465                                                        glong end_row,
466                                                        glong end_col,
467                                                        gboolean wrap,
468                                                        gboolean(*is_selected)(VteTerminal *,
469                                                                               glong,
470                                                                               glong,
471                                                                               gpointer),
472                                                        gpointer data,
473                                                        GArray *attributes);
474 static char *vte_terminal_get_text_maybe_wrapped(VteTerminal *terminal,
475                                                  gboolean wrap,
476                                                  gboolean(*is_selected)(VteTerminal *,
477                                                                         glong,
478                                                                         glong,
479                                                                         gpointer),
480                                                  gpointer data,
481                                                  GArray *attributes);
482 static void _vte_terminal_disconnect_pty_read(VteTerminal *terminal);
483 static void _vte_terminal_disconnect_pty_write(VteTerminal *terminal);
484
485 /* Free a no-longer-used row data array. */
486 static void
487 vte_free_row_data(gpointer freeing, gpointer data)
488 {
489         if (freeing) {
490                 VteRowData *row = (VteRowData*) freeing;
491                 g_array_free(row->cells, TRUE);
492                 g_free(row);
493         }
494 }
495
496 /* Append a single item to a GArray a given number of times. Centralizing all
497  * of the places we do this may let me do something more clever later. */
498 static void
499 vte_g_array_fill(GArray *array, gpointer item, guint final_size)
500 {
501         g_assert(array != NULL);
502         if (array->len >= final_size) {
503                 return;
504         }
505         g_assert(item != NULL);
506
507         while (array->len < final_size) {
508                 g_array_append_vals(array, item, 1);
509         }
510 }
511
512 /* Allocate a new line. */
513 static VteRowData *
514 vte_new_row_data(VteTerminal *terminal)
515 {
516         VteRowData *row = NULL;
517         row = g_malloc0(sizeof(VteRowData));
518         row->cells = g_array_new(FALSE, FALSE, sizeof(struct vte_charcell));
519         row->soft_wrapped = 0;
520         return row;
521 }
522
523 /* Allocate a new line of a given size. */
524 static VteRowData *
525 vte_new_row_data_sized(VteTerminal *terminal, gboolean fill)
526 {
527         VteRowData *row = NULL;
528         row = g_malloc0(sizeof(VteRowData));
529         row->cells = g_array_sized_new(FALSE, FALSE,
530                                        sizeof(struct vte_charcell),
531                                        terminal->column_count);
532         row->soft_wrapped = 0;
533         if (fill) {
534                 vte_g_array_fill(row->cells,
535                                  &terminal->pvt->screen->fill_defaults,
536                                  terminal->column_count);
537         }
538         return row;
539 }
540
541 /* Check how long a string of unichars is.  Slow version. */
542 static gssize
543 vte_unichar_strlen(gunichar *c)
544 {
545         int i;
546         for (i = 0; c[i] != 0; i++) ;
547         return i;
548 }
549
550 /* Reset defaults for character insertion. */
551 static void
552 vte_terminal_set_default_attributes(VteTerminal *terminal)
553 {
554         g_return_if_fail(VTE_IS_TERMINAL(terminal));
555         terminal->pvt->screen->defaults.c = ' ';
556         terminal->pvt->screen->defaults.columns = 1;
557         terminal->pvt->screen->defaults.fragment = 0;
558         terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
559         terminal->pvt->screen->defaults.back = VTE_DEF_BG;
560         terminal->pvt->screen->defaults.reverse = 0;
561         terminal->pvt->screen->defaults.bold = 0;
562         terminal->pvt->screen->defaults.invisible = 0;
563         terminal->pvt->screen->defaults.protect = 0;
564         terminal->pvt->screen->defaults.standout = 0;
565         terminal->pvt->screen->defaults.underline = 0;
566         terminal->pvt->screen->defaults.strikethrough = 0;
567         terminal->pvt->screen->defaults.half = 0;
568         terminal->pvt->screen->defaults.blink = 0;
569         /* Alternate charset isn't an attribute, though we treat it as one.
570          * terminal->pvt->screen->defaults.alternate = 0; */
571         terminal->pvt->screen->basic_defaults = terminal->pvt->screen->defaults;
572         terminal->pvt->screen->color_defaults = terminal->pvt->screen->defaults;
573         terminal->pvt->screen->fill_defaults = terminal->pvt->screen->defaults;
574 }
575
576 /* Cause certain cells to be repainted. */
577 static void
578 vte_invalidate_cells(VteTerminal *terminal,
579                      glong column_start, gint column_count,
580                      glong row_start, gint row_count)
581 {
582         GdkRectangle rect;
583         GtkWidget *widget;
584
585         g_return_if_fail(VTE_IS_TERMINAL(terminal));
586         widget = GTK_WIDGET(terminal);
587         if (!GTK_WIDGET_REALIZED(widget)) {
588                 return;
589         }
590         if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
591                 return;
592         }
593
594         /* Subtract the scrolling offset from the row start so that the
595          * resulting rectangle is relative to the visible portion of the
596          * buffer. */
597         row_start -= terminal->pvt->screen->scroll_delta;
598
599         /* Clamp the start values to reasonable numbers. */
600         column_start = (column_start > 0) ? column_start : 0;
601         row_start = (row_start > 0) ? row_start : 0;
602
603         /* Convert the column and row start and end to pixel values
604          * by multiplying by the size of a character cell. */
605         rect.x = column_start * terminal->char_width + VTE_PAD_WIDTH;
606         rect.width = column_count * terminal->char_width;
607         if (column_start == 0) {
608                 /* Include the left border. */
609                 rect.x -= VTE_PAD_WIDTH;
610                 rect.width += VTE_PAD_WIDTH;
611         }
612         if (column_start + column_count == terminal->column_count) {
613                 /* Include the right border. */
614                 rect.width += VTE_PAD_WIDTH;
615         }
616
617         rect.y = row_start * terminal->char_height + VTE_PAD_WIDTH;
618         rect.height = row_count * terminal->char_height;
619         if (rect.y == 0) {
620                 /* Include the top border. */
621                 rect.y -= VTE_PAD_WIDTH;
622                 rect.height += VTE_PAD_WIDTH;
623         }
624         if (row_start + row_count == terminal->row_count) {
625                 /* Include the bottom border. */
626                 rect.height += VTE_PAD_WIDTH;
627         }
628
629         /* Invalidate the rectangle. */
630         gdk_window_invalidate_rect(widget->window, &rect, FALSE);
631 }
632
633 /* Redraw the entire visible portion of the window. */
634 static void
635 vte_invalidate_all(VteTerminal *terminal)
636 {
637         GdkRectangle rect;
638         GtkWidget *widget;
639         int width, height;
640
641         g_return_if_fail(VTE_IS_TERMINAL(terminal));
642         if (!GTK_IS_WIDGET(terminal)) {
643                return;
644         }
645         widget = GTK_WIDGET(terminal);
646         if (!GTK_WIDGET_REALIZED(widget)) {
647                 return;
648         }
649         if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
650                 return;
651         }
652
653         /* Expose the entire widget area. */
654         width = height = 0;
655         gdk_drawable_get_size(widget->window, &width, &height);
656         rect.x = 0;
657         rect.y = 0;
658         rect.width = width;
659         rect.height = height;
660         gdk_window_invalidate_rect(widget->window, &rect, FALSE);
661 }
662
663 /* Scroll a rectangular region up or down by a fixed number of lines. */
664 static void
665 vte_terminal_scroll_region(VteTerminal *terminal,
666                            long row, glong count, glong delta)
667 {
668         gboolean repaint = TRUE;
669         glong height;
670
671         if ((delta == 0) || (count == 0)) {
672                 /* Shenanigans! */
673                 return;
674         }
675
676         /* We only do this if we're scrolling the entire window. */
677         if (!terminal->pvt->bg_transparent &&
678             (terminal->pvt->bg_pixbuf == NULL) &&
679             (terminal->pvt->bg_file == NULL) &&
680             (row == terminal->pvt->screen->scroll_delta) &&
681             (count == terminal->row_count) &&
682             (terminal->pvt->scroll_lock_count == 0)) {
683                 height = terminal->char_height;
684                 gdk_window_scroll((GTK_WIDGET(terminal))->window,
685                                   0, delta * height);
686                 if (delta > 0) {
687                         vte_invalidate_cells(terminal,
688                                              0, terminal->column_count,
689                                              0, delta);
690                 } else {
691                         vte_invalidate_cells(terminal,
692                                              0, terminal->column_count,
693                                              terminal->row_count + delta,
694                                              -delta);
695                 }
696                 repaint = FALSE;
697         }
698
699         if (repaint) {
700                 if (terminal->pvt->scroll_background) {
701                         /* We have to repaint the entire window. */
702                         vte_invalidate_all(terminal);
703                 } else {
704                         /* We have to repaint the area which is to be
705                          * scrolled. */
706                         vte_invalidate_cells(terminal,
707                                              0, terminal->column_count,
708                                              row, count);
709                 }
710         }
711 }
712
713 /* Find the character an the given position in the backscroll buffer. */
714 static struct vte_charcell *
715 vte_terminal_find_charcell(VteTerminal *terminal, glong col, glong row)
716 {
717         VteRowData *rowdata;
718         struct vte_charcell *ret = NULL;
719         VteScreen *screen;
720         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
721         screen = terminal->pvt->screen;
722         if (_vte_ring_contains(screen->row_data, row)) {
723                 rowdata = _vte_ring_index(screen->row_data, VteRowData *, row);
724                 if (rowdata->cells->len > col) {
725                         ret = &g_array_index(rowdata->cells,
726                                              struct vte_charcell,
727                                              col);
728                 }
729         }
730         return ret;
731 }
732
733 /* Determine the width of the portion of the preedit string which lies
734  * to the left of the cursor, or the entire string, in columns. */
735 static gssize
736 vte_terminal_preedit_width(VteTerminal *terminal, gboolean left_only)
737 {
738         gunichar c;
739         int i;
740         gssize ret = 0;
741         const char *preedit = NULL;
742
743         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 0);
744
745         if (terminal->pvt->im_preedit != NULL) {
746                 preedit = terminal->pvt->im_preedit;
747                 for (i = 0;
748                      (preedit != NULL) &&
749                      (preedit[0] != '\0') &&
750                      (!left_only || (i < terminal->pvt->im_preedit_cursor));
751                      i++) {
752                         c = g_utf8_get_char(preedit);
753                         ret += _vte_iso2022_unichar_width(c);
754                         preedit = g_utf8_next_char(preedit);
755                 }
756         }
757
758         return ret;
759 }
760
761 /* Determine the length of the portion of the preedit string which lies
762  * to the left of the cursor, or the entire string, in gunichars. */
763 static gssize
764 vte_terminal_preedit_length(VteTerminal *terminal, gboolean left_only)
765 {
766         gunichar c;
767         int i = 0;
768         const char *preedit = NULL;
769
770         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 0);
771
772         if (terminal->pvt->im_preedit != NULL) {
773                 preedit = terminal->pvt->im_preedit;
774                 for (i = 0;
775                      (preedit != NULL) &&
776                      (preedit[0] != '\0') &&
777                      (!left_only || (i < terminal->pvt->im_preedit_cursor));
778                      i++) {
779                         c = g_utf8_get_char(preedit);
780                         preedit = g_utf8_next_char(preedit);
781                 }
782         }
783
784         return i;
785 }
786
787 /* Cause the cursor to be redrawn. */
788 static void
789 vte_invalidate_cursor_once(gpointer data, gboolean periodic)
790 {
791         VteTerminal *terminal;
792         VteScreen *screen;
793         struct vte_charcell *cell;
794         gssize preedit_width;
795         int column, columns, row;
796
797         if (!VTE_IS_TERMINAL(data)) {
798                 return;
799         }
800
801         terminal = VTE_TERMINAL(data);
802
803         if (terminal->pvt->visibility_state == GDK_VISIBILITY_FULLY_OBSCURED) {
804                 return;
805         }
806
807         if (periodic) {
808                 if (!terminal->pvt->cursor_blinks) {
809                         return;
810                 }
811         }
812
813         if (terminal->pvt->cursor_visible &&
814             GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
815                 preedit_width = vte_terminal_preedit_width(terminal, FALSE);
816
817                 screen = terminal->pvt->screen;
818                 row = screen->cursor_current.row;
819                 column = screen->cursor_current.col;
820                 columns = 1;
821                 cell = vte_terminal_find_charcell(terminal,
822                                                   column,
823                                                   screen->cursor_current.row);
824                 while ((cell != NULL) && (cell->fragment) && (column > 0)) {
825                         column--;
826                         cell = vte_terminal_find_charcell(terminal,
827                                                           column,
828                                                           row);
829                 }
830                 if (cell != NULL) {
831                         columns = cell->columns;
832                         if (_vte_draw_get_char_width(terminal->pvt->draw,
833                                                      cell->c,
834                                                      cell->columns) >
835                             terminal->char_width * columns) {
836                                 columns++;
837                         }
838                 }
839                 if (preedit_width > 0) {
840                         columns += preedit_width;
841                         columns++; /* one more for the preedit cursor */
842                 }
843                 if (column + columns > terminal->column_count) {
844                         column = MAX(0, terminal->column_count - columns);
845                 }
846
847                 vte_invalidate_cells(terminal,
848                                      column, columns,
849                                      row, 1);
850 #ifdef VTE_DEBUG
851                 if (_vte_debug_on(VTE_DEBUG_UPDATES)) {
852                         fprintf(stderr, "Invalidating cursor at (%ld,%d-%d)."
853                                 "\n", screen->cursor_current.row,
854                                 column,
855                                 column + columns);
856                 }
857 #endif
858         }
859 }
860
861 /* Invalidate the cursor repeatedly. */
862 static gboolean
863 vte_invalidate_cursor_periodic(gpointer data)
864 {
865         VteTerminal *terminal;
866         GtkWidget *widget;
867         GtkSettings *settings;
868         gint blink_cycle = 1000;
869
870         g_return_val_if_fail(VTE_IS_TERMINAL(data), FALSE);
871         widget = GTK_WIDGET(data);
872         if (!GTK_WIDGET_REALIZED(widget)) {
873                 return TRUE;
874         }
875         if (!GTK_WIDGET_HAS_FOCUS(widget)) {
876                 return TRUE;
877         }
878
879         terminal = VTE_TERMINAL(widget);
880         if (terminal->pvt->cursor_blinks) {
881                 vte_invalidate_cursor_once(terminal, TRUE);
882         }
883
884         settings = gtk_widget_get_settings(GTK_WIDGET(data));
885         if (G_IS_OBJECT(settings)) {
886                 g_object_get(G_OBJECT(settings), "gtk-cursor-blink-time",
887                              &blink_cycle, NULL);
888         }
889
890         if (terminal->pvt->cursor_blink_timeout != blink_cycle) {
891                 terminal->pvt->cursor_blink_tag = g_timeout_add_full(G_PRIORITY_LOW,
892                                                                      blink_cycle / 2,
893                                                                      vte_invalidate_cursor_periodic,
894                                                                      terminal,
895                                                                      NULL);
896                 terminal->pvt->cursor_blink_timeout = blink_cycle;
897                 return FALSE;
898         } else {
899                 return TRUE;
900         }
901 }
902
903 /* Emit a "selection_changed" signal. */
904 static void
905 vte_terminal_emit_selection_changed(VteTerminal *terminal)
906 {
907 #ifdef VTE_DEBUG
908         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
909                 fprintf(stderr, "Emitting `selection-changed'.\n");
910         }
911 #endif
912         g_signal_emit_by_name(terminal, "selection-changed");
913 }
914
915 /* Emit a "commit" signal. */
916 static void
917 vte_terminal_emit_commit(VteTerminal *terminal, gchar *text, guint length)
918 {
919         char *wrapped = NULL;
920 #ifdef VTE_DEBUG
921         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
922                 fprintf(stderr, "Emitting `commit' of %d bytes.\n", length);
923         }
924 #endif
925         if (length == -1) {
926                 length = strlen(text);
927                 wrapped = text;
928         } else {
929                 wrapped = g_malloc0(length + 1);
930                 memcpy(wrapped, text, length);
931         }
932         g_signal_emit_by_name(terminal, "commit", wrapped, length);
933         if (wrapped != text) {
934                 g_free(wrapped);
935         }
936 }
937
938 /* Emit an "emulation-changed" signal. */
939 static void
940 vte_terminal_emit_emulation_changed(VteTerminal *terminal)
941 {
942 #ifdef VTE_DEBUG
943         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
944                 fprintf(stderr, "Emitting `emulation-changed'.\n");
945         }
946 #endif
947         g_signal_emit_by_name(terminal, "emulation-changed");
948 }
949
950 /* Emit an "encoding-changed" signal. */
951 static void
952 vte_terminal_emit_encoding_changed(VteTerminal *terminal)
953 {
954 #ifdef VTE_DEBUG
955         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
956                 fprintf(stderr, "Emitting `encoding-changed'.\n");
957         }
958 #endif
959         g_signal_emit_by_name(terminal, "encoding-changed");
960 }
961
962 /* Emit a "child-exited" signal. */
963 static void
964 vte_terminal_emit_child_exited(VteTerminal *terminal)
965 {
966 #ifdef VTE_DEBUG
967         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
968                 fprintf(stderr, "Emitting `child-exited'.\n");
969         }
970 #endif
971         g_signal_emit_by_name(terminal, "child-exited");
972 }
973
974 /* Emit a "contents_changed" signal. */
975 static void
976 vte_terminal_emit_contents_changed(VteTerminal *terminal)
977 {
978 #ifdef VTE_DEBUG
979         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
980                 fprintf(stderr, "Emitting `contents-changed'.\n");
981         }
982 #endif
983         g_signal_emit_by_name(terminal, "contents-changed");
984 }
985
986 /* Emit a "cursor_moved" signal. */
987 static void
988 vte_terminal_emit_cursor_moved(VteTerminal *terminal)
989 {
990 #ifdef VTE_DEBUG
991         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
992                 fprintf(stderr, "Emitting `cursor-moved'.\n");
993         }
994 #endif
995         g_signal_emit_by_name(terminal, "cursor-moved");
996 }
997
998 /* Emit an "icon-title-changed" signal. */
999 static void
1000 vte_terminal_emit_icon_title_changed(VteTerminal *terminal)
1001 {
1002 #ifdef VTE_DEBUG
1003         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1004                 fprintf(stderr, "Emitting `icon-title-changed'.\n");
1005         }
1006 #endif
1007         g_signal_emit_by_name(terminal, "icon-title-changed");
1008 }
1009
1010 /* Emit a "window-title-changed" signal. */
1011 static void
1012 vte_terminal_emit_window_title_changed(VteTerminal *terminal)
1013 {
1014 #ifdef VTE_DEBUG
1015         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1016                 fprintf(stderr, "Emitting `window-title-changed'.\n");
1017         }
1018 #endif
1019         g_signal_emit_by_name(terminal, "window-title-changed");
1020 }
1021
1022 /* Emit a "deiconify-window" signal. */
1023 static void
1024 vte_terminal_emit_deiconify_window(VteTerminal *terminal)
1025 {
1026 #ifdef VTE_DEBUG
1027         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1028                 fprintf(stderr, "Emitting `deiconify-window'.\n");
1029         }
1030 #endif
1031         g_signal_emit_by_name(terminal, "deiconify-window");
1032 }
1033
1034 /* Emit a "iconify-window" signal. */
1035 static void
1036 vte_terminal_emit_iconify_window(VteTerminal *terminal)
1037 {
1038 #ifdef VTE_DEBUG
1039         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1040                 fprintf(stderr, "Emitting `iconify-window'.\n");
1041         }
1042 #endif
1043         g_signal_emit_by_name(terminal, "iconify-window");
1044 }
1045
1046 /* Emit a "raise-window" signal. */
1047 static void
1048 vte_terminal_emit_raise_window(VteTerminal *terminal)
1049 {
1050 #ifdef VTE_DEBUG
1051         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1052                 fprintf(stderr, "Emitting `raise-window'.\n");
1053         }
1054 #endif
1055         g_signal_emit_by_name(terminal, "raise-window");
1056 }
1057
1058 /* Emit a "lower-window" signal. */
1059 static void
1060 vte_terminal_emit_lower_window(VteTerminal *terminal)
1061 {
1062 #ifdef VTE_DEBUG
1063         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1064                 fprintf(stderr, "Emitting `lower-window'.\n");
1065         }
1066 #endif
1067         g_signal_emit_by_name(terminal, "lower-window");
1068 }
1069
1070 /* Emit a "maximize-window" signal. */
1071 static void
1072 vte_terminal_emit_maximize_window(VteTerminal *terminal)
1073 {
1074 #ifdef VTE_DEBUG
1075         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1076                 fprintf(stderr, "Emitting `maximize-window'.\n");
1077         }
1078 #endif
1079         g_signal_emit_by_name(terminal, "maximize-window");
1080 }
1081
1082 /* Emit a "refresh-window" signal. */
1083 static void
1084 vte_terminal_emit_refresh_window(VteTerminal *terminal)
1085 {
1086 #ifdef VTE_DEBUG
1087         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1088                 fprintf(stderr, "Emitting `refresh-window'.\n");
1089         }
1090 #endif
1091         g_signal_emit_by_name(terminal, "refresh-window");
1092 }
1093
1094 /* Emit a "restore-window" signal. */
1095 static void
1096 vte_terminal_emit_restore_window(VteTerminal *terminal)
1097 {
1098 #ifdef VTE_DEBUG
1099         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1100                 fprintf(stderr, "Emitting `restore-window'.\n");
1101         }
1102 #endif
1103         g_signal_emit_by_name(terminal, "restore-window");
1104 }
1105
1106 /* Emit a "eof" signal. */
1107 static void
1108 vte_terminal_emit_eof(VteTerminal *terminal)
1109 {
1110 #ifdef VTE_DEBUG
1111         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1112                 fprintf(stderr, "Emitting `eof'.\n");
1113         }
1114 #endif
1115         g_signal_emit_by_name(terminal, "eof");
1116 }
1117
1118 /* Emit a "char-size-changed" signal. */
1119 static void
1120 vte_terminal_emit_char_size_changed(VteTerminal *terminal,
1121                                     guint width, guint height)
1122 {
1123 #ifdef VTE_DEBUG
1124         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1125                 fprintf(stderr, "Emitting `char-size-changed'.\n");
1126         }
1127 #endif
1128         g_signal_emit_by_name(terminal, "char-size-changed",
1129                               width, height);
1130 }
1131
1132 /* Emit a "resize-window" signal.  (Pixels.) */
1133 static void
1134 vte_terminal_emit_resize_window(VteTerminal *terminal,
1135                                 guint width, guint height)
1136 {
1137 #ifdef VTE_DEBUG
1138         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1139                 fprintf(stderr, "Emitting `resize-window'.\n");
1140         }
1141 #endif
1142         g_signal_emit_by_name(terminal, "resize-window", width, height);
1143 }
1144
1145 /* Emit a "move-window" signal.  (Pixels.) */
1146 static void
1147 vte_terminal_emit_move_window(VteTerminal *terminal, guint x, guint y)
1148 {
1149 #ifdef VTE_DEBUG
1150         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1151                 fprintf(stderr, "Emitting `move-window'.\n");
1152         }
1153 #endif
1154         g_signal_emit_by_name(terminal, "move-window", x, y);
1155 }
1156
1157 /* Emit a "status-line-changed" signal. */
1158 static void
1159 vte_terminal_emit_status_line_changed(VteTerminal *terminal)
1160 {
1161 #ifdef VTE_DEBUG
1162         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1163                 fprintf(stderr, "Emitting `status-line-changed'.\n");
1164         }
1165 #endif
1166         g_signal_emit_by_name(terminal, "status-line-changed");
1167 }
1168
1169 /* Emit an "increase-font-size" signal. */
1170 static void
1171 vte_terminal_emit_increase_font_size(VteTerminal *terminal)
1172 {
1173 #ifdef VTE_DEBUG
1174         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1175                 fprintf(stderr, "Emitting `increase-font-size'.\n");
1176         }
1177 #endif
1178         g_signal_emit_by_name(terminal, "increase-font-size");
1179 }
1180
1181 /* Emit a "decrease-font-size" signal. */
1182 static void
1183 vte_terminal_emit_decrease_font_size(VteTerminal *terminal)
1184 {
1185 #ifdef VTE_DEBUG
1186         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1187                 fprintf(stderr, "Emitting `decrease-font-size'.\n");
1188         }
1189 #endif
1190         g_signal_emit_by_name(terminal, "decrease-font-size");
1191 }
1192
1193 /* Emit a "text-inserted" signal. */
1194 static void
1195 vte_terminal_emit_text_inserted(VteTerminal *terminal)
1196 {
1197         if (!terminal->pvt->accessible_emit) {
1198                 return;
1199         }
1200 #ifdef VTE_DEBUG
1201         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1202                 fprintf(stderr, "Emitting `text-inserted'.\n");
1203         }
1204 #endif
1205         g_signal_emit_by_name(terminal, "text-inserted");
1206 }
1207
1208 /* Emit a "text-deleted" signal. */
1209 static void
1210 vte_terminal_emit_text_deleted(VteTerminal *terminal)
1211 {
1212         if (!terminal->pvt->accessible_emit) {
1213                 return;
1214         }
1215 #ifdef VTE_DEBUG
1216         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1217                 fprintf(stderr, "Emitting `text-deleted'.\n");
1218         }
1219 #endif
1220         g_signal_emit_by_name(terminal, "text-deleted");
1221 }
1222
1223 /* Emit a "text-modified" signal. */
1224 static void
1225 vte_terminal_emit_text_modified(VteTerminal *terminal)
1226 {
1227         if (!terminal->pvt->accessible_emit) {
1228                 return;
1229         }
1230 #ifdef VTE_DEBUG
1231         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1232                 fprintf(stderr, "Emitting `text-modified'.\n");
1233         }
1234 #endif
1235         g_signal_emit_by_name(terminal, "text-modified");
1236 }
1237
1238 /* Emit a "text-scrolled" signal. */
1239 static void
1240 vte_terminal_emit_text_scrolled(VteTerminal *terminal, gint delta)
1241 {
1242         if (!terminal->pvt->accessible_emit) {
1243                 return;
1244         }
1245 #ifdef VTE_DEBUG
1246         if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1247                 fprintf(stderr, "Emitting `text-scrolled'(%d).\n", delta);
1248         }
1249 #endif
1250         g_signal_emit_by_name(terminal, "text-scrolled", delta);
1251 }
1252
1253 /* Deselect anything which is selected and refresh the screen if needed. */
1254 static void
1255 vte_terminal_deselect_all(VteTerminal *terminal)
1256 {
1257         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1258         if (terminal->pvt->has_selection) {
1259                 terminal->pvt->has_selection = FALSE;
1260 #ifdef VTE_DEBUG
1261                 if (_vte_debug_on(VTE_DEBUG_SELECTION)) {
1262                         fprintf(stderr, "Deselecting all text.\n");
1263                 }
1264 #endif
1265                 vte_terminal_emit_selection_changed(terminal);
1266                 vte_invalidate_all(terminal);
1267         }
1268 }
1269
1270 /* Reset the set of tab stops to the default. */
1271 static void
1272 vte_terminal_set_tabstop(VteTerminal *terminal, int column)
1273 {
1274         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1275         if (terminal->pvt->tabstops != NULL) {
1276                 /* Just set a non-NULL pointer for this column number. */
1277                 g_hash_table_insert(terminal->pvt->tabstops,
1278                                     GINT_TO_POINTER(2 * column + 1),
1279                                     terminal);
1280         }
1281 }
1282
1283 /* Remove a tabstop. */
1284 static void
1285 vte_terminal_clear_tabstop(VteTerminal *terminal, int column)
1286 {
1287         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1288         if (terminal->pvt->tabstops != NULL) {
1289                 /* Remove a tab stop from the hash table. */
1290                 g_hash_table_remove(terminal->pvt->tabstops,
1291                                     GINT_TO_POINTER(2 * column + 1));
1292         }
1293 }
1294
1295 /* Check if we have a tabstop at a given position. */
1296 static gboolean
1297 vte_terminal_get_tabstop(VteTerminal *terminal, int column)
1298 {
1299         gpointer hash;
1300         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
1301         if (terminal->pvt->tabstops != NULL) {
1302                 hash = g_hash_table_lookup(terminal->pvt->tabstops,
1303                                            GINT_TO_POINTER(2 * column + 1));
1304                 return (hash != NULL);
1305         } else {
1306                 return FALSE;
1307         }
1308 }
1309
1310 /* Reset the set of tab stops to the default. */
1311 static void
1312 vte_terminal_set_default_tabstops(VteTerminal *terminal)
1313 {
1314         int i, width;
1315         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1316         if (terminal->pvt->tabstops != NULL) {
1317                 g_hash_table_destroy(terminal->pvt->tabstops);
1318         }
1319         terminal->pvt->tabstops = g_hash_table_new(g_direct_hash,
1320                                                    g_direct_equal);
1321         width = _vte_termcap_find_numeric(terminal->pvt->termcap,
1322                                           terminal->pvt->emulation,
1323                                           "it");
1324         if (width == 0) {
1325                 width = VTE_TAB_WIDTH;
1326         }
1327         for (i = 0; i <= VTE_TAB_MAX; i += width) {
1328                 vte_terminal_set_tabstop(terminal, i);
1329         }
1330 }
1331
1332 /* Clear the cache of the screen contents we keep. */
1333 static void
1334 vte_terminal_match_contents_clear(VteTerminal *terminal)
1335 {
1336         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1337         if (terminal->pvt->match_contents != NULL) {
1338                 g_free(terminal->pvt->match_contents);
1339                 terminal->pvt->match_contents = NULL;;
1340         }
1341         if (terminal->pvt->match_attributes != NULL) {
1342                 g_array_free(terminal->pvt->match_attributes, TRUE);
1343                 terminal->pvt->match_attributes = NULL;
1344         }
1345         vte_terminal_match_hilite_clear(terminal);
1346 }
1347
1348 /* Refresh the cache of the screen contents we keep. */
1349 static gboolean
1350 always_selected(VteTerminal *terminal, glong row, glong column, gpointer data)
1351 {
1352         return TRUE;
1353 }
1354 static void
1355 vte_terminal_match_contents_refresh(VteTerminal *terminal)
1356 {
1357         GArray *array;
1358         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1359         vte_terminal_match_contents_clear(terminal);
1360         array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
1361         terminal->pvt->match_contents = vte_terminal_get_text(terminal,
1362                                                               always_selected,
1363                                                               NULL,
1364                                                               array);
1365         terminal->pvt->match_attributes = array;
1366 }
1367
1368 /**
1369  * vte_terminal_match_clear_all:
1370  * @terminal: a #VteTerminal
1371  *
1372  * Clears the list of regular expressions the terminal uses to highlight text
1373  * when the user moves the mouse cursor.
1374  *
1375  */
1376 void
1377 vte_terminal_match_clear_all(VteTerminal *terminal)
1378 {
1379         struct vte_match_regex *regex;
1380         int i;
1381         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1382         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1383                 regex = &g_array_index(terminal->pvt->match_regexes,
1384                                        struct vte_match_regex,
1385                                        i);
1386                 /* Unless this is a hole, clean it up. */
1387                 if (regex->tag >= 0) {
1388                         if (regex->cursor != NULL) {
1389                                 gdk_cursor_unref(regex->cursor);
1390                                 regex->cursor = NULL;
1391                         }
1392                         _vte_regex_free(regex->reg);
1393                         regex->reg = NULL;
1394                         regex->tag = -1;
1395                 }
1396         }
1397         g_array_set_size(terminal->pvt->match_regexes, 0);
1398         vte_terminal_match_hilite_clear(terminal);
1399 }
1400
1401 /**
1402  * vte_terminal_match_remove:
1403  * @terminal: a #VteTerminal
1404  * @tag: the tag of the regex to remove
1405  *
1406  * Removes the regular expression which is associated with the given @tag from
1407  * the list of expressions which the terminal will highlight when the user
1408  * moves the mouse cursor over matching text.
1409  *
1410  */
1411 void
1412 vte_terminal_match_remove(VteTerminal *terminal, int tag)
1413 {
1414         struct vte_match_regex *regex;
1415         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1416         if (terminal->pvt->match_regexes->len > tag) {
1417                 /* The tag is an index, so find the corresponding struct. */
1418                 regex = &g_array_index(terminal->pvt->match_regexes,
1419                                        struct vte_match_regex,
1420                                        tag);
1421                 /* If it's already been removed, return. */
1422                 if (regex->tag < 0) {
1423                         return;
1424                 }
1425                 /* Remove this item and leave a hole in its place. */
1426                 if (regex->cursor != NULL) {
1427                         gdk_cursor_unref(regex->cursor);
1428                         regex->cursor = NULL;
1429                 }
1430                 _vte_regex_free(regex->reg);
1431                 regex->reg = NULL;
1432                 regex->tag = -1;
1433         }
1434         vte_terminal_match_hilite_clear(terminal);
1435 }
1436
1437 static GdkCursor *
1438 vte_terminal_cursor_new(VteTerminal *terminal, GdkCursorType cursor_type)
1439 {
1440 #if GTK_CHECK_VERSION(2,2,0)
1441         GdkDisplay *display;
1442         GdkCursor *cursor;
1443
1444         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1445
1446         display = gtk_widget_get_display(GTK_WIDGET(terminal));
1447         cursor = gdk_cursor_new_for_display(display, cursor_type);
1448 #else
1449         GdkCursor *cursor;
1450
1451         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1452
1453         cursor = gdk_cursor_new(cursor_type);
1454 #endif
1455         return cursor;
1456 }
1457
1458 /**
1459  * vte_terminal_match_add:
1460  * @terminal: a #VteTerminal
1461  * @match: a regular expression
1462  *
1463  * Adds a regular expression to the list of matching expressions.  When the
1464  * user moves the mouse cursor over a section of displayed text which matches
1465  * this expression, the text will be highlighted.
1466  *
1467  * Returns: an integer associated with this expression
1468  */
1469 int
1470 vte_terminal_match_add(VteTerminal *terminal, const char *match)
1471 {
1472         struct vte_match_regex new_regex, *regex;
1473         int ret;
1474         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
1475         g_return_val_if_fail(match != NULL, -1);
1476         g_return_val_if_fail(strlen(match) > 0, -1);
1477         memset(&new_regex, 0, sizeof(new_regex));
1478         new_regex.reg = _vte_regex_compile(match);
1479         if (new_regex.reg == NULL) {
1480                 g_warning(_("Error compiling regular expression \"%s\"."),
1481                           match);
1482                 return -1;
1483         }
1484
1485         /* Search for a hole. */
1486         for (ret = 0; ret < terminal->pvt->match_regexes->len; ret++) {
1487                 regex = &g_array_index(terminal->pvt->match_regexes,
1488                                        struct vte_match_regex,
1489                                        ret);
1490                 if (regex->tag == -1) {
1491                         break;
1492                 }
1493         }
1494         /* Set the tag to the insertion point. */
1495         new_regex.tag = ret;
1496         new_regex.cursor = vte_terminal_cursor_new(terminal,
1497                                                    VTE_DEFAULT_CURSOR);
1498         if (ret < terminal->pvt->match_regexes->len) {
1499                 /* Overwrite. */
1500                 g_array_index(terminal->pvt->match_regexes,
1501                               struct vte_match_regex,
1502                               ret) = new_regex;
1503         } else {
1504                 /* Append. */
1505                 g_array_append_val(terminal->pvt->match_regexes, new_regex);
1506         }
1507         return new_regex.tag;
1508 }
1509
1510 /**
1511  * vte_terminal_match_set_cursor:
1512  * @terminal: a #VteTerminal
1513  * @tag: the tag of the regex which should use the specified cursor
1514  * @cursor: the cursor which the terminal should use when the pattern is
1515  * highlighted
1516  *
1517  * Sets which cursor the terminal will use if the pointer is over the pattern
1518  * specified by @tag.  The terminal keeps a reference to @cursor.
1519  *
1520  * Since: 0.11
1521  *
1522  */
1523 void
1524 vte_terminal_match_set_cursor(VteTerminal *terminal, int tag, GdkCursor *cursor)
1525 {
1526         struct vte_match_regex *regex;
1527         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1528         g_return_if_fail(tag < terminal->pvt->match_regexes->len);
1529         regex = &g_array_index(terminal->pvt->match_regexes,
1530                                struct vte_match_regex,
1531                                tag);
1532         if (regex->cursor != NULL) {
1533                 gdk_cursor_unref(regex->cursor);
1534         }
1535         regex->cursor = gdk_cursor_ref(cursor);
1536         vte_terminal_match_hilite_clear(terminal);
1537 }
1538
1539 /**
1540  * vte_terminal_match_set_cursor:
1541  * @terminal: a #VteTerminal
1542  * @tag: the tag of the regex which should use the specified cursor
1543  * @cursor: a #GdkCursorType
1544  *
1545  * Sets which cursor the terminal will use if the pointer is over the pattern
1546  * specified by @tag.  A convenience wrapper for
1547  * vte_terminal_match_set_cursor().
1548  *
1549  * Since: 0.11.9
1550  *
1551  */
1552 void
1553 vte_terminal_match_set_cursor_type(VteTerminal *terminal,
1554                                    int tag, GdkCursorType cursor_type)
1555 {
1556         GdkCursor *cursor;
1557         cursor = vte_terminal_cursor_new(terminal, cursor_type);
1558         vte_terminal_match_set_cursor(terminal, tag, cursor);
1559         gdk_cursor_unref(cursor);
1560 }
1561
1562 /* Check if a given cell on the screen contains part of a matched string.  If
1563  * it does, return the string, and store the match tag in the optional tag
1564  * argument. */
1565 static char *
1566 vte_terminal_match_check_internal(VteTerminal *terminal,
1567                                   long column, glong row,
1568                                   int *tag, int *start, int *end)
1569 {
1570         int i, j, ret, offset;
1571         struct vte_match_regex *regex = NULL;
1572         struct _VteCharAttributes *attr = NULL;
1573         gssize coffset;
1574         struct _vte_regex_match matches[256];
1575 #ifdef VTE_DEBUG
1576         if (_vte_debug_on(VTE_DEBUG_EVENTS)) {
1577                 fprintf(stderr, "Checking for match at (%ld,%ld).\n",
1578                         row, column);
1579         }
1580 #endif
1581         if (tag != NULL) {
1582                 *tag = -1;
1583         }
1584         if (start != NULL) {
1585                 *start = 0;
1586         }
1587         if (end != NULL) {
1588                 *end = 0;
1589         }
1590         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1591         if (terminal->pvt->match_contents == NULL) {
1592                 vte_terminal_match_contents_refresh(terminal);
1593         }
1594         /* Map the pointer position to a portion of the string. */
1595         for (offset = terminal->pvt->match_attributes->len - 1;
1596              offset >= 0;
1597              offset--) {
1598                 attr = &g_array_index(terminal->pvt->match_attributes,
1599                                       struct _VteCharAttributes,
1600                                       offset);
1601                 if ((row == attr->row) &&
1602                     (column == attr->column) &&
1603                     (terminal->pvt->match_contents[offset] != ' ')) {
1604                         break;
1605                 }
1606         }
1607 #ifdef VTE_DEBUG
1608         if (_vte_debug_on(VTE_DEBUG_EVENTS)) {
1609                 if (offset < 0) {
1610                         fprintf(stderr, "Cursor is not on a character.\n");
1611                 } else {
1612                         fprintf(stderr, "Cursor is on character %d.\n", offset);
1613                 }
1614         }
1615 #endif
1616
1617         /* If the pointer isn't on a matchable character, bug out. */
1618         if (offset < 0) {
1619                 terminal->pvt->match_previous = -1;
1620                 return NULL;
1621         }
1622
1623         /* If the pointer is on a newline, bug out. */
1624         if ((g_ascii_isspace(terminal->pvt->match_contents[offset])) ||
1625             (terminal->pvt->match_contents[offset] == '\0')) {
1626 #ifdef VTE_DEBUG
1627                 if (_vte_debug_on(VTE_DEBUG_EVENTS)) {
1628                         fprintf(stderr, "Cursor is on whitespace.\n");
1629                 }
1630 #endif
1631                 terminal->pvt->match_previous = -1;
1632                 return NULL;
1633         }
1634
1635         /* Now iterate over each regex we need to match against. */
1636         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
1637                 regex = &g_array_index(terminal->pvt->match_regexes,
1638                                        struct vte_match_regex,
1639                                        i);
1640                 /* Skip holes. */
1641                 if (regex->tag < 0) {
1642                         continue;
1643                 }
1644                 /* We'll only match the first item in the buffer which
1645                  * matches, so we'll have to skip each match until we
1646                  * stop getting matches. */
1647                 coffset = 0;
1648                 ret = _vte_regex_exec(regex->reg,
1649                                       terminal->pvt->match_contents + coffset,
1650                                       G_N_ELEMENTS(matches),
1651                                       matches);
1652                 while (ret == 0) {
1653                         for (j = 0;
1654                              (j < G_N_ELEMENTS(matches)) &&
1655                              (matches[j].rm_so != -1);
1656                              j++) {
1657                                 /* The offsets should be "sane". */
1658                                 g_assert(matches[j].rm_so + coffset <
1659                                          terminal->pvt->match_attributes->len);
1660                                 g_assert(matches[j].rm_eo + coffset <=
1661                                          terminal->pvt->match_attributes->len);
1662 #ifdef VTE_DEBUG
1663                                 if (_vte_debug_on(VTE_DEBUG_MISC)) {
1664                                         char *match;
1665                                         struct _VteCharAttributes *sattr, *eattr;
1666                                         match = g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset,
1667                                                           matches[j].rm_eo - matches[j].rm_so);
1668                                         sattr = &g_array_index(terminal->pvt->match_attributes,
1669                                                                struct _VteCharAttributes,
1670                                                                matches[j].rm_so + coffset);
1671                                         eattr = &g_array_index(terminal->pvt->match_attributes,
1672                                                                struct _VteCharAttributes,
1673                                                                matches[j].rm_eo + coffset - 1);
1674                                         fprintf(stderr, "Match %d `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
1675                                                 j, match,
1676                                                 matches[j].rm_so + coffset,
1677                                                 sattr->column,
1678                                                 sattr->row,
1679                                                 matches[j].rm_eo + coffset - 1,
1680                                                 eattr->column,
1681                                                 eattr->row,
1682                                                 offset);
1683                                         g_free(match);
1684
1685                                 }
1686 #endif
1687                                 /* Snip off any final newlines. */
1688                                 while ((matches[j].rm_eo > matches[j].rm_so) &&
1689                                        (terminal->pvt->match_contents[coffset + matches[j].rm_eo - 1] == '\n')) {
1690                                         matches[j].rm_eo--;
1691                                 }
1692                                 /* If the pointer is in this substring,
1693                                  * then we're done. */
1694                                 if ((offset >= (matches[j].rm_so + coffset)) &&
1695                                     (offset < (matches[j].rm_eo + coffset))) {
1696                                         if (tag != NULL) {
1697                                                 *tag = regex->tag;
1698                                         }
1699                                         if (start != NULL) {
1700                                                 *start = coffset +
1701                                                          matches[j].rm_so;
1702                                         }
1703                                         if (end != NULL) {
1704                                                 *end = coffset +
1705                                                        matches[j].rm_eo - 1;
1706                                         }
1707                                         if (GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
1708                                                 gdk_window_set_cursor((GTK_WIDGET(terminal))->window,
1709                                                                       regex->cursor);
1710                                         }
1711                                         terminal->pvt->match_previous = regex->tag;
1712                                         return g_strndup(terminal->pvt->match_contents + coffset + matches[j].rm_so,
1713                                                          matches[j].rm_eo - matches[j].rm_so);
1714                                 }
1715                         }
1716                         /* Skip past the beginning of this match to
1717                          * look for more. */
1718                         coffset += (matches[0].rm_so + 1);
1719                         ret = _vte_regex_exec(regex->reg,
1720                                               terminal->pvt->match_contents +
1721                                               coffset,
1722                                               G_N_ELEMENTS(matches),
1723                                               matches);
1724                 }
1725         }
1726         terminal->pvt->match_previous = -1;
1727         return NULL;
1728 }
1729
1730 /**
1731  * vte_terminal_match_check:
1732  * @terminal: a #VteTerminal
1733  * @column: the text column
1734  * @row: the text row
1735  * @tag: pointer to an integer
1736  *
1737  * Checks if the text in and around the specified position matches any of the
1738  * regular expressions previously set using vte_terminal_match_add().  If a
1739  * match exists, the text string is returned and if @tag is not NULL, the number
1740  * associated with the matched regular expression will be stored in @tag.
1741  *
1742  * If more than one regular expression has been set with
1743  * vte_terminal_match_add(), then expressions are checked in the order in
1744  * which they were added.
1745  *
1746  * Returns: a string which matches one of the previously set regular
1747  * expressions, and which must be freed by the caller.
1748  */
1749 char *
1750 vte_terminal_match_check(VteTerminal *terminal, glong column, glong row,
1751                          int *tag)
1752 {
1753         long delta;
1754         char *ret;
1755         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
1756         delta = terminal->pvt->screen->scroll_delta;
1757 #ifdef VTE_DEBUG
1758         if (_vte_debug_on(VTE_DEBUG_EVENTS)) {
1759                 fprintf(stderr, "Checking for match at (%ld,%ld).\n",
1760                         row, column);
1761         }
1762 #endif
1763         ret = vte_terminal_match_check_internal(terminal,
1764                                                 column, row + delta,
1765                                                 tag, NULL, NULL);
1766 #ifdef VTE_DEBUG
1767         if ((ret != NULL) && _vte_debug_on(VTE_DEBUG_EVENTS)) {
1768                 fprintf(stderr, "Matched `%s'.\n", ret);
1769         }
1770 #endif
1771         return ret;
1772 }
1773
1774 /* Emit an adjustment changed signal on our adjustment object. */
1775 static gboolean
1776 vte_terminal_emit_adjustment_changed(gpointer data)
1777 {
1778         VteTerminal *terminal;
1779         terminal = VTE_TERMINAL(data);
1780         if (terminal->pvt->adjustment_changed_tag) {
1781 #ifdef VTE_DEBUG
1782                 if (_vte_debug_on(VTE_DEBUG_SIGNALS)) {
1783                         fprintf(stderr, "Emitting adjustment_changed.\n");
1784                 }
1785 #endif
1786                 terminal->pvt->adjustment_changed_tag = 0;
1787                 gtk_adjustment_changed(terminal->adjustment);
1788         }
1789         return FALSE;
1790 }
1791
1792 /* Queue an adjustment-changed signal to be delivered when convenient. */
1793 static void
1794 vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
1795 {
1796         if (terminal->pvt->adjustment_changed_tag == 0) {
1797                 terminal->pvt->adjustment_changed_tag =
1798                                 g_idle_add_full(VTE_ADJUSTMENT_PRIORITY,
1799                                                 vte_terminal_emit_adjustment_changed,
1800                                                 terminal,
1801                                                 NULL);
1802         } else {
1803 #ifdef VTE_DEBUG
1804                 if (_vte_debug_on(VTE_DEBUG_EVENTS)) {
1805                         fprintf(stderr, "Swallowing duplicate "
1806                                 "adjustment-changed signal.\n");
1807                 }
1808 #endif
1809         }
1810 }
1811
1812 /* Update the adjustment field of the widget.  This function should be called
1813  * whenever we add rows to or remove rows from the history or switch screens. */
1814 static void
1815 vte_terminal_adjust_adjustments(VteTerminal *terminal, gboolean immediate)
1816 {
1817         VteScreen *screen;
1818         gboolean changed;
1819         long delta;
1820         long rows;
1821
1822         g_return_if_fail(terminal->pvt->screen != NULL);
1823         g_return_if_fail(terminal->pvt->screen->row_data != NULL);
1824
1825         /* Adjust the vertical, uh, adjustment. */
1826         changed = FALSE;
1827
1828         /* The lower value should be the first row in the buffer. */
1829         screen = terminal->pvt->screen;
1830         delta = _vte_ring_delta(screen->row_data);
1831 #ifdef VTE_DEBUG
1832         if (_vte_debug_on(VTE_DEBUG_IO)) {
1833                 fprintf(stderr, "Changing adjustment values "
1834                         "(delta = %ld, scroll = %ld).\n",
1835                         delta, screen->scroll_delta);
1836         }
1837 #endif
1838         if (terminal->adjustment->lower != delta) {
1839                 terminal->adjustment->lower = delta;
1840                 changed = TRUE;
1841         }
1842
1843         /* Snap the insert delta and the cursor position to be in the visible
1844          * area.  Leave the scrolling delta alone because it will be updated
1845          * when the adjustment changes. */
1846         screen->insert_delta = MAX(screen->insert_delta, delta);
1847         screen->cursor_current.row = MAX(screen->cursor_current.row,
1848                                          screen->insert_delta);
1849
1850         /* The upper value is the number of rows which might be visible.  (Add
1851          * one to the cursor offset because it's zero-based.) */
1852         rows = MAX(_vte_ring_next(terminal->pvt->screen->row_data),
1853                    terminal->pvt->screen->cursor_current.row + 1);
1854         if (terminal->adjustment->upper != rows) {
1855                 terminal->adjustment->upper = rows;
1856                 changed = TRUE;
1857         }
1858
1859         /* The step increment should always be one. */
1860         if (terminal->adjustment->step_increment != 1) {
1861                 terminal->adjustment->step_increment = 1;
1862                 changed = TRUE;
1863         }
1864
1865         /* Set the number of rows the user sees to the number of rows the
1866          * user sees. */
1867         if (terminal->adjustment->page_size != terminal->row_count) {
1868                 terminal->adjustment->page_size = terminal->row_count;
1869                 changed = TRUE;
1870         }
1871
1872         /* Clicking in the empty area should scroll one screen, so set the
1873          * page size to the number of visible rows. */
1874         if (terminal->adjustment->page_increment != terminal->row_count) {
1875                 terminal->adjustment->page_increment = terminal->row_count;
1876                 changed = TRUE;
1877         }
1878
1879         /* Set the scrollbar adjustment to where the screen wants it to be. */
1880         if (floor(terminal->adjustment->value) !=
1881             terminal->pvt->screen->scroll_delta) {
1882                 /* This emits a "value-changed" signal, so no need to screw
1883                  * with anything else for just this. */
1884                 gtk_adjustment_set_value(terminal->adjustment,
1885                                          terminal->pvt->screen->scroll_delta);
1886         }
1887
1888         /* If anything changed, signal that there was a change. */
1889         if (changed == TRUE) {
1890 #ifdef VTE_DEBUG
1891                 if (_vte_debug_on(VTE_DEBUG_IO)) {
1892                         fprintf(stderr, "Changed adjustment values "
1893                                 "(delta = %ld, scroll = %ld).\n",
1894                                 delta, terminal->pvt->screen->scroll_delta);
1895                 }
1896 #endif
1897                 if (immediate) {
1898                         gtk_adjustment_changed(terminal->adjustment);
1899                 } else {
1900                         vte_terminal_queue_adjustment_changed(terminal);
1901                 }
1902         }
1903 }
1904
1905 /* Scroll up or down in the current screen. */
1906 static void
1907 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
1908 {
1909         glong destination;
1910         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1911 #ifdef VTE_DEBUG
1912         if (_vte_debug_on(VTE_DEBUG_IO)) {
1913                 fprintf(stderr, "Scrolling %d pages.\n", pages);
1914         }
1915 #endif
1916         /* Calculate the ideal position where we want to be before clamping. */
1917         destination = floor(gtk_adjustment_get_value(terminal->adjustment));
1918         destination += (pages * terminal->row_count);
1919         /* Can't scroll past data we have. */
1920         destination = CLAMP(destination,
1921                             terminal->adjustment->lower,
1922                             terminal->adjustment->upper - terminal->row_count);
1923         /* Tell the scrollbar to adjust itself. */
1924         gtk_adjustment_set_value(terminal->adjustment, destination);
1925         /* Clear dingus match set. */
1926         vte_terminal_match_contents_clear(terminal);
1927         /* Notify viewers that the contents have changed. */
1928         vte_terminal_emit_contents_changed(terminal);
1929 }
1930
1931 /* Scroll so that the scroll delta is the minimum value. */
1932 static void
1933 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
1934 {
1935         long delta;
1936         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1937         if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
1938             _vte_ring_delta(terminal->pvt->screen->row_data)) {
1939                 delta = _vte_ring_delta(terminal->pvt->screen->row_data);
1940                 gtk_adjustment_set_value(terminal->adjustment, delta);
1941         }
1942 }
1943
1944 /* Scroll so that the scroll delta is the insertion delta. */
1945 static void
1946 vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
1947 {
1948         glong delta;
1949         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1950         if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
1951             terminal->pvt->screen->insert_delta) {
1952                 delta = terminal->pvt->screen->insert_delta;
1953                 gtk_adjustment_set_value(terminal->adjustment, delta);
1954         }
1955 }
1956
1957 /* Call another function, offsetting any long arguments by the given
1958  * increment value. */
1959 static gboolean
1960 vte_sequence_handler_offset(VteTerminal *terminal,
1961                             const char *match,
1962                             GQuark match_quark,
1963                             GValueArray *params,
1964                             int increment,
1965                             VteTerminalSequenceHandler handler)
1966 {
1967         guint i;
1968         long val;
1969         GValue *value;
1970         /* Decrement the parameters and let the _cs handler deal with it. */
1971         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
1972                 value = g_value_array_get_nth(params, i);
1973                 if (G_VALUE_HOLDS_LONG(value)) {
1974                         val = g_value_get_long(value);
1975                         val += increment;
1976                         g_value_set_long(value, val);
1977                 }
1978         }
1979         return handler(terminal, match, match_quark, params);
1980 }
1981
1982 /* Call another function a given number of times, or once. */
1983 static gboolean
1984 vte_sequence_handler_multiple(VteTerminal *terminal,
1985                               const char *match,
1986                               GQuark match_quark,
1987                               GValueArray *params,
1988                               VteTerminalSequenceHandler handler)
1989 {
1990         long val = 1;
1991         int i, again;
1992         GValue *value;
1993
1994         if ((params != NULL) && (params->n_values > 0)) {
1995                 value = g_value_array_get_nth(params, 0);
1996                 if (G_VALUE_HOLDS_LONG(value)) {
1997                         val = g_value_get_long(value);
1998                         val = MAX(val, 1);      /* FIXME: vttest. */
1999                 }
2000         }
2001         again = 0;
2002         for (i = 0; i < val; i++) {
2003                 if (handler(terminal, match, match_quark, NULL)) {
2004                         again++;
2005                 }
2006         }
2007         return (again > 0);
2008 }
2009
2010 /* Insert a blank line at an arbitrary position. */
2011 static void
2012 vte_insert_line_internal(VteTerminal *terminal, glong position)
2013 {
2014         VteRowData *row;
2015         /* Pad out the line data to the insertion point. */
2016         while (_vte_ring_next(terminal->pvt->screen->row_data) < position) {
2017                 row = vte_new_row_data_sized(terminal, TRUE);
2018                 _vte_ring_append(terminal->pvt->screen->row_data, row);
2019         }
2020         /* If we haven't inserted a line yet, insert a new one. */
2021         row = vte_new_row_data_sized(terminal, TRUE);
2022         if (_vte_ring_next(terminal->pvt->screen->row_data) >= position) {
2023                 _vte_ring_insert(terminal->pvt->screen->row_data,
2024                                  position, row);
2025         } else {
2026                 _vte_ring_append(terminal->pvt->screen->row_data, row);
2027         }
2028 }
2029
2030 /* Remove a line at an arbitrary position. */
2031 static void
2032 vte_remove_line_internal(VteTerminal *terminal, glong position)
2033 {
2034         if (_vte_ring_next(terminal->pvt->screen->row_data) > position) {
2035                 _vte_ring_remove(terminal->pvt->screen->row_data,
2036                                  position, TRUE);
2037         }
2038 }
2039
2040 /**
2041  * vte_terminal_set_encoding:
2042  * @terminal: a #VteTerminal
2043  * @codeset: a valid #g_iconv target
2044  *
2045  * Changes the encoding the terminal will expect data from the child to
2046  * be encoded with.  For certain terminal types, applications executing in the
2047  * terminal can change the encoding.  The default encoding is defined by the
2048  * application's locale settings.
2049  *
2050  */
2051 void
2052 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
2053 {
2054         const char *old_codeset;
2055         GQuark encoding_quark;
2056         VteConv conv;
2057         char *obuf1, *obuf2;
2058         gsize bytes_written;
2059
2060         old_codeset = terminal->pvt->encoding;
2061         if (codeset == NULL) {
2062                 g_get_charset(&codeset);
2063         }
2064         if ((old_codeset != NULL) && (strcmp(codeset, old_codeset) == 0)) {
2065                 /* Nothing to do! */
2066                 return;
2067         }
2068
2069         /* Open new conversions. */
2070         conv = _vte_conv_open(codeset, "UTF-8");
2071         if (conv == ((VteConv) -1)) {
2072                 g_warning(_("Unable to convert characters from %s to %s."),
2073                           "UTF-8", codeset);
2074                 return;
2075         }
2076         if (terminal->pvt->outgoing_conv != (VteConv) -1) {
2077                 _vte_conv_close(terminal->pvt->outgoing_conv);
2078         }
2079         terminal->pvt->outgoing_conv = conv;
2080
2081         /* Set the terminal's encoding to the new value. */
2082         encoding_quark = g_quark_from_string(codeset);
2083         terminal->pvt->encoding = g_quark_to_string(encoding_quark);
2084
2085         /* Convert any buffered output bytes. */
2086         if ((_vte_buffer_length(terminal->pvt->outgoing) > 0) &&
2087             (old_codeset != NULL)) {
2088                 /* Convert back to UTF-8. */
2089                 obuf1 = g_convert(terminal->pvt->outgoing->bytes,
2090                                   _vte_buffer_length(terminal->pvt->outgoing),
2091                                   "UTF-8",
2092                                   old_codeset,
2093                                   NULL,
2094                                   &bytes_written,
2095                                   NULL);
2096                 if (obuf1 != NULL) {
2097                         /* Convert to the new encoding. */
2098                         obuf2 = g_convert(obuf1,
2099                                           bytes_written,
2100                                           codeset,
2101                                           "UTF-8",
2102                                           NULL,
2103                                           &bytes_written,
2104                                           NULL);
2105                         if (obuf2 != NULL) {
2106                                 _vte_buffer_clear(terminal->pvt->outgoing);
2107                                 _vte_buffer_append(terminal->pvt->outgoing,
2108                                                    obuf2, bytes_written);
2109                                 g_free(obuf2);
2110                         }
2111                         g_free(obuf1);
2112                 }
2113         }
2114
2115         /* Set the encoding for incoming text. */
2116         _vte_iso2022_state_set_codeset(terminal->pvt->iso2022,
2117                                        terminal->pvt->encoding);
2118
2119 #ifdef VTE_DEBUG
2120         if (_vte_debug_on(VTE_DEBUG_IO)) {
2121                 fprintf(stderr, "Set terminal encoding to `%s'.\n",
2122                         terminal->pvt->encoding);
2123         }
2124 #endif
2125         vte_terminal_emit_encoding_changed(terminal);
2126 }
2127
2128 /**
2129  * vte_terminal_get_encoding:
2130  * @terminal: a #VteTerminal
2131  *
2132  * Determines the name of the encoding in which the terminal expects data to be
2133  * encoded.
2134  *
2135  * Returns: the current encoding for the terminal.
2136  */
2137 const char *
2138 vte_terminal_get_encoding(VteTerminal *terminal)
2139 {
2140         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
2141         return terminal->pvt->encoding;
2142 }
2143
2144 /* End alternate character set. */
2145 static gboolean
2146 vte_sequence_handler_ae(VteTerminal *terminal,
2147                         const char *match,
2148                         GQuark match_quark,
2149                         GValueArray *params)
2150 {
2151         terminal->pvt->screen->defaults.alternate = 0;
2152         return FALSE;
2153 }
2154
2155 /* Add a line at the current cursor position. */
2156 static gboolean
2157 vte_sequence_handler_al(VteTerminal *terminal,
2158                         const char *match,
2159                         GQuark match_quark,
2160                         GValueArray *params)
2161 {
2162         VteScreen *screen;
2163         VteRowData *rowdata;
2164         long start, end, param, i;
2165         GValue *value;
2166
2167         /* Find out which part of the screen we're messing with. */
2168         screen = terminal->pvt->screen;
2169         start = screen->cursor_current.row;
2170         if (screen->scrolling_restricted) {
2171                 end = screen->insert_delta + screen->scrolling_region.end;
2172         } else {
2173                 end = screen->insert_delta + terminal->row_count - 1;
2174         }
2175
2176         /* Extract any parameters. */
2177         param = 1;
2178         if ((params != NULL) && (params->n_values > 0)) {
2179                 value = g_value_array_get_nth(params, 0);
2180                 param = g_value_get_long(value);
2181         }
2182
2183         /* Insert the right number of lines. */
2184         for (i = 0; i < param; i++) {
2185                 /* Clear a line off the end of the region and add one to the
2186                  * top of the region. */
2187                 vte_remove_line_internal(terminal, end);
2188                 vte_insert_line_internal(terminal, start);
2189                 /* Get the data for the new row. */
2190                 rowdata = _vte_ring_index(screen->row_data,
2191                                           VteRowData *, start);
2192                 /* Add enough cells to it so that it has the default colors. */
2193                 vte_g_array_fill(rowdata->cells, &screen->fill_defaults,
2194                                  terminal->column_count);
2195                 /* Adjust the scrollbars if necessary. */
2196                 vte_terminal_adjust_adjustments(terminal, FALSE);
2197         }
2198
2199         /* Update the display. */
2200         vte_terminal_scroll_region(terminal, start, end - start + 1, param);
2201
2202         /* We've modified the display.  Make a note of it. */
2203         terminal->pvt->text_deleted_count++;
2204         return FALSE;
2205 }
2206
2207 /* Add N lines at the current cursor position. */
2208 static gboolean
2209 vte_sequence_handler_AL(VteTerminal *terminal,
2210                         const char *match,
2211                         GQuark match_quark,
2212                         GValueArray *params)
2213 {
2214         return vte_sequence_handler_al(terminal, match, match_quark, params);
2215 }
2216
2217 /* Start using alternate character set. */
2218 static gboolean
2219 vte_sequence_handler_as(VteTerminal *terminal,
2220                         const char *match,
2221                         GQuark match_quark,
2222                         GValueArray *params)
2223 {
2224         terminal->pvt->screen->defaults.alternate = 1;
2225         return FALSE;
2226 }
2227
2228 static void
2229 vte_terminal_beep(VteTerminal *terminal)
2230 {
2231 #if GTK_CHECK_VERSION(2,2,0)
2232         GdkDisplay *display;
2233
2234         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2235         display = gtk_widget_get_display(GTK_WIDGET(terminal));
2236         gdk_display_beep(display);
2237 #else
2238         gdk_beep();
2239 #endif
2240 }
2241
2242 /* Beep. */
2243 static gboolean
2244 vte_sequence_handler_bl(VteTerminal *terminal,
2245                         const char *match,
2246                         GQuark match_quark,
2247                         GValueArray *params)
2248 {
2249         if (terminal->pvt->audible_bell) {
2250                 /* Feep. */
2251                 vte_terminal_beep(terminal);
2252         }
2253         if (terminal->pvt->visible_bell) {
2254                 /* Visual bell. */
2255                 vte_sequence_handler_vb(terminal, match, match_quark, params);
2256         }
2257         return FALSE;
2258 }
2259
2260 /* Backtab. */
2261 static gboolean
2262 vte_sequence_handler_bt(VteTerminal *terminal,
2263                         const char *match,
2264                         GQuark match_quark,
2265                         GValueArray *params)
2266 {
2267         long newcol;
2268
2269         /* Calculate which column is the previous tab stop. */
2270         newcol = terminal->pvt->screen->cursor_current.col;
2271
2272         if (terminal->pvt->tabstops != NULL) {
2273                 /* Find the next tabstop. */
2274                 while (newcol >= 0) {
2275                         if (vte_terminal_get_tabstop(terminal,
2276                                                      newcol % terminal->column_count)) {
2277                                 break;
2278                         }
2279                         newcol--;
2280                 }
2281         }
2282
2283         /* If we have no tab stops, stop at the first column. */
2284         if (newcol <= 0) {
2285                 newcol = 0;
2286         }
2287
2288         /* Warp the cursor. */
2289 #ifdef VTE_DEBUG
2290         if (_vte_debug_on(VTE_DEBUG_PARSE)) {
2291                 fprintf(stderr, "Moving cursor to column %ld.\n", (long)newcol);
2292         }
2293 #endif
2294         terminal->pvt->screen->cursor_current.col = newcol;
2295         return FALSE;
2296 }
2297
2298 /* Clear from the cursor position to the beginning of the line. */
2299 static gboolean
2300 vte_sequence_handler_cb(VteTerminal *terminal,
2301                         const char *match,
2302                         GQuark match_quark,
2303                         GValueArray *params)
2304 {
2305         VteRowData *rowdata;
2306         long i;
2307         VteScreen *screen;
2308         struct vte_charcell *pcell;
2309         screen = terminal->pvt->screen;
2310
2311         /* Get the data for the row which the cursor points to. */
2312         vte_terminal_ensure_cursor(terminal, FALSE);
2313         rowdata = _vte_ring_index(screen->row_data,
2314                                   VteRowData *,
2315                                   screen->cursor_current.row);
2316         /* Clear the data up to the current column with the default
2317          * attributes.  If there is no such character cell, we need
2318          * to add one. */
2319         for (i = 0; i <= screen->cursor_current.col; i++) {
2320                 if (i < rowdata->cells->len) {
2321                         /* Muck with the cell in this location. */
2322                         pcell = &g_array_index(rowdata->cells,
2323                                                struct vte_charcell,
2324                                                i);
2325                         *pcell = screen->color_defaults;
2326                 } else {
2327                         /* Add new cells until we have one here. */
2328                         g_array_append_val(rowdata->cells,
2329                                            screen->color_defaults);
2330                 }
2331         }
2332         /* Repaint this row. */
2333         vte_invalidate_cells(terminal,
2334                              0, terminal->column_count,
2335                              screen->cursor_current.row, 1);
2336
2337         /* We've modified the display.  Make a note of it. */
2338         terminal->pvt->text_deleted_count++;
2339         return FALSE;
2340 }
2341
2342 /* Clear to the right of the cursor and below the current line. */
2343 static gboolean
2344 vte_sequence_handler_cd(VteTerminal *terminal,
2345                         const char *match,
2346                         GQuark match_quark,
2347                         GValueArray *params)
2348 {
2349         VteRowData *rowdata;
2350         long i;
2351         VteScreen *screen;
2352
2353         screen = terminal->pvt->screen;
2354         /* If the cursor is actually on the screen, clear the rest of the
2355          * row the cursor is on and all of the rows below the cursor. */
2356         i = screen->cursor_current.row;
2357         if (i < _vte_ring_next(screen->row_data)) {
2358                 /* Get the data for the row we're clipping. */
2359                 rowdata = _vte_ring_index(screen->row_data, VteRowData *, i);
2360                 /* Clear everything to the right of the cursor. */
2361                 if ((rowdata != NULL) &&
2362                     (rowdata->cells->len > screen->cursor_current.col)) {
2363                         g_array_set_size(rowdata->cells,
2364                                          screen->cursor_current.col);
2365                 }
2366         }
2367         /* Now for the rest of the lines. */
2368         for (i = screen->cursor_current.row + 1;
2369              i < _vte_ring_next(screen->row_data);
2370              i++) {
2371                 /* Get the data for the row we're removing. */
2372                 rowdata = _vte_ring_index(screen->row_data, VteRowData *, i);
2373                 /* Remove it. */
2374                 if ((rowdata != NULL) && (rowdata->cells->len > 0)) {
2375                         g_array_set_size(rowdata->cells, 0);
2376                 }
2377         }
2378         /* Now fill the cleared areas. */
2379         for (i = screen->cursor_current.row;
2380              i < screen->insert_delta + terminal->row_count;
2381              i++) {
2382                 /* Retrieve the row's data, creating it if necessary. */
2383                 if (_vte_ring_contains(screen->row_data, i)) {
2384                         rowdata = _vte_ring_index(screen->row_data,
2385                                                   VteRowData *, i);
2386                 } else {
2387                         rowdata = vte_new_row_data(terminal);
2388                         _vte_ring_append(screen->row_data, rowdata);
2389                 }
2390                 /* Pad out the row. */
2391                 vte_g_array_fill(rowdata->cells,
2392                                  &screen->fill_defaults,
2393                                  terminal->column_count);
2394                 /* Repaint this row. */
2395                 vte_invalidate_cells(terminal,
2396                                      0, terminal->column_count,
2397                                      i, 1);
2398         }
2399
2400         /* We've modified the display.  Make a note of it. */
2401         terminal->pvt->text_deleted_count++;
2402         return FALSE;
2403 }
2404
2405 /* Clear from the cursor position to the end of the line. */
2406 static gboolean
2407 vte_sequence_handler_ce(VteTerminal *terminal,
2408                         const char *match,
2409                         GQuark match_quark,
2410                         GValueArray *params)
2411 {
2412         VteRowData *rowdata;
2413         VteScreen *screen;
2414
2415         screen = terminal->pvt->screen;
2416         /* Get the data for the row which the cursor points to. */
2417         vte_terminal_ensure_cursor(terminal, FALSE);
2418         rowdata = _vte_ring_index(screen->row_data, VteRowData *,
2419                                   screen->cursor_current.row);
2420         /* Remove the data at the end of the array until the current column
2421          * is the end of the array. */
2422         if (rowdata->cells->len > screen->cursor_current.col) {
2423                 g_array_set_size(rowdata->cells, screen->cursor_current.col);
2424         }
2425         /* Add enough cells to the end of the line to fill out the row. */
2426         vte_g_array_fill(rowdata->cells,
2427                          &screen->fill_defaults,
2428                          terminal->column_count);
2429         /* Repaint this row. */
2430         vte_invalidate_cells(terminal,
2431                              0, terminal->column_count,
2432                              screen->cursor_current.row, 1);
2433
2434         /* We've modified the display.  Make a note of it. */
2435         terminal->pvt->text_deleted_count++;
2436         return FALSE;
2437 }
2438
2439 /* Move the cursor to the given column (horizontal position). */
2440 static gboolean
2441 vte_sequence_handler_ch(VteTerminal *terminal,
2442                         const char *match,
2443                         GQuark match_quark,
2444                         GValueArray *params)
2445 {
2446         VteScreen *screen;
2447         GValue *value;
2448         long val;
2449
2450         screen = terminal->pvt->screen;
2451         /* We only care if there's a parameter in there. */
2452         if ((params != NULL) && (params->n_values > 0)) {
2453                 value = g_value_array_get_nth(params, 0);
2454                 if (G_VALUE_HOLDS_LONG(value)) {
2455                         val = CLAMP(g_value_get_long(value),
2456                                     0,
2457                                     terminal->column_count - 1);
2458                         /* Move the cursor. */
2459                         screen->cursor_current.col = val;
2460                 }
2461         }
2462         return FALSE;
2463 }
2464
2465 /* Clear the screen and home the cursor. */
2466 static gboolean
2467 vte_sequence_handler_cl(VteTerminal *terminal,
2468                         const char *match,
2469                         GQuark match_quark,
2470                         GValueArray *params)
2471 {
2472         vte_sequence_handler_clear_screen(terminal, NULL, 0, NULL);
2473         vte_sequence_handler_ho(terminal, NULL, 0, NULL);
2474
2475         /* We've modified the display.  Make a note of it. */
2476         terminal->pvt->text_deleted_count++;
2477         return FALSE;
2478 }
2479
2480 /* Move the cursor to the given position. */
2481 static gboolean
2482 vte_sequence_handler_cm(VteTerminal *terminal,
2483                         const char *match,
2484                         GQuark match_quark,
2485                         GValueArray *params)
2486 {
2487         GValue *row, *col;
2488         long rowval, colval, origin;
2489         VteScreen *screen;
2490
2491         screen = terminal->pvt->screen;
2492         /* We need at least two parameters. */
2493         if ((params != NULL) && (params->n_values >= 2)) {
2494                 /* The first is the row, the second is the column. */
2495                 row = g_value_array_get_nth(params, 0);
2496                 col = g_value_array_get_nth(params, 1);
2497                 if (G_VALUE_HOLDS_LONG(row) &&
2498                     G_VALUE_HOLDS_LONG(col)) {
2499                         if (screen->origin_mode &&
2500                             screen->scrolling_restricted) {
2501                                 origin = screen->scrolling_region.start;
2502                         } else {
2503                                 origin = 0;
2504                         }
2505                         rowval = g_value_get_long(row) + origin;
2506                         colval = g_value_get_long(col);
2507                         rowval = CLAMP(rowval, 0, terminal->row_count - 1);
2508                         colval = CLAMP(colval, 0, terminal->column_count - 1);
2509                         screen->cursor_current.row = rowval +
2510                                                      screen->insert_delta;
2511                         screen->cursor_current.col = colval;
2512                 }
2513         }
2514         return FALSE;
2515 }
2516
2517 /* Clear the current line. */
2518 static gboolean
2519 vte_sequence_handler_clear_current_line(VteTerminal *terminal,
2520                                         const char *match,
2521                                         GQuark match_quark,
2522                                         GValueArray *params)
2523 {
2524         VteRowData *rowdata;
2525         VteScreen *screen;
2526
2527         screen = terminal->pvt->screen;
2528
2529         /* If the cursor is actually on the screen, clear data in the row
2530          * which corresponds to the cursor. */
2531         if (_vte_ring_next(screen->row_data) > screen->cursor_current.row) {
2532                 /* Get the data for the row which the cursor points to. */
2533                 rowdata = _vte_ring_index(screen->row_data, VteRowData *,
2534                                           screen->cursor_current.row);
2535                 /* Remove it. */
2536                 if (rowdata->cells->len > 0) {
2537                         g_array_set_size(rowdata->cells, 0);
2538                 }
2539                 /* Add enough cells to the end of the line to fill out the
2540                  * row. */
2541                 vte_g_array_fill(rowdata->cells,
2542                                  &screen->fill_defaults,
2543                                  terminal->column_count);
2544                 /* Repaint this row. */
2545                 vte_invalidate_cells(terminal,
2546                                      0, terminal->column_count,
2547                                      screen->cursor_current.row, 1);
2548         }
2549
2550         /* We've modified the display.  Make a note of it. */
2551         terminal->pvt->text_deleted_count++;
2552         return FALSE;
2553 }
2554
2555 /* Carriage return. */
2556 static gboolean
2557 vte_sequence_handler_cr(VteTerminal *terminal,
2558                         const char *match,
2559                         GQuark match_quark,
2560                         GValueArray *params)
2561 {
2562         terminal->pvt->screen->cursor_current.col = 0;
2563         return FALSE;
2564 }
2565
2566 /* Restrict scrolling and updates to a subset of the visible lines. */
2567 static gboolean
2568 vte_sequence_handler_cs(VteTerminal *terminal,
2569                         const char *match,
2570                         GQuark match_quark,
2571                         GValueArray *params)
2572 {
2573         long start, end, rows;
2574         GValue *value;
2575         VteScreen *screen;
2576
2577         /* We require two parameters.  Anything less is a reset. */
2578         screen = terminal->pvt->screen;
2579         if ((params == NULL) || (params->n_values < 2)) {
2580                 screen->scrolling_restricted = FALSE;
2581                 return FALSE;
2582         }
2583         /* Extract the two values. */
2584         value = g_value_array_get_nth(params, 0);
2585         start = g_value_get_long(value);
2586         value = g_value_array_get_nth(params, 1);
2587         end = g_value_get_long(value);
2588         /* Catch garbage. */
2589         rows = terminal->row_count;
2590         if ((start <= 0) || (start >= rows)) {
2591                 start = 0;
2592         }
2593         if ((end <= 0) || (end >= rows)) {
2594                 end = rows - 1;
2595         }
2596         /* Set the right values. */
2597         screen->scrolling_region.start = start;
2598         screen->scrolling_region.end = end;
2599         screen->scrolling_restricted = TRUE;
2600         /* Special case -- run wild, run free. */
2601         if ((screen->scrolling_region.start == 0) &&
2602             (screen->scrolling_region.end == rows - 1)) {
2603                 screen->scrolling_restricted = FALSE;
2604         }
2605         /* Clamp the cursor to the scrolling region. */
2606         screen->cursor_current.row = CLAMP(screen->cursor_current.row,
2607                                            screen->insert_delta + start,
2608                                            screen->insert_delta + end);
2609         vte_terminal_ensure_cursor(terminal, TRUE);
2610         return FALSE;
2611 }
2612
2613 /* Restrict scrolling and updates to a subset of the visible lines, because
2614  * GNU Emacs is special. */
2615 static gboolean
2616 vte_sequence_handler_cS(VteTerminal *terminal,
2617                         const char *match,
2618                         GQuark match_quark,
2619                         GValueArray *params)
2620 {
2621         long start, end, rows;
2622         GValue *value;
2623         VteScreen *screen;
2624
2625         /* We require four parameters. */
2626         screen = terminal->pvt->screen;
2627         if ((params == NULL) || (params->n_values < 2)) {
2628                 screen->scrolling_restricted = FALSE;
2629                 return FALSE;
2630         }
2631         /* Extract the two parameters we care about, encoded as the number
2632          * of lines above and below the scrolling region, respectively. */
2633         value = g_value_array_get_nth(params, 1);
2634         start = g_value_get_long(value);
2635         value = g_value_array_get_nth(params, 2);
2636         end = (terminal->row_count - 1) - g_value_get_long(value);
2637         /* Set the right values. */
2638         screen->scrolling_region.start = start;
2639         screen->scrolling_region.end = end;
2640         screen->scrolling_restricted = TRUE;
2641         /* Special case -- run wild, run free. */
2642         rows = terminal->row_count;
2643         if ((screen->scrolling_region.start == 0) &&
2644             (screen->scrolling_region.end == rows - 1)) {
2645                 screen->scrolling_restricted = FALSE;
2646         }
2647         /* Clamp the cursor to the scrolling region. */
2648         screen->cursor_current.row = CLAMP(screen->cursor_current.row,
2649                                            screen->insert_delta + start,
2650                                            screen->insert_delta + end);
2651         vte_terminal_ensure_cursor(terminal, TRUE);
2652         return FALSE;
2653 }
2654
2655 /* Clear all tab stops. */
2656 static gboolean
2657 vte_sequence_handler_ct(VteTerminal *terminal,
2658                         const char *match,
2659                         GQuark match_quark,
2660                         GValueArray *params)
2661 {
2662         if (terminal->pvt->tabstops != NULL) {
2663                 g_hash_table_destroy(terminal->pvt->tabstops);
2664                 terminal->pvt->tabstops = NULL;
2665         }
2666         return FALSE;
2667 }
2668
2669 /* Move the cursor to the lower left-hand corner. */
2670 static gboolean
2671 vte_sequence_handler_cursor_lower_left(VteTerminal *terminal,
2672                                        const char *match,
2673                                        GQuark match_quark,
2674                                        GValueArray *params)
2675 {
2676         VteScreen *screen;
2677         long row;
2678         screen = terminal->pvt->screen;
2679         row = MAX(0, terminal->row_count - 1);
2680         screen->cursor_current.row = screen->insert_delta + row;
2681         screen->cursor_current.col = 0;
2682         vte_terminal_ensure_cursor(terminal, TRUE);
2683         return FALSE;
2684 }
2685
2686 /* Move the cursor to the beginning of the next line, scrolling if necessary. */
2687 static gboolean
2688 vte_sequence_handler_cursor_next_line(VteTerminal *terminal,
2689                                       const char *match,
2690                                       GQuark match_quark,
2691                                       GValueArray *params)
2692 {
2693         terminal->pvt->screen->cursor_current.col = 0;
2694         return vte_sequence_handler_DO(terminal, match, match_quark, params);
2695 }
2696
2697 /* Move the cursor to the beginning of the next line, scrolling if necessary. */
2698 static gboolean
2699 vte_sequence_handler_cursor_preceding_line(VteTerminal *terminal,
2700                                            const char *match,
2701                                            GQuark match_quark,
2702                                            GValueArray *params)
2703 {
2704         terminal->pvt->screen->cursor_current.col = 0;
2705         return vte_sequence_handler_UP(terminal, match, match_quark, params);
2706 }
2707
2708 /* Move the cursor to the given row (vertical position). */
2709 static gboolean
2710 vte_sequence_handler_cv(VteTerminal *terminal,
2711                         const char *match,
2712                         GQuark match_quark,
2713                         GValueArray *params)
2714 {
2715         VteScreen *screen;
2716         GValue *value;
2717         long val, origin;
2718         screen = terminal->pvt->screen;
2719         /* We only care if there's a parameter in there. */
2720         if ((params != NULL) && (params->n_values > 0)) {
2721                 value = g_value_array_get_nth(params, 0);
2722                 if (G_VALUE_HOLDS_LONG(value)) {
2723                         /* Move the cursor. */
2724                         if (screen->origin_mode &&
2725                             screen->scrolling_restricted) {
2726                                 origin = screen->scrolling_region.start;
2727                         } else {
2728                                 origin = 0;
2729                         }
2730                         val = g_value_get_long(value) + origin;
2731                         val = CLAMP(val, 0, terminal->row_count - 1);
2732                         screen->cursor_current.row = screen->insert_delta + val;
2733                 }
2734         }
2735         return FALSE;
2736 }
2737
2738 /* Delete a character at the current cursor position. */
2739 static gboolean
2740 vte_sequence_handler_dc(VteTerminal *terminal,
2741                         const char *match,
2742                         GQuark match_quark,
2743                         GValueArray *params)
2744 {
2745         VteScreen *screen;
2746         VteRowData *rowdata;
2747         long col;
2748
2749         screen = terminal->pvt->screen;
2750
2751         if (_vte_ring_next(screen->row_data) > screen->cursor_current.row) {
2752                 /* Get the data for the row which the cursor points to. */
2753                 rowdata = _vte_ring_index(screen->row_data,
2754                                           VteRowData *,
2755                                           screen->cursor_current.row);
2756                 col = screen->cursor_current.col;
2757                 /* Remove the column. */
2758                 if (col < rowdata->cells->len) {
2759                         g_array_remove_index(rowdata->cells, col);
2760                 }
2761                 /* Add new cells until we have enough to fill the row. */
2762                 vte_g_array_fill(rowdata->cells,
2763                                  &screen->color_defaults,
2764                                  terminal->column_count);
2765                 /* Repaint this row. */
2766                 vte_invalidate_cells(terminal,
2767                                      0, terminal->column_count,
2768                                      screen->cursor_current.row, 1);
2769         }
2770
2771         /* We've modified the display.  Make a note of it. */
2772         terminal->pvt->text_deleted_count++;
2773         return FALSE;
2774 }
2775
2776 /* Delete N characters at the current cursor position. */
2777 static gboolean
2778 vte_sequence_handler_DC(VteTerminal *terminal,
2779                         const char *match,
2780                         GQuark match_quark,
2781                         GValueArray *params)
2782 {
2783         return vte_sequence_handler_multiple(terminal, match, match_quark,
2784                                              params, vte_sequence_handler_dc);
2785 }
2786
2787 /* Delete a line at the current cursor position. */
2788 static gboolean
2789 vte_sequence_handler_dl(VteTerminal *terminal,
2790                         const char *match,
2791                         GQuark match_quark,
2792                         GValueArray *params)
2793 {
2794         VteScreen *screen;
2795         long start, end, param, i;
2796         GValue *value;
2797
2798         /* Find out which part of the screen we're messing with. */
2799         screen = terminal->pvt->screen;
2800         start = screen->cursor_current.row;
2801         if (screen->scrolling_restricted) {
2802                 end = screen->insert_delta + screen->scrolling_region.end;
2803         } else {
2804                 end = screen->insert_delta + terminal->row_count - 1;
2805         }
2806
2807         /* Extract any parameters. */
2808         param = 1;
2809         if ((params != NULL) && (params->n_values > 0)) {
2810                 value = g_value_array_get_nth(params, 0);
2811                 param = g_value_get_long(value);
2812         }
2813
2814         /* Delete the right number of lines. */
2815         for (i = 0; i < param; i++) {
2816                 /* Clear a line off the end of the region and add one to the
2817                  * top of the region. */
2818                 vte_remove_line_internal(terminal, start);
2819                 vte_insert_line_internal(terminal, end);
2820                 /* Adjust the scrollbars if necessary. */
2821                 vte_terminal_adjust_adjustments(terminal, FALSE);
2822         }
2823
2824         /* Update the display. */
2825         vte_terminal_scroll_region(terminal, start, end - start + 1, -param);
2826
2827         /* We've modified the display.  Make a note of it. */
2828         terminal->pvt->text_deleted_count++;
2829         return FALSE;
2830 }
2831
2832 /* Delete N lines at the current cursor position. */
2833 static gboolean
2834 vte_sequence_handler_DL(VteTerminal *terminal,
2835                         const char *match,
2836                         GQuark match_quark,
2837                         GValueArray *params)
2838 {
2839         return vte_sequence_handler_dl(terminal, match, match_quark, params);
2840 }
2841
2842 /* Make sure we have enough rows and columns to hold data at the current
2843  * cursor position. */
2844 static void
2845 vte_terminal_ensure_cursor(VteTerminal *terminal, gboolean current)
2846 {
2847         VteRowData *row;
2848         VteScreen *screen;
2849         gboolean readjust = FALSE, fill = FALSE;
2850
2851         /* Must make sure we're in a sane area. */
2852         screen = terminal->pvt->screen;
2853
2854         /* Figure out how many rows we need to add. */
2855         fill = (terminal->pvt->screen->defaults.back != VTE_DEF_BG);
2856         while (screen->cursor_current.row >= _vte_ring_next(screen->row_data)) {
2857                 /* Create a new row. */
2858                 if (fill) {
2859                         row = vte_new_row_data_sized(terminal, TRUE);
2860                 } else {
2861                         row = vte_new_row_data(terminal);
2862                 }
2863                 _vte_ring_append(screen->row_data, row);
2864                 readjust = TRUE;
2865         }
2866         if (readjust) {
2867                 vte_terminal_adjust_adjustments(terminal, FALSE);
2868         }
2869
2870         /* Find the row the cursor is in. */
2871         row = _vte_ring_index(screen->row_data,
2872                               VteRowData *,
2873                               screen->cursor_current.row);
2874         if ((row->cells->len <= screen->cursor_current.col) &&
2875             (row->cells->len < terminal->column_count)) {
2876                 /* Set up defaults we'll use when adding new cells. */
2877                 if (current) {
2878                         /* Add new cells until we have one here. */
2879                         vte_g_array_fill(row->cells,
2880                                          &screen->color_defaults,
2881                                          screen->cursor_current.col + 1);
2882                 } else {
2883                         /* Add enough cells at the end to make sure we have
2884                          * enough for all visible columns. */
2885                         vte_g_array_fill(row->cells,
2886                                          &screen->basic_defaults,
2887                                          screen->cursor_current.col + 1);
2888                 }
2889         }
2890 }
2891
2892 /* Update the insert delta so that the screen which includes it also
2893  * includes the end of the buffer. */
2894 static void
2895 vte_terminal_update_insert_delta(VteTerminal *terminal)
2896 {
2897         long delta;
2898         VteScreen *screen;
2899
2900         screen = terminal->pvt->screen;
2901
2902         /* Make sure that the bottom row is visible, and that it's in
2903          * the buffer (even if it's empty).  This usually causes the
2904          * top row to become a history-only row. */
2905         delta = MAX(screen->insert_delta,
2906                     screen->cursor_current.row - (terminal->row_count - 1));
2907         delta = MAX(delta, _vte_ring_delta(screen->row_data));
2908
2909         /* Adjust the insert delta and scroll if needed. */
2910         if (delta != screen->insert_delta) {
2911                 vte_terminal_ensure_cursor(terminal, FALSE);
2912                 vte_terminal_adjust_adjustments(terminal, TRUE);
2913                 screen->insert_delta = delta;
2914         }
2915 }
2916
2917 /* Cursor down, no scrolling. */
2918 static gboolean
2919 vte_sequence_handler_do(VteTerminal *terminal,
2920                         const char *match,
2921                         GQuark match_quark,
2922                         GValueArray *params)
2923 {
2924         GtkWidget *widget;
2925         long start, end;
2926         VteScreen *screen;
2927
2928         widget = GTK_WIDGET(terminal);
2929         screen = terminal->pvt->screen;
2930
2931         if (screen->scrolling_restricted) {
2932                 start = screen->insert_delta + screen->scrolling_region.start;
2933                 end = screen->insert_delta + screen->scrolling_region.end;
2934         } else {
2935                 start = screen->insert_delta;
2936                 end = start + terminal->row_count - 1;
2937         }
2938
2939         /* Move the cursor down. */
2940         screen->cursor_current.row = MIN(screen->cursor_current.row + 1, end);
2941         return FALSE;
2942 }
2943
2944 /* Cursor down, no scrolling. */
2945 static gboolean
2946 vte_sequence_handler_DO(VteTerminal *terminal,
2947                         const char *match,
2948                         GQuark match_quark,
2949                         GValueArray *params)
2950 {
2951         return vte_sequence_handler_multiple(terminal, match, match_quark,
2952                                              params, vte_sequence_handler_do);
2953 }
2954
2955 /* Start using alternate character set. */
2956 static gboolean
2957 vte_sequence_handler_eA(VteTerminal *terminal,
2958                         const char *match,
2959                         GQuark match_quark,
2960                         GValueArray *params)
2961 {
2962         return vte_sequence_handler_ae(terminal, match, match_quark, params);
2963 }
2964
2965 /* Erase characters starting at the cursor position (overwriting N with
2966  * spaces, but not moving the cursor). */
2967 static gboolean
2968 vte_sequence_handler_ec(VteTerminal *terminal,
2969                         const char *match,
2970                         GQuark match_quark,
2971                         GValueArray *params)
2972 {
2973         VteScreen *screen;
2974         VteRowData *rowdata;
2975         GValue *value;
2976         struct vte_charcell *cell;
2977         long col, i, count;
2978
2979         screen = terminal->pvt->screen;
2980
2981         /* If we got a parameter, use it. */
2982         count = 1;
2983         if ((params != NULL) && (params->n_values > 0)) {
2984                 value = g_value_array_get_nth(params, 0);
2985                 if (G_VALUE_HOLDS_LONG(value)) {
2986                         count = g_value_get_long(value);
2987                 }
2988         }
2989
2990         /* Clear out the given number of characters. */
2991         vte_terminal_ensure_cursor(terminal, TRUE);
2992         if (_vte_ring_next(screen->row_data) > screen->cursor_current.row) {
2993                 /* Get the data for the row which the cursor points to. */
2994                 rowdata = _vte_ring_index(screen->row_data,
2995                                           VteRowData *,
2996                                           screen->cursor_current.row);
2997                 /* Write over the characters.  (If there aren't enough, we'll
2998                  * need to create them.) */
2999                 for (i = 0; i < count; i++) {
3000                         col = screen->cursor_current.col + i;
3001                         if (col >= 0) {
3002                                 if (col < rowdata->cells->len) {
3003                                         /* Replace this cell with the current
3004                                          * defaults. */
3005                                         cell = &g_array_index(rowdata->cells,
3006                                                               struct vte_charcell,
3007                                                               col);
3008                                         *cell = screen->color_defaults;
3009                                 } else {
3010                                         /* Add new cells until we have one here. */
3011                                         vte_g_array_fill(rowdata->cells,
3012                                                          &screen->color_defaults,
3013                                                          col);
3014                                 }
3015                         }
3016                 }
3017                 /* Repaint this row. */
3018                 vte_invalidate_cells(terminal,
3019                                      0, terminal->column_count,
3020                                      screen->cursor_current.row, 1);
3021         }
3022
3023         /* We've modified the display.  Make a note of it. */
3024         terminal->pvt->text_deleted_count++;
3025         return FALSE;
3026 }
3027
3028 /* End insert mode. */
3029 static gboolean
3030 vte_sequence_handler_ei(VteTerminal *terminal,
3031                         const char *match,
3032                         GQuark match_quark,
3033                         GValueArray *params)
3034 {
3035         terminal->pvt->screen->insert_mode = FALSE;
3036         return FALSE;
3037 }
3038
3039 /* Form-feed / next-page. */
3040 static gboolean
3041 vte_sequence_handler_form_feed(VteTerminal *terminal,
3042                                const char *match,
3043                                GQuark match_quark,
3044                                GValueArray *params)
3045 {
3046         return vte_sequence_handler_index(terminal, match, match_quark, params);
3047 }
3048
3049 /* Move from status line. */
3050 static gboolean
3051 vte_sequence_handler_fs(VteTerminal *terminal,
3052                         const char *match,
3053                         GQuark match_quark,
3054                         GValueArray *params)
3055 {
3056         terminal->pvt->screen->status_line = FALSE;
3057         return FALSE;
3058 }
3059
3060 /* Move the cursor to the home position. */
3061 static gboolean
3062 vte_sequence_handler_ho(VteTerminal *terminal,
3063                         const char *match,
3064                         GQuark match_quark,
3065                         GValueArray *params)
3066 {
3067         VteScreen *screen;
3068         screen = terminal->pvt->screen;
3069         screen->cursor_current.row = screen->insert_delta;
3070         screen->cursor_current.col = 0;
3071         return FALSE;
3072 }
3073
3074 /* Move the cursor to a specified position. */
3075 static gboolean
3076 vte_sequence_handler_horizontal_and_vertical_position(VteTerminal *terminal,
3077                                                       const char *match,
3078                                                       GQuark match_quark,
3079                                                       GValueArray *params)
3080 {
3081         return vte_sequence_handler_offset(terminal, match, match_quark, params,
3082                                            -1, vte_sequence_handler_cm);
3083 }
3084
3085 /* Insert a character. */
3086 static gboolean
3087 vte_sequence_handler_ic(VteTerminal *terminal,
3088                         const char *match,
3089                         GQuark match_quark,
3090                         GValueArray *params)
3091 {
3092         struct vte_cursor_position save;
3093         VteScreen *screen;
3094
3095         screen = terminal->pvt->screen;
3096
3097         save = screen->cursor_current;
3098
3099         vte_terminal_insert_char(terminal, ' ', TRUE, TRUE, TRUE, TRUE, 0);
3100
3101         screen->cursor_current = save;
3102
3103         return FALSE;
3104 }
3105
3106 /* Insert N characters. */
3107 static gboolean
3108 vte_sequence_handler_IC(VteTerminal *terminal,
3109                         const char *match,
3110                         GQuark match_quark,
3111                         GValueArray *params)
3112 {
3113         return vte_sequence_handler_multiple(terminal, match, match_quark,
3114                                              params, vte_sequence_handler_ic);
3115 }
3116
3117 /* Begin insert mode. */
3118 static gboolean
3119 vte_sequence_handler_im(VteTerminal *terminal,
3120                         const char *match,
3121                         GQuark match_quark,
3122                         GValueArray *params)
3123 {
3124         terminal->pvt->screen->insert_mode = TRUE;
3125         return FALSE;
3126 }
3127
3128 /* Cursor down, with scrolling. */
3129 static gboolean
3130 vte_sequence_handler_index(VteTerminal *terminal,
3131                            const char *match,
3132                            GQuark match_quark,
3133                            GValueArray *params)
3134 {
3135         return vte_sequence_handler_sf(terminal, match, match_quark, params);
3136 }
3137
3138 /* Send me a backspace key sym, will you?  Guess that the application meant
3139  * to send the cursor back one position. */
3140 static gboolean
3141 vte_sequence_handler_kb(VteTerminal *terminal,
3142                         const char *match,
3143                         GQuark match_quark,
3144                         GValueArray *params)
3145 {
3146         /* Move the cursor left. */
3147         return vte_sequence_handler_le(terminal, match, match_quark, params);
3148 }
3149
3150 /* Keypad mode end. */
3151 static gboolean
3152 vte_sequence_handler_ke(VteTerminal *terminal,
3153                         const char *match,
3154                         GQuark match_quark,
3155                         GValueArray *params)
3156 {
3157         terminal->pvt->keypad_mode = VTE_KEYMODE_NORMAL;
3158         return FALSE;
3159 }
3160
3161 /* Keypad mode start. */
3162 static gboolean
3163 vte_sequence_handler_ks(VteTerminal *terminal,
3164                         const char *match,
3165                         GQuark match_quark,
3166                         GValueArray *params)
3167 {
3168         terminal->pvt->keypad_mode = VTE_KEYMODE_APPLICATION;
3169         return FALSE;
3170 }
3171
3172 /* Cursor left. */
3173 static gboolean
3174 vte_sequence_handler_le(VteTerminal *terminal,
3175                         const char *match,
3176                         GQuark match_quark,
3177                         GValueArray *params)
3178 {
3179         VteScreen *screen;
3180
3181         screen = terminal->pvt->screen;
3182         if (screen->cursor_current.col > 0) {
3183                 /* There's room to move left, so do so. */
3184                 screen->cursor_current.col--;
3185         } else {
3186                 if (terminal->pvt->flags.bw) {
3187                         /* Wrap to the previous line. */
3188                         screen->cursor_current.col = terminal->column_count - 1;
3189                         screen->cursor_current.row = MAX(screen->cursor_current.row - 1,
3190                                                          screen->insert_delta);
3191                 } else {
3192                         /* Stick to the first column. */
3193                         screen->cursor_current.col = 0;
3194                 }
3195         }
3196         return FALSE;
3197 }
3198
3199 /* Move the cursor left N columns. */
3200 static gboolean
3201 vte_sequence_handler_LE(VteTerminal *terminal,
3202                         const char *match,
3203                         GQuark match_quark,
3204                         GValueArray *params)
3205 {
3206         return vte_sequence_handler_multiple(terminal, match, match_quark,
3207                                              params, vte_sequence_handler_le);
3208 }
3209
3210 /* Move the cursor to the lower left corner of the display. */
3211 static gboolean
3212 vte_sequence_handler_ll(VteTerminal *terminal,
3213                         const char *match,
3214                         GQuark match_quark,
3215                         GValueArray *params)
3216 {
3217         VteScreen *screen;
3218         screen = terminal->pvt->screen;
3219         screen->cursor_current.row = MAX(screen->insert_delta,
3220                                          screen->insert_delta +
3221                                          terminal->row_count - 1);
3222         screen->cursor_current.col = 0;
3223         return FALSE;
3224 }
3225
3226 /* Blink on. */
3227 static gboolean
3228 vte_sequence_handler_mb(VteTerminal *terminal,
3229                         const char *match,
3230                         GQuark match_quark,
3231                         GValueArray *params)
3232 {
3233         terminal->pvt->screen->defaults.blink = 1;
3234         return FALSE;
3235 }
3236
3237 /* Bold on. */
3238 static gboolean
3239 vte_sequence_handler_md(VteTerminal *terminal,
3240                         const char *match,
3241                         GQuark match_quark,
3242                         GValueArray *params)
3243 {
3244         terminal->pvt->screen->defaults.bold = 1;
3245         terminal->pvt->screen->defaults.half = 0;
3246         return FALSE;
3247 }
3248
3249 /* End modes. */
3250 static gboolean
3251 vte_sequence_handler_me(VteTerminal *terminal,
3252                         const char *match,
3253                         GQuark match_quark,
3254                         GValueArray *params)
3255 {
3256         vte_terminal_set_default_attributes(terminal);
3257         return FALSE;
3258 }
3259
3260 /* Half-bright on. */
3261 static gboolean
3262 vte_sequence_handler_mh(VteTerminal *terminal,
3263                         const char *match,
3264                         GQuark match_quark,
3265                         GValueArray *params)
3266 {
3267         terminal->pvt->screen->defaults.half = 1;
3268         terminal->pvt->screen->defaults.bold = 0;
3269         return FALSE;
3270 }
3271
3272 /* Invisible on. */
3273 static gboolean
3274 vte_sequence_handler_mk(VteTerminal *terminal,
3275                         const char *match,
3276                         GQuark match_quark,
3277                         GValueArray *params)
3278 {
3279         terminal->pvt->screen->defaults.invisible = 1;
3280         return FALSE;
3281 }
3282
3283 /* Protect on. */
3284 static gboolean
3285 vte_sequence_handler_mp(VteTerminal *terminal,
3286                         const char *match,
3287                         GQuark match_quark,
3288                         GValueArray *params)
3289 {
3290         terminal->pvt->screen->defaults.protect = 1;
3291         return FALSE;
3292 }
3293
3294 /* Reverse on. */
3295 static gboolean
3296 vte_sequence_handler_mr(VteTerminal *terminal,
3297                         const char *match,
3298                         GQuark match_quark,
3299                         GValueArray *params)
3300 {
3301         terminal->pvt->screen->defaults.reverse = 1;
3302         return FALSE;
3303 }
3304
3305 /* Cursor right. */
3306 static gboolean
3307 vte_sequence_handler_nd(VteTerminal *terminal,
3308                         const char *match,
3309                         GQuark match_quark,
3310                         GValueArray *params)
3311 {
3312         VteScreen *screen;
3313         screen = terminal->pvt->screen;
3314         if ((screen->cursor_current.col + 1) < terminal->column_count) {
3315                 /* There's room to move right. */
3316                 screen->cursor_current.col++;
3317         }
3318         return FALSE;
3319 }
3320
3321 /* Move the cursor to the beginning of the next line, scrolling if necessary. */
3322 static gboolean
3323 vte_sequence_handler_next_line(VteTerminal *terminal,
3324                                const char *match,
3325                                GQuark match_quark,
3326                                GValueArray *params)
3327 {
3328         terminal->pvt->screen->cursor_current.col = 0;
3329         return vte_sequence_handler_DO(terminal, match, match_quark, params);
3330 }
3331
3332 /* No-op. */
3333 static gboolean
3334 vte_sequence_handler_noop(VteTerminal *terminal,
3335                           const char *match,
3336                           GQuark match_quark,
3337                           GValueArray *params)
3338 {
3339         return FALSE;
3340 }
3341
3342 /* Carriage return command(?). */
3343 static gboolean
3344 vte_sequence_handler_nw(VteTerminal *terminal,
3345                         const char *match,
3346                         GQuark match_quark,
3347                         GValueArray *params)
3348 {
3349         return vte_sequence_handler_cr(terminal, match, match_quark, params);
3350 }
3351
3352 /* Restore cursor (position). */
3353 static gboolean
3354 vte_sequence_handler_rc(VteTerminal *terminal,
3355                         const char *match,
3356                         GQuark match_quark,
3357                         GValueArray *params)
3358 {
3359         VteScreen *screen;
3360         screen = terminal->pvt->screen;
3361         screen->cursor_current.col = screen->cursor_saved.col;
3362         screen->cursor_current.row = CLAMP(screen->cursor_saved.row +
3363                                            screen->insert_delta,
3364                                            screen->insert_delta,
3365                                            screen->insert_delta +
3366                                            terminal->row_count - 1);
3367         return FALSE;
3368 }
3369
3370 /* Cursor down, with scrolling. */
3371 static gboolean
3372 vte_sequence_handler_reverse_index(VteTerminal *terminal,
3373                                    const char *match,
3374                                    GQuark match_quark,
3375                                    GValueArray *params)
3376 {
3377         return vte_sequence_handler_sr(terminal, match, match_quark, params);
3378 }
3379
3380 /* Cursor right N characters. */
3381 static gboolean
3382 vte_sequence_handler_RI(VteTerminal *terminal,
3383                         const char *match,
3384                         GQuark match_quark,
3385                         GValueArray *params)
3386 {
3387         return vte_sequence_handler_multiple(terminal, match, match_quark,
3388                                              params, vte_sequence_handler_nd);
3389 }
3390
3391 /* Save cursor (position). */
3392 static gboolean
3393 vte_sequence_handler_sc(VteTerminal *terminal,
3394                         const char *match,
3395                         GQuark match_quark,
3396                         GValueArray *params)
3397 {
3398         VteScreen *screen;
3399         screen = terminal->pvt->screen;
3400         screen->cursor_saved.col = screen->cursor_current.col;
3401         screen->cursor_saved.row = CLAMP(screen->cursor_current.row -
3402                                          screen->insert_delta,
3403                                          0, terminal->row_count - 1);
3404         return FALSE;
3405 }
3406
3407 /* Standout end. */
3408 static gboolean
3409 vte_sequence_handler_se(VteTerminal *terminal,
3410                         const char *match,
3411                         GQuark match_quark,
3412                         GValueArray *params)
3413 {
3414         char *bold, *underline, *standout, *reverse, *half, *blink;
3415
3416         /* Standout may be mapped to another attribute, so attempt to do
3417          * the Right Thing here. */
3418         standout = _vte_termcap_find_string(terminal->pvt->termcap,
3419                                             terminal->pvt->emulation,
3420                                             "so");
3421         g_assert(standout != NULL);
3422         blink = _vte_termcap_find_string(terminal->pvt->termcap,
3423                                          terminal->pvt->emulation,
3424                                          "mb");
3425         bold = _vte_termcap_find_string(terminal->pvt->termcap,
3426                                         terminal->pvt->emulation,
3427                                         "md");
3428         half = _vte_termcap_find_string(terminal->pvt->termcap,
3429                                         terminal->pvt->emulation,
3430                                         "mh");
3431         reverse = _vte_termcap_find_string(terminal->pvt->termcap,
3432                                            terminal->pvt->emulation,
3433                                            "mr");
3434         underline = _vte_termcap_find_string(terminal->pvt->termcap,
3435                                              terminal->pvt->emulation,
3436                                              "us");
3437
3438         /* If the standout sequence is the same as another sequence, do what
3439          * we'd do for that other sequence instead. */
3440         if (blink && (g_ascii_strcasecmp(standout, blink) == 0)) {
3441                 vte_sequence_handler_me(terminal, match, match_quark, params);
3442         } else
3443         if (bold && (g_ascii_strcasecmp(standout, bold) == 0)) {
3444                 vte_sequence_handler_me(terminal, match, match_quark, params);
3445         } else
3446         if (half && (g_ascii_strcasecmp(standout, half) == 0)) {
3447                 vte_sequence_handler_me(terminal, match, match_quark, params);
3448         } else
3449         if (reverse && (g_ascii_strcasecmp(standout, reverse) == 0)) {
3450                 vte_sequence_handler_me(terminal, match, match_quark, params);
3451         } else
3452         if (underline && (g_ascii_strcasecmp(standout, underline) == 0)) {
3453                 vte_sequence_handler_ue(terminal, match, match_quark, params);
3454         } else {
3455                 /* Otherwise just set standout mode. */
3456                 terminal->pvt->screen->defaults.standout = 0;
3457         }
3458
3459         if (blink) {
3460                 g_free(blink);
3461         }
3462         if (bold) {
3463                 g_free(bold);
3464         }
3465         if (half) {
3466                 g_free(half);
3467         }
3468         if (reverse) {
3469                 g_free(reverse);
3470         }
3471         if (underline) {
3472                 g_free(underline);
3473         }
3474         g_free(standout);
3475         return FALSE;
3476 }
3477
3478 /* Cursor down, with scrolling. */
3479 static gboolean
3480 vte_sequence_handler_sf(VteTerminal *terminal,
3481                         const char *match,
3482                         GQuark match_quark,
3483                         GValueArray *params)
3484 {
3485         GtkWidget *widget;
3486         VteRowData *row;
3487         long start, end, top, bottom;
3488         VteScreen *screen;
3489
3490         widget = GTK_WIDGET(terminal);
3491         screen = terminal->pvt->screen;
3492
3493         if (screen->scrolling_restricted) {
3494                 start = screen->insert_delta + screen->scrolling_region.start;
3495                 end = screen->insert_delta + screen->scrolling_region.end;
3496         } else {
3497                 start = screen->insert_delta;
3498                 end = start + terminal->row_count - 1;
3499         }
3500
3501         if (screen->cursor_current.row == end) {
3502                 if (screen->scrolling_restricted) {
3503                         if (start == screen->insert_delta) {
3504                                 /* Scroll this line into the scrollback
3505                                  * buffer by inserting a line at the next
3506                                  * line and scrolling the area up. */
3507                                 row = vte_new_row_data_sized(terminal, TRUE);
3508                                 screen->insert_delta++;
3509                                 screen->scroll_delta++;
3510                                 screen->cursor_current.row++;
3511                                 _vte_ring_insert_preserve(terminal->pvt->screen->row_data,
3512                                                           screen->cursor_current.row,
3513                                                           row);
3514                                 /* This may generate multiple redraws, so
3515                                  * disable fast scrolling for now. */
3516                                 terminal->pvt->scroll_lock_count++;
3517                                 gdk_window_freeze_updates(widget->window);
3518                                 /* Force the areas below the region to be
3519                                  * redrawn -- they've moved. */
3520                                 top = screen->cursor_current.row;
3521                                 bottom = screen->insert_delta +
3522                                          terminal->row_count - 1;
3523                                 vte_terminal_scroll_region(terminal, start,
3524                                                            end - start + 1, 1);
3525                                 /* Force scroll. */
3526                                 vte_terminal_ensure_cursor(terminal, FALSE);
3527                                 vte_terminal_adjust_adjustments(terminal, TRUE);
3528                                 /* Allow updates again. */
3529                                 gdk_window_thaw_updates(widget->window);
3530                                 terminal->pvt->scroll_lock_count--;
3531                         } else {
3532                                 /* If we're at the bottom of the scrolling
3533                                  * region, add a line at the top to scroll the
3534                                  * bottom off. */
3535                                 vte_remove_line_internal(terminal, start);
3536                                 vte_insert_line_internal(terminal, end);
3537                                 /* This may generate multiple redraws, so
3538                                  * disable fast scrolling for now. */
3539                                 terminal->pvt->scroll_lock_count++;
3540                                 gdk_window_freeze_updates(widget->window);
3541                                 /* Update the display. */
3542                                 vte_terminal_scroll_region(terminal, start,
3543                                                            end - start + 1, -1);
3544                                 vte_invalidate_cells(terminal,
3545                                                      0, terminal->column_count,
3546                                                      end - 2, 2);
3547                                 /* Allow updates again. */
3548                                 gdk_window_thaw_updates(widget->window);
3549                                 terminal->pvt->scroll_lock_count--;
3550                         }
3551                 } else {
3552                         /* Scroll up with history. */
3553                         screen->cursor_current.row++;
3554                         vte_terminal_update_insert_delta(terminal);
3555                 }
3556         } else {
3557                 /* Otherwise, just move the cursor down. */
3558                 screen->cursor_current.row++;
3559                 vte_terminal_ensure_cursor(terminal, TRUE);
3560         }
3561         /* Adjust the scrollbars if necessary. */
3562         vte_terminal_adjust_adjustments(terminal, FALSE);
3563         return FALSE;
3564 }
3565
3566 /* Cursor down, with scrolling. */
3567 static gboolean
3568 vte_sequence_handler_SF(VteTerminal *terminal,
3569                         const char *match,
3570                         GQuark match_quark,
3571                         GValueArray *params)
3572 {
3573         return vte_sequence_handler_multiple(terminal, match, match_quark,
3574                                              params, vte_sequence_handler_sf);
3575 }
3576
3577 /* Standout start. */
3578 static gboolean
3579 vte_sequence_handler_so(VteTerminal *terminal,
3580                         const char *match,
3581                         GQuark match_quark,
3582                         GValueArray *params)
3583 {
3584         char *bold, *underline, *standout, *reverse, *half, *blink;
3585
3586         /* Standout may be mapped to another attribute, so attempt to do
3587          * the Right Thing here. */
3588         standout = _vte_termcap_find_string(terminal->pvt->termcap,
3589                                      terminal->pvt->emulation,
3590                                     "so");
3591         g_assert(standout != NULL);
3592         blink = _vte_termcap_find_string(terminal->pvt->termcap,
3593                                          terminal->pvt->emulation,
3594                                          "mb");
3595         bold = _vte_termcap_find_string(terminal->pvt->termcap,
3596                                         terminal->pvt->emulation,
3597                                         "md");
3598         half = _vte_termcap_find_string(terminal->pvt->termcap,
3599                                         terminal->pvt->emulation,
3600                                         "mh");
3601         reverse = _vte_termcap_find_string(terminal->pvt->termcap,
3602                                            terminal->pvt->emulation,
3603                                            "mr");
3604         underline = _vte_termcap_find_string(terminal->pvt->termcap,
3605                                              terminal->pvt->emulation,
3606                                              "us");
3607
3608         /* If the standout sequence is the same as another sequence, do what
3609          * we'd do for that other sequence instead. */
3610         if (blink && (g_ascii_strcasecmp(standout, blink) == 0)) {
3611                 vte_sequence_handler_mb(terminal, match, match_quark, params);
3612         } else
3613         if (bold && (g_ascii_strcasecmp(standout, bold) == 0)) {
3614                 vte_sequence_handler_md(terminal, match, match_quark, params);
3615         } else
3616         if (half && (g_ascii_strcasecmp(standout, half) == 0)) {
3617                 vte_sequence_handler_mh(terminal, match, match_quark, params);
3618         } else
3619         if (reverse && (g_ascii_strcasecmp(standout, reverse) == 0)) {
3620                 vte_sequence_handler_mr(terminal, match, match_quark, params);
3621         } else
3622         if (underline && (g_ascii_strcasecmp(standout, underline) == 0)) {
3623                 vte_sequence_handler_us(terminal, match, match_quark, params);
3624         } else {
3625                 /* Otherwise just set standout mode. */
3626                 terminal->pvt->screen->defaults.standout = 1;
3627         }
3628
3629         if (blink) {
3630                 g_free(blink);
3631         }
3632         if (bold) {
3633                 g_free(bold);
3634         }
3635         if (half) {
3636                 g_free(half);
3637         }
3638         if (reverse) {
3639                 g_free(reverse);
3640         }
3641         if (underline) {
3642                 g_free(underline);
3643         }
3644         g_free(standout);
3645         return FALSE;
3646 }
3647
3648 /* Cursor up, scrolling if need be. */
3649 static gboolean
3650 vte_sequence_handler_sr(VteTerminal *terminal,
3651                         const char *match,
3652                         GQuark match_quark,
3653                         GValueArray *params)
3654 {
3655         GtkWidget *widget;
3656         long start, end;
3657         VteScreen *screen;
3658
3659         widget = GTK_WIDGET(terminal);
3660         screen = terminal->pvt->screen;
3661
3662         if (screen->scrolling_restricted) {
3663                 start = screen->scrolling_region.start + screen->insert_delta;
3664                 end = screen->scrolling_region.end + screen->insert_delta;
3665         } else {
3666                 start = terminal->pvt->screen->insert_delta;
3667                 end = start + terminal->row_count - 1;
3668         }
3669
3670         if (screen->cursor_current.row == start) {
3671                 /* If we're at the top of the scrolling region, add a
3672                  * line at the top to scroll the bottom off. */
3673                 vte_remove_line_internal(terminal, end);
3674                 vte_insert_line_internal(terminal, start);
3675                 /* Update the display. */
3676                 vte_terminal_scroll_region(terminal, start, end - start + 1, 1);
3677                 vte_invalidate_cells(terminal,
3678                                      0, terminal->column_count,
3679                                      start, 2);
3680         } else {
3681                 /* Otherwise, just move the cursor up. */
3682                 screen->cursor_current.row--;
3683         }
3684         /* Adjust the scrollbars if necessary. */
3685         vte_terminal_adjust_adjustments(terminal, FALSE);
3686         /* We modified the display, so make a note of it. */
3687         terminal->pvt->text_modified_flag = TRUE;
3688         return FALSE;
3689 }
3690
3691 /* Cursor up, with scrolling. */
3692 static gboolean
3693 vte_sequence_handler_SR(VteTerminal *terminal,
3694                         const char *match,
3695                         GQuark match_quark,
3696                         GValueArray *params)
3697 {
3698         return vte_sequence_handler_multiple(terminal, match, match_quark,
3699                                              params, vte_sequence_handler_sr);
3700 }
3701
3702 /* Set tab stop in the current column. */
3703 static gboolean
3704 vte_sequence_handler_st(VteTerminal *terminal,
3705                         const char *match,
3706                         GQuark match_quark,
3707                         GValueArray *params)
3708 {
3709         if (terminal->pvt->tabstops == NULL) {
3710                 terminal->pvt->tabstops = g_hash_table_new(g_direct_hash,
3711                                                            g_direct_equal);
3712         }
3713         vte_terminal_set_tabstop(terminal,
3714                                  terminal->pvt->screen->cursor_current.col);
3715         return FALSE;
3716 }
3717
3718 /* Tab. */
3719 static gboolean
3720 vte_sequence_handler_ta(VteTerminal *terminal,
3721                         const char *match,
3722                         GQuark match_quark,
3723                         GValueArray *params)
3724 {
3725         long newcol;
3726
3727         /* Calculate which column is the next tab stop. */
3728         newcol = terminal->pvt->screen->cursor_current.col;
3729
3730         if (terminal->pvt->tabstops != NULL) {
3731                 /* Find the next tabstop. */
3732                 for (newcol++; newcol < VTE_TAB_MAX; newcol++) {
3733                         if (vte_terminal_get_tabstop(terminal, newcol)) {
3734                                 break;
3735                         }
3736                 }
3737         }
3738
3739         /* If we have no tab stops or went past the end of the line, stop
3740          * at the right-most column. */
3741         if (newcol >= terminal->column_count) {
3742                 newcol = terminal->column_count - 1;
3743         }
3744
3745         terminal->pvt->screen->cursor_current.col = newcol;
3746         return FALSE;
3747 }
3748
3749 /* Clear tabs selectively. */
3750 static gboolean
3751 vte_sequence_handler_tab_clear(VteTerminal *terminal,
3752                                const char *match,
3753                                GQuark match_quark,
3754                                GValueArray *params)
3755 {
3756         GValue *value;
3757         long param = 0;
3758
3759         if ((params != NULL) && (params->n_values > 0)) {
3760                 value = g_value_array_get_nth(params, 0);
3761                 if (G_VALUE_HOLDS_LONG(value)) {
3762                         param = g_value_get_long(value);
3763                 }
3764         }
3765         if (param == 0) {
3766                 vte_terminal_clear_tabstop(terminal,
3767                                            terminal->pvt->screen->cursor_current.col);
3768         } else
3769         if (param == 3) {
3770                 if (terminal->pvt->tabstops != NULL) {
3771                         g_hash_table_destroy(terminal->pvt->tabstops);
3772                         terminal->pvt->tabstops = NULL;
3773                 }
3774         }
3775         return FALSE;
3776 }
3777
3778 /* Move to status line. */
3779 static gboolean
3780 vte_sequence_handler_ts(VteTerminal *terminal,
3781                         const char *match,
3782                         GQuark match_quark,
3783                         GValueArray *params)
3784 {
3785         terminal->pvt->screen->status_line = TRUE;
3786         g_string_truncate(terminal->pvt->screen->status_line_contents, 0);
3787         vte_terminal_emit_status_line_changed(terminal);
3788         return FALSE;
3789 }
3790
3791 /* Underline this character and move right. */
3792 static gboolean
3793 vte_sequence_handler_uc(VteTerminal *terminal,
3794                         const char *match,
3795                         GQuark match_quark,
3796                         GValueArray *params)
3797 {
3798         struct vte_charcell *cell;
3799         int column;
3800         VteScreen *screen;
3801
3802         screen = terminal->pvt->screen;
3803         column = screen->cursor_current.col;
3804         cell = vte_terminal_find_charcell(terminal,
3805                                           column,
3806                                           screen->cursor_current.row);
3807         while ((cell != NULL) && (cell->fragment) && (column > 0)) {
3808                 column--;
3809                 cell = vte_terminal_find_charcell(terminal,
3810                                                   column,
3811                                                   screen->cursor_current.row);
3812         }
3813         if (cell != NULL) {
3814                 /* Set this character to be underlined. */
3815                 cell->underline = 1;
3816                 /* Cause the character to be repainted. */
3817                 vte_invalidate_cells(terminal,
3818                                      column, cell->columns,
3819                                      screen->cursor_current.row, 1);
3820                 /* Move the cursor right. */
3821                 vte_sequence_handler_nd(terminal, match, match_quark, params);
3822         }
3823
3824         /* We've modified the display without changing the text.  Make a note
3825          * of it. */
3826         terminal->pvt->text_modified_flag = TRUE;
3827         return FALSE;
3828 }
3829
3830 /* Underline end. */
3831 static gboolean
3832 vte_sequence_handler_ue(VteTerminal *terminal,
3833                         const char *match,
3834                         GQuark match_quark,
3835                         GValueArray *params)
3836 {
3837         terminal->pvt->screen->defaults.underline = 0;
3838         return FALSE;
3839 }
3840
3841 /* Cursor up, no scrolling. */
3842 static gboolean
3843 vte_sequence_handler_up(VteTerminal *terminal,
3844                         const char *match,
3845                         GQuark match_quark,
3846                         GValueArray *params)
3847 {
3848         VteScreen *screen;
3849         long start, end;
3850
3851         screen = terminal->pvt->screen;
3852
3853         if (screen->scrolling_restricted) {
3854                 start = screen->insert_delta + screen->scrolling_region.start;
3855                 end = screen->insert_delta + screen->scrolling_region.end;
3856         } else {
3857                 start = screen->insert_delta;
3858                 end = start + terminal->row_count - 1;
3859         }
3860
3861         screen->cursor_current.row = MAX(screen->cursor_current.row - 1, start);
3862         return FALSE;
3863 }
3864
3865 /* Cursor up N lines, no scrolling. */
3866 static gboolean
3867 vte_sequence_handler_UP(VteTerminal *terminal,
3868                         const char *match,
3869                         GQuark match_quark,
3870                         GValueArray *params)
3871 {
3872         return vte_sequence_handler_multiple(terminal, match, match_quark,
3873                                              params, vte_sequence_handler_up);
3874 }
3875
3876 /* Underline start. */
3877 static gboolean
3878 vte_sequence_handler_us(VteTerminal *terminal,
3879                         const char *match,
3880                         GQuark match_quark,
3881                         GValueArray *params)
3882 {
3883         terminal->pvt->screen->defaults.underline = 1;
3884         return FALSE;
3885 }
3886
3887 /* Visible bell. */
3888 static gboolean
3889 vte_sequence_handler_vb(VteTerminal *terminal,
3890                         const char *match,
3891                         GQuark match_quark,
3892                         GValueArray *params)
3893 {
3894         GtkWidget *widget;
3895         gint width, height, state;
3896
3897         widget = GTK_WIDGET(terminal);
3898         if (GTK_WIDGET_REALIZED(widget)) {
3899                 gdk_drawable_get_size(widget->window, &width, &height);
3900                 state = GTK_WIDGET_STATE(widget);
3901                 /* Fill the screen with the default foreground color, and then
3902                  * repaint everything, to provide visual bell. */
3903                 gdk_draw_rectangle(widget->window,
3904                                    widget->style->fg_gc[state],
3905                                    TRUE,
3906                                    0, 0,
3907                                    width, height);
3908                 gdk_window_process_updates(widget->window, TRUE);
3909                 /* Force the repaint. */
3910                 vte_invalidate_all(terminal);
3911                 gdk_window_process_updates(widget->window, TRUE);
3912         }
3913         return FALSE;
3914 }
3915
3916 /* Cursor visible. */
3917 static gboolean
3918 vte_sequence_handler_ve(VteTerminal *terminal,
3919                         const char *match,
3920                         GQuark match_quark,
3921                         GValueArray *params)
3922 {
3923         terminal->pvt->cursor_visible = TRUE;
3924         return FALSE;
3925 }
3926
3927 /* Vertical tab. */
3928 static gboolean
3929 vte_sequence_handler_vertical_tab(VteTerminal *terminal,
3930                                   const char *match,
3931                                   GQuark match_quark,
3932                                   GValueArray *params)
3933 {
3934         return vte_sequence_handler_index(terminal, match, match_quark, params);
3935 }
3936
3937 /* Cursor invisible. */
3938 static gboolean
3939 vte_sequence_handler_vi(VteTerminal *terminal,
3940                         const char *match,
3941                         GQuark match_quark,
3942                         GValueArray *params)
3943 {
3944         terminal->pvt->cursor_visible = FALSE;
3945         return FALSE;
3946 }
3947
3948 /* Cursor standout. */
3949 static gboolean
3950 vte_sequence_handler_vs(VteTerminal *terminal,
3951                         const char *match,
3952                         GQuark match_quark,
3953                         GValueArray *params)
3954 {
3955         terminal->pvt->cursor_visible = TRUE; /* FIXME: should be *more*
3956                                                  visible. */
3957         return FALSE;
3958 }
3959
3960 /* Handle ANSI color setting and related stuffs (SGR). */
3961 static gboolean
3962 vte_sequence_handler_character_attributes(VteTerminal *terminal,
3963                                           const char *match,
3964                                           GQuark match_quark,
3965                                           GValueArray *params)
3966 {
3967         unsigned int i;
3968         GValue *value;
3969         long param;
3970         /* The default parameter is zero. */
3971         param = 0;
3972         /* Step through each numeric parameter. */
3973         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
3974                 /* If this parameter isn't a number, skip it. */
3975                 value = g_value_array_get_nth(params, i);
3976                 if (!G_VALUE_HOLDS_LONG(value)) {
3977                         continue;
3978                 }
3979                 param = g_value_get_long(value);
3980                 switch (param) {
3981                 case 0:
3982                         vte_terminal_set_default_attributes(terminal);
3983                         break;
3984                 case 1:
3985                         terminal->pvt->screen->defaults.bold = 1;
3986                         terminal->pvt->screen->defaults.half = 0;
3987                         break;
3988                 case 2:
3989                         terminal->pvt->screen->defaults.half = 1;
3990                         terminal->pvt->screen->defaults.bold = 0;
3991                         break;
3992                 case 4:
3993                         terminal->pvt->screen->defaults.underline = 1;
3994                         break;
3995                 case 5:
3996                         terminal->pvt->screen->defaults.blink = 1;
3997                         break;
3998                 case 7:
3999                         terminal->pvt->screen->defaults.reverse = 1;
4000                         break;
4001                 case 8:
4002                         terminal->pvt->screen->defaults.invisible = 1;
4003                         break;
4004                 case 9:
4005                         terminal->pvt->screen->defaults.strikethrough = 1;
4006                         break;
4007                 case 21: /* Error in old versions of linux console. */
4008                 case 22: /* ECMA 48. */
4009                         terminal->pvt->screen->defaults.bold = 0;
4010                         terminal->pvt->screen->defaults.half = 0;
4011                         break;
4012                 case 24:
4013                         terminal->pvt->screen->defaults.underline = 0;
4014                         break;
4015                 case 25:
4016                         terminal->pvt->screen->defaults.blink = 0;
4017                         break;
4018                 case 27:
4019                         terminal->pvt->screen->defaults.reverse = 0;
4020                         break;
4021                 case 28:
4022                         terminal->pvt->screen->defaults.invisible = 0;
4023                         break;
4024                 case 29:
4025                         terminal->pvt->screen->defaults.strikethrough = 0;
4026                         break;
4027                 case 30:
4028                 case 31:
4029                 case 32:
4030                 case 33:
4031                 case 34:
4032                 case 35:
4033                 case 36:
4034                 case 37:
4035                         terminal->pvt->screen->defaults.fore = param - 30;
4036                         break;
4037                 case 38:
4038                         /* default foreground, underscore */
4039                         terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
4040                         terminal->pvt->screen->defaults.underline = 1;
4041                         break;
4042                 case 39:
4043                         /* default foreground, no underscore */
4044                         terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
4045                         /* By ECMA 48, this underline off has no business
4046                            being here, but the Linux console specifies it. */
4047                         terminal->pvt->screen->defaults.underline = 0;
4048                         break;
4049                 case 40:
4050                 case 41:
4051                 case 42:
4052                 case 43:
4053                 case 44:
4054                 case 45:
4055                 case 46:
4056                 case 47:
4057                         terminal->pvt->screen->defaults.back = param - 40;
4058                         break;
4059                 case 49:
4060                         /* default background */
4061                         terminal->pvt->screen->defaults.back = VTE_DEF_BG;
4062                         break;
4063                 case 90:
4064                 case 91:
4065                 case 92:
4066                 case 93:
4067                 case 94:
4068                 case 95:
4069                 case 96:
4070                 case 97:
4071                         terminal->pvt->screen->defaults.fore = param - 90 + VTE_COLOR_BRIGHT_OFFSET;
4072                         break;
4073                 case 100:
4074                 case 101:
4075                 case 102:
4076                 case 103:
4077                 case 104:
4078                 case 105:
4079                 case 106:
4080                 case 107:
4081                         terminal->pvt->screen->defaults.back = param - 100 + VTE_COLOR_BRIGHT_OFFSET;
4082                         break;