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