Filter key release events through input methods. (Patch from otaylor.)
[vte.git] / src / vte.c
1 /*
2  * Copyright (C) 2001,2002 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 #include "../config.h"
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/param.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <iconv.h>
29 #include <langinfo.h>
30 #include <math.h>
31 #include <pwd.h>
32 #include <regex.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <termios.h>
38 #include <unistd.h>
39 #include <wchar.h>
40 #include <wctype.h>
41 #include <glib.h>
42 #include <glib-object.h>
43 #include <gdk/gdk.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <gdk/gdkx.h>
46 #include <gtk/gtk.h>
47 #include <pango/pango.h>
48 #include <pango/pangox.h>
49 #include "caps.h"
50 #include "debug.h"
51 #include "marshal.h"
52 #include "pty.h"
53 #include "termcap.h"
54 #include "ring.h"
55 #include "trie.h"
56 #include "vte.h"
57 #include "vteaccess.h"
58 #include <X11/Xlib.h>
59 #ifdef HAVE_XFT
60 #include <X11/extensions/Xrender.h>
61 #include <X11/Xft/Xft.h>
62 #endif
63
64 #define VTE_TAB_WIDTH   8
65 #define VTE_LINE_WIDTH  1
66 #define VTE_DEF_FG      16
67 #define VTE_DEF_BG      17
68 #define VTE_SATURATION_MAX 10000
69 #define VTE_SCROLLBACK_MIN 100
70 #define VTE_DEFAULT_EMULATION "xterm"
71 #define VTE_DEFAULT_CURSOR GDK_XTERM
72 #define VTE_MOUSING_CURSOR GDK_LEFT_PTR
73 #define VTE_TAB_MAX     999
74
75 /* The structure we use to hold characters we're supposed to display -- this
76  * includes any supported visible attributes. */
77 struct vte_charcell {
78         wchar_t c;              /* The wide character. */
79         guint16 columns: 2;     /* Number of visible columns (as determined
80                                    by wcwidth(c)). */
81         guint16 fore: 5;        /* Indices in the color palette for the */
82         guint16 back: 5;        /* foreground and background of the cell. */
83         guint16 standout: 1;    /* Single-bit attributes. */
84         guint16 underline: 1;
85         guint16 reverse: 1;
86         guint16 blink: 1;
87         guint16 half: 1;
88         guint16 bold: 1;
89         guint16 invisible: 1;
90         guint16 protect: 1;
91         guint16 alternate: 1;
92 };
93
94 /* A match regex, with a tag. */
95 struct vte_match_regex {
96         regex_t reg;
97         gint tag;
98 };
99
100 /* The terminal's keypad state.  A terminal can either be using the normal
101  * keypad, or the "application" keypad.  Arrow key sequences, for example,
102  * are really only defined for "application" mode. */
103 typedef enum {
104         VTE_KEYPAD_NORMAL,
105         VTE_KEYPAD_APPLICATION,
106 } VteKeypad;
107
108 typedef struct _VteScreen VteScreen;
109
110 typedef struct _VteWordCharRange {
111         wchar_t start, end;
112 } VteWordCharRange;
113
114 /* Terminal private data. */
115 struct _VteTerminalPrivate {
116         /* Emulation setup data. */
117         struct vte_termcap *termcap;    /* termcap storage */
118         struct vte_trie *trie;          /* control sequence trie */
119         const char *termcap_path;       /* path to termcap file */
120         const char *terminal;           /* terminal type to emulate */
121         GTree *sequences;               /* sequence handlers, keyed by GQuark
122                                            based on the sequence name */
123         struct {                        /* boolean termcap flags */
124                 gboolean am;
125                 gboolean bw;
126                 gboolean ul;
127         } flags;
128
129         /* PTY handling data. */
130         const char *shell;              /* shell we started */
131         int pty_master;                 /* pty master descriptor */
132         GIOChannel *pty_input;          /* master input watch */
133         GIOChannel *pty_output;         /* master output watch */
134         pid_t pty_pid;                  /* pid of child using pty slave */
135         const char *encoding;           /* the pty's encoding */
136         const char *gxencoding[4];      /* alternate encodings */
137
138         /* Input data queues. */
139         iconv_t incoming_conv;          /* narrow/wide conversion state */
140         unsigned char *incoming;        /* pending output characters */
141         size_t n_incoming;
142         gboolean processing;
143         guint processing_tag;
144
145         /* Output data queue. */
146         unsigned char *outgoing;        /* pending input characters */
147         size_t n_outgoing;
148         iconv_t outgoing_conv_wide;
149         iconv_t outgoing_conv_utf8;
150
151         /* Data used when rendering the text. */
152         struct {
153                 guint16 red, green, blue;
154                 unsigned long pixel;
155 #ifdef HAVE_XFT
156                 XRenderColor rcolor;
157                 XftColor ftcolor;
158 #endif
159         } palette[18];
160         XFontSet fontset;
161 #ifdef HAVE_XFT
162         XftFont *ftfont;
163 #endif
164         gboolean use_xft;
165         PangoFontDescription *fontdesc;
166         PangoLayout *layout;
167         gboolean use_pango;
168
169         /* Emulation state. */
170         int keypad;
171
172         /* Screen data.  We support the normal screen, and an alternate
173          * screen, which seems to be a DEC-specific feature. */
174         struct _VteScreen {
175                 VteRing *row_data;      /* row data, arranged as a GArray of
176                                            vte_charcell structures */
177                 struct {
178                         long row, col;
179                 } cursor_current, cursor_saved;
180                                         /* the current and saved positions of
181                                            the [insertion] cursor */
182                 gboolean cursor_visible;
183                 gboolean reverse_mode;  /* reverse mode */
184                 gboolean insert_mode;   /* insert mode */
185                 struct {
186                         int start, end;
187                 } scrolling_region;     /* the region we scroll in */
188                 gboolean scrolling_restricted;
189                 long scroll_delta;      /* scroll offset */
190                 long insert_delta;      /* insertion offset */
191                 struct vte_charcell defaults;   /* default characteristics
192                                                    for insertion of any new
193                                                    characters */
194         } normal_screen, alternate_screen, *screen;
195
196         /* Saved settings. */
197         GHashTable *dec_saved;
198
199         /* Selection information. */
200         gboolean has_selection;
201         char *selection;
202         enum {
203                 selection_type_char,
204                 selection_type_word,
205                 selection_type_line,
206         } selection_type;
207         struct {
208                 double x, y;
209         } selection_origin, selection_last;
210         struct {
211                 long x, y;
212         } selection_start, selection_end;
213
214         /* Miscellaneous options. */
215         GArray *word_chars;
216         VteTerminalEraseBinding backspace_binding, delete_binding;
217         gboolean alt_sends_escape;
218         gboolean audible_bell;
219         gboolean xterm_font_tweak;
220         GHashTable *tabstops;
221
222         /* Scrolling options. */
223         gboolean scroll_on_output;
224         gboolean scroll_on_keystroke;
225         long scrollback_lines;
226
227         /* Cursor blinking. */
228         gboolean cursor_blinks;
229         gint cursor_blink_tag;
230
231         /* Background images/"transparency". */
232         gboolean bg_transparent;
233         gboolean bg_transparent_update_pending;
234         guint bg_transparent_update_tag;
235         GdkAtom bg_transparent_atom;
236         GdkWindow *bg_transparent_window;
237         GdkPixbuf *bg_transparent_image;
238         GdkPixbuf *bg_image;
239         GtkWidget *bg_toplevel;
240         long bg_saturation;     /* out of VTE_SATURATION_MAX */
241
242         /* Input method support. */
243         GtkIMContext *im_context;
244         char *im_preedit;
245         int im_preedit_cursor;
246
247         /* Input device options. */
248         guint last_keypress_time;
249         gboolean mouse_send_xy_on_click;
250         gboolean mouse_send_xy_on_button;
251         gboolean mouse_hilite_tracking;
252         gboolean mouse_cell_motion_tracking;
253         gboolean mouse_all_motion_tracking;
254         GdkCursor *mouse_default_cursor,
255                   *mouse_mousing_cursor,
256                   *mouse_inviso_cursor;
257         guint mouse_last_button;
258         gdouble mouse_last_x, mouse_last_y;
259         gboolean mouse_autohide;
260
261         /* State variables for handling match checks. */
262         char *match_contents;
263         GArray *match_attributes;
264         GArray *match_regexes;
265         struct {
266                 long row, column;
267         } match_start, match_end;
268 };
269
270 /* A function which can handle a terminal control sequence. */
271 typedef void (*VteTerminalSequenceHandler)(VteTerminal *terminal,
272                                            const char *match,
273                                            GQuark match_quark,
274                                            GValueArray *params);
275 static void vte_terminal_ensure_cursor(VteTerminal *terminal);
276 static void vte_terminal_insert_char(GtkWidget *widget, wchar_t c,
277                                      gboolean force_insert);
278 static void vte_sequence_handler_clear_screen(VteTerminal *terminal,
279                                               const char *match,
280                                               GQuark match_quark,
281                                               GValueArray *params);
282 static void vte_sequence_handler_do(VteTerminal *terminal,
283                                     const char *match,
284                                     GQuark match_quark,
285                                     GValueArray *params);
286 static void vte_sequence_handler_ho(VteTerminal *terminal,
287                                     const char *match,
288                                     GQuark match_quark,
289                                     GValueArray *params);
290 static void vte_sequence_handler_nd(VteTerminal *terminal,
291                                     const char *match,
292                                     GQuark match_quark,
293                                     GValueArray *params);
294 static void vte_sequence_handler_ue(VteTerminal *terminal,
295                                     const char *match,
296                                     GQuark match_quark,
297                                     GValueArray *params);
298 static void vte_sequence_handler_up(VteTerminal *terminal,
299                                     const char *match,
300                                     GQuark match_quark,
301                                     GValueArray *params);
302 static void vte_sequence_handler_us(VteTerminal *terminal,
303                                     const char *match,
304                                     GQuark match_quark,
305                                     GValueArray *params);
306 static void vte_sequence_handler_vb(VteTerminal *terminal,
307                                     const char *match,
308                                     GQuark match_quark,
309                                     GValueArray *params);
310 static gboolean vte_terminal_io_read(GIOChannel *channel,
311                                      GdkInputCondition condition,
312                                      gpointer data);
313 static gboolean vte_terminal_io_write(GIOChannel *channel,
314                                       GdkInputCondition condition,
315                                       gpointer data);
316 static GdkFilterReturn vte_terminal_filter_property_changes(GdkXEvent *xevent,
317                                                             GdkEvent *event,
318                                                             gpointer data);
319 static void vte_terminal_match_hilite_clear(VteTerminal *terminal);
320 static void vte_terminal_queue_background_update(VteTerminal *terminal);
321
322 /* Free a no-longer-used row data array. */
323 static void
324 vte_free_row_data(gpointer freeing, gpointer data)
325 {
326         if (freeing) {
327                 g_array_free((GArray*)freeing, FALSE);
328         }
329 }
330
331 /* Allocate a new line. */
332 static GArray *
333 vte_new_row_data(void)
334 {
335         return g_array_new(FALSE, FALSE, sizeof(struct vte_charcell));
336 }
337
338 /* Reset defaults for character insertion. */
339 static void
340 vte_terminal_set_default_attributes(VteTerminal *terminal)
341 {
342         g_return_if_fail(VTE_IS_TERMINAL(terminal));
343         terminal->pvt->screen->defaults.c = 0;
344         terminal->pvt->screen->defaults.columns = 1;
345         terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
346         terminal->pvt->screen->defaults.back = VTE_DEF_BG;
347         terminal->pvt->screen->defaults.reverse = 0;
348         terminal->pvt->screen->defaults.bold = 0;
349         terminal->pvt->screen->defaults.invisible = 0;
350         terminal->pvt->screen->defaults.protect = 0;
351         terminal->pvt->screen->defaults.standout = 0;
352         terminal->pvt->screen->defaults.underline = 0;
353         terminal->pvt->screen->defaults.half = 0;
354         terminal->pvt->screen->defaults.blink = 0;
355         /* Alternate charset isn't an attribute, though we treat it as one.
356          * terminal->pvt->screen->defaults.alternate = 0; */
357 }
358
359 /* Cause certain cells to be updated. */
360 static void
361 vte_invalidate_cells(VteTerminal *terminal,
362                      glong column_start, gint column_count,
363                      glong row_start, gint row_count)
364 {
365         GdkRectangle rect;
366         GtkWidget *widget;
367
368         g_return_if_fail(VTE_IS_TERMINAL(terminal));
369         widget = GTK_WIDGET(terminal);
370         if (!GTK_WIDGET_REALIZED(widget)) {
371                 return;
372         }
373
374         /* Subtract the scrolling offset from the row start so that the
375          * resulting rectangle is relative to the visible portion of the
376          * buffer. */
377         row_start -= terminal->pvt->screen->scroll_delta;
378
379         /* Clamp the start values to reasonable numbers. */
380         column_start = (column_start > 0) ? column_start : 0;
381         row_start = (row_start > 0) ? row_start : 0;
382
383         /* Convert the column and row start and end to pixel values
384          * by multiplying by the size of a character cell. */
385         rect.x = column_start * terminal->char_width;
386         rect.width = column_count * terminal->char_width;
387         rect.y = row_start * terminal->char_height;
388         rect.height = row_count * terminal->char_height;
389
390         /* Invalidate the rectangle. */
391         gdk_window_invalidate_rect(widget->window, &rect, TRUE);
392 }
393
394 /* Redraw the entire window. */
395 static void
396 vte_invalidate_all(VteTerminal *terminal)
397 {
398         g_return_if_fail(VTE_IS_TERMINAL(terminal));
399         if (!GTK_IS_WIDGET(terminal) ||
400             !GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
401                 return;
402         }
403         vte_invalidate_cells(terminal,
404                              0, terminal->column_count,
405                              terminal->pvt->screen->scroll_delta,
406                              terminal->row_count);
407 }
408
409 /* Find the character in the given "virtual" position. */
410 static struct vte_charcell *
411 vte_terminal_find_charcell(VteTerminal *terminal, long col, long row)
412 {
413         GArray *rowdata;
414         struct vte_charcell *ret = NULL;
415         VteScreen *screen;
416         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
417         screen = terminal->pvt->screen;
418         if (vte_ring_contains(screen->row_data, row)) {
419                 rowdata = vte_ring_index(screen->row_data, GArray*, row);
420                 if (rowdata->len > col) {
421                         ret = &g_array_index(rowdata, struct vte_charcell, col);
422                 }
423         }
424         return ret;
425 }
426
427 /* Cause the cursor to be redrawn. */
428 static void
429 vte_invalidate_cursor_once(gpointer data)
430 {
431         VteTerminal *terminal;
432         VteScreen *screen;
433         struct vte_charcell *cell;
434         size_t preedit_length;
435         int columns;
436
437         if (!VTE_IS_TERMINAL(data)) {
438                 return;
439         }
440         terminal = VTE_TERMINAL(data);
441
442         if (GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
443                 if (terminal->pvt->im_preedit != NULL) {
444                         preedit_length = strlen(terminal->pvt->im_preedit);
445                 } else {
446                         preedit_length = 0;
447                 }
448
449                 screen = terminal->pvt->screen;
450                 columns = 1;
451                 cell = vte_terminal_find_charcell(terminal,
452                                                   screen->cursor_current.col,
453                                                   screen->cursor_current.row);
454                 if (cell != NULL) {
455                         columns = cell->columns;
456                 }
457
458                 vte_invalidate_cells(terminal,
459                                      screen->cursor_current.col,
460                                      columns + preedit_length,
461                                      screen->cursor_current.row,
462                                      1);
463 #ifdef VTE_DEBUG
464                 if (vte_debug_on(VTE_DEBUG_UPDATES)) {
465                         fprintf(stderr, "Invalidating cursor at (%ld,%ld-%ld)."
466                                 "\n", screen->cursor_current.row,
467                                 screen->cursor_current.col,
468                                 screen->cursor_current.col +
469                                 columns + preedit_length);
470                 }
471 #endif
472         }
473 }
474
475 /* Invalidate the cursor repeatedly. */
476 static gboolean
477 vte_invalidate_cursor_periodic(gpointer data)
478 {
479         VteTerminal *terminal;
480         GtkSettings *settings;
481         gint blink_cycle = 1000;
482
483         g_return_val_if_fail(VTE_IS_TERMINAL(data), FALSE);
484         if (!GTK_WIDGET_REALIZED(GTK_WIDGET(data))) {
485                 return TRUE;
486         }
487
488         terminal = VTE_TERMINAL(data);
489         vte_invalidate_cursor_once(data);
490
491         settings = gtk_widget_get_settings(GTK_WIDGET(data));
492         if (G_IS_OBJECT(settings)) {
493                 g_object_get(G_OBJECT(settings), "gtk-cursor-blink-time",
494                              &blink_cycle, NULL);
495         }
496
497         terminal->pvt->cursor_blink_tag = g_timeout_add(blink_cycle / 2,
498                                                         vte_invalidate_cursor_periodic,
499                                                         terminal);
500
501         return FALSE;
502 }
503
504 /* Emit a "selection_changed" signal. */
505 static void
506 vte_terminal_emit_selection_changed(VteTerminal *terminal)
507 {
508         g_signal_emit_by_name(terminal, "selection-changed");
509 }
510
511 /* Emit a "contents_changed" signal. */
512 static void
513 vte_terminal_emit_contents_changed(VteTerminal *terminal)
514 {
515         g_signal_emit_by_name(terminal, "contents-changed");
516 }
517
518 /* Emit a "cursor_moved" signal. */
519 static void
520 vte_terminal_emit_cursor_moved(VteTerminal *terminal)
521 {
522         g_signal_emit_by_name(terminal, "cursor-moved");
523 }
524
525 /* Emit an "icon-title-changed" signal. */
526 static void
527 vte_terminal_emit_icon_title_changed(VteTerminal *terminal)
528 {
529         g_signal_emit_by_name(terminal, "icon-title-changed");
530 }
531
532 /* Emit a "window-title-changed" signal. */
533 static void
534 vte_terminal_emit_window_title_changed(VteTerminal *terminal)
535 {
536         g_signal_emit_by_name(terminal, "window-title-changed");
537 }
538
539 /* Emit a "deiconify-window" signal. */
540 static void
541 vte_terminal_emit_deiconify_window(VteTerminal *terminal)
542 {
543         g_signal_emit_by_name(terminal, "deiconify-window");
544 }
545
546 /* Emit a "iconify-window" signal. */
547 static void
548 vte_terminal_emit_iconify_window(VteTerminal *terminal)
549 {
550         g_signal_emit_by_name(terminal, "iconify-window");
551 }
552
553 /* Emit a "raise-window" signal. */
554 static void
555 vte_terminal_emit_raise_window(VteTerminal *terminal)
556 {
557         g_signal_emit_by_name(terminal, "raise-window");
558 }
559
560 /* Emit a "lower-window" signal. */
561 static void
562 vte_terminal_emit_lower_window(VteTerminal *terminal)
563 {
564         g_signal_emit_by_name(terminal, "lower-window");
565 }
566
567 /* Emit a "maximize-window" signal. */
568 static void
569 vte_terminal_emit_maximize_window(VteTerminal *terminal)
570 {
571         g_signal_emit_by_name(terminal, "maximize-window");
572 }
573
574 /* Emit a "refresh-window" signal. */
575 static void
576 vte_terminal_emit_refresh_window(VteTerminal *terminal)
577 {
578         g_signal_emit_by_name(terminal, "refresh-window");
579 }
580
581 /* Emit a "restore-window" signal. */
582 static void
583 vte_terminal_emit_restore_window(VteTerminal *terminal)
584 {
585         g_signal_emit_by_name(terminal, "restore-window");
586 }
587
588 /* Emit a "eof" signal. */
589 static void
590 vte_terminal_emit_eof(VteTerminal *terminal)
591 {
592         g_signal_emit_by_name(terminal, "eof");
593 }
594
595 /* Emit a "char-size-changed" signal. */
596 static void
597 vte_terminal_emit_char_size_changed(VteTerminal *terminal,
598                                     guint width, guint height)
599 {
600         g_signal_emit_by_name(terminal, "char-size-changed",
601                               width, height);
602 }
603
604 /* Emit a "resize-window" signal.  (Pixels.) */
605 static void
606 vte_terminal_emit_resize_window(VteTerminal *terminal,
607                                 guint width, guint height)
608 {
609         g_signal_emit_by_name(terminal, "resize-window", width, height);
610 }
611
612 /* Emit a "move-window" signal.  (Pixels.) */
613 static void
614 vte_terminal_emit_move_window(VteTerminal *terminal, guint x, guint y)
615 {
616         g_signal_emit_by_name(terminal, "move-window", x, y);
617 }
618
619 /* Deselect anything which is selected and refresh the screen if needed. */
620 static void
621 vte_terminal_deselect_all(VteTerminal *terminal)
622 {
623         g_return_if_fail(VTE_IS_TERMINAL(terminal));
624         if (terminal->pvt->has_selection) {
625                 terminal->pvt->has_selection = FALSE;
626                 vte_terminal_emit_selection_changed (terminal);
627                 vte_invalidate_all(terminal);
628         }
629 }
630
631 /* Reset the set of tab stops to the default. */
632 static void
633 vte_terminal_set_tabstop(VteTerminal *terminal, int column)
634 {
635         g_return_if_fail(VTE_IS_TERMINAL(terminal));
636         if (terminal->pvt->tabstops != NULL) {
637                 /* Just set a non-NULL pointer for this column number. */
638                 g_hash_table_insert(terminal->pvt->tabstops,
639                                     GINT_TO_POINTER(2 * column + 1),
640                                     terminal);
641         }
642 }
643
644 /* Remove a tabstop. */
645 static void
646 vte_terminal_clear_tabstop(VteTerminal *terminal, int column)
647 {
648         g_return_if_fail(VTE_IS_TERMINAL(terminal));
649         if (terminal->pvt->tabstops != NULL) {
650                 /* Remove a tab stop from the hash table. */
651                 g_hash_table_remove(terminal->pvt->tabstops,
652                                     GINT_TO_POINTER(2 * column + 1));
653         }
654 }
655
656 /* Check if we have a tabstop at a given position. */
657 static gboolean
658 vte_terminal_get_tabstop(VteTerminal *terminal, int column)
659 {
660         gpointer hash;
661         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
662         if (terminal->pvt->tabstops != NULL) {
663                 hash = g_hash_table_lookup(terminal->pvt->tabstops,
664                                            GINT_TO_POINTER(2 * column + 1));
665                 return (hash != NULL);
666         } else {
667                 return FALSE;
668         }
669 }
670
671 /* Reset the set of tab stops to the default. */
672 static void
673 vte_terminal_set_default_tabstops(VteTerminal *terminal)
674 {
675         int i;
676         g_return_if_fail(VTE_IS_TERMINAL(terminal));
677         if (terminal->pvt->tabstops != NULL) {
678                 g_hash_table_destroy(terminal->pvt->tabstops);
679         }
680         terminal->pvt->tabstops = g_hash_table_new(g_direct_hash,
681                                                    g_direct_equal);
682         for (i = 0; i <= VTE_TAB_MAX; i += VTE_TAB_WIDTH) {
683                 vte_terminal_set_tabstop(terminal, i);
684         }
685 }
686
687 /* Clear the cache of the screen contents we keep. */
688 static void
689 vte_terminal_match_contents_clear(VteTerminal *terminal)
690 {
691         g_return_if_fail(VTE_IS_TERMINAL(terminal));
692         if (terminal->pvt->match_contents != NULL) {
693                 g_free(terminal->pvt->match_contents);
694                 terminal->pvt->match_contents = NULL;;
695         }
696         while (terminal->pvt->match_attributes != NULL) {
697                 g_array_free(terminal->pvt->match_attributes, FALSE);
698                 terminal->pvt->match_attributes = NULL;
699         }
700         vte_terminal_match_hilite_clear(terminal);
701 }
702
703 /* Refresh the cache of the screen contents we keep. */
704 static gboolean
705 always_selected(VteTerminal *terminal, long row, long column)
706 {
707         return TRUE;
708 }
709 static void
710 vte_terminal_match_contents_refresh(VteTerminal *terminal)
711 {
712         GArray *array;
713         g_return_if_fail(VTE_IS_TERMINAL(terminal));
714         vte_terminal_match_contents_clear(terminal);
715         array = g_array_new(FALSE, TRUE, sizeof(struct vte_char_attributes));
716         terminal->pvt->match_contents = vte_terminal_get_text(terminal,
717                                                               always_selected,
718                                                               array);
719         terminal->pvt->match_attributes = array;
720 }
721
722 /* Display string matching:  clear all matching expressions. */
723 void
724 vte_terminal_match_clear_all(VteTerminal *terminal)
725 {
726         struct vte_match_regex *regex;
727         g_return_if_fail(VTE_IS_TERMINAL(terminal));
728         while (terminal->pvt->match_regexes->len > 0) {
729                 regex = &g_array_index(terminal->pvt->match_regexes,
730                                        struct vte_match_regex,
731                                        terminal->pvt->match_regexes->len - 1);
732                 regfree(&regex->reg);
733                 memset(&regex->reg, 0, sizeof(regex->reg));
734                 regex->tag = 0;
735                 g_array_remove_index(terminal->pvt->match_regexes,
736                                      terminal->pvt->match_regexes->len - 1);
737         }
738         vte_terminal_match_hilite_clear(terminal);
739 }
740
741 /* Add a matching expression, returning the tag the widget assigns to that
742  * expression. */
743 int
744 vte_terminal_match_add(VteTerminal *terminal, const char *match)
745 {
746         struct vte_match_regex regex;
747         int ret;
748         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), -1);
749         memset(&regex, 0, sizeof(regex));
750         ret = regcomp(&regex.reg, match, REG_EXTENDED);
751         if (ret != 0) {
752                 g_warning("Error compiling regular expression \"%s\".", match);
753                 return -1;
754         }
755         regex.tag = terminal->pvt->match_regexes->len;
756         g_array_append_val(terminal->pvt->match_regexes, regex);
757         return regex.tag;
758 }
759
760 /* Check if a given cell on the screen contains part of a matched string.  If
761  * it does, return the string, and store the match tag in the optional tag
762  * argument. */
763 static char *
764 vte_terminal_match_check_int(VteTerminal *terminal, long column,
765                              long row, int *tag, int *start, int *end)
766 {
767         int i, j, ret, offset;
768         struct vte_match_regex *regex = NULL;
769         struct vte_char_attributes *attr = NULL;
770         size_t coffset;
771         regmatch_t matches[256];
772         if (tag != NULL) {
773                 *tag = -1;
774         }
775         if (start != NULL) {
776                 *start = 0;
777         }
778         if (end != NULL) {
779                 *end = 0;
780         }
781         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
782         if (terminal->pvt->match_contents == NULL) {
783                 vte_terminal_match_contents_refresh(terminal);
784         }
785         /* Map the pointer position to a portion of the string. */
786         for (offset = 0;
787              offset < terminal->pvt->match_attributes->len;
788              offset++) {
789                 attr = &g_array_index(terminal->pvt->match_attributes,
790                                       struct vte_char_attributes,
791                                       offset);
792                 if (attr != NULL) {
793                         if ((row == attr->row) && (column == attr->column)) {
794                                 break;
795                         }
796                 }
797         }
798         /* If the pointer isn't on a matchable character, bug out. */
799         if (offset >= terminal->pvt->match_attributes->len) {
800                 return NULL;
801         }
802         /* If the pointer is on a newline, bug out. */
803         if (g_ascii_isspace(terminal->pvt->match_contents[offset])) {
804                 return NULL;
805         }
806
807         /* Now iterate over each regex we need to match against. */
808         for (i = 0; i < terminal->pvt->match_regexes->len; i++) {
809                 regex = &g_array_index(terminal->pvt->match_regexes,
810                                        struct vte_match_regex,
811                                        i);
812                 if (regex != NULL) {
813                         /* We'll only match the first item in the buffer which
814                          * matches, so we'll have to skip each match until we
815                          * stop getting matches. */
816                         coffset = 0;
817                         ret = regexec(&regex->reg,
818                                       terminal->pvt->match_contents + coffset,
819                                       G_N_ELEMENTS(matches),
820                                       matches,
821                                       0);
822                         while (ret == 0) {
823                                 for (j = 0;
824                                      j < G_N_ELEMENTS(matches) &&
825                                      (matches[j].rm_so != -1);
826                                      j++) {
827                                         /* The offsets should be "sane". */
828                                         g_assert(matches[j].rm_so + coffset < terminal->pvt->match_attributes->len);
829                                         g_assert(matches[j].rm_eo + coffset < terminal->pvt->match_attributes->len);
830 #ifdef VTE_DEBUG
831                                         if (vte_debug_on(VTE_DEBUG_MISC)) {
832                                                 char *match;
833                                                 struct vte_char_attributes *sattr, *eattr;
834                                                 match = g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset,
835                                                                   matches[j].rm_eo - matches[j].rm_so);
836                                                 sattr = &g_array_index(terminal->pvt->match_attributes,
837                                                                        struct vte_char_attributes,
838                                                                        matches[j].rm_so + coffset);
839                                                 eattr = &g_array_index(terminal->pvt->match_attributes,
840                                                                        struct vte_char_attributes,
841                                                                        matches[j].rm_eo + coffset);
842                                                 fprintf(stderr, "Match %d `%s' from %d(%ld,%ld) to %d(%ld,%ld) (%d).\n",
843                                                         j, match,
844                                                         matches[j].rm_so + coffset,
845                                                         sattr->column,
846                                                         sattr->row,
847                                                         matches[j].rm_eo + coffset,
848                                                         eattr->column,
849                                                         eattr->row,
850                                                         offset);
851                                                 g_free(match);
852
853                                         }
854 #endif
855                                         /* If the pointer is in this substring,
856                                          * then we're done. */
857                                         if ((offset >= matches[j].rm_so + coffset) &&
858                                             (offset <= matches[j].rm_eo + coffset)) {
859                                                 if (tag != NULL) {
860                                                         *tag = regex->tag;
861                                                 }
862                                                 if (start != NULL) {
863                                                         *start = matches[j].rm_so + coffset;
864                                                 }
865                                                 if (end != NULL) {
866                                                         *end = matches[j].rm_eo + coffset;
867                                                 }
868                                                 return g_strndup(terminal->pvt->match_contents + matches[j].rm_so + coffset,
869                                                                  matches[j].rm_eo - matches[j].rm_so);
870                                         }
871                                 }
872                                 /* Skip past the beginning of this match to
873                                  * look for more. */
874                                 coffset += (matches[0].rm_so + 1);
875                                 ret = regexec(&regex->reg,
876                                               terminal->pvt->match_contents + coffset,
877                                               G_N_ELEMENTS(matches),
878                                               matches,
879                                               0);
880                         }
881                 }
882         }
883         return NULL;
884 }
885
886 char *
887 vte_terminal_match_check(VteTerminal *terminal, long column, long row, int *tag)
888 {
889         return vte_terminal_match_check_int(terminal, column, row, tag,
890                                             NULL, NULL);
891 }
892
893 /* Update the adjustment field of the widget.  This function should be called
894  * whenever we add rows to the history or switch screens. */
895 static void
896 vte_terminal_adjust_adjustments(VteTerminal *terminal)
897 {
898         gboolean changed;
899         long delta, next;
900         long page_size;
901         long rows;
902
903         g_return_if_fail(VTE_IS_TERMINAL(terminal));
904         g_return_if_fail(terminal->pvt->screen != NULL);
905         g_return_if_fail(terminal->pvt->screen->row_data != NULL);
906
907         /* Adjust the vertical, uh, adjustment. */
908         changed = FALSE;
909
910         /* The lower value should be the first row in the buffer. */
911         delta = vte_ring_delta(terminal->pvt->screen->row_data);
912 #ifdef VTE_DEBUG
913         if (vte_debug_on(VTE_DEBUG_IO)) {
914                 fprintf(stderr, "Changing adjustment values "
915                         "(delta = %ld, scroll = %ld).\n",
916                         delta, terminal->pvt->screen->scroll_delta);
917         }
918 #endif
919         if (terminal->adjustment->lower != delta) {
920                 terminal->adjustment->lower = delta;
921                 changed = TRUE;
922         }
923
924         /* The upper value is the number of rows which might be visible.  (Add
925          * one to the cursor offset because it's zero-based.) */
926         next = vte_ring_delta(terminal->pvt->screen->row_data) +
927                vte_ring_length(terminal->pvt->screen->row_data);
928         rows = MAX(next,
929                    terminal->pvt->screen->cursor_current.row + 1);
930         if (terminal->adjustment->upper != rows) {
931                 terminal->adjustment->upper = rows;
932                 changed = TRUE;
933         }
934
935         /* The step increment should always be one. */
936         if (terminal->adjustment->step_increment != 1) {
937                 terminal->adjustment->step_increment = 1;
938                 changed = TRUE;
939         }
940
941         /* Set the number of rows the user sees to the number of rows the
942          * user sees. */
943         page_size = terminal->row_count;
944         if (terminal->adjustment->page_size != page_size) {
945                 terminal->adjustment->page_size = page_size;
946                 changed = TRUE;
947         }
948
949         /* Clicking in the empty area should scroll one screen, so set the
950          * page size to the number of visible rows. */
951         if (terminal->adjustment->page_increment != page_size) {
952                 terminal->adjustment->page_increment = page_size;
953                 changed = TRUE;
954         }
955
956         /* Set the scrollbar adjustment to where the screen wants it to be. */
957         if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
958             terminal->pvt->screen->scroll_delta) {
959                 gtk_adjustment_set_value(terminal->adjustment,
960                                          terminal->pvt->screen->scroll_delta);
961                 changed = TRUE;
962         }
963
964         /* If anything changed, signal that there was a change. */
965         if (changed == TRUE) {
966 #ifdef VTE_DEBUG
967                 if (vte_debug_on(VTE_DEBUG_IO)) {
968                         fprintf(stderr, "Changed adjustment values "
969                                 "(delta = %ld, scroll = %ld).\n",
970                                 delta, terminal->pvt->screen->scroll_delta);
971                 }
972 #endif
973                 vte_terminal_match_contents_clear(terminal);
974                 vte_terminal_emit_contents_changed(terminal);
975                 gtk_adjustment_changed(terminal->adjustment);
976         }
977 }
978
979 /* Scroll up or down in the current screen. */
980 static void
981 vte_terminal_scroll_pages(VteTerminal *terminal, gint pages)
982 {
983         glong destination;
984         g_return_if_fail(VTE_IS_TERMINAL(terminal));
985 #ifdef VTE_DEBUG
986         if (vte_debug_on(VTE_DEBUG_IO)) {
987                 fprintf(stderr, "Scrolling %d pages.\n", pages);
988         }
989 #endif
990         /* Calculate the ideal position where we want to be before clamping. */
991         destination = floor(gtk_adjustment_get_value(terminal->adjustment));
992         destination += (pages * terminal->row_count);
993         /* Can't scroll past data we have. */
994         destination = MIN(destination,
995                           terminal->adjustment->upper - terminal->row_count);
996         /* Can't scroll up past zero. */
997         destination = MAX(terminal->adjustment->lower, destination);
998         /* Tell the scrollbar to adjust itself. */
999         gtk_adjustment_set_value(terminal->adjustment, destination);
1000         gtk_adjustment_changed(terminal->adjustment);
1001 }
1002
1003 /* Scroll so that the scroll delta is the insertion delta. */
1004 static void
1005 vte_terminal_scroll_to_bottom(VteTerminal *terminal)
1006 {
1007         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1008         if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
1009             terminal->pvt->screen->insert_delta) {
1010                 gtk_adjustment_set_value(terminal->adjustment,
1011                                          terminal->pvt->screen->insert_delta);
1012                 gtk_adjustment_changed(terminal->adjustment);
1013         }
1014 }
1015
1016 /* Call another function, offsetting any long arguments by the given
1017  * increment value. */
1018 static void
1019 vte_sequence_handler_offset(VteTerminal *terminal,
1020                             const char *match,
1021                             GQuark match_quark,
1022                             GValueArray *params,
1023                             int increment,
1024                             VteTerminalSequenceHandler handler)
1025 {
1026         int i;
1027         long val;
1028         GValue *value;
1029         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1030         /* Decrement the parameters and let the _cs handler deal with it. */
1031         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
1032                 value = g_value_array_get_nth(params, i);
1033                 if (G_VALUE_HOLDS_LONG(value)) {
1034                         val = g_value_get_long(value);
1035                         val += increment;
1036                         g_value_set_long(value, val);
1037                 }
1038         }
1039         handler(terminal, match, match_quark, params);
1040 }
1041
1042 /* Call another function a given number of times, or once. */
1043 static void
1044 vte_sequence_handler_multiple(VteTerminal *terminal,
1045                               const char *match,
1046                               GQuark match_quark,
1047                               GValueArray *params,
1048                               VteTerminalSequenceHandler handler)
1049 {
1050         long val = 1;
1051         int i;
1052         GValue *value;
1053         if ((params != NULL) && (params->n_values > 0)) {
1054                 value = g_value_array_get_nth(params, 0);
1055                 if (G_VALUE_HOLDS_LONG(value)) {
1056                         val = g_value_get_long(value);
1057                 }
1058         }
1059         for (i = 0; i < val; i++) {
1060                 handler(terminal, match, match_quark, NULL);
1061         }
1062 }
1063
1064 /* Insert a blank line at an arbitrary position. */
1065 static void
1066 vte_insert_line_int(VteTerminal *terminal, long position)
1067 {
1068         GArray *array;
1069         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1070         /* Pad out the line data to the insertion point. */
1071         while (vte_ring_next(terminal->pvt->screen->row_data) < position) {
1072                 array = vte_new_row_data();
1073                 vte_ring_append(terminal->pvt->screen->row_data, array);
1074         }
1075         /* If we haven't inserted a line yet, insert a new one. */
1076         array = vte_new_row_data();
1077         if (vte_ring_next(terminal->pvt->screen->row_data) >= position) {
1078                 vte_ring_insert(terminal->pvt->screen->row_data, position, array);
1079         } else {
1080                 vte_ring_append(terminal->pvt->screen->row_data, array);
1081         }
1082 }
1083
1084 /* Remove a line at an arbitrary position. */
1085 static void
1086 vte_remove_line_int(VteTerminal *terminal, long position)
1087 {
1088         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1089         if (vte_ring_next(terminal->pvt->screen->row_data) > position) {
1090                 vte_ring_remove(terminal->pvt->screen->row_data, position, TRUE);
1091         }
1092 }
1093
1094 /* Change the encoding used for the terminal to the given codeset, or the
1095  * locale default if NULL is passed in. */
1096 static void
1097 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
1098 {
1099         const char *old_codeset;
1100         GQuark encoding_quark;
1101         iconv_t conv;
1102         char *ibuf, *obuf, *obufptr;
1103         size_t icount, ocount;
1104
1105         old_codeset = terminal->pvt->encoding;
1106
1107         if (codeset == NULL) {
1108                 codeset = nl_langinfo(CODESET);
1109         }
1110
1111         /* Set up the conversion for incoming-to-wchars. */
1112         if (terminal->pvt->incoming_conv != NULL) {
1113                 iconv_close(terminal->pvt->incoming_conv);
1114         }
1115         terminal->pvt->incoming_conv = iconv_open("WCHAR_T", codeset);
1116
1117         /* Set up the conversions for wchar/utf-8 to outgoing. */
1118         if (terminal->pvt->outgoing_conv_wide != NULL) {
1119                 iconv_close(terminal->pvt->outgoing_conv_wide);
1120         }
1121         terminal->pvt->outgoing_conv_wide = iconv_open(codeset, "WCHAR_T");
1122
1123         if (terminal->pvt->outgoing_conv_utf8 != NULL) {
1124                 iconv_close(terminal->pvt->outgoing_conv_utf8);
1125         }
1126         terminal->pvt->outgoing_conv_utf8 = iconv_open(codeset, "UTF-8");
1127
1128         /* Set the terminal's encoding to the new value. */
1129         encoding_quark = g_quark_from_string(codeset);
1130         terminal->pvt->encoding = g_quark_to_string(encoding_quark);
1131
1132         /* Convert any buffered output bytes. */
1133         if (terminal->pvt->n_outgoing > 0) {
1134                 icount = terminal->pvt->n_outgoing;
1135                 ibuf = terminal->pvt->outgoing;
1136                 ocount = icount * VTE_UTF8_BPC + 1;
1137                 obuf = obufptr = g_malloc(ocount);
1138                 conv = iconv_open(codeset, old_codeset);
1139                 if (conv != NULL) {
1140                         if (iconv(conv, &ibuf, &icount, &obuf, &ocount) == -1) {
1141                                 /* Darn, it failed.  Leave it alone. */
1142                                 g_free(obufptr);
1143 #ifdef VTE_DEBUG
1144                                 if (vte_debug_on(VTE_DEBUG_IO)) {
1145                                         fprintf(stderr, "Error converting %ld "
1146                                                 "pending output bytes (%s) "
1147                                                 "skipping.\n",
1148                                                 (long) terminal->pvt->n_outgoing,
1149                                                 strerror(errno));
1150                                 }
1151 #endif
1152                         } else {
1153                                 g_free(terminal->pvt->outgoing);
1154                                 terminal->pvt->outgoing = obufptr;
1155                                 terminal->pvt->n_outgoing = obuf - obufptr;
1156 #ifdef VTE_DEBUG
1157                                 if (vte_debug_on(VTE_DEBUG_IO)) {
1158                                         fprintf(stderr, "Converted %ld pending "
1159                                                 "output bytes.\n",
1160                                                 (long) terminal->pvt->n_outgoing);
1161                                 }
1162 #endif
1163                         }
1164                         iconv_close(conv);
1165                 }
1166         }
1167
1168 #ifdef VTE_DEBUG
1169         if (vte_debug_on(VTE_DEBUG_IO)) {
1170                 fprintf(stderr, "Set terminal encoding to `%s'.\n",
1171                         terminal->pvt->encoding);
1172         }
1173 #endif
1174 }
1175
1176 /* End alternate character set. */
1177 static void
1178 vte_sequence_handler_ae(VteTerminal *terminal,
1179                         const char *match,
1180                         GQuark match_quark,
1181                         GValueArray *params)
1182 {
1183         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1184         terminal->pvt->screen->defaults.alternate = 0;
1185 }
1186
1187 /* Add a line at the current cursor position. */
1188 static void
1189 vte_sequence_handler_al(VteTerminal *terminal,
1190                         const char *match,
1191                         GQuark match_quark,
1192                         GValueArray *params)
1193 {
1194         VteScreen *screen;
1195         GtkWidget *widget;
1196         long start, end;
1197         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1198         widget = GTK_WIDGET(terminal);
1199         screen = terminal->pvt->screen;
1200         if (screen->scrolling_restricted) {
1201                 start = screen->insert_delta + screen->scrolling_region.start;
1202                 end = screen->insert_delta + screen->scrolling_region.end;
1203         } else {
1204                 start = screen->insert_delta;
1205                 end = screen->insert_delta + terminal->row_count - 1;
1206         }
1207         vte_remove_line_int(terminal, end);
1208         vte_insert_line_int(terminal, screen->cursor_current.row);
1209         screen->cursor_current.row++;
1210         if (start - screen->insert_delta < terminal->row_count / 2) {
1211 #if 0
1212                 gdk_window_scroll(widget->window,
1213                                   0,
1214                                   terminal->char_height);
1215                 vte_invalidate_cells(terminal,
1216                                      0, terminal->column_count,
1217                                      0, start + 2);
1218                 vte_invalidate_cells(terminal,
1219                                      0, terminal->column_count,
1220                                      end, terminal->row_count);
1221 #else
1222                 vte_invalidate_cells(terminal,
1223                                      0, terminal->column_count,
1224                                      start, end - start + 1);
1225 #endif
1226         } else {
1227                 vte_invalidate_cells(terminal,
1228                                      0, terminal->column_count,
1229                                      start, end - start + 1);
1230         }
1231 }
1232
1233 /* Add N lines at the current cursor position. */
1234 static void
1235 vte_sequence_handler_AL(VteTerminal *terminal,
1236                         const char *match,
1237                         GQuark match_quark,
1238                         GValueArray *params)
1239 {
1240         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1241         vte_sequence_handler_multiple(terminal, match, match_quark, params,
1242                                       vte_sequence_handler_al);
1243 }
1244
1245 /* Start using alternate character set. */
1246 static void
1247 vte_sequence_handler_as(VteTerminal *terminal,
1248                         const char *match,
1249                         GQuark match_quark,
1250                         GValueArray *params)
1251 {
1252         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1253         terminal->pvt->screen->defaults.alternate = 1;
1254 }
1255
1256 /* Beep. */
1257 static void
1258 vte_sequence_handler_bl(VteTerminal *terminal,
1259                         const char *match,
1260                         GQuark match_quark,
1261                         GValueArray *params)
1262 {
1263         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1264         if (terminal->pvt->audible_bell) {
1265                 /* Feep. */
1266                 gdk_beep();
1267         } else {
1268                 /* Visual bell. */
1269                 vte_sequence_handler_vb(terminal, match, match_quark, params);
1270         }
1271 }
1272
1273 /* Backtab. */
1274 static void
1275 vte_sequence_handler_bt(VteTerminal *terminal,
1276                         const char *match,
1277                         GQuark match_quark,
1278                         GValueArray *params)
1279 {
1280         long newcol;
1281         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1282
1283         /* Calculate which column is the previous tab stop. */
1284         newcol = terminal->pvt->screen->cursor_current.col;
1285
1286         if (terminal->pvt->tabstops != NULL) {
1287                 /* Find the next tabstop. */
1288                 for (newcol += (terminal->column_count - 1);
1289                      newcol >= 0;
1290                      newcol--) {
1291                         if (vte_terminal_get_tabstop(terminal,
1292                                                      newcol % terminal->column_count)) {
1293                                 break;
1294                         }
1295                 }
1296         }
1297
1298         /* If we have no tab stops, stop right here. */
1299         if (newcol <= 0) {
1300                 return;
1301         }
1302
1303         /* Warp the cursor. */
1304 #ifdef VTE_DEBUG
1305         if (vte_debug_on(VTE_DEBUG_PARSE)) {
1306                 fprintf(stderr, "Moving cursor to column %ld.\n", (long)newcol);
1307         }
1308 #endif
1309         terminal->pvt->screen->cursor_current.col = newcol;
1310 }
1311
1312 /* Clear from the cursor position to the beginning of the line. */
1313 static void
1314 vte_sequence_handler_cb(VteTerminal *terminal,
1315                         const char *match,
1316                         GQuark match_quark,
1317                         GValueArray *params)
1318 {
1319         GArray *rowdata;
1320         long i;
1321         VteScreen *screen;
1322         struct vte_charcell *pcell;
1323         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1324         screen = terminal->pvt->screen;
1325         /* If the cursor is actually on the screen, clear data in the row
1326          * which corresponds to the cursor. */
1327         if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
1328                 struct vte_charcell defaults;
1329                 /* Get the data for the row which the cursor points to. */
1330                 rowdata = vte_ring_index(screen->row_data,
1331                                          GArray*,
1332                                          screen->cursor_current.row);
1333                 /* Clear the data up to the current column with the default
1334                  * attributes. */
1335                 memset(&defaults, 0, sizeof(defaults));
1336                 defaults.fore = screen->defaults.fore;
1337                 defaults.back = screen->defaults.back;
1338                 defaults.c = 0;
1339                 defaults.columns = 1;
1340                 for (i = 0;
1341                      (i < screen->cursor_current.col) && (i < rowdata->len);
1342                      i++) {
1343                         pcell = &g_array_index(rowdata, struct vte_charcell, i);
1344                         if (pcell != NULL) {
1345                                 *pcell = defaults;
1346                         }
1347                 }
1348                 /* Repaint this row. */
1349                 vte_invalidate_cells(terminal,
1350                                      0, terminal->column_count,
1351                                      screen->cursor_current.row, 1);
1352         }
1353 }
1354
1355 /* Clear below the current line. */
1356 static void
1357 vte_sequence_handler_cd(VteTerminal *terminal,
1358                         const char *match,
1359                         GQuark match_quark,
1360                         GValueArray *params)
1361 {
1362         GArray *rowdata;
1363         long i;
1364         VteScreen *screen;
1365         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1366         screen = terminal->pvt->screen;
1367         /* If the cursor is actually on the screen, clear data in the row
1368          * the cursor is in and all rows below the cursor. */
1369         for (i = screen->cursor_current.row;
1370              i < vte_ring_next(screen->row_data);
1371              i++) {
1372                 /* Get the data for the row we're removing. */
1373                 rowdata = vte_ring_index(screen->row_data, GArray*, i);
1374                 /* Remove it. */
1375                 while ((rowdata != NULL) && (rowdata->len > 0)) {
1376                         g_array_remove_index(rowdata, rowdata->len - 1);
1377                 }
1378                 /* Repaint this row. */
1379                 vte_invalidate_cells(terminal,
1380                                      0, terminal->column_count,
1381                                      i, 1);
1382         }
1383 }
1384
1385 /* Clear from the cursor position to the end of the line. */
1386 static void
1387 vte_sequence_handler_ce(VteTerminal *terminal,
1388                         const char *match,
1389                         GQuark match_quark,
1390                         GValueArray *params)
1391 {
1392         GArray *rowdata;
1393         VteScreen *screen;
1394         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1395         screen = terminal->pvt->screen;
1396         /* Get the data for the row which the cursor points to. */
1397         vte_terminal_ensure_cursor(terminal);
1398         rowdata = vte_ring_index(screen->row_data, GArray*,
1399                                  screen->cursor_current.row);
1400         /* Remove the data at the end of the array. */
1401         while (rowdata->len > screen->cursor_current.col) {
1402                 g_array_remove_index(rowdata, rowdata->len - 1);
1403         }
1404         /* Now insert empty cells with the default attributes. */
1405         while (rowdata->len < terminal->column_count) {
1406                 g_array_append_val(rowdata, screen->defaults);
1407         }
1408         /* Repaint this row. */
1409         vte_invalidate_cells(terminal,
1410                              0, terminal->column_count,
1411                              screen->cursor_current.row, 1);
1412 }
1413
1414 /* Move the cursor to the given column (horizontal position). */
1415 static void
1416 vte_sequence_handler_ch(VteTerminal *terminal,
1417                         const char *match,
1418                         GQuark match_quark,
1419                         GValueArray *params)
1420 {
1421         VteScreen *screen;
1422         GValue *value;
1423         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1424         screen = terminal->pvt->screen;
1425         /* We only care if there's a parameter in there. */
1426         if ((params != NULL) && (params->n_values > 0)) {
1427                 value = g_value_array_get_nth(params, 0);
1428                 if (G_VALUE_HOLDS_LONG(value)) {
1429                         /* Move the cursor. */
1430                         screen->cursor_current.col = g_value_get_long(value);
1431                 }
1432         }
1433 }
1434
1435 /* Clear the screen and home the cursor. */
1436 static void
1437 vte_sequence_handler_cl(VteTerminal *terminal,
1438                         const char *match,
1439                         GQuark match_quark,
1440                         GValueArray *params)
1441 {
1442         vte_sequence_handler_clear_screen(terminal, NULL, 0, NULL);
1443         vte_sequence_handler_ho(terminal, NULL, 0, NULL);
1444 }
1445
1446 /* Move the cursor to the given position. */
1447 static void
1448 vte_sequence_handler_cm(VteTerminal *terminal,
1449                         const char *match,
1450                         GQuark match_quark,
1451                         GValueArray *params)
1452 {
1453         GValue *row, *col;
1454         long rowval, colval;
1455         VteScreen *screen;
1456         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1457         screen = terminal->pvt->screen;
1458         /* We need at least two parameters. */
1459         if ((params != NULL) && (params->n_values >= 2)) {
1460                 /* The first is the row, the second is the column. */
1461                 row = g_value_array_get_nth(params, 0);
1462                 col = g_value_array_get_nth(params, 1);
1463                 if (G_VALUE_HOLDS_LONG(row) &&
1464                     G_VALUE_HOLDS_LONG(col)) {
1465                         rowval = g_value_get_long(row);
1466                         colval = g_value_get_long(col);
1467                         rowval = MAX(0, rowval);
1468                         rowval = MIN(rowval, terminal->row_count - 1);
1469                         colval = MAX(0, colval);
1470                         colval = MIN(colval, terminal->column_count - 1);
1471                         screen->cursor_current.row = rowval +
1472                                                      screen->insert_delta;
1473                         screen->cursor_current.col = colval;
1474                 }
1475         }
1476 }
1477
1478 /* Clear from the current line. */
1479 static void
1480 vte_sequence_handler_clear_current_line(VteTerminal *terminal,
1481                                         const char *match,
1482                                         GQuark match_quark,
1483                                         GValueArray *params)
1484 {
1485         GArray *rowdata;
1486         VteScreen *screen;
1487         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1488         screen = terminal->pvt->screen;
1489         /* If the cursor is actually on the screen, clear data in the row
1490          * which corresponds to the cursor. */
1491         if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
1492                 /* Get the data for the row which the cursor points to. */
1493                 rowdata = vte_ring_index(screen->row_data, GArray*,
1494                                          screen->cursor_current.row);
1495                 /* Remove it. */
1496                 while (rowdata->len > 0) {
1497                         g_array_remove_index(rowdata, rowdata->len - 1);
1498                 }
1499                 /* Repaint this row. */
1500                 vte_invalidate_cells(terminal,
1501                                      0, terminal->column_count,
1502                                      screen->cursor_current.row, 1);
1503         }
1504 }
1505
1506 /* Carriage return. */
1507 static void
1508 vte_sequence_handler_cr(VteTerminal *terminal,
1509                         const char *match,
1510                         GQuark match_quark,
1511                         GValueArray *params)
1512 {
1513         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1514         terminal->pvt->screen->cursor_current.col = 0;
1515 }
1516
1517 /* Restrict scrolling and updates to a subset of the visible lines. */
1518 static void
1519 vte_sequence_handler_cs(VteTerminal *terminal,
1520                         const char *match,
1521                         GQuark match_quark,
1522                         GValueArray *params)
1523 {
1524         long start, end, rows;
1525         GValue *value;
1526         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1527         /* We require two parameters. */
1528         if ((params == NULL) || (params->n_values < 2)) {
1529                 terminal->pvt->screen->scrolling_restricted = FALSE;
1530                 return;
1531         }
1532         /* Extract the two values. */
1533         value = g_value_array_get_nth(params, 0);
1534         start = g_value_get_long(value);
1535         value = g_value_array_get_nth(params, 1);
1536         end = g_value_get_long(value);
1537         /* Set the right values. */
1538         terminal->pvt->screen->scrolling_region.start = start;
1539         terminal->pvt->screen->scrolling_region.end = end;
1540         terminal->pvt->screen->scrolling_restricted = TRUE;
1541         /* Special case -- run wild, run free. */
1542         rows = terminal->row_count;
1543         if ((terminal->pvt->screen->scrolling_region.start == 0) &&
1544             (terminal->pvt->screen->scrolling_region.end == rows - 1)) {
1545                 terminal->pvt->screen->scrolling_restricted = FALSE;
1546         }
1547 }
1548
1549 /* Restrict scrolling and updates to a subset of the visible lines, because
1550  * GNU Emacs is special. */
1551 static void
1552 vte_sequence_handler_cS(VteTerminal *terminal,
1553                         const char *match,
1554                         GQuark match_quark,
1555                         GValueArray *params)
1556 {
1557         long start, end, rows;
1558         GValue *value;
1559         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1560         /* We require four parameters. */
1561         if ((params == NULL) || (params->n_values < 2)) {
1562                 terminal->pvt->screen->scrolling_restricted = FALSE;
1563                 return;
1564         }
1565         /* Extract the two parameters we care about, encoded as the number
1566          * of lines above and below the scrolling region, respectively. */
1567         value = g_value_array_get_nth(params, 1);
1568         start = g_value_get_long(value);
1569         value = g_value_array_get_nth(params, 2);
1570         end = (terminal->row_count - 1) - g_value_get_long(value);
1571         /* Set the right values. */
1572         terminal->pvt->screen->scrolling_region.start = start;
1573         terminal->pvt->screen->scrolling_region.end = end;
1574         terminal->pvt->screen->scrolling_restricted = TRUE;
1575         /* Special case -- run wild, run free. */
1576         rows = terminal->row_count;
1577         if ((terminal->pvt->screen->scrolling_region.start == 0) &&
1578             (terminal->pvt->screen->scrolling_region.end == rows - 1)) {
1579                 terminal->pvt->screen->scrolling_restricted = FALSE;
1580         }
1581 }
1582
1583 /* Clear all tab stops. */
1584 static void
1585 vte_sequence_handler_ct(VteTerminal *terminal,
1586                         const char *match,
1587                         GQuark match_quark,
1588                         GValueArray *params)
1589 {
1590         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1591         if (terminal->pvt->tabstops != NULL) {
1592                 g_hash_table_destroy(terminal->pvt->tabstops);
1593                 terminal->pvt->tabstops = NULL;
1594         }
1595 }
1596
1597 /* Move the cursor to the given row (vertical position). */
1598 static void
1599 vte_sequence_handler_cv(VteTerminal *terminal,
1600                         const char *match,
1601                         GQuark match_quark,
1602                         GValueArray *params)
1603 {
1604         VteScreen *screen;
1605         GValue *value;
1606         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1607         screen = terminal->pvt->screen;
1608         /* We only care if there's a parameter in there. */
1609         if ((params != NULL) && (params->n_values > 0)) {
1610                 value = g_value_array_get_nth(params, 0);
1611                 if (G_VALUE_HOLDS_LONG(value)) {
1612                         /* Move the cursor. */
1613                         screen->cursor_current.row = g_value_get_long(value) +
1614                                                      screen->insert_delta;
1615                 }
1616         }
1617 }
1618
1619 /* Delete a character at the current cursor position. */
1620 static void
1621 vte_sequence_handler_dc(VteTerminal *terminal,
1622                         const char *match,
1623                         GQuark match_quark,
1624                         GValueArray *params)
1625 {
1626         VteScreen *screen;
1627         GArray *rowdata;
1628         long col;
1629
1630         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1631         screen = terminal->pvt->screen;
1632
1633         if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
1634                 /* Get the data for the row which the cursor points to. */
1635                 rowdata = vte_ring_index(screen->row_data,
1636                                          GArray*,
1637                                          screen->cursor_current.row);
1638                 col = screen->cursor_current.col;
1639                 /* Remove the column. */
1640                 if (col < rowdata->len) {
1641                         g_array_remove_index(rowdata, col);
1642                 }
1643                 /* Repaint this row. */
1644                 vte_invalidate_cells(terminal,
1645                                      0, terminal->column_count,
1646                                      screen->cursor_current.row, 1);
1647         }
1648 }
1649
1650 /* Delete N characters at the current cursor position. */
1651 static void
1652 vte_sequence_handler_DC(VteTerminal *terminal,
1653                         const char *match,
1654                         GQuark match_quark,
1655                         GValueArray *params)
1656 {
1657         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1658         vte_sequence_handler_multiple(terminal, match, match_quark, params,
1659                                       vte_sequence_handler_dc);
1660 }
1661
1662 /* Delete a line at the current cursor position. */
1663 static void
1664 vte_sequence_handler_dl(VteTerminal *terminal,
1665                         const char *match,
1666                         GQuark match_quark,
1667                         GValueArray *params)
1668 {
1669         VteScreen *screen;
1670         long end;
1671         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1672         screen = terminal->pvt->screen;
1673         if (screen->scrolling_restricted) {
1674                 end = screen->insert_delta + screen->scrolling_region.end;
1675         } else {
1676                 end = screen->insert_delta + terminal->row_count - 1;
1677         }
1678         vte_remove_line_int(terminal, screen->cursor_current.row);
1679         vte_insert_line_int(terminal, end);
1680         /* Repaint the entire screen. */
1681         vte_invalidate_all(terminal);
1682 }
1683
1684 /* Delete N lines at the current cursor position. */
1685 static void
1686 vte_sequence_handler_DL(VteTerminal *terminal,
1687                         const char *match,
1688                         GQuark match_quark,
1689                         GValueArray *params)
1690 {
1691         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1692         vte_sequence_handler_multiple(terminal, match, match_quark, params,
1693                                       vte_sequence_handler_dl);
1694 }
1695
1696 /* Make sure we have enough rows and columns to hold data at the current
1697  * cursor position. */
1698 static void
1699 vte_terminal_ensure_cursor(VteTerminal *terminal)
1700 {
1701         GArray *array;
1702         VteScreen *screen;
1703         struct vte_charcell cell;
1704
1705         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1706
1707         screen = terminal->pvt->screen;
1708
1709         while (screen->cursor_current.row >= vte_ring_next(screen->row_data)) {
1710                 array = vte_new_row_data();
1711                 vte_ring_append(screen->row_data, array);
1712         }
1713
1714         array = vte_ring_index(screen->row_data,
1715                                GArray*,
1716                                screen->cursor_current.row);
1717
1718         if (array != NULL) {
1719                 /* Add enough characters to fill out the row. */
1720                 memset(&cell, 0, sizeof(cell));
1721                 cell.fore = VTE_DEF_FG;
1722                 cell.back = VTE_DEF_BG;
1723                 cell.c = ' ';
1724                 cell.columns = wcwidth(cell.c);
1725                 /* Add enough cells at the end to make sure we have one for
1726                  * this column. */
1727                 while ((array->len <= screen->cursor_current.col) &&
1728                        (array->len < terminal->column_count)) {
1729                         array = g_array_append_val(array, cell);
1730                 }
1731         }
1732 }
1733
1734 static void
1735 vte_terminal_scroll_insertion(VteTerminal *terminal)
1736 {
1737         long rows, delta;
1738         VteScreen *screen;
1739
1740         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1741         screen = terminal->pvt->screen;
1742
1743         /* Make sure that the bottom row is visible, and that it's in
1744          * the buffer (even if it's empty).  This usually causes the
1745          * top row to become a history-only row. */
1746         rows = MAX(vte_ring_next(screen->row_data),
1747                    screen->cursor_current.row + 1);
1748         delta = MAX(0, rows - terminal->row_count);
1749
1750         /* Adjust the insert delta and scroll if needed. */
1751         if (delta != screen->insert_delta) {
1752                 vte_terminal_ensure_cursor(terminal);
1753                 screen->insert_delta = delta;
1754                 /* Update scroll bar adjustments. */
1755                 vte_terminal_adjust_adjustments(terminal);
1756         }
1757 }
1758
1759 /* Scroll forward. */
1760 static void
1761 vte_sequence_handler_do(VteTerminal *terminal,
1762                         const char *match,
1763                         GQuark match_quark,
1764                         GValueArray *params)
1765 {
1766         GtkWidget *widget;
1767         long col, row, start, end;
1768         VteScreen *screen;
1769
1770         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1771         widget = GTK_WIDGET(terminal);
1772         screen = terminal->pvt->screen;
1773
1774         start = screen->scrolling_region.start + screen->insert_delta;
1775         end = screen->scrolling_region.end + screen->insert_delta;
1776         col = screen->cursor_current.col;
1777         row = screen->cursor_current.row;
1778
1779         if (screen->scrolling_restricted) {
1780                 if (row == end) {
1781                         /* If we're at the end of the scrolling region, add a
1782                          * line at the bottom to scroll the top off. */
1783                         vte_remove_line_int(terminal, start);
1784                         vte_insert_line_int(terminal, end);
1785                         if ((terminal->pvt->bg_image == NULL) &&
1786                             (!terminal->pvt->bg_transparent)) {
1787 #if 0
1788                                 /* Scroll the window. */
1789                                 gdk_window_scroll(widget->window,
1790                                                   0,
1791                                                   -terminal->char_height);
1792                                 /* We need to redraw the last row of the
1793                                  * scrolling region, and anything beyond. */
1794                                 vte_invalidate_cells(terminal,
1795                                                      0, terminal->column_count,
1796                                                      end, terminal->row_count);
1797                                 /* Also redraw anything above the scrolling
1798                                  * region. */
1799                                 vte_invalidate_cells(terminal,
1800                                                      0, terminal->column_count,
1801                                                      0, start);
1802 #else
1803                                 vte_invalidate_cells(terminal,
1804                                                      0,
1805                                                      terminal->column_count,
1806                                                      start,
1807                                                      end - start + 1);
1808 #endif
1809                         } else {
1810                                 /* If we have a background image, we need to
1811                                  * redraw the entire window. */
1812                                 vte_invalidate_cells(terminal,
1813                                                      0,
1814                                                      terminal->column_count,
1815                                                      start,
1816                                                      end - start + 1);
1817                         }
1818                 } else {
1819                         /* Otherwise, just move the cursor down. */
1820                         screen->cursor_current.row++;
1821                 }
1822         } else {
1823                 /* Move the cursor down. */
1824                 screen->cursor_current.row++;
1825
1826                 /* Adjust the insert delta so that the row the cursor is on
1827                  * is viewable if the insert delta is equal to the scrolling
1828                  * delta. */
1829                 vte_terminal_scroll_insertion(terminal);
1830         }
1831 }
1832
1833 /* Cursor down. */
1834 static void
1835 vte_sequence_handler_DO(VteTerminal *terminal,
1836                         const char *match,
1837                         GQuark match_quark,
1838                         GValueArray *params)
1839 {
1840         vte_sequence_handler_multiple(terminal, match, match_quark, params,
1841                                       vte_sequence_handler_do);
1842 }
1843
1844 /* Start using alternate character set. */
1845 static void
1846 vte_sequence_handler_eA(VteTerminal *terminal,
1847                         const char *match,
1848                         GQuark match_quark,
1849                         GValueArray *params)
1850 {
1851         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1852         vte_sequence_handler_ae(terminal, match, match_quark, params);
1853 }
1854
1855 /* Erase characters starting at the cursor position (overwriting N with
1856  * spaces, but not moving the cursor). */
1857 static void
1858 vte_sequence_handler_ec(VteTerminal *terminal,
1859                         const char *match,
1860                         GQuark match_quark,
1861                         GValueArray *params)
1862 {
1863         VteScreen *screen;
1864         GArray *rowdata;
1865         GValue *value;
1866         struct vte_charcell *cell;
1867         long col, i, count;
1868
1869         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1870         screen = terminal->pvt->screen;
1871
1872         /* If we got a parameter, use it. */
1873         count = 1;
1874         if ((params != NULL) && (params->n_values > 0)) {
1875                 value = g_value_array_get_nth(params, 0);
1876                 if (G_VALUE_HOLDS_LONG(value)) {
1877                         count = g_value_get_long(value);
1878                 }
1879         }
1880
1881         /* Clear out the given number of characters. */
1882         if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
1883                 /* Get the data for the row which the cursor points to. */
1884                 rowdata = vte_ring_index(screen->row_data,
1885                                          GArray*,
1886                                          screen->cursor_current.row);
1887                 /* Write over the same characters. */
1888                 for (i = 0; i < count; i++) {
1889                         col = screen->cursor_current.col + i;
1890                         if ((col < rowdata->len) && (col >= 0)) {
1891                                 cell = &g_array_index(rowdata,
1892                                                       struct vte_charcell,
1893                                                       col);
1894                                 *cell = screen->defaults;
1895                         }
1896                 }
1897                 /* Repaint this row. */
1898                 vte_invalidate_cells(terminal,
1899                                      0, terminal->column_count,
1900                                      screen->cursor_current.row, 1);
1901         }
1902 }
1903
1904 /* End insert mode. */
1905 static void
1906 vte_sequence_handler_ei(VteTerminal *terminal,
1907                         const char *match,
1908                         GQuark match_quark,
1909                         GValueArray *params)
1910 {
1911         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1912         terminal->pvt->screen->insert_mode = FALSE;
1913 }
1914
1915 /* Move the cursor to the home position. */
1916 static void
1917 vte_sequence_handler_ho(VteTerminal *terminal,
1918                         const char *match,
1919                         GQuark match_quark,
1920                         GValueArray *params)
1921 {
1922         VteScreen *screen;
1923         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1924         screen = terminal->pvt->screen;
1925         screen->cursor_current.row = screen->insert_delta;
1926         screen->cursor_current.col = 0;
1927 }
1928
1929 /* Insert a character. */
1930 static void
1931 vte_sequence_handler_ic(VteTerminal *terminal,
1932                         const char *match,
1933                         GQuark match_quark,
1934                         GValueArray *params)
1935 {
1936         long row, col;
1937         VteScreen *screen;
1938
1939         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1940         screen = terminal->pvt->screen;
1941
1942         row = screen->cursor_current.row;
1943         col = screen->cursor_current.col;
1944
1945         vte_terminal_insert_char(GTK_WIDGET(terminal), ' ', TRUE);
1946
1947         screen->cursor_current.row = row;
1948         screen->cursor_current.col = col;
1949 }
1950
1951 /* Insert N characters. */
1952 static void
1953 vte_sequence_handler_IC(VteTerminal *terminal,
1954                         const char *match,
1955                         GQuark match_quark,
1956                         GValueArray *params)
1957 {
1958         vte_sequence_handler_multiple(terminal, match, match_quark, params,
1959                                       vte_sequence_handler_ic);
1960 }
1961
1962 /* Begin insert mode. */
1963 static void
1964 vte_sequence_handler_im(VteTerminal *terminal,
1965                         const char *match,
1966                         GQuark match_quark,
1967                         GValueArray *params)
1968 {
1969         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1970         terminal->pvt->screen->insert_mode = TRUE;
1971 }
1972
1973 /* Send me a backspace key sym, will you?  Guess that the application meant
1974  * to send the cursor back one position. */
1975 static void
1976 vte_sequence_handler_kb(VteTerminal *terminal,
1977                         const char *match,
1978                         GQuark match_quark,
1979                         GValueArray *params)
1980 {
1981         VteScreen *screen;
1982         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1983         screen = terminal->pvt->screen;
1984         if (screen->cursor_current.col > 0) {
1985                 /* There's room to move left, so do so. */
1986                 screen->cursor_current.col--;
1987         } else {
1988                 if (terminal->pvt->flags.bw) {
1989                         /* Wrap to the previous line. */
1990                         screen->cursor_current.col = terminal->column_count - 1;
1991                         screen->cursor_current.row = MAX(screen->cursor_current.row - 1,
1992                                                          screen->insert_delta);
1993                 } else {
1994                         /* Stick to the first column. */
1995                         screen->cursor_current.col = 0;
1996                 }
1997         }
1998 }
1999
2000 /* Keypad mode end. */
2001 static void
2002 vte_sequence_handler_ke(VteTerminal *terminal,
2003                         const char *match,
2004                         GQuark match_quark,
2005                         GValueArray *params)
2006 {
2007         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2008         terminal->pvt->keypad = VTE_KEYPAD_NORMAL;
2009 }
2010
2011 /* Keypad mode start. */
2012 static void
2013 vte_sequence_handler_ks(VteTerminal *terminal,
2014                         const char *match,
2015                         GQuark match_quark,
2016                         GValueArray *params)
2017 {
2018         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2019         terminal->pvt->keypad = VTE_KEYPAD_APPLICATION;
2020 }
2021
2022 /* Cursor left. */
2023 static void
2024 vte_sequence_handler_le(VteTerminal *terminal,
2025                         const char *match,
2026                         GQuark match_quark,
2027                         GValueArray *params)
2028 {
2029         vte_sequence_handler_kb(terminal, match, match_quark, params);
2030 }
2031
2032 /* Move the cursor left N columns. */
2033 static void
2034 vte_sequence_handler_LE(VteTerminal *terminal,
2035                         const char *match,
2036                         GQuark match_quark,
2037                         GValueArray *params)
2038 {
2039         vte_sequence_handler_multiple(terminal, match, match_quark, params,
2040                                       vte_sequence_handler_le);
2041 }
2042
2043 /* Move the cursor to the lower left corner of the display. */
2044 static void
2045 vte_sequence_handler_ll(VteTerminal *terminal,
2046                         const char *match,
2047                         GQuark match_quark,
2048                         GValueArray *params)
2049 {
2050         VteScreen *screen;
2051         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2052         screen = terminal->pvt->screen;
2053         screen->cursor_current.row = screen->insert_delta +
2054                                      terminal->row_count - 1;
2055         screen->cursor_current.col = 0;
2056 }
2057
2058 /* Blink on. */
2059 static void
2060 vte_sequence_handler_mb(VteTerminal *terminal,
2061                         const char *match,
2062                         GQuark match_quark,
2063                         GValueArray *params)
2064 {
2065         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2066         terminal->pvt->screen->defaults.blink = 1;
2067 }
2068
2069 /* Bold on. */
2070 static void
2071 vte_sequence_handler_md(VteTerminal *terminal,
2072                         const char *match,
2073                         GQuark match_quark,
2074                         GValueArray *params)
2075 {
2076         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2077         terminal->pvt->screen->defaults.bold = 1;
2078 }
2079
2080 /* End modes. */
2081 static void
2082 vte_sequence_handler_me(VteTerminal *terminal,
2083                         const char *match,
2084                         GQuark match_quark,
2085                         GValueArray *params)
2086 {
2087         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2088         vte_terminal_set_default_attributes(terminal);
2089 }
2090
2091 /* Half-bright on. */
2092 static void
2093 vte_sequence_handler_mh(VteTerminal *terminal,
2094                         const char *match,
2095                         GQuark match_quark,
2096                         GValueArray *params)
2097 {
2098         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2099         terminal->pvt->screen->defaults.half = 1;
2100 }
2101
2102 /* Invisible on. */
2103 static void
2104 vte_sequence_handler_mk(VteTerminal *terminal,
2105                         const char *match,
2106                         GQuark match_quark,
2107                         GValueArray *params)
2108 {
2109         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2110         terminal->pvt->screen->defaults.invisible = 1;
2111 }
2112
2113 /* Protect on. */
2114 static void
2115 vte_sequence_handler_mp(VteTerminal *terminal,
2116                         const char *match,
2117                         GQuark match_quark,
2118                         GValueArray *params)
2119 {
2120         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2121         terminal->pvt->screen->defaults.protect = 1;
2122 }
2123
2124 /* Reverse on. */
2125 static void
2126 vte_sequence_handler_mr(VteTerminal *terminal,
2127                         const char *match,
2128                         GQuark match_quark,
2129                         GValueArray *params)
2130 {
2131         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2132         terminal->pvt->screen->defaults.reverse = 1;
2133 }
2134
2135 /* Cursor right. */
2136 static void
2137 vte_sequence_handler_nd(VteTerminal *terminal,
2138                         const char *match,
2139                         GQuark match_quark,
2140                         GValueArray *params)
2141 {
2142         VteScreen *screen;
2143         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2144         screen = terminal->pvt->screen;
2145         if ((screen->cursor_current.col + 1) < terminal->column_count) {
2146                 /* Room to move right. */
2147                 screen->cursor_current.col++;
2148         } else {
2149                 /* Wrap? */
2150                 if (terminal->pvt->flags.am) {
2151                         /* Move on to the next line. */
2152                         screen->cursor_current.col = 0;
2153                         screen->cursor_current.row++;
2154                         /* Scroll to make the new line viewable if need be. */
2155                         vte_terminal_scroll_insertion(terminal);
2156                 } else {
2157                         /* Nope, peg to the rightmost column. */
2158                         screen->cursor_current.col = terminal->column_count - 1;
2159                 }
2160         }
2161 }
2162
2163 /* No-op. */
2164 static void
2165 vte_sequence_handler_noop(VteTerminal *terminal,
2166                           const char *match,
2167                           GQuark match_quark,
2168                           GValueArray *params)
2169 {
2170         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2171 }
2172
2173 /* Restore cursor (position). */
2174 static void
2175 vte_sequence_handler_rc(VteTerminal *terminal,
2176                         const char *match,
2177                         GQuark match_quark,
2178                         GValueArray *params)
2179 {
2180         VteScreen *screen;
2181         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2182         screen = terminal->pvt->screen;
2183         screen->cursor_current.col = screen->cursor_saved.col;
2184         screen->cursor_current.row = screen->cursor_saved.row +
2185                                      screen->insert_delta;
2186 }
2187
2188 /* Cursor right N characters. */
2189 static void
2190 vte_sequence_handler_RI(VteTerminal *terminal,
2191                         const char *match,
2192                         GQuark match_quark,
2193                         GValueArray *params)
2194 {
2195         vte_sequence_handler_multiple(terminal, match, match_quark, params,
2196                                       vte_sequence_handler_nd);
2197 }
2198
2199 /* Save cursor (position). */
2200 static void
2201 vte_sequence_handler_sc(VteTerminal *terminal,
2202                         const char *match,
2203                         GQuark match_quark,
2204                         GValueArray *params)
2205 {
2206         VteScreen *screen;
2207         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2208         screen = terminal->pvt->screen;
2209         screen->cursor_saved.col = screen->cursor_current.col;
2210         screen->cursor_saved.row = screen->cursor_current.row -
2211                                    screen->insert_delta;
2212 }
2213
2214 /* Standout end. */
2215 static void
2216 vte_sequence_handler_se(VteTerminal *terminal,
2217                         const char *match,
2218                         GQuark match_quark,
2219                         GValueArray *params)
2220 {
2221         char *bold, *underline, *standout, *reverse, *half, *blink;
2222         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2223
2224         /* Standout may be mapped to another attribute, so attempt to do
2225          * the Right Thing here. */
2226
2227         standout = vte_termcap_find_string(terminal->pvt->termcap,
2228                                            terminal->pvt->terminal,
2229                                            "so");
2230         g_assert(standout != NULL);
2231         blink = vte_termcap_find_string(terminal->pvt->termcap,
2232                                         terminal->pvt->terminal,
2233                                         "mb");
2234         bold = vte_termcap_find_string(terminal->pvt->termcap,
2235                                        terminal->pvt->terminal,
2236                                        "md");
2237         half = vte_termcap_find_string(terminal->pvt->termcap,
2238                                        terminal->pvt->terminal,
2239                                        "mh");
2240         reverse = vte_termcap_find_string(terminal->pvt->termcap,
2241                                           terminal->pvt->terminal,
2242                                           "mr");
2243         underline = vte_termcap_find_string(terminal->pvt->termcap,
2244                                             terminal->pvt->terminal,
2245                                             "us");
2246
2247         /* If the standout sequence is the same as another sequence, do what
2248          * we'd do for that other sequence instead. */
2249         if (blink && (g_ascii_strcasecmp(standout, blink) == 0)) {
2250                 vte_sequence_handler_me(terminal, match, match_quark, params);
2251         } else
2252         if (bold && (g_ascii_strcasecmp(standout, bold) == 0)) {
2253                 vte_sequence_handler_me(terminal, match, match_quark, params);
2254         } else
2255         if (half && (g_ascii_strcasecmp(standout, half) == 0)) {
2256                 vte_sequence_handler_me(terminal, match, match_quark, params);
2257         } else
2258         if (reverse && (g_ascii_strcasecmp(standout, reverse) == 0)) {
2259                 vte_sequence_handler_me(terminal, match, match_quark, params);
2260         } else
2261         if (underline && (g_ascii_strcasecmp(standout, underline) == 0)) {
2262                 vte_sequence_handler_ue(terminal, match, match_quark, params);
2263         } else {
2264                 /* Otherwise just set standout mode. */
2265                 terminal->pvt->screen->defaults.standout = 0;
2266         }
2267
2268         if (blink) {
2269                 g_free(blink);
2270         }
2271         if (bold) {
2272                 g_free(bold);
2273         }
2274         if (half) {
2275                 g_free(half);
2276         }
2277         if (reverse) {
2278                 g_free(reverse);
2279         }
2280         if (underline) {
2281                 g_free(underline);
2282         }
2283         g_free(standout);
2284 }
2285
2286 /* Standout start. */
2287 static void
2288 vte_sequence_handler_so(VteTerminal *terminal,
2289                         const char *match,
2290                         GQuark match_quark,
2291                         GValueArray *params)
2292 {
2293         char *bold, *underline, *standout, *reverse, *half, *blink;
2294         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2295
2296         /* Standout may be mapped to another attribute, so attempt to do
2297          * the Right Thing here. */
2298
2299         standout = vte_termcap_find_string(terminal->pvt->termcap,
2300                                            terminal->pvt->terminal,
2301                                            "so");
2302         g_assert(standout != NULL);
2303         blink = vte_termcap_find_string(terminal->pvt->termcap,
2304                                         terminal->pvt->terminal,
2305                                         "mb");
2306         bold = vte_termcap_find_string(terminal->pvt->termcap,
2307                                        terminal->pvt->terminal,
2308                                        "md");
2309         half = vte_termcap_find_string(terminal->pvt->termcap,
2310                                        terminal->pvt->terminal,
2311                                        "mh");
2312         reverse = vte_termcap_find_string(terminal->pvt->termcap,
2313                                           terminal->pvt->terminal,
2314                                           "mr");
2315         underline = vte_termcap_find_string(terminal->pvt->termcap,
2316                                             terminal->pvt->terminal,
2317                                             "us");
2318
2319         /* If the standout sequence is the same as another sequence, do what
2320          * we'd do for that other sequence instead. */
2321         if (blink && (g_ascii_strcasecmp(standout, blink) == 0)) {
2322                 vte_sequence_handler_mb(terminal, match, match_quark, params);
2323         } else
2324         if (bold && (g_ascii_strcasecmp(standout, bold) == 0)) {
2325                 vte_sequence_handler_md(terminal, match, match_quark, params);
2326         } else
2327         if (half && (g_ascii_strcasecmp(standout, half) == 0)) {
2328                 vte_sequence_handler_mh(terminal, match, match_quark, params);
2329         } else
2330         if (reverse && (g_ascii_strcasecmp(standout, reverse) == 0)) {
2331                 vte_sequence_handler_mr(terminal, match, match_quark, params);
2332         } else
2333         if (underline && (g_ascii_strcasecmp(standout, underline) == 0)) {
2334                 vte_sequence_handler_us(terminal, match, match_quark, params);
2335         } else {
2336                 /* Otherwise just set standout mode. */
2337                 terminal->pvt->screen->defaults.standout = 1;
2338         }
2339
2340         if (blink) {
2341                 g_free(blink);
2342         }
2343         if (bold) {
2344                 g_free(bold);
2345         }
2346         if (half) {
2347                 g_free(half);
2348         }
2349         if (reverse) {
2350                 g_free(reverse);
2351         }
2352         if (underline) {
2353                 g_free(underline);
2354         }
2355         g_free(standout);
2356 }
2357
2358 /* Set tab stop in the current column. */
2359 static void
2360 vte_sequence_handler_st(VteTerminal *terminal,
2361                         const char *match,
2362                         GQuark match_quark,
2363                         GValueArray *params)
2364 {
2365         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2366         if (terminal->pvt->tabstops == NULL) {
2367                 terminal->pvt->tabstops = g_hash_table_new(g_direct_hash,
2368                                                            g_direct_equal);
2369         }
2370         vte_terminal_set_tabstop(terminal,
2371                                  terminal->pvt->screen->cursor_current.col);
2372 }
2373
2374 /* Tab. */
2375 static void
2376 vte_sequence_handler_ta(VteTerminal *terminal,
2377                         const char *match,
2378                         GQuark match_quark,
2379                         GValueArray *params)
2380 {
2381         long newcol;
2382         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2383
2384         /* Calculate which column is the next tab stop. */
2385         newcol = terminal->pvt->screen->cursor_current.col;
2386
2387         if (terminal->pvt->tabstops != NULL) {
2388                 /* Find the next tabstop. */
2389                 for (newcol++; newcol < VTE_TAB_MAX; newcol++) {
2390                         if (vte_terminal_get_tabstop(terminal, newcol)) {
2391                                 break;
2392                         }
2393                 }
2394         }
2395
2396         /* If we have no tab stops, stop at the right-most column. */
2397         if (newcol >= VTE_TAB_MAX) {
2398                 newcol = terminal->column_count - 1;
2399         }
2400
2401         /* Wrap to the next line if need be. */
2402         if (newcol >= terminal->column_count) {
2403                 if (terminal->pvt->flags.am) {
2404                         /* Move to the next line. */
2405                         terminal->pvt->screen->cursor_current.col = 0;
2406                         vte_sequence_handler_do(terminal, match,
2407                                                 match_quark, params);
2408                 } else {
2409                         /* Stay in the rightmost column. */
2410                         newcol = terminal->column_count - 1;
2411                 }
2412         } else {
2413                 terminal->pvt->screen->cursor_current.col = newcol;
2414         }
2415 #ifdef VTE_DEBUG
2416         if (vte_debug_on(VTE_DEBUG_PARSE)) {
2417                 fprintf(stderr, "Moving cursor to column %ld.\n", (long)newcol);
2418         }
2419 #endif
2420 }
2421
2422 /* Clear tabs selectively. */
2423 static void
2424 vte_sequence_handler_tab_clear(VteTerminal *terminal,
2425                                const char *match,
2426                                GQuark match_quark,
2427                                GValueArray *params)
2428 {
2429         GValue *value;
2430         long param = 0;
2431         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2432         if ((params != NULL) && (params->n_values > 0)) {
2433                 value = g_value_array_get_nth(params, 0);
2434                 if (G_VALUE_HOLDS_LONG(value)) {
2435                         param = g_value_get_long(value);
2436                 }
2437         }
2438         if (param == 0) {
2439                 vte_terminal_clear_tabstop(terminal,
2440                                            terminal->pvt->screen->cursor_current.col);
2441         } else
2442         if (param == 3) {
2443                 if (terminal->pvt->tabstops != NULL) {
2444                         g_hash_table_destroy(terminal->pvt->tabstops);
2445                         terminal->pvt->tabstops = NULL;
2446                 }
2447         }
2448 }
2449
2450 /* Terminal usage starts. */
2451 static void
2452 vte_sequence_handler_ts(VteTerminal *terminal,
2453                         const char *match,
2454                         GQuark match_quark,
2455                         GValueArray *params)
2456 {
2457         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2458         /* I think this is a no-op. */
2459 }
2460
2461 /* Underline this character and move right. */
2462 static void
2463 vte_sequence_handler_uc(VteTerminal *terminal,
2464                         const char *match,
2465                         GQuark match_quark,
2466                         GValueArray *params)
2467 {
2468         struct vte_charcell *cell;
2469         VteScreen *screen;
2470         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2471         screen = terminal->pvt->screen;
2472         cell = vte_terminal_find_charcell(terminal,
2473                                           screen->cursor_current.col,
2474                                           screen->cursor_current.row);
2475         if (cell != NULL) {
2476                 /* Set this character to be underlined. */
2477                 cell->underline = 1;
2478                 /* Cause it to be repainted. */
2479                 vte_invalidate_cells(terminal,
2480                                      screen->cursor_current.col, 2,
2481                                      screen->cursor_current.row, 1);
2482                 /* Move the cursor right. */
2483                 vte_sequence_handler_nd(terminal, match, match_quark, params);
2484         }
2485 }
2486
2487 /* Underline end. */
2488 static void
2489 vte_sequence_handler_ue(VteTerminal *terminal,
2490                         const char *match,
2491                         GQuark match_quark,
2492                         GValueArray *params)
2493 {
2494         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2495         terminal->pvt->screen->defaults.underline = 0;
2496 }
2497
2498 /* Cursor up, scrolling if need be. */
2499 static void
2500 vte_sequence_handler_up(VteTerminal *terminal,
2501                         const char *match,
2502                         GQuark match_quark,
2503                         GValueArray *params)
2504 {
2505         GtkWidget *widget;
2506         long col, row, start, end;
2507         VteScreen *screen;
2508
2509         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2510         widget = GTK_WIDGET(terminal);
2511         screen = terminal->pvt->screen;
2512
2513         col = screen->cursor_current.col;
2514         row = screen->cursor_current.row;
2515
2516         if (screen->scrolling_restricted) {
2517                 start = screen->scrolling_region.start + screen->insert_delta;
2518                 end = screen->scrolling_region.end + screen->insert_delta;
2519                 if (row == start) {
2520                         /* If we're at the top of the scrolling region, add a
2521                          * line at the top to scroll the bottom off. */
2522                         vte_remove_line_int(terminal, end);
2523                         vte_insert_line_int(terminal, start);
2524                         if ((terminal->pvt->bg_image == NULL) &&
2525                             (!terminal->pvt->bg_transparent)) {
2526 #if 0
2527                                 /* Scroll the window. */
2528                                 gdk_window_scroll(widget->window,
2529                                                   0,
2530                                                   terminal->char_height);
2531                                 /* We need to redraw the first row of the
2532                                  * scrolling region, and anything above. */
2533                                 vte_invalidate_cells(terminal,
2534                                                      0, terminal->column_count,
2535                                                      0, start + 1);
2536                                 /* Also redraw anything below the scrolling
2537                                  * region. */
2538                                 vte_invalidate_cells(terminal,
2539                                                      0, terminal->column_count,
2540                                                      end, terminal->row_count);
2541 #endif
2542                                 vte_invalidate_cells(terminal,
2543                                                      0, terminal->column_count,
2544                                                      start, end - start + 1);
2545                         } else {
2546                                 /* If we have a background image, we need to
2547                                  * redraw the entire window. */
2548                                 vte_invalidate_cells(terminal,
2549                                                      0, terminal->column_count,
2550                                                      start, end - start + 1);
2551                         }
2552                 } else {
2553                         /* Otherwise, just move the cursor up. */
2554                         screen->cursor_current.row--;
2555                         row = screen->cursor_current.row;
2556                 }
2557         } else {
2558                 start = terminal->pvt->screen->insert_delta;
2559                 end = start + terminal->row_count - 1;
2560                 if (row == start) {
2561                         /* Insert a blank line and remove one from the bottom,
2562                          * to simulate a proper scroll without screwing up the
2563                          * history. */
2564                         vte_remove_line_int(terminal, end);
2565                         vte_insert_line_int(terminal, start);
2566                         if ((terminal->pvt->bg_image == NULL) &&
2567                             (!terminal->pvt->bg_transparent)) {
2568 #if 0
2569                                 /* Scroll the window. */
2570                                 gdk_window_scroll(widget->window,
2571                                                   0,
2572                                                   terminal->char_height);
2573                                 /* We need to redraw the bottom row. */
2574                                 vte_invalidate_cells(terminal,
2575                                                      0,
2576                                                      terminal->column_count,
2577                                                      terminal->row_count - 1,
2578                                                      1);
2579 #else
2580                                 vte_invalidate_all(terminal);
2581 #endif
2582                         } else {
2583                                 /* If we have a background image, we need to
2584                                  * redraw the entire window. */
2585                                 vte_invalidate_all(terminal);
2586                         }
2587                 } else {
2588                         /* Move the cursor up. */
2589                         screen->cursor_current.row--;
2590                         row = screen->cursor_current.row;
2591                 }
2592         }
2593 }
2594
2595 /* Cursor up. */
2596 static void
2597 vte_sequence_handler_UP(VteTerminal *terminal,
2598                         const char *match,
2599                         GQuark match_quark,
2600                         GValueArray *params)
2601 {
2602         vte_sequence_handler_multiple(terminal, match, match_quark, params,
2603                                       vte_sequence_handler_up);
2604 }
2605
2606 /* Underline start. */
2607 static void
2608 vte_sequence_handler_us(VteTerminal *terminal,
2609                         const char *match,
2610                         GQuark match_quark,
2611                         GValueArray *params)
2612 {
2613         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2614         terminal->pvt->screen->defaults.underline = 1;
2615 }
2616
2617 /* Visible bell. */
2618 static void
2619 vte_sequence_handler_vb(VteTerminal *terminal,
2620                         const char *match,
2621                         GQuark match_quark,
2622                         GValueArray *params)
2623 {
2624         Display *display;
2625         GdkDrawable *gdrawable;
2626         Drawable drawable;
2627         GC gc;
2628         gint x_offs, y_offs;
2629
2630         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2631
2632         if (GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
2633                 /* Fill the screen with the default foreground color, and then
2634                  * repaint everything, to provide visual bell. */
2635                 gdk_window_get_internal_paint_info(GTK_WIDGET(terminal)->window,
2636                                                    &gdrawable,
2637                                                    &x_offs,
2638                                                    &y_offs);
2639                 display = gdk_x11_drawable_get_xdisplay(gdrawable);
2640                 drawable = gdk_x11_drawable_get_xid(gdrawable);
2641                 gc = XCreateGC(display, drawable, 0, NULL);
2642
2643                 XSetForeground(display, gc,
2644                                terminal->pvt->palette[VTE_DEF_FG].pixel);
2645                 XFillRectangle(display, drawable, gc,
2646                                x_offs, y_offs,
2647                                terminal->column_count * terminal->char_width,
2648                                terminal->row_count * terminal->char_height);
2649                 gdk_window_process_all_updates();
2650
2651                 vte_invalidate_all(terminal);
2652                 gdk_window_process_all_updates();
2653         }
2654 }
2655
2656 /* Cursor visible. */
2657 static void
2658 vte_sequence_handler_ve(VteTerminal *terminal,
2659                         const char *match,
2660                         GQuark match_quark,
2661                         GValueArray *params)
2662 {
2663         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2664         terminal->pvt->screen->cursor_visible = TRUE;
2665 }
2666
2667 /* Cursor invisible. */
2668 static void
2669 vte_sequence_handler_vi(VteTerminal *terminal,
2670                         const char *match,
2671                         GQuark match_quark,
2672                         GValueArray *params)
2673 {
2674         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2675         terminal->pvt->screen->cursor_visible = FALSE;
2676 }
2677
2678 /* Cursor standout. */
2679 static void
2680 vte_sequence_handler_vs(VteTerminal *terminal,
2681                         const char *match,
2682                         GQuark match_quark,
2683                         GValueArray *params)
2684 {
2685         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2686         terminal->pvt->screen->cursor_visible = TRUE; /* FIXME: should be
2687                                                          *more* visible. */
2688 }
2689
2690 /* Handle ANSI color setting and related stuffs (SGR). */
2691 static void
2692 vte_sequence_handler_character_attributes(VteTerminal *terminal,
2693                                           const char *match,
2694                                           GQuark match_quark,
2695                                           GValueArray *params)
2696 {
2697         unsigned int i;
2698         GValue *value;
2699         long param;
2700         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2701         /* The default parameter is zero. */
2702         param = 0;
2703         /* Step through each numeric parameter. */
2704         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
2705                 /* If this parameter isn't a number, skip it. */
2706                 value = g_value_array_get_nth(params, i);
2707                 if (!G_VALUE_HOLDS_LONG(value)) {
2708                         continue;
2709                 }
2710                 param = g_value_get_long(value);
2711                 switch (param) {
2712                         case 0:
2713                                 vte_terminal_set_default_attributes(terminal);
2714                                 break;
2715                         case 1:
2716                                 terminal->pvt->screen->defaults.bold = 1;
2717                                 break;
2718                         case 4:
2719                                 terminal->pvt->screen->defaults.underline = 1;
2720                                 break;
2721                         case 5:
2722                                 terminal->pvt->screen->defaults.blink = 1;
2723                                 break;
2724                         case 7:
2725                                 terminal->pvt->screen->defaults.reverse = 1;
2726                                 break;
2727                         case 8:
2728                                 terminal->pvt->screen->defaults.invisible = 1;
2729                                 break;
2730                         case 21: /* one of these is the linux console */
2731                         case 22: /* one of these is ecma, i forget which */
2732                                 terminal->pvt->screen->defaults.bold = 0;
2733                                 break;
2734                         case 24:
2735                                 terminal->pvt->screen->defaults.underline = 0;
2736                                 break;
2737                         case 25:
2738                                 terminal->pvt->screen->defaults.blink = 0;
2739                                 break;
2740                         case 27:
2741                                 terminal->pvt->screen->defaults.reverse = 0;
2742                                 break;
2743                         case 28:
2744                                 terminal->pvt->screen->defaults.invisible = 0;
2745                                 break;
2746                         case 30:
2747                         case 31:
2748                         case 32:
2749                         case 33:
2750                         case 34:
2751                         case 35:
2752                         case 36:
2753                         case 37:
2754                                 terminal->pvt->screen->defaults.fore = param - 30;
2755                                 break;
2756                         case 38:
2757                                 /* default foreground, underscore */
2758                                 terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
2759                                 terminal->pvt->screen->defaults.underline = 1;
2760                                 break;
2761                         case 39:
2762                                 /* default foreground, no underscore */
2763                                 terminal->pvt->screen->defaults.fore = VTE_DEF_FG;
2764                                 terminal->pvt->screen->defaults.underline = 0;
2765                                 break;
2766                         case 40:
2767                         case 41:
2768                         case 42:
2769                         case 43:
2770                         case 44:
2771                         case 45:
2772                         case 46:
2773                         case 47:
2774                                 terminal->pvt->screen->defaults.back = param - 40;
2775                                 break;
2776                         case 49:
2777                                 /* default background */
2778                                 terminal->pvt->screen->defaults.back = VTE_DEF_BG;
2779                                 break;
2780                         case 90:
2781                         case 91:
2782                         case 92:
2783                         case 93:
2784                         case 94:
2785                         case 95:
2786                         case 96:
2787                         case 97:
2788                                 terminal->pvt->screen->defaults.fore = param - 90;
2789                                 break;
2790                         case 100:
2791                         case 101:
2792                         case 102:
2793                         case 103:
2794                         case 104:
2795                         case 105:
2796                         case 106:
2797                         case 107:
2798                                 terminal->pvt->screen->defaults.back = param - 100;
2799                                 break;
2800                 }
2801         }
2802         /* If we had no parameters, default to the defaults. */
2803         if (i == 0) {
2804                 vte_terminal_set_default_attributes(terminal);
2805         }
2806 }
2807
2808 /* Clear above the current line. */
2809 static void
2810 vte_sequence_handler_clear_above_current(VteTerminal *terminal,
2811                                          const char *match,
2812                                          GQuark match_quark,
2813                                          GValueArray *params)
2814 {
2815         GArray *rowdata;
2816         long i;
2817         VteScreen *screen;
2818         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2819         screen = terminal->pvt->screen;
2820         /* If the cursor is actually on the screen, clear data in the row
2821          * which corresponds to the cursor. */
2822         for (i = screen->insert_delta; i < screen->cursor_current.row; i++) {
2823                 if (vte_ring_next(screen->row_data) > i) {
2824                         /* Get the data for the row we're erasing. */
2825                         rowdata = vte_ring_index(screen->row_data, GArray*, i);
2826                         /* Remove it. */
2827                         while (rowdata->len > 0) {
2828                                 g_array_remove_index(rowdata, rowdata->len - 1);
2829                         }
2830                         /* Repaint this row. */
2831                         vte_invalidate_cells(terminal,
2832                                              0, terminal->column_count,
2833                                              i, 1);
2834                 }
2835         }
2836 }
2837
2838 /* Clear the entire screen. */
2839 static void
2840 vte_sequence_handler_clear_screen(VteTerminal *terminal,
2841                                   const char *match,
2842                                   GQuark match_quark,
2843                                   GValueArray *params)
2844 {
2845         GArray *rowdata;
2846         long i;
2847         VteScreen *screen;
2848         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2849         screen = terminal->pvt->screen;
2850         /* Clear the data in all of the visible rows. */
2851         for (i = screen->insert_delta;
2852              i < screen->insert_delta + terminal->row_count;
2853              i++) {
2854                 if (vte_ring_contains(screen->row_data, i)) {
2855                         /* Get the data for the row we're removing. */
2856                         rowdata = vte_ring_index(screen->row_data, GArray*, i);
2857                         /* Remove it. */
2858                         while (rowdata->len > 0) {
2859                                 g_array_remove_index(rowdata, rowdata->len - 1);
2860                         }
2861                         /* Repaint this row. */
2862                         vte_invalidate_cells(terminal,
2863                                              0, terminal->column_count,
2864                                              i, 1);
2865                 }
2866         }
2867 }
2868
2869 /* Move the cursor to the given column, 1-based. */
2870 static void
2871 vte_sequence_handler_cursor_character_absolute(VteTerminal *terminal,
2872                                                const char *match,
2873                                                GQuark match_quark,
2874                                                GValueArray *params)
2875 {
2876         vte_sequence_handler_offset(terminal, match, match_quark, params,
2877                                     -1, vte_sequence_handler_ch);
2878 }
2879
2880 /* Move the cursor to the given position, 1-based. */
2881 static void
2882 vte_sequence_handler_cursor_position(VteTerminal *terminal,
2883                                      const char *match,
2884                                      GQuark match_quark,
2885                                      GValueArray *params)
2886 {
2887         vte_sequence_handler_offset(terminal, match, match_quark, params,
2888                                     -1, vte_sequence_handler_cm);
2889 }
2890
2891 /* Set icon/window titles. */
2892 static void
2893 vte_sequence_handler_set_title_int(VteTerminal *terminal,
2894                                    const char *match,
2895                                    GQuark match_quark,
2896                                    GValueArray *params,
2897                                    const char *signal)
2898 {
2899         GValue *value;
2900         iconv_t conv;
2901         char *inbuf = NULL, *outbuf = NULL, *outbufptr = NULL;
2902         size_t inbuf_len, outbuf_len;
2903         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2904         /* Get the string parameter's value. */
2905         value = g_value_array_get_nth(params, 0);
2906         if (value) {
2907                 if (G_VALUE_HOLDS_LONG(value)) {
2908                         /* Convert the long to a string. */
2909                         outbufptr = g_strdup_printf("%ld",
2910                                                     g_value_get_long(value));
2911                 } else
2912                 if (G_VALUE_HOLDS_STRING(value)) {
2913                         /* Copy the string into the buffer. */
2914                         outbufptr = g_value_dup_string(value);
2915                 } else
2916                 if (G_VALUE_HOLDS_POINTER(value)) {
2917                         /* Convert the wide-character string into a
2918                          * multibyte string. */
2919                         conv = iconv_open("UTF-8", "WCHAR_T");
2920                         inbuf = g_value_get_pointer(value);
2921                         inbuf_len = wcslen((wchar_t*)inbuf) * sizeof(wchar_t);
2922                         outbuf_len = (inbuf_len * VTE_UTF8_BPC) + 1;
2923                         outbuf = outbufptr = g_malloc0(outbuf_len);
2924                         if (iconv(conv, &inbuf, &inbuf_len,
2925                                   &outbuf, &outbuf_len) == -1) {
2926 #ifdef VTE_DEBUG
2927                                 if (vte_debug_on(VTE_DEBUG_IO)) {
2928                                         fprintf(stderr, "Error converting %ld "
2929                                                 "title bytes (%s), skipping.\n",
2930                                                 (long) terminal->pvt->n_outgoing,
2931                                                 strerror(errno));
2932                                 }
2933 #endif
2934                                 g_free(outbufptr);
2935                                 outbufptr = NULL;
2936                         }
2937                 }
2938                 if (outbufptr != NULL) {
2939                         /* Emit the signal */
2940                         if (strcmp(signal, "window_title_changed") == 0) {
2941                                 g_free(terminal->window_title);
2942                                 terminal->window_title = outbufptr;
2943                                 vte_terminal_emit_window_title_changed(terminal);
2944                         }
2945                         else if (strcmp (signal, "icon_title_changed") == 0) {
2946                                 g_free (terminal->icon_title);
2947                                 terminal->icon_title = outbufptr;
2948                                 vte_terminal_emit_icon_title_changed(terminal);
2949                         }
2950                 }
2951         }
2952 }
2953
2954 /* Set one or the other. */
2955 static void
2956 vte_sequence_handler_set_icon_title(VteTerminal *terminal,
2957                                     const char *match,
2958                                     GQuark match_quark,
2959                                     GValueArray *params)
2960 {
2961         vte_sequence_handler_set_title_int(terminal, match, match_quark,
2962                                            params, "icon_title_changed");
2963 }
2964 static void
2965 vte_sequence_handler_set_window_title(VteTerminal *terminal,
2966                                       const char *match,
2967                                       GQuark match_quark,
2968                                       GValueArray *params)
2969 {
2970         vte_sequence_handler_set_title_int(terminal, match, match_quark,
2971                                            params, "window_title_changed");
2972 }
2973
2974 /* Set both the window and icon titles to the same string. */
2975 static void
2976 vte_sequence_handler_set_icon_and_window_title(VteTerminal *terminal,
2977                                                   const char *match,
2978                                                   GQuark match_quark,
2979                                                   GValueArray *params)
2980 {
2981         vte_sequence_handler_set_title_int(terminal, match, match_quark,
2982                                            params, "icon_title_changed");
2983         vte_sequence_handler_set_title_int(terminal, match, match_quark,
2984                                            params, "window_title_changed");
2985 }
2986
2987 /* Restrict the scrolling region. */
2988 static void
2989 vte_sequence_handler_set_scrolling_region(VteTerminal *terminal,
2990                                           const char *match,
2991                                           GQuark match_quark,
2992                                           GValueArray *params)
2993 {
2994         vte_sequence_handler_offset(terminal, match, match_quark, params,
2995                                     -1, vte_sequence_handler_cs);
2996 }
2997
2998 /* Show or hide the pointer. */
2999 static void
3000 vte_terminal_set_pointer_visible(VteTerminal *terminal, gboolean visible)
3001 {
3002         GdkCursor *cursor = NULL;
3003         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3004         if (visible || !terminal->pvt->mouse_autohide) {
3005                 if (terminal->pvt->mouse_send_xy_on_click ||
3006                     terminal->pvt->mouse_send_xy_on_button ||
3007                     terminal->pvt->mouse_hilite_tracking ||
3008                     terminal->pvt->mouse_cell_motion_tracking ||
3009                     terminal->pvt->mouse_all_motion_tracking) {
3010 #ifdef VTE_DEBUG
3011                         if (vte_debug_on(VTE_DEBUG_IO)) {
3012                                 fprintf(stderr, "Setting mousing cursor.\n");
3013                         }
3014 #endif
3015                         cursor = terminal->pvt->mouse_mousing_cursor;
3016                 } else {
3017 #ifdef VTE_DEBUG
3018                         if (vte_debug_on(VTE_DEBUG_IO)) {
3019                                 fprintf(stderr, "Setting default mouse "
3020                                         "cursor.\n");
3021                         }
3022 #endif
3023                         cursor = terminal->pvt->mouse_default_cursor;
3024                 }
3025         } else {
3026 #ifdef VTE_DEBUG
3027                 if (vte_debug_on(VTE_DEBUG_IO)) {
3028                         fprintf(stderr, "Setting to invisible cursor.\n");
3029                 }
3030 #endif
3031                 cursor = terminal->pvt->mouse_inviso_cursor;
3032         }
3033         if (cursor) {
3034                 if (GTK_WIDGET_REALIZED(GTK_WIDGET(terminal))) {
3035                         gdk_window_set_cursor((GTK_WIDGET(terminal))->window,
3036                                               cursor);
3037                 }
3038         }
3039 }
3040
3041 /* Manipulate certain terminal attributes. */
3042 static void
3043 vte_sequence_handler_decset_internal(VteTerminal *terminal,
3044                                      int setting,
3045                                      gboolean restore,
3046                                      gboolean save,
3047                                      gboolean set)
3048 {
3049         gpointer p;
3050         int i;
3051         struct {
3052                 int setting;
3053                 gboolean *bvalue;
3054                 gint *ivalue;
3055                 gpointer *pvalue;
3056                 gpointer fvalue;
3057                 gpointer tvalue;
3058         } settings[] = {
3059                 /* Application/normal keypad. */
3060                 {1, NULL, &terminal->pvt->keypad, NULL,
3061                  GINT_TO_POINTER(VTE_KEYPAD_NORMAL),
3062                  GINT_TO_POINTER(VTE_KEYPAD_APPLICATION),},
3063                 /* Reverse video. */
3064                 {5, &terminal->pvt->screen->reverse_mode, NULL, NULL,
3065                  GINT_TO_POINTER(FALSE),
3066                  GINT_TO_POINTER(TRUE),},
3067                 /* Send-coords-on-click. */
3068                 {9, &terminal->pvt->mouse_send_xy_on_click, NULL, NULL,
3069                  GINT_TO_POINTER(FALSE),
3070                  GINT_TO_POINTER(TRUE),},
3071                 /* Cursor visible. */
3072                 {25, &terminal->pvt->screen->cursor_visible, NULL, NULL,
3073                  GINT_TO_POINTER(FALSE),
3074                  GINT_TO_POINTER(TRUE),},
3075                 /* Alternate screen. */
3076                 {47, NULL, NULL, (gpointer*) &terminal->pvt->screen,
3077                  &terminal->pvt->normal_screen,
3078                  &terminal->pvt->alternate_screen,},
3079                 /* Send-coords-on-button. */
3080                 {1000, &terminal->pvt->mouse_send_xy_on_button, NULL, NULL,
3081                  GINT_TO_POINTER(FALSE),
3082                  GINT_TO_POINTER(TRUE),},
3083                 /* Hilite tracking*/
3084                 {1001, &terminal->pvt->mouse_hilite_tracking, NULL, NULL,
3085                  GINT_TO_POINTER(FALSE),
3086                  GINT_TO_POINTER(TRUE),},
3087                 /* Cell motion tracking*/
3088                 {1002, &terminal->pvt->mouse_cell_motion_tracking, NULL, NULL,
3089                  GINT_TO_POINTER(FALSE),
3090                  GINT_TO_POINTER(TRUE),},
3091                 /* All motion tracking*/
3092                 {1003, &terminal->pvt->mouse_all_motion_tracking, NULL, NULL,
3093                  GINT_TO_POINTER(FALSE),
3094                  GINT_TO_POINTER(TRUE),},
3095         };
3096
3097         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3098
3099         /* Handle the setting. */
3100         for (i = 0; i < G_N_ELEMENTS(settings); i++)
3101         if (settings[i].setting == setting) {
3102                 /* Read the old setting. */
3103                 if (restore) {
3104                         p = g_hash_table_lookup(terminal->pvt->dec_saved,
3105                                                 GINT_TO_POINTER(setting));
3106                         set = (p != NULL);
3107 #ifdef VTE_DEBUG
3108                         if (vte_debug_on(VTE_DEBUG_PARSE)) {
3109                                 fprintf(stderr, "Setting %d was %s.\n",
3110                                         setting, set ? "set" : "unset");
3111                         }
3112 #endif
3113                 }
3114                 /* Save the current setting. */
3115                 if (save) {
3116                         if (settings[i].bvalue) {
3117                                 set = *(settings[i].bvalue) != FALSE;
3118                         } else
3119                         if (settings[i].ivalue) {
3120                                 set = *(settings[i].ivalue) ==
3121                                       GPOINTER_TO_INT(settings[i].tvalue);
3122                         } else
3123                         if (settings[i].pvalue) {
3124                                 set = *(settings[i].pvalue) ==
3125                                       settings[i].tvalue;
3126                         }
3127 #ifdef VTE_DEBUG
3128                         if (vte_debug_on(VTE_DEBUG_PARSE)) {
3129                                 fprintf(stderr, "Setting %d is %s, saving.\n",
3130                                         setting, set ? "set" : "unset");
3131                         }
3132 #endif
3133                         g_hash_table_insert(terminal->pvt->dec_saved,
3134                                             GINT_TO_POINTER(setting),
3135                                             GINT_TO_POINTER(set));
3136                 }
3137                 /* Change the current setting to match the new/saved value. */
3138                 if (!save) {
3139 #ifdef VTE_DEBUG
3140                         if (vte_debug_on(VTE_DEBUG_PARSE)) {
3141                                 fprintf(stderr, "Setting %d to %s.\n",
3142                                         setting, set ? "set" : "unset");
3143                         }
3144 #endif
3145                         if (settings[i].bvalue) {
3146                                 *(settings[i].bvalue) = set;
3147                         } else
3148                         if (settings[i].ivalue) {
3149                                 *(settings[i].ivalue) = set ?
3150                                         GPOINTER_TO_INT(settings[i].tvalue) :
3151                                         GPOINTER_TO_INT(settings[i].fvalue);
3152                         } else
3153                         if (settings[i].pvalue) {
3154                                 *(settings[i].pvalue) = set ?
3155                                         settings[i].tvalue :
3156                                         settings[i].fvalue;
3157                         }
3158                 }
3159         }
3160
3161         /* Do whatever's necessary when the setting changes. */
3162         switch (setting) {
3163                 case 5:
3164                         /* Repaint everything in reverse mode. */
3165                         vte_invalidate_all(terminal);
3166                         break;
3167                 case 25:
3168                         /* Repaint the cell the cursor is in. */
3169                         vte_invalidate_cursor_once(terminal);
3170                         break;
3171                 case 47:
3172                         /* Reset scrollbars and repaint everything. */
3173                         vte_terminal_adjust_adjustments(terminal);
3174                         vte_invalidate_all(terminal);
3175                         break;
3176                 case 9:
3177                 case 1000:
3178                 case 1001:
3179                 case 1002:
3180                 case 1003:
3181                         /* Make the pointer visible. */
3182                         vte_terminal_set_pointer_visible(terminal, TRUE);
3183                         break;
3184                 default:
3185                         break;
3186         }
3187 }
3188
3189 /* Set the application or normal keypad. */
3190 static void
3191 vte_sequence_handler_application_keypad(VteTerminal *terminal,
3192                                         const char *match,
3193                                         GQuark match_quark,
3194                                         GValueArray *params)
3195 {
3196         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3197         terminal->pvt->keypad = VTE_KEYPAD_APPLICATION;
3198 }
3199
3200 static void
3201 vte_sequence_handler_normal_keypad(VteTerminal *terminal,
3202                                    const char *match,
3203                                    GQuark match_quark,
3204                                    GValueArray *params)
3205 {
3206         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3207         terminal->pvt->keypad = VTE_KEYPAD_NORMAL;
3208 }
3209
3210 /* Move the cursor. */
3211 static void
3212 vte_sequence_handler_character_position_absolute(VteTerminal *terminal,
3213                                                  const char *match,
3214                                                  GQuark match_quark,
3215                                                  GValueArray *params)
3216 {
3217         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3218         vte_sequence_handler_offset(terminal, match, match_quark, params,
3219                                     -1, vte_sequence_handler_ch);
3220 }
3221 static void
3222 vte_sequence_handler_line_position_absolute(VteTerminal *terminal,
3223                                             const char *match,
3224                                             GQuark match_quark,
3225                                             GValueArray *params)
3226 {
3227         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3228         vte_sequence_handler_offset(terminal, match, match_quark, params,
3229                                     -1, vte_sequence_handler_cv);
3230 }
3231
3232 /* Set certain terminal attributes. */
3233 static void
3234 vte_sequence_handler_decset(VteTerminal *terminal,
3235                             const char *match,
3236                             GQuark match_quark,
3237                             GValueArray *params)
3238 {
3239         GValue *value;
3240         long setting;
3241         int i;
3242         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3243         if ((params == NULL) || (params->n_values == 0)) {
3244                 return;
3245         }
3246         for (i = 0; i < params->n_values; i++) {
3247                 value = g_value_array_get_nth(params, i);
3248                 if (!G_VALUE_HOLDS_LONG(value)) {
3249                         continue;
3250                 }
3251                 setting = g_value_get_long(value);
3252                 vte_sequence_handler_decset_internal(terminal, setting,
3253                                                      FALSE, FALSE, TRUE);
3254         }
3255 }
3256
3257 /* Unset certain terminal attributes. */
3258 static void
3259 vte_sequence_handler_decreset(VteTerminal *terminal,
3260                               const char *match,
3261                               GQuark match_quark,
3262                               GValueArray *params)
3263 {
3264         GValue *value;
3265         long setting;
3266         int i;
3267         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3268         if ((params == NULL) || (params->n_values == 0)) {
3269                 return;
3270         }
3271         for (i = 0; i < params->n_values; i++) {
3272                 value = g_value_array_get_nth(params, i);
3273                 if (!G_VALUE_HOLDS_LONG(value)) {
3274                         continue;
3275                 }
3276                 setting = g_value_get_long(value);
3277                 vte_sequence_handler_decset_internal(terminal, setting,
3278                                                      FALSE, FALSE, FALSE);
3279         }
3280 }
3281
3282 /* Erase certain lines in the display. */
3283 static void
3284 vte_sequence_handler_erase_in_display(VteTerminal *terminal,
3285                                       const char *match,
3286                                       GQuark match_quark,
3287                                       GValueArray *params)
3288 {
3289         GValue *value;
3290         long param;
3291         int i;
3292         /* The default parameter is 0. */
3293         param = 0;
3294         /* Pull out a parameter. */
3295         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
3296                 value = g_value_array_get_nth(params, i);
3297                 if (!G_VALUE_HOLDS_LONG(value)) {
3298                         continue;
3299                 }
3300                 param = g_value_get_long(value);
3301         }
3302         /* Clear the right area. */
3303         switch (param) {
3304                 case 0:
3305                         /* Clear below the current line. */
3306                         vte_sequence_handler_cd(terminal, NULL, 0, NULL);
3307                         break;
3308                 case 1:
3309                         /* Clear above the current line. */
3310                         vte_sequence_handler_clear_above_current(terminal,
3311                                                                  NULL,
3312                                                                  0,
3313                                                                  NULL);
3314                         break;
3315                 case 2:
3316                         /* Clear the entire screen. */
3317                         vte_sequence_handler_clear_screen(terminal,
3318                                                           NULL,
3319                                                           0,
3320                                                           NULL);
3321                         break;
3322                 default:
3323                         break;
3324         }
3325 }
3326
3327 /* Erase certain parts of the current line in the display. */
3328 static void
3329 vte_sequence_handler_erase_in_line(VteTerminal *terminal,
3330                                    const char *match,
3331                                    GQuark match_quark,
3332                                    GValueArray *params)
3333 {
3334         GValue *value;
3335         long param;
3336         int i;
3337         /* The default parameter is 0. */
3338         param = 0;
3339         /* Pull out a parameter. */
3340         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
3341                 value = g_value_array_get_nth(params, i);
3342                 if (!G_VALUE_HOLDS_LONG(value)) {
3343                         continue;
3344                 }
3345                 param = g_value_get_long(value);
3346         }
3347         /* Clear the right area. */
3348         switch (param) {
3349                 case 0:
3350                         /* Clear to end of the line. */
3351                         vte_sequence_handler_ce(terminal, NULL, 0, NULL);
3352                         break;
3353                 case 1:
3354                         /* Clear to start of the line. */
3355                         vte_sequence_handler_cb(terminal, NULL, 0, NULL);
3356                         break;
3357                 case 2:
3358                         /* Clear the entire line. */
3359                         vte_sequence_handler_clear_current_line(terminal,
3360                                                                 NULL, 0, NULL);
3361                         break;
3362                 default:
3363                         break;
3364         }
3365 }
3366
3367 /* Perform a full-bore reset. */
3368 static void
3369 vte_sequence_handler_full_reset(VteTerminal *terminal,
3370                                 const char *match,
3371                                 GQuark match_quark,
3372                                 GValueArray *params)
3373 {
3374         vte_terminal_reset(terminal, TRUE, TRUE);
3375 }
3376
3377 /* Insert a certain number of lines below the current cursor. */
3378 static void
3379 vte_sequence_handler_insert_lines(VteTerminal *terminal,
3380                                   const char *match,
3381