00e5addbab2477cd374337efd8d12f4ac94db713
[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, passing the string. */
1532                         g_signal_emit_by_name(terminal, signal, outbufptr);
1533                         g_free(outbufptr);
1534                 }
1535         }
1536 }
1537
1538 /* Set one or the other. */
1539 static void
1540 vte_sequence_handler_set_icon_title(VteTerminal *terminal,
1541                                     const char *match,
1542                                     GQuark match_quark,
1543                                     GValueArray *params)
1544 {
1545         vte_sequence_handler_set_title_int(terminal, match, match_quark,
1546                                            params, "set_icon_title");
1547 }
1548 static void
1549 vte_sequence_handler_set_window_title(VteTerminal *terminal,
1550                                       const char *match,
1551                                       GQuark match_quark,
1552                                       GValueArray *params)
1553 {
1554         vte_sequence_handler_set_title_int(terminal, match, match_quark,
1555                                            params, "set_window_title");
1556 }
1557
1558 /* Set both the window and icon titles to the same string. */
1559 static void
1560 vte_sequence_handler_set_icon_and_window_title(VteTerminal *terminal,
1561                                                   const char *match,
1562                                                   GQuark match_quark,
1563                                                   GValueArray *params)
1564 {
1565         vte_sequence_handler_set_title_int(terminal, match, match_quark,
1566                                            params, "set_icon_title");
1567         vte_sequence_handler_set_title_int(terminal, match, match_quark,
1568                                            params, "set_window_title");
1569 }
1570
1571 /* Restrict the scrolling region. */
1572 static void
1573 vte_sequence_handler_set_scrolling_region(VteTerminal *terminal,
1574                                           const char *match,
1575                                           GQuark match_quark,
1576                                           GValueArray *params)
1577 {
1578         vte_sequence_handler_offset(terminal, match, match_quark, params,
1579                                     -1, vte_sequence_handler_cs);
1580 }
1581
1582 /* Manipulate certain terminal attributes. */
1583 static void
1584 vte_sequence_handler_decset_internal(VteTerminal *terminal,
1585                                      const char *match,
1586                                      GQuark match_quark,
1587                                      GValueArray *params,
1588                                      gboolean set)
1589 {
1590         GValue *value;
1591         long param;
1592         int i;
1593         if ((params == NULL) || (params->n_values == 0)) {
1594                 return;
1595         }
1596         for (i = 0; i < params->n_values; i++) {
1597                 value = g_value_array_get_nth(params, i);
1598                 if (!G_VALUE_HOLDS_LONG(value)) {
1599                         continue;
1600                 }
1601                 param = g_value_get_long(value);
1602                 switch (param) {
1603                         case 1:
1604                                 /* Set the application keypad. */
1605                                 terminal->pvt->keypad = set ?
1606                                                         VTE_KEYPAD_NORMAL :
1607                                                         VTE_KEYPAD_APPLICATION;
1608                                 break;
1609                         case 2:
1610                                 /* FIXME: reset alternate character sets to
1611                                  * ASCII. */
1612                                 break;
1613                         case 3:
1614                                 /* FIXME: set 132 (reset to 80) column mode. */
1615                                 break;
1616                         case 4:
1617                                 /* FIXME: set or unset smooth-scrolling. */
1618                                 break;
1619                         case 5:
1620                                 /* normal or reverse video. */
1621                                 terminal->pvt->screen->defaults.reverse = set;
1622                                 break;
1623                         case 6:
1624                                 /* FIXME: origin or normal cursor mode. */
1625                                 break;
1626                         case 7:
1627                                 /* FIXME: set or unset wraparound mode. */
1628                                 break;
1629                         case 8:
1630                                 /* FIXME: set or unset autorepeat keys. */
1631                                 break;
1632                         case 9:
1633                                 /* FIXME: send mouse X and Y on button. */
1634                                 break;
1635                         case 38:
1636                                 /* FIXME: Tektronix/Xterm mode. */
1637                                 break;
1638                         case 40:
1639                                 /* FIXME: Allow/disallow 80/132 column mode. */
1640                                 break;
1641                         case 41:
1642                                 /* FIXME: more(1) fix. */
1643                                 break;
1644                         case 44:
1645                                 /* FIXME: set/unset margin bell. */
1646                                 break;
1647                         case 45:
1648                                 /* FIXME: set/unset reverse-wraparound mode. */
1649                                 break;
1650                         case 46:
1651                                 /* FIXME(?): enable/disable logging. */
1652                                 break;
1653                         case 47:
1654                                 /* Set or restore alternate screen. */
1655                                 terminal->pvt->screen = set ?
1656                                                         &terminal->pvt->alternate_screen :
1657                                                         &terminal->pvt->normal_screen;
1658                                 /* Fixup the scrollbars. */
1659                                 vte_terminal_adjust_adjustments(terminal);
1660                                 /* Force the screen to be redrawn. */
1661                                 vte_invalidate_cells(terminal,
1662                                                      0,
1663                                                      terminal->column_count,
1664                                                      terminal->pvt->screen->scroll_delta,
1665                                                      terminal->row_count);
1666                                 break;
1667                         case 1000:
1668                                 /* FIXME: send mouse X and Y on press and
1669                                  * release. */
1670                                 break;
1671                         case 1001:
1672                                 /* FIXME: use (or not) hilite mouse tracking. */
1673                                 break;
1674                         default:
1675                                 break;
1676                 }
1677         }
1678 }
1679
1680 /* Set the application or normal keypad. */
1681 static void
1682 vte_sequence_handler_application_keypad(VteTerminal *terminal,
1683                                         const char *match,
1684                                         GQuark match_quark,
1685                                         GValueArray *params)
1686 {
1687         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1688         terminal->pvt->keypad = VTE_KEYPAD_APPLICATION;
1689 }
1690
1691 static void
1692 vte_sequence_handler_normal_keypad(VteTerminal *terminal,
1693                                    const char *match,
1694                                    GQuark match_quark,
1695                                    GValueArray *params)
1696 {
1697         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1698         terminal->pvt->keypad = VTE_KEYPAD_NORMAL;
1699 }
1700
1701 /* Move the cursor. */
1702 static void
1703 vte_sequence_handler_character_position_absolute(VteTerminal *terminal,
1704                                                  const char *match,
1705                                                  GQuark match_quark,
1706                                                  GValueArray *params)
1707 {
1708         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1709         vte_sequence_handler_offset(terminal, match, match_quark, params,
1710                                     -1, vte_sequence_handler_ch);
1711 }
1712 static void
1713 vte_sequence_handler_line_position_absolute(VteTerminal *terminal,
1714                                             const char *match,
1715                                             GQuark match_quark,
1716                                             GValueArray *params)
1717 {
1718         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1719         vte_sequence_handler_offset(terminal, match, match_quark, params,
1720                                     -1, vte_sequence_handler_cv);
1721 }
1722
1723 /* Set certain terminal attributes. */
1724 static void
1725 vte_sequence_handler_decset(VteTerminal *terminal,
1726                             const char *match,
1727                             GQuark match_quark,
1728                             GValueArray *params)
1729 {
1730         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1731         vte_sequence_handler_decset_internal(terminal, match, match_quark,
1732                                              params, TRUE);
1733 }
1734
1735 /* Unset certain terminal attributes. */
1736 static void
1737 vte_sequence_handler_decreset(VteTerminal *terminal,
1738                               const char *match,
1739                               GQuark match_quark,
1740                               GValueArray *params)
1741 {
1742         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1743         vte_sequence_handler_decset_internal(terminal, match, match_quark,
1744                                              params, FALSE);
1745 }
1746
1747 /* Erase certain lines in the display. */
1748 static void
1749 vte_sequence_handler_erase_in_display(VteTerminal *terminal,
1750                                       const char *match,
1751                                       GQuark match_quark,
1752                                       GValueArray *params)
1753 {
1754         GValue *value;
1755         long param;
1756         int i;
1757         /* The default parameter is 0. */
1758         param = 0;
1759         /* Pull out a parameter. */
1760         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
1761                 value = g_value_array_get_nth(params, i);
1762                 if (!G_VALUE_HOLDS_LONG(value)) {
1763                         continue;
1764                 }
1765                 param = g_value_get_long(value);
1766         }
1767         /* Clear the right area. */
1768         switch (param) {
1769                 case 0:
1770                         /* Clear below the current line. */
1771                         vte_sequence_handler_cd(terminal, NULL, 0, NULL);
1772                         break;
1773                 case 1:
1774                         /* Clear above the current line. */
1775                         vte_sequence_handler_clear_above_current(terminal,
1776                                                                  NULL,
1777                                                                  0,
1778                                                                  NULL);
1779                         break;
1780                 case 2:
1781                         /* Clear the entire screen. */
1782                         vte_sequence_handler_clear_screen(terminal,
1783                                                           NULL,
1784                                                           0,
1785                                                           NULL);
1786                         break;
1787                 default:
1788                         break;
1789         }
1790 }
1791
1792 /* Erase certain parts of the current line in the display. */
1793 static void
1794 vte_sequence_handler_erase_in_line(VteTerminal *terminal,
1795                                    const char *match,
1796                                    GQuark match_quark,
1797                                    GValueArray *params)
1798 {
1799         GValue *value;
1800         long param;
1801         int i;
1802         /* The default parameter is 0. */
1803         param = 0;
1804         /* Pull out a parameter. */
1805         for (i = 0; (params != NULL) && (i < params->n_values); i++) {
1806                 value = g_value_array_get_nth(params, i);
1807                 if (!G_VALUE_HOLDS_LONG(value)) {
1808                         continue;
1809                 }
1810                 param = g_value_get_long(value);
1811         }
1812         /* Clear the right area. */
1813         switch (param) {
1814                 case 0:
1815                         /* Clear to end of the line. */
1816                         vte_sequence_handler_ce(terminal, NULL, 0, NULL);
1817                         break;
1818                 case 1:
1819                         /* Clear to start of the line. */
1820                         vte_sequence_handler_cb(terminal, NULL, 0, NULL);
1821                         break;
1822                 case 2:
1823                         /* Clear the entire line. */
1824                         vte_sequence_handler_clear_current_line(terminal,
1825                                                                 NULL, 0, NULL);
1826                         break;
1827                 default:
1828                         break;
1829         }
1830 }
1831
1832 /* Insert a certain number of lines below the current cursor. */
1833 static void
1834 vte_sequence_handler_insert_lines(VteTerminal *terminal,
1835                                   const char *match,
1836                                   GQuark match_quark,
1837                                   GValueArray *params)
1838 {
1839         GValue *value;
1840         struct _VteScreen *screen;
1841         long param, end, row;
1842         int i;
1843         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1844         screen = terminal->pvt->screen;
1845         /* The default is one. */
1846         param = 1;
1847         /* Extract any parameters. */
1848         if ((params != NULL) && (params->n_values > 0)) {
1849                 value = g_value_array_get_nth(params, 0);
1850                 param = g_value_get_long(value);
1851         }
1852         row = screen->cursor_current.row;
1853         end = screen->scrolling_region.end + screen->insert_delta;
1854         /* Insert the new lines at the cursor. */
1855         for (i = 0; i < param; i++) {
1856                 /* Clear lines off the bottom of the scrolling region. */
1857                 if (screen->scrolling_restricted) {
1858                         /* Clear a line off the end of the region. */
1859                         vte_remove_line_int(terminal, end);
1860                 }
1861                 vte_insert_line_int(terminal, row);
1862         }
1863         /* Refresh the modified area. */
1864         vte_invalidate_cells(terminal,
1865                              0, terminal->column_count,
1866                              row, end - row + 1);
1867 }
1868
1869 /* Delete certain lines from the scrolling region. */
1870 static void
1871 vte_sequence_handler_delete_lines(VteTerminal *terminal,
1872                                   const char *match,
1873                                   GQuark match_quark,
1874                                   GValueArray *params)
1875 {
1876         GValue *value;
1877         struct _VteScreen *screen;
1878         long param, end, row;
1879         int i;
1880         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1881         screen = terminal->pvt->screen;
1882         /* The default is one. */
1883         param = 1;
1884         /* Extract any parameters. */
1885         if ((params != NULL) && (params->n_values > 0)) {
1886                 value = g_value_array_get_nth(params, 0);
1887                 param = g_value_get_long(value);
1888         }
1889         /* Clear the lines which we need to clear. */
1890         row = screen->cursor_current.row;
1891         end = screen->insert_delta + screen->scrolling_region.end;
1892         /* Clear them from below the current cursor. */
1893         for (i = 0; i < param; i++) {
1894                 /* Insert any new empty lines. */
1895                 if (screen->scrolling_restricted) {
1896                         vte_insert_line_int(terminal, end);
1897                 }
1898                 /* Remove the line at the top of the area. */
1899                 vte_remove_line_int(terminal, row);
1900         }
1901         /* Refresh the modified area. */
1902         vte_invalidate_cells(terminal,
1903                              0, terminal->column_count,
1904                              row, end - row + 1);
1905 }
1906
1907 /* Index.  Move the cursor down a row, and if it's in a scrolling region,
1908  * scroll to keep it on the screen. */
1909 static void
1910 vte_sequence_handler_index(VteTerminal *terminal,
1911                            const char *match,
1912                            GQuark match_quark,
1913                            GValueArray *params)
1914 {
1915         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1916         vte_sequence_handler_DO(terminal, match, match_quark, params);
1917 }
1918
1919 /* Reverse index.  Move the cursor up a row, and if it's in a scrolling
1920  * region, scroll to keep it on the screen. */
1921 static void
1922 vte_sequence_handler_reverse_index(VteTerminal *terminal,
1923                                    const char *match,
1924                                    GQuark match_quark,
1925                                    GValueArray *params)
1926 {
1927         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1928         vte_sequence_handler_UP(terminal, match, match_quark, params);
1929 }
1930
1931 /* Set the terminal encoding. */
1932 static void
1933 vte_sequence_handler_iso8859_1(VteTerminal *terminal,
1934                                const char *match,
1935                                GQuark match_quark,
1936                                GValueArray *params)
1937 {
1938         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1939         vte_terminal_set_encoding(terminal, "ISO-8859-1");
1940 }
1941
1942 static void
1943 vte_sequence_handler_utf_8(VteTerminal *terminal,
1944                            const char *match,
1945                            GQuark match_quark,
1946                            GValueArray *params)
1947 {
1948         g_return_if_fail(VTE_IS_TERMINAL(terminal));
1949         vte_terminal_set_encoding(terminal, "UTF-8");
1950 }
1951
1952 /* The table of handlers.  Primarily used at initialization time. */
1953 static struct {
1954         const char *code;
1955         VteTerminalSequenceHandler handler;
1956 } vte_sequence_handlers[] = {
1957         {"!1", NULL},
1958         {"!2", NULL},
1959         {"!3", NULL},
1960
1961         {"#1", NULL},
1962         {"#2", NULL},
1963         {"#3", NULL},
1964         {"#4", NULL},
1965
1966         {"%1", NULL},
1967         {"%2", NULL},
1968         {"%3", NULL},
1969         {"%4", NULL},
1970         {"%5", NULL},
1971         {"%6", NULL},
1972         {"%7", NULL},
1973         {"%8", NULL},
1974         {"%9", NULL},
1975         {"%a", NULL},
1976         {"%b", NULL},
1977         {"%c", NULL},
1978         {"%d", NULL},
1979         {"%e", NULL},
1980         {"%f", NULL},
1981         {"%g", NULL},
1982         {"%h", NULL},
1983         {"%i", NULL},
1984         {"%j", NULL},
1985
1986         {"&0", NULL},
1987         {"&1", NULL},
1988         {"&2", NULL},
1989         {"&3", NULL},
1990         {"&4", NULL},
1991         {"&5", NULL},
1992         {"&6", NULL},
1993         {"&7", NULL},
1994         {"&8", NULL},
1995         {"&9", NULL},
1996
1997         {"*0", NULL},
1998         {"*1", NULL},
1999         {"*2", NULL},
2000         {"*3", NULL},
2001         {"*4", NULL},
2002         {"*5", NULL},
2003         {"*6", NULL},
2004         {"*7", NULL},
2005         {"*8", NULL},
2006         {"*9", NULL},
2007
2008         {"@0", NULL},
2009         {"@1", NULL},
2010         {"@2", NULL},
2011         {"@3", NULL},
2012         {"@4", NULL},
2013         {"@5", NULL},
2014         {"@6", NULL},
2015         {"@7", NULL},
2016         {"@8", NULL},
2017         {"@9", NULL},
2018
2019         {"al", vte_sequence_handler_al},
2020         {"AL", vte_sequence_handler_AL},
2021         {"ac", NULL},
2022         {"ae", vte_sequence_handler_ae},
2023         {"as", vte_sequence_handler_as},
2024
2025         {"bc", NULL},
2026         {"bl", vte_sequence_handler_bl},
2027         {"bt", NULL},
2028
2029         {"cb", vte_sequence_handler_cb},
2030         {"cc", NULL},
2031         {"cd", vte_sequence_handler_cd},
2032         {"ce", vte_sequence_handler_ce},
2033         {"ch", vte_sequence_handler_ch},
2034         {"cl", vte_sequence_handler_cl},
2035         {"cm", vte_sequence_handler_cm},
2036         {"cr", vte_sequence_handler_cr},
2037         {"cs", vte_sequence_handler_cs},
2038         {"ct", NULL},
2039         {"cv", vte_sequence_handler_cv},
2040
2041         {"dc", NULL},
2042         {"DC", NULL},
2043         {"dl", vte_sequence_handler_dl},
2044         {"DL", vte_sequence_handler_DL},
2045         {"dm", NULL},
2046         {"do", vte_sequence_handler_do},
2047         {"DO", vte_sequence_handler_DO},
2048         {"ds", NULL},
2049
2050         {"eA", NULL},
2051         {"ec", NULL},
2052         {"ed", NULL},
2053         {"ei", vte_sequence_handler_ei},
2054
2055         {"ff", NULL},
2056         {"fs", NULL},
2057         {"F1", NULL},
2058         {"F2", NULL},
2059         {"F3", NULL},
2060         {"F4", NULL},
2061         {"F5", NULL},
2062         {"F6", NULL},
2063         {"F7", NULL},
2064         {"F8", NULL},
2065         {"F9", NULL},
2066         {"FA", NULL},
2067         {"FB", NULL},
2068         {"FC", NULL},
2069         {"FD", NULL},
2070         {"FE", NULL},
2071         {"FF", NULL},
2072         {"FG", NULL},
2073         {"FH", NULL},
2074         {"FI", NULL},
2075         {"FJ", NULL},
2076         {"FK", NULL},
2077         {"FL", NULL},
2078         {"FM", NULL},
2079         {"FN", NULL},
2080         {"FO", NULL},
2081         {"FP", NULL},
2082         {"FQ", NULL},
2083         {"FR", NULL},
2084         {"FS", NULL},
2085         {"FT", NULL},
2086         {"FU", NULL},
2087         {"FV", NULL},
2088         {"FW", NULL},
2089         {"FX", NULL},
2090         {"FY", NULL},
2091         {"FZ", NULL},
2092
2093         {"Fa", NULL},
2094         {"Fb", NULL},
2095         {"Fc", NULL},
2096         {"Fd", NULL},
2097         {"Fe", NULL},
2098         {"Ff", NULL},
2099         {"Fg", NULL},
2100         {"Fh", NULL},
2101         {"Fi", NULL},
2102         {"Fj", NULL},
2103         {"Fk", NULL},
2104         {"Fl", NULL},
2105         {"Fm", NULL},
2106         {"Fn", NULL},
2107         {"Fo", NULL},
2108         {"Fp", NULL},
2109         {"Fq", NULL},
2110         {"Fr", NULL},
2111
2112         {"hd", NULL},
2113         {"ho", vte_sequence_handler_ho},
2114         {"hu", NULL},
2115
2116         {"i1", NULL},
2117         {"i3", NULL},
2118
2119         {"is", NULL},
2120         {"ic", NULL},
2121         {"IC", NULL},
2122         {"if", NULL},
2123         {"im", vte_sequence_handler_im},
2124         {"ip", NULL},
2125         {"iP", NULL},
2126
2127         {"K1", NULL},
2128         {"K2", NULL},
2129         {"K3", NULL},
2130         {"K4", NULL},
2131         {"K5", NULL},
2132
2133         {"k0", NULL},
2134         {"k1", NULL},
2135         {"k2", NULL},
2136         {"k3", NULL},
2137         {"k4", NULL},
2138         {"k5", NULL},
2139         {"k6", NULL},
2140         {"k7", NULL},
2141         {"k8", NULL},
2142         {"k9", NULL},
2143         {"k;", NULL},
2144         {"ka", NULL},
2145         {"kA", NULL},
2146         {"kb", NULL},
2147         {"kB", NULL},
2148         {"kC", NULL},
2149         {"kd", NULL},
2150         {"kD", NULL},
2151         {"ke", NULL},
2152         {"kE", NULL},
2153         {"kF", NULL},
2154         {"kh", NULL},
2155         {"kH", NULL},
2156         {"kI", NULL},
2157         {"kl", NULL},
2158         {"kL", NULL},
2159         {"kM", NULL},
2160         {"kN", NULL},
2161         {"kP", NULL},
2162         {"kr", NULL},
2163         {"kR", NULL},
2164         {"ks", NULL},
2165         {"kS", NULL},
2166         {"kt", NULL},
2167         {"kT", NULL},
2168         {"ku", NULL},
2169
2170         {"l0", NULL},
2171         {"l1", NULL},
2172         {"l2", NULL},
2173         {"l3", NULL},
2174         {"l4", NULL},
2175         {"l5", NULL},
2176         {"l6", NULL},
2177         {"l7", NULL},
2178         {"l8", NULL},
2179         {"l9", NULL},
2180
2181         {"la", NULL},
2182         {"le", vte_sequence_handler_le},
2183         {"LE", vte_sequence_handler_LE},
2184         {"LF", NULL},
2185         {"ll", NULL},
2186         {"LO", NULL},
2187
2188         {"mb", vte_sequence_handler_mb},
2189         {"MC", NULL},
2190         {"md", vte_sequence_handler_md},
2191         {"me", vte_sequence_handler_me},
2192         {"mh", NULL},
2193         {"mk", vte_sequence_handler_mk},
2194         {"ML", NULL},
2195         {"mm", NULL},
2196         {"mo", NULL},
2197         {"mp", NULL},
2198         {"mr", vte_sequence_handler_mr},
2199         {"MR", NULL},
2200
2201         {"nd", vte_sequence_handler_nd},
2202         {"nw", NULL},
2203
2204         {"pc", NULL},
2205         {"pf", NULL},
2206         {"pk", NULL},
2207         {"pl", NULL},
2208         {"pn", NULL},
2209         {"po", NULL},
2210         {"pO", NULL},
2211         {"ps", NULL},
2212         {"px", NULL},
2213
2214         {"r1", NULL},
2215         {"r2", NULL},
2216         {"r3", NULL},
2217
2218         {"..rp", NULL},
2219         {"RA", NULL},
2220         {"rc", vte_sequence_handler_rc},
2221         {"rf", NULL},
2222         {"RF", NULL},
2223         {"RI", vte_sequence_handler_RI},
2224         {"rp", NULL},
2225         {"rP", NULL},
2226         {"rs", NULL},
2227         {"RX", NULL},
2228
2229         {"s0", NULL},
2230         {"s1", NULL},
2231         {"s2", NULL},
2232         {"s3", NULL},
2233
2234         {"..sa", NULL},
2235         {"sa", NULL},
2236         {"SA", NULL},
2237         {"sc", vte_sequence_handler_sc},
2238         {"se", vte_sequence_handler_se},
2239         {"sf", vte_sequence_handler_do},
2240         {"SF", vte_sequence_handler_DO},
2241         {"so", vte_sequence_handler_so},
2242         {"sr", vte_sequence_handler_up},
2243         {"SR", vte_sequence_handler_UP},
2244         {"st", NULL},
2245         {"SX", NULL},
2246
2247         {"ta", vte_sequence_handler_ta},
2248         {"te", NULL},
2249         {"ti", NULL},
2250         {"ts", NULL},
2251
2252         {"uc", NULL},
2253         {"ue", vte_sequence_handler_ue},
2254         {"up", vte_sequence_handler_up},
2255         {"UP", vte_sequence_handler_UP},
2256         {"us", vte_sequence_handler_us},
2257
2258         {"vb", NULL},
2259         {"ve", NULL},
2260         {"vi", vte_sequence_handler_vi},
2261         {"vs", vte_sequence_handler_vs},
2262
2263         {"wi", NULL},
2264
2265         {"XF", NULL},
2266
2267         {"character-attributes", vte_sequence_handler_character_attributes},
2268
2269         {"cursor-backward", vte_sequence_handler_le},
2270         {"cursor-forward", vte_sequence_handler_RI},
2271         {"cursor-up", vte_sequence_handler_UP},
2272         {"cursor-down", vte_sequence_handler_DO},
2273         {"cursor-position", vte_sequence_handler_cursor_position},
2274
2275         {"set-icon-title",
2276          vte_sequence_handler_set_icon_title},
2277         {"set-window-title",
2278          vte_sequence_handler_set_window_title},
2279         {"set-icon-and-window-title",
2280          vte_sequence_handler_set_icon_and_window_title},
2281
2282         {"application-keypad", vte_sequence_handler_application_keypad},
2283         {"normal-keypad", vte_sequence_handler_normal_keypad},
2284         {"decset", vte_sequence_handler_decset},
2285         {"decreset", vte_sequence_handler_decreset},
2286         {"save-cursor", vte_sequence_handler_sc},
2287         {"restore-cursor", vte_sequence_handler_rc},
2288         {"normal-keypad", vte_sequence_handler_normal_keypad},
2289         {"application-keypad", vte_sequence_handler_application_keypad},
2290         {"erase-in-display", vte_sequence_handler_erase_in_display},
2291         {"erase-in-line", vte_sequence_handler_erase_in_line},
2292         {"set-scrolling-region", vte_sequence_handler_set_scrolling_region},
2293         {"insert-lines", vte_sequence_handler_insert_lines},
2294         {"delete-lines", vte_sequence_handler_delete_lines},
2295         {"index", vte_sequence_handler_index},
2296         {"reverse-index", vte_sequence_handler_reverse_index},
2297         {"iso8859-1-character-set", vte_sequence_handler_iso8859_1},
2298         {"utf-8-character-set", vte_sequence_handler_utf_8},
2299         {"character-position-absolute", vte_sequence_handler_character_position_absolute},
2300         {"line-position-absolute", vte_sequence_handler_line_position_absolute},
2301 };
2302
2303 /* Create the basic widget.  This more or less creates and initializes a
2304  * GtkWidget and clears out the rest of the data which is specific to our
2305  * widget class. */
2306 GtkWidget *
2307 vte_terminal_new(void)
2308 {
2309         return GTK_WIDGET(g_object_new(vte_terminal_get_type(), NULL));
2310 }
2311
2312 /* Reset palette defaults for character colors. */
2313 static void
2314 vte_terminal_set_default_palette(VteTerminal *terminal)
2315 {
2316         int i;
2317         XColor color;
2318         GtkWidget *widget;
2319         Display *display;
2320         GdkColormap *gcolormap;
2321         Colormap colormap;
2322         GdkVisual *gvisual;
2323         Visual *visual;
2324         int bright, red, green, blue;
2325
2326         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2327         if (terminal->pvt->palette_initialized) {
2328                 return;
2329         }
2330         memset(&color, 0, sizeof(color));
2331
2332         widget = NULL;
2333         display = NULL;
2334         gcolormap = NULL;
2335         colormap = 0;
2336         gvisual = NULL;
2337         visual = NULL;
2338
2339         /* Initialize each item in the palette. */
2340         for (i = 0; i < G_N_ELEMENTS(terminal->pvt->palette); i++) {
2341                 /* Get X11 attributes used by GDK for the widget. */
2342                 if (widget == NULL) {
2343                         widget = GTK_WIDGET(terminal);
2344                         display = GDK_DISPLAY();
2345                         gcolormap = gtk_widget_get_colormap(widget);
2346                         colormap = gdk_x11_colormap_get_xcolormap(gcolormap);
2347                         gvisual = gtk_widget_get_visual(widget);
2348                         visual = gdk_x11_visual_get_xvisual(gvisual);
2349                 }
2350
2351                 /* Make the difference between normal and bright about three
2352                  * fourths of the total available brightness. */
2353                 bright = (i & 8) ? 0x3fff : 0;
2354                 blue = (i & 4) ? 0xc000 : 0;
2355                 green = (i & 2) ? 0xc000 : 0;
2356                 red = (i & 1) ? 0xc000 : 0;
2357
2358                 /* Allocate a color from the colormap. */
2359                 color.pixel = i;
2360                 color.red = bright + red;
2361                 color.green = bright + green;
2362                 color.blue = bright + blue;
2363
2364                 if (XAllocColor(display, colormap, &color)) {
2365                         terminal->pvt->palette[i].red = color.red;
2366                         terminal->pvt->palette[i].green = color.green;
2367                         terminal->pvt->palette[i].blue = color.blue;
2368                         terminal->pvt->palette[i].pixel = color.pixel;
2369                 }
2370
2371 #ifdef HAVE_XFT
2372                 if (terminal->pvt->use_xft) {
2373                         terminal->pvt->palette[i].rcolor.red = color.red;
2374                         terminal->pvt->palette[i].rcolor.green = color.green;
2375                         terminal->pvt->palette[i].rcolor.blue = color.blue;
2376                         terminal->pvt->palette[i].rcolor.alpha = 0xffff;
2377                         if (!XftColorAllocValue(display,
2378                                                 visual,
2379                                                 colormap,
2380                                                 &terminal->pvt->palette[i].rcolor,
2381                                                 &terminal->pvt->palette[i].ftcolor)) {
2382                                 terminal->pvt->use_xft = FALSE;
2383                         }
2384                 }
2385 #endif
2386         }
2387         terminal->pvt->palette_initialized = TRUE;
2388 }
2389
2390 /* Insert a single character into the stored data array. */
2391 static void
2392 vte_terminal_insert_char(GtkWidget *widget, wchar_t c)
2393 {
2394         VteTerminal *terminal;
2395         GArray *array;
2396         struct vte_charcell cell, *pcell;
2397         int columns, i;
2398         long col;
2399         struct _VteScreen *screen;
2400
2401         g_return_if_fail(widget != NULL);
2402         g_return_if_fail(VTE_IS_TERMINAL(widget));
2403         terminal = VTE_TERMINAL(widget);
2404         screen = terminal->pvt->screen;
2405
2406         /* Make sure we have enough rows to hold this data. */
2407         while (screen->cursor_current.row >= screen->row_data->len) {
2408                 array = vte_new_row_data();
2409                 g_array_append_val(screen->row_data, array);
2410         }
2411
2412         /* Get a handle on the array for the insertion row. */
2413         array = g_array_index(screen->row_data,
2414                               GArray*,
2415                               screen->cursor_current.row);
2416
2417         /* Figure out how many columns this character should occupy. */
2418         columns = wcwidth(c);
2419
2420         /* Read the deltas. */
2421         for (i = 0; i < columns; i++) {
2422                 col = terminal->pvt->screen->cursor_current.col;
2423
2424                 /* Make sure we have enough columns in this row. */
2425                 if (array->len <= col) {
2426                         /* Add enough characters. */
2427                         memset(&cell, 0, sizeof(cell));
2428                         cell.c = ' ';
2429                         cell.columns = 1;
2430                         cell.fore = 7;
2431                         cell.back = 0;
2432                         while (array->len < col) {
2433                                 g_array_append_val(array, cell);
2434                         }
2435                         /* Add one more cell to the end of the line to get
2436                          * it into the column, and use it. */
2437                         g_array_append_val(array, cell);
2438                         pcell = &g_array_index(array,
2439                                                struct vte_charcell,
2440                                                col);
2441                 } else {
2442                         /* If we're in insert mode, insert a new cell here
2443                          * and use it. */
2444                         if (screen->insert) {
2445                                 memset(&cell, 0, sizeof(cell));
2446                                 cell.c = ' ';
2447                                 cell.columns = 1;
2448                                 cell.fore = 7;
2449                                 cell.back = 0;
2450                                 g_array_insert_val(array, col, cell);
2451                                 pcell = &g_array_index(array,
2452                                                        struct vte_charcell,
2453                                                        col);
2454                         } else {
2455                                 /* We're in overtype mode, so use the existing
2456                                  * character. */
2457                                 pcell = &g_array_index(array,
2458                                                        struct vte_charcell,
2459                                                        col);
2460                         }
2461                 }
2462
2463                 /* Initialize the character cell with the proper data. */
2464                 pcell->c = c;
2465                 pcell->columns = (i == 0) ? columns : 0;
2466                 pcell->fore = terminal->pvt->screen->defaults.fore;
2467                 pcell->back = terminal->pvt->screen->defaults.back;
2468                 pcell->reverse = terminal->pvt->screen->defaults.reverse;
2469                 pcell->invisible = terminal->pvt->screen->defaults.invisible;
2470                 pcell->half = terminal->pvt->screen->defaults.half;
2471                 pcell->underline = terminal->pvt->screen->defaults.underline;
2472                 pcell->bold = terminal->pvt->screen->defaults.bold;
2473                 pcell->standout = terminal->pvt->screen->defaults.standout;
2474                 pcell->alternate = terminal->pvt->screen->defaults.alternate;
2475
2476                 /* Signal that this part of the window needs drawing. */
2477                 if (terminal->pvt->screen->insert) {
2478                         vte_invalidate_cells(terminal,
2479                                              screen->cursor_current.col - 1,
2480                                              terminal->column_count -
2481                                              screen->cursor_current.col + 1,
2482                                              screen->cursor_current.row, 2);
2483                 } else {
2484                         vte_invalidate_cells(terminal,
2485                                              screen->cursor_current.col - 1, 3,
2486                                              screen->cursor_current.row, 2);
2487                 }
2488
2489                 /* And take a step to the to the right.  We invalidated this
2490                  * part of the screen already, so no need to do it again. */
2491                 screen->cursor_current.col++;
2492         }
2493 }
2494
2495 static void
2496 display_control_sequence(const char *name, GValueArray *params)
2497 {
2498         /* Display the control sequence with its parameters, to
2499          * help me debug this thing.  I don't have all of the
2500          * sequences implemented yet. */
2501         int i;
2502         long l;
2503         const char *s;
2504         const wchar_t *w;
2505         GValue *value;
2506         fprintf(stderr, "%s(", name);
2507         if (params != NULL) {
2508                 for (i = 0; i < params->n_values; i++) {
2509                         value = g_value_array_get_nth(params, i);
2510                         if (i > 0) {
2511                                 fprintf(stderr, ", ");
2512                         }
2513                         if (G_VALUE_HOLDS_LONG(value)) {
2514                                 l = g_value_get_long(value);
2515                                 fprintf(stderr, "%ld", l);
2516                         } else
2517                         if (G_VALUE_HOLDS_STRING(value)) {
2518                                 s = g_value_get_string(value);
2519                                 fprintf(stderr, "\"%s\"", s);
2520                         } else
2521                         if (G_VALUE_HOLDS_POINTER(value)) {
2522                                 w = g_value_get_pointer(value);
2523                                 fprintf(stderr, "\"%ls\"", w);
2524                         }
2525                 }
2526         }
2527         fprintf(stderr, ")\n");
2528 }
2529
2530 /* Handle a terminal control sequence and its parameters. */
2531 static void
2532 vte_terminal_handle_sequence(GtkWidget *widget,
2533                              const char *match_s,
2534                              GQuark match,
2535                              GValueArray *params)
2536 {
2537         VteTerminal *terminal;
2538         VteTerminalSequenceHandler handler;
2539         struct _VteScreen *screen;
2540
2541         g_return_if_fail(widget != NULL);
2542         g_return_if_fail(VTE_IS_TERMINAL(widget));
2543         terminal = VTE_TERMINAL(widget);
2544         screen = terminal->pvt->screen;
2545
2546         /* This may generate multiple redraws, so freeze it while we do them. */
2547         gdk_window_freeze_updates(widget->window);
2548
2549         /* Signal that the cursor's current position needs redrawing. */
2550         vte_invalidate_cells(terminal,
2551                              screen->cursor_current.col - 1, 3,
2552                              screen->cursor_current.row, 1);
2553
2554         /* Find the handler for this control sequence. */
2555         handler = g_tree_lookup(terminal->pvt->sequences, GINT_TO_POINTER(match));
2556 #ifdef VTE_DEBUG
2557         display_control_sequence(match_s, params);
2558 #endif
2559         if (handler != NULL) {
2560                 /* Let the handler handle it. */
2561                 handler(terminal, match_s, match, params);
2562         } else {
2563                 g_warning("No handler for control sequence `%s' defined.\n",
2564                           match_s);
2565         }
2566
2567         /* We probably need to update the cursor's new position, too. */
2568         vte_invalidate_cells(terminal,
2569                              screen->cursor_current.col - 1, 3,
2570                              screen->cursor_current.row, 1);
2571
2572         /* Let the updating begin. */
2573         gdk_window_thaw_updates(widget->window);
2574 }
2575
2576 /* Start up a command in a slave PTY. */
2577 void
2578 vte_terminal_fork_command(VteTerminal *terminal, const char *command,
2579                           const char **argv)
2580 {
2581         const char **env_add;
2582         char *term, *colorterm;
2583         int i;
2584
2585         /* Start up the command and get the PTY of the master. */
2586         env_add = g_malloc0(sizeof(char*) * 3);
2587         term = g_strdup_printf("TERM=%s", terminal->pvt->terminal);
2588         colorterm = g_strdup("COLORTERM=" PACKAGE);
2589         env_add[0] = term;
2590         env_add[1] = colorterm;
2591         terminal->pvt->pty_master = vte_pty_open(&terminal->pvt->pty_pid,
2592                                                  env_add,
2593                                                  command ?:
2594                                                  terminal->pvt->shell,
2595                                                  argv);
2596         g_free(term);
2597         g_free(colorterm);
2598         g_free((char**)env_add);
2599
2600         /* Set the pty to be non-blocking. */
2601         i = fcntl(terminal->pvt->pty_master, F_GETFL);
2602         fcntl(terminal->pvt->pty_master, F_SETFL, i | O_NONBLOCK);
2603
2604         /* Open a channel to listen for input on. */
2605         terminal->pvt->pty_input =
2606                 g_io_channel_unix_new(terminal->pvt->pty_master);
2607         terminal->pvt->pty_output = NULL;
2608         g_io_add_watch_full(terminal->pvt->pty_input,
2609                             G_PRIORITY_DEFAULT,
2610                             G_IO_IN | G_IO_HUP,
2611                             vte_terminal_io_read,
2612                             terminal,
2613                             NULL);
2614 }
2615
2616 /* Handle an EOF from the client. */
2617 static void
2618 vte_terminal_eof(GIOChannel *source, gpointer data)
2619 {
2620         VteTerminal *terminal;
2621
2622         g_return_if_fail(VTE_IS_TERMINAL(data));
2623         terminal = VTE_TERMINAL(data);
2624
2625         /* Close the connections to the child. */
2626         g_io_channel_unref(source);
2627         if (source == terminal->pvt->pty_input) {
2628                 terminal->pvt->pty_input = NULL;
2629                 if (terminal->pvt->pty_output != NULL) {
2630                         g_io_channel_unref(terminal->pvt->pty_output);
2631                         terminal->pvt->pty_output = NULL;
2632                 }
2633         }
2634
2635         /* Emit a signal that we read an EOF. */
2636         g_signal_emit_by_name(terminal, "eof");
2637 }
2638
2639 /* Process incoming data, first converting it to wide characters, and then
2640  * processing escape sequences. */
2641 static gboolean
2642 vte_terminal_process_incoming(gpointer data)
2643 {
2644         GValueArray *params;
2645         VteTerminal *terminal;
2646         GtkWidget *widget;
2647         char *ibuf, *obuf, *obufptr, *ubuf, *ubufptr;
2648         size_t icount, ocount, ucount;
2649         wchar_t *wbuf, c;
2650         int i, j, wcount;
2651         const char *match, *encoding;
2652         iconv_t unconv;
2653         GQuark quark;
2654         gboolean leftovers, inserted, again;
2655
2656         g_return_val_if_fail(GTK_IS_WIDGET(data), FALSE);
2657         g_return_val_if_fail(VTE_IS_TERMINAL(data), FALSE);
2658         widget = GTK_WIDGET(data);
2659         terminal = VTE_TERMINAL(data);
2660
2661 #ifdef VTE_DEBUG
2662         fprintf(stderr, "Handler processing %d bytes.\n",
2663                 terminal->pvt->n_incoming);
2664 #endif
2665
2666         /* We should only be called when there's data to process. */
2667         g_assert(terminal->pvt->n_incoming > 0);
2668
2669         /* Try to convert the data into wide characters. */
2670         ocount = sizeof(wchar_t) * terminal->pvt->n_incoming;
2671         obuf = obufptr = g_malloc(ocount);
2672         icount = terminal->pvt->n_incoming;
2673         ibuf = terminal->pvt->incoming;
2674
2675         /* Convert the data to wide characters. */
2676         if (iconv(terminal->pvt->incoming_conv, &ibuf, &icount,
2677                   &obuf, &ocount) == -1) {
2678                 /* No dice.  Try again when we have more data. */
2679                 return FALSE;
2680         }
2681
2682         /* Store the current encoding. */
2683         encoding = terminal->pvt->encoding;
2684
2685         /* Compute the number of wide characters we got. */
2686         wcount = (obuf - obufptr) / sizeof(wchar_t);
2687         wbuf = (wchar_t*) obufptr;
2688
2689         /* Try initial substrings. */
2690         i = 0;
2691         inserted = leftovers = FALSE;
2692         while ((i < wcount) && !leftovers) {
2693                 for (j = i + 1; j <= wcount; j++) {
2694                         /* Check if the contents of the array is a control
2695                          * string or not.  The match function returns NULL if
2696                          * the data is not a control sequence, the name of
2697                          * the control sequence if it is one, and an empty
2698                          * string if it might be the beginning of a control
2699                          * sequence. */
2700                         vte_trie_match(terminal->pvt->trie,
2701                                        &wbuf[i],
2702                                        j - i,
2703                                        &match,
2704                                        &quark,
2705                                        &params);
2706                         if (match == NULL) {
2707                                 /* Nothing interesting here, so insert this
2708                                  * character into the buffer. */
2709                                 c = wbuf[i];
2710 #ifdef VTE_DEBUG
2711                                 if (c > 255) {
2712                                         fprintf(stderr, "%ld\n", (long) c);
2713                                 } else {
2714                                         if (c > 127) {
2715                                                 fprintf(stderr, "%ld = ",
2716                                                         (long) c);
2717                                         }
2718                                         if (c < 32) {
2719                                                 fprintf(stderr, "^%lc\n",
2720                                                         (wint_t)c + 64);
2721                                         } else {
2722                                                 fprintf(stderr, "`%lc'\n",
2723                                                         (wint_t)c);
2724                                         }
2725                                 }
2726 #endif
2727                                 vte_terminal_insert_char(widget, c);
2728                                 inserted = TRUE;
2729                                 i++;
2730                                 break;
2731                         }
2732                         if (match[0] != '\0') {
2733                                 /* A terminal sequence. */
2734                                 vte_terminal_handle_sequence(GTK_WIDGET(terminal),
2735                                                              match,
2736                                                              quark,
2737                                                              params);
2738                                 if (params != NULL) {
2739                                         g_value_array_free(params);
2740                                 }
2741                                 /* Skip over the proper number of wide chars. */
2742                                 i = j;
2743                                 /* Check if the encoding's changed. */
2744                                 if (strcmp(encoding, terminal->pvt->encoding)) {
2745                                         leftovers = TRUE;
2746                                 }
2747                                 break;
2748                         } else {
2749                                 /* Empty string. */
2750                                 if (j == wcount) {
2751                                         /* We have the initial portion of a
2752                                          * control sequence, but no more
2753                                          * data. */
2754                                         leftovers = TRUE;
2755                                         g_warning("Unhandled data.\n");
2756                                 }
2757                         }
2758                 }
2759         }
2760         again = TRUE;
2761         if (leftovers) {
2762                 /* There are leftovers, so convert them back to the terminal's
2763                  * encoding and save them for later. */
2764                 unconv = iconv_open(encoding, "WCHAR_T");
2765                 if (unconv != NULL) {
2766                         icount = sizeof(wchar_t) * (wcount - i);
2767                         ibuf = (char*) &wbuf[i];
2768                         ucount = VTE_UTF8_BPC * (wcount - i + 1);
2769                         ubuf = ubufptr = g_malloc(ucount);
2770                         if (iconv(unconv, &ibuf, &icount,
2771                                   &ubuf, &ucount) != -1) {
2772                                 /* Store it. */
2773                                 g_free(terminal->pvt->incoming);
2774                                 terminal->pvt->incoming = ubufptr;
2775                                 terminal->pvt->n_incoming = ubuf - ubufptr;
2776                                 *ubuf = '\0';
2777                                 again = TRUE;
2778                         } else {
2779                                 g_free(ubufptr);
2780                                 again = FALSE;
2781                         }
2782                         iconv_close(unconv);
2783                 } else {
2784                         /* Discard the data, we can't use it. */
2785                         terminal->pvt->n_incoming = 0;
2786                         g_free(terminal->pvt->incoming);
2787                         again = FALSE;
2788                 }
2789         } else {
2790                 /* No leftovers, clean out the data. */
2791                 terminal->pvt->n_incoming = 0;
2792                 g_free(terminal->pvt->incoming);
2793                 again = FALSE;
2794         }
2795
2796         if (inserted) {
2797                 /* Keep the cursor on-screen. */
2798                 if (terminal->pvt->scroll_on_output) {
2799                         vte_terminal_scroll_on_something(terminal);
2800                 }
2801                 /* Deselect any existing selection. */
2802                 vte_terminal_deselect_all(terminal);
2803         }
2804
2805 #ifdef VTE_DEBUG
2806         fprintf(stderr, "%d bytes left to process.\n",
2807                 terminal->pvt->n_incoming);
2808 #endif
2809         terminal->pvt->processing = again && (terminal->pvt->n_incoming > 0);
2810 #ifdef VTE_DEBUG
2811         if (terminal->pvt->processing) {
2812                 fprintf(stderr, "Leaving processing handler on.\n");
2813         } else {
2814                 fprintf(stderr, "Turning processing handler off.\n");
2815         }
2816 #endif
2817         return terminal->pvt->processing;
2818 }
2819
2820 /* Read and handle data from the child. */
2821 static gboolean
2822 vte_terminal_io_read(GIOChannel *channel,
2823                      GdkInputCondition condition,
2824                      gpointer data)
2825 {
2826         VteTerminal *terminal;
2827         GtkWidget *widget;
2828         char *buf;
2829         size_t bufsize;
2830         int bcount, fd;
2831         gboolean empty, leave_open = TRUE;
2832
2833         widget = GTK_WIDGET(data);
2834         terminal = VTE_TERMINAL(data);
2835
2836         /* Allocate a buffer to hold whatever data's available. */
2837         bufsize = terminal->pvt->n_incoming + LINE_MAX;
2838         buf = g_malloc0(bufsize);
2839         if (terminal->pvt->n_incoming > 0) {
2840                 memcpy(buf, terminal->pvt->incoming, terminal->pvt->n_incoming);
2841         }
2842         empty = (terminal->pvt->n_incoming == 0);
2843
2844         /* Read some more data in from this channel. */
2845         fd = g_io_channel_unix_get_fd(channel);
2846         bcount = read(fd, buf + terminal->pvt->n_incoming,
2847                       bufsize - terminal->pvt->n_incoming);
2848
2849         /* Catch errors. */
2850         leave_open = TRUE;
2851         switch (bcount) {
2852                 case 0:
2853                         /* EOF */
2854                         vte_terminal_eof(terminal->pvt->pty_input, data);
2855                         leave_open = FALSE;
2856                         break;
2857                 case -1:
2858                         switch (errno) {
2859                                 case EIO: /* Fake an EOF. */
2860                                         vte_terminal_eof(terminal->pvt->pty_input,
2861                                                          data);
2862                                         leave_open = FALSE;
2863                                         break;
2864                                 case EAGAIN:
2865                                 case EBUSY:
2866                                         leave_open = TRUE;
2867                                         break;
2868                                 default:
2869                                         g_warning("Error reading from child: "
2870                                                   "%s.\n", strerror(errno));
2871                                         leave_open = TRUE;
2872                                         break;
2873                         }
2874                         break;
2875                 default:
2876                         break;
2877         }
2878
2879         /* If we got data, modify the pending buffer. */
2880         if (bcount >= 0) {
2881                 terminal->pvt->incoming = buf;
2882                 terminal->pvt->n_incoming += bcount;
2883         } else {
2884                 g_free(buf);
2885         }
2886
2887         /* If we have data to process, schedule some time to process it. */
2888         if (!terminal->pvt->processing && (terminal->pvt->n_incoming > 0)) {
2889 #ifdef VTE_DEBUG
2890                 fprintf(stderr, "Queuing handler to process bytes.\n");
2891 #endif
2892                 terminal->pvt->processing = TRUE;
2893                 g_idle_add(vte_terminal_process_incoming, terminal);
2894         }
2895
2896         /* If there's more data coming, return TRUE, otherwise return FALSE. */
2897         return leave_open;
2898 }
2899
2900 /* Render some UTF-8 text. */
2901 void
2902 vte_terminal_feed(VteTerminal *terminal, const char *data, size_t length)
2903 {
2904         char *buf;
2905         gboolean empty;
2906
2907         /* Allocate space for old and new data. */
2908         buf = g_malloc(terminal->pvt->n_incoming + length + 1);
2909         empty = (terminal->pvt->n_incoming == 0);
2910
2911         /* If we got data, modify the pending buffer. */
2912         if (length >= 0) {
2913                 if (terminal->pvt->n_incoming > 0) {
2914                         memcpy(buf, terminal->pvt->incoming,
2915                                terminal->pvt->n_incoming);
2916                         g_free(terminal->pvt->incoming);
2917                 }
2918                 memcpy(buf + terminal->pvt->n_incoming,
2919                        data, length);
2920                 terminal->pvt->incoming = buf;
2921                 terminal->pvt->n_incoming += length;
2922         } else {
2923                 g_free(buf);
2924         }
2925
2926         /* If we didn't have data before, but we do now, process it. */
2927         if (!terminal->pvt->processing && (terminal->pvt->n_incoming > 0)) {
2928 #ifdef VTE_DEBUG
2929                 fprintf(stderr, "Queuing handler to process bytes.\n");
2930 #endif
2931                 terminal->pvt->processing = TRUE;
2932                 g_idle_add(vte_terminal_process_incoming, terminal);
2933         }
2934 }
2935
2936 /* Send wide characters to the child. */
2937 static gboolean
2938 vte_terminal_io_write(GIOChannel *channel,
2939                       GdkInputCondition condition,
2940                       gpointer data)
2941 {
2942         VteTerminal *terminal;
2943         ssize_t count;
2944         int fd;
2945         gboolean leave_open;
2946
2947         g_return_val_if_fail(VTE_IS_TERMINAL(data), FALSE);
2948         terminal = VTE_TERMINAL(data);
2949
2950         fd = g_io_channel_unix_get_fd(channel);
2951
2952         count = write(fd, terminal->pvt->outgoing, terminal->pvt->n_outgoing);
2953         if (count != -1) {
2954 #ifdef VTE_DEBUG
2955                 int i;
2956                 for (i = 0; i < count; i++) {
2957                         fprintf(stderr, "Wrote %c%c\n",
2958                                 terminal->pvt->outgoing[i] > 32 ?  ' ' : '^',
2959                                 terminal->pvt->outgoing[i] > 32 ?
2960                                 terminal->pvt->outgoing[i] : 
2961                                 terminal->pvt->outgoing[i]  + 64);
2962                 }
2963 #endif
2964                 memmove(terminal->pvt->outgoing,
2965                         terminal->pvt->outgoing + count,
2966                         terminal->pvt->n_outgoing - count);
2967                 terminal->pvt->n_outgoing -= count;
2968         }
2969
2970         if (terminal->pvt->n_outgoing == 0) {
2971                 g_io_channel_unref(channel);
2972                 if (channel == terminal->pvt->pty_output) {
2973                         terminal->pvt->pty_output = NULL;
2974                 }
2975                 leave_open = FALSE;
2976         } else {
2977                 leave_open = TRUE;
2978         }
2979
2980         return leave_open;
2981 }
2982
2983 /* Convert some arbitrarily-encoded data to send to the child. */
2984 static void
2985 vte_terminal_send(VteTerminal *terminal, const char *encoding,
2986                   const void *data, size_t length)
2987 {
2988         size_t icount, ocount;
2989         char *ibuf, *obuf, *obufptr;
2990         char *outgoing;
2991         size_t n_outgoing;
2992         iconv_t *conv;
2993
2994         g_return_if_fail(VTE_IS_TERMINAL(terminal));
2995         g_assert((strcmp(encoding, "UTF-8") == 0) ||
2996                  (strcmp(encoding, "WCHAR_T") == 0));
2997
2998         conv = NULL;
2999         if (strcmp(encoding, "UTF-8") == 0) {
3000                 conv = &terminal->pvt->outgoing_conv_utf8;
3001         }
3002         if (strcmp(encoding, "WCHAR_T") == 0) {
3003                 conv = &terminal->pvt->outgoing_conv_wide;
3004         }
3005         g_assert(conv != NULL);
3006         g_assert(*conv != NULL);
3007
3008         icount = length;
3009         ibuf = (char *) data;
3010         ocount = ((length + 1) * VTE_UTF8_BPC) + 1;
3011         obuf = obufptr = g_malloc0(ocount);
3012
3013         if (iconv(*conv, &ibuf, &icount, &obuf, &ocount) == -1) {
3014                 g_warning("%s converting data for child\n", strerror(errno));
3015         } else {
3016                 n_outgoing = terminal->pvt->n_outgoing + (obuf - obufptr);
3017                 outgoing = g_realloc(terminal->pvt->outgoing, n_outgoing);
3018                 /* Move some data around. */
3019                 memcpy(outgoing + terminal->pvt->n_outgoing,
3020                        obufptr, obuf - obufptr);
3021                 /* Save the new outgoing buffer. */
3022                 terminal->pvt->n_outgoing = n_outgoing;
3023                 terminal->pvt->outgoing = outgoing;
3024                 /* If we need to start waiting for the child pty to become
3025                  * available for writing, set that up here. */
3026                 if (terminal->pvt->pty_output == NULL) {
3027                         terminal->pvt->pty_output = g_io_channel_unix_new(terminal->pvt->pty_master);
3028                         g_io_add_watch_full(terminal->pvt->pty_output,
3029                                             G_PRIORITY_DEFAULT,
3030                                             G_IO_OUT,
3031                                             vte_terminal_io_write,
3032                                             terminal,
3033                                             NULL);
3034                 }
3035         }
3036         g_free(obufptr);
3037         return;
3038 }
3039
3040 /* Read and handle a keypress event. */
3041 static gint
3042 vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
3043 {
3044         VteTerminal *terminal;
3045         GdkModifierType modifiers;
3046         struct vte_termcap *termcap;
3047         const char *tterm;
3048         unsigned char *normal = NULL;
3049         size_t normal_length = 0;
3050         unsigned char *special = NULL;
3051         struct termios tio;
3052         gboolean scrolled = FALSE;
3053
3054         g_return_val_if_fail(widget != NULL, FALSE);
3055         g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE);
3056         terminal = VTE_TERMINAL(widget);
3057
3058         if (event->type == GDK_KEY_PRESS) {
3059                 /* Read the modifiers. */
3060                 if (gdk_event_get_state((GdkEvent*)event,
3061                                         &modifiers) == FALSE) {
3062                         modifiers = 0;
3063                 }
3064                 /* Map the key to a sequence name if we can. */
3065                 switch (event->keyval) {
3066                         case GDK_BackSpace:
3067                                 /* Use the tty's erase character. */
3068                                 if (tcgetattr(terminal->pvt->pty_master,
3069                                               &tio) != -1) {
3070                                         normal = g_strdup_printf("%c",
3071                                                                  tio.c_cc[VERASE]);
3072                                         normal_length = 1;
3073                                 }
3074                                 break;
3075                         case GDK_Delete:
3076                                 special = "kD";
3077                                 break;
3078                         case GDK_KP_Home:
3079                         case GDK_Home:
3080                                 special = "kh";
3081                                 break;
3082                         case GDK_KP_End:
3083                         case GDK_End:
3084                                 special = "@7";
3085                                 break;
3086                         case GDK_F1:
3087                                 special = "k1";
3088                                 break;
3089                         case GDK_F2:
3090                                 special = "k2";
3091                                 break;
3092                         case GDK_F3:
3093                                 special = "k3";
3094                                 break;
3095                         case GDK_F4:
3096                                 special = "k4";
3097                                 break;
3098                         case GDK_F5:
3099                                 special = "k5";
3100                                 break;
3101                         case GDK_F6:
3102                                 special = "k6";
3103                                 break;
3104                         case GDK_F7:
3105                                 special = "k7";
3106                                 break;
3107                         case GDK_F8:
3108                                 special = "k8";
3109                                 break;
3110                         case GDK_F9:
3111                                 special = "k9";
3112                                 break;
3113                         case GDK_F10:
3114                                 special = "k0";
3115                                 break;
3116                         case GDK_F11:
3117                                 special = "k;";
3118                                 break;
3119                         case GDK_F12:
3120                                 special = "F1";
3121                                 break;
3122                         /* Cursor keys. */
3123                         case GDK_KP_Up:
3124                         case GDK_Up:
3125                                 special = "ku";
3126                                 break;
3127                         case GDK_KP_Down:
3128                         case GDK_Down:
3129                                 special = "kd";
3130                                 break;
3131                         case GDK_KP_Left:
3132                         case GDK_Left:
3133                                 special = "kl";
3134                                 break;
3135                         case GDK_KP_Right:
3136                         case GDK_Right:
3137                                 special = "kr";
3138                                 break;
3139                         case GDK_Page_Up:
3140                                 if (modifiers & GDK_SHIFT_MASK) {
3141                                         vte_terminal_scroll_pages(terminal, -1);
3142                                         scrolled = TRUE;
3143                                 } else {
3144                                         special = "kP";
3145                                 }
3146                                 break;
3147                         case GDK_Page_Down:
3148                                 if (modifiers & GDK_SHIFT_MASK) {
3149                                         vte_terminal_scroll_pages(terminal, 1);
3150                                         scrolled = TRUE;
3151                                 } else {
3152                                         special = "kN";
3153                                 }
3154                                 break;
3155                         case GDK_Tab:
3156                                 if (modifiers & GDK_SHIFT_MASK) {
3157                                         special = "kB";
3158                                 } else {
3159                                         normal = g_strdup("\t");
3160                                         normal_length = 1;
3161                                 }
3162                                 break;
3163                         /* The default is to just send the string. */
3164                         default:
3165                                 if (event->string != NULL) {
3166                                         normal = g_strdup(event->string);
3167                                         normal_length = strlen(normal);
3168                                 }
3169                                 break;
3170                 }
3171                 /* If we got normal characters, send them to the child. */
3172                 if (normal != NULL) {
3173                         if (terminal->pvt->alt_sends_escape &&
3174                             (normal_length > 0) &&
3175                             (modifiers & GDK_MOD1_MASK)) {
3176                                 vte_terminal_send(terminal, "UTF-8", "\e", 1);
3177                         }
3178                         vte_terminal_send(terminal, "UTF-8",
3179                                           normal, normal_length);
3180                         g_free(normal);
3181                         normal = NULL;
3182                 } else
3183                 /* If the key maps to characters, send them to the child. */
3184                 if (special != NULL) {
3185                         termcap = terminal->pvt->termcap;
3186                         tterm = terminal->pvt->terminal;
3187                         normal = vte_termcap_find_string_length(termcap,
3188                                                                 tterm,
3189                                                                 special,
3190                                                                 &normal_length);
3191                         special = g_strdup_printf(normal, 1);
3192                         vte_terminal_send(terminal, "UTF-8",
3193                                           special, strlen(special));
3194                         g_free(special);
3195                 }
3196                 /* Keep the cursor on-screen. */
3197                 if (!scrolled && terminal->pvt->scroll_on_keypress) {
3198                         vte_terminal_scroll_on_something(terminal);
3199                 }
3200                 return TRUE;
3201         }
3202         return FALSE;
3203 }
3204
3205 /* Classify a wide character with some value, useful only for comparing
3206  * for equality. */
3207 static guint
3208 vte_charclass(wchar_t c)
3209 {
3210         if (iswalnum(c)) {
3211                 return 1;
3212         }
3213         if (iswpunct(c)) {
3214                 return 2;
3215         }
3216         if (iswblank(c)) {
3217                 return 3;
3218         }
3219         return 0;
3220 }
3221
3222 /* Find the character in the given "virtual" position. */
3223 struct vte_charcell *
3224 vte_terminal_find_charcell(VteTerminal *terminal, long row, long col)
3225 {
3226         GArray *rowdata;
3227         struct vte_charcell *ret = NULL;
3228         struct _VteScreen *screen;
3229         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
3230         screen = terminal->pvt->screen;
3231         if (screen->row_data->len > row) {
3232                 rowdata = g_array_index(screen->row_data, GArray*, row);
3233                 if (rowdata->len > col) {
3234                         ret = &g_array_index(rowdata, struct vte_charcell, col);
3235                 }
3236         }
3237         return ret;
3238 }
3239
3240 /* Check if the characters in the given block are in the same class. */
3241 static gboolean
3242 vte_uniform_class(VteTerminal *terminal, long row, long scol, long ecol)
3243 {
3244         struct vte_charcell *pcell = NULL;
3245         long col;
3246         guint cclass;
3247         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
3248         if ((pcell = vte_terminal_find_charcell(terminal, row, scol)) != NULL) {
3249                 cclass = vte_charclass(pcell->c);
3250                 for (col = scol; col <= ecol; col++) {
3251                         pcell = vte_terminal_find_charcell(terminal, row, col);
3252                         if (pcell == NULL) {
3253                                 return FALSE;
3254                         }
3255                         if (cclass != vte_charclass(pcell->c)) {
3256                                 return FALSE;
3257                         }
3258                 }
3259                 return TRUE;
3260         }
3261         return FALSE;
3262 }
3263
3264 /* Check if a cell is selected or not. */
3265 static gboolean
3266 vte_cell_is_selected(VteTerminal *terminal, long row, long col)
3267 {
3268         guint scol, ecol, cclass, ccol;
3269         struct vte_charcell *pcell;
3270
3271         /* If there's nothing selected, it's an easy question to answer. */
3272         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
3273         if (!terminal->pvt->selection) {
3274                 return FALSE;
3275         }
3276
3277         /* Sort the two columns, for the cases where the selection is
3278          * entirely within a single line. */
3279         scol = MIN(terminal->pvt->selection_start.x,
3280                    terminal->pvt->selection_end.x);
3281         ecol = MAX(terminal->pvt->selection_start.x,
3282                    terminal->pvt->selection_end.x);
3283
3284         switch (terminal->pvt->selection_type) {
3285                 case selection_type_char:
3286                         /* A cell is selected if it's on the line where the
3287                          * selected area starts, or the line where it ends,
3288                          * or any of the lines in between. */
3289                         if ((row > terminal->pvt->selection_start.y) &&
3290                             (row < terminal->pvt->selection_end.y)) {
3291                                 return TRUE;
3292                         } else
3293                         /* It's also selected if the selection is confined to
3294                          * one line and the character lies between the start
3295                          * and end columns (which may not be in the more obvious
3296                          * of two possible orders). */
3297                         if ((terminal->pvt->selection_start.y == row) &&
3298                             (terminal->pvt->selection_end.y == row)) {
3299                                 if ((col >= scol) && (col < ecol)) {
3300                                         return TRUE;
3301                                 }
3302                         } else
3303                         /* It's also selected if it's on the line where the
3304                          * selected area starts and it's after the start column,
3305                          * or on the line where the selection ends, after the
3306                          * last selected column. */
3307                         if ((row == terminal->pvt->selection_start.y) &&
3308                             (col >= terminal->pvt->selection_start.x)) {
3309                                 return TRUE;
3310                         } else
3311                         if ((row == terminal->pvt->selection_end.y) &&
3312                             (col < terminal->pvt->selection_end.x)) {
3313                                 return TRUE;
3314                         }
3315                         break;
3316                 case selection_type_word:
3317                         /* A cell is selected if it's on the line where the
3318                          * selected area starts, or the line where it ends,
3319                          * or any of the lines in between. */
3320                         if ((row > terminal->pvt->selection_start.y) &&
3321                             (row < terminal->pvt->selection_end.y)) {
3322                                 return TRUE;
3323                         } else
3324                         /* It's also selected if the selection is confined to
3325                          * one line and the character lies between the start
3326                          * and end columns (which may not be in the more obvious
3327                          * of two possible orders). */
3328                         if ((terminal->pvt->selection_start.y == row) &&
3329                             (terminal->pvt->selection_end.y == row)) {
3330                                 if ((col >= scol) && (col <= ecol)) {
3331                                         return TRUE;
3332                                 } else
3333                                 /* If the character is before the beginning of
3334                                  * the region, it's also selected if it and
3335                                  * everything else in between belongs to the
3336                                  * same character class. */
3337                                 if (col < scol) {
3338                                         if (vte_uniform_class(terminal,
3339                                                               row,
3340                                                               col,
3341                                                               scol)) {
3342                                                 return TRUE;
3343                                         }
3344                                 } else
3345                                 if (col > ecol) {
3346                                         if (vte_uniform_class(terminal,
3347                                                               row,
3348                                                               ecol,
3349                                                               col)) {
3350                                                 return TRUE;
3351                                         }
3352                                 }
3353                         } else
3354                         /* It's also selected if it's on the line where the
3355                          * selected area starts and it's after the start column,
3356                          * or on the line where the selection ends, after the
3357                          * last selected column. */
3358                         if (row == terminal->pvt->selection_start.y) {
3359                                 if (col >= terminal->pvt->selection_start.x) {
3360                                         return TRUE;
3361                                 } else
3362                                 if (vte_uniform_class(terminal,
3363                                                       row,
3364                                                       col,
3365                                                       terminal->pvt->selection_start.x)) {
3366                                         return TRUE;
3367                                 }
3368                         } else
3369                         if (row == terminal->pvt->selection_end.y) {
3370                                 if (col < terminal->pvt->selection_end.x) {
3371                                         return TRUE;
3372                                 } else
3373                                 if (vte_uniform_class(terminal,
3374                                                       row,
3375                                                       terminal->pvt->selection_end.x,
3376                                                       col)) {
3377                                         return TRUE;
3378                                 }
3379                         }
3380                         break;
3381                 case selection_type_line:
3382                         /* A cell is selected if it's on the line where the
3383                          * selected area starts, or the line where it ends,
3384                          * or any of the lines in between. */
3385                         if ((row >= terminal->pvt->selection_start.y) &&
3386                             (row <= terminal->pvt->selection_end.y)) {
3387                                 return TRUE;
3388                         }
3389                         break;
3390                 default:
3391                         break;
3392         }
3393         return FALSE;
3394 }
3395
3396 /* Once we get text data, actually paste it in. */
3397 static void
3398 vte_terminal_paste_cb(GtkClipboard *clipboard, const gchar *text, gpointer data)
3399 {
3400         VteTerminal *terminal;
3401         g_return_if_fail(VTE_IS_TERMINAL(data));
3402         terminal = VTE_TERMINAL(data);
3403         if (text != NULL) {
3404                 vte_terminal_send(terminal, "UTF-8", text, strlen(text));
3405         }
3406 }
3407
3408 /* Read and handle a motion event. */
3409 static gint
3410 vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event)
3411 {
3412         VteTerminal *terminal;
3413         struct {
3414                 long x, y;
3415         } o, p, q, origin, last;
3416         long delta, top, height, w, h;
3417
3418         g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE);
3419         terminal = VTE_TERMINAL(widget);
3420
3421         terminal->pvt->selection = TRUE;
3422
3423         w = terminal->char_width;
3424         h = terminal->char_height;
3425         origin.x = (terminal->pvt->selection_origin.x + w / 2) / w;
3426         origin.y = (terminal->pvt->selection_origin.y) / h;
3427         last.x = (event->x + w / 2) / w;
3428         last.y = (event->y) / h;
3429         o.x = (terminal->pvt->selection_last.x + w / 2) / w;
3430         o.y = (terminal->pvt->selection_last.y) / h;
3431
3432         terminal->pvt->selection_last.x = event->x;
3433         terminal->pvt->selection_last.y = event->y;
3434
3435         if (last.y > origin.y) {
3436                 p.x = origin.x;
3437                 p.y = origin.y;
3438                 q.x = last.x;
3439                 q.y = last.y;
3440         } else
3441         if (last.y < origin.y) {
3442                 p.x = last.x;
3443                 p.y = last.y;
3444                 q.x = origin.x;
3445                 q.y = origin.y;
3446         } else
3447         if (last.x > origin.x) {
3448                 p.x = origin.x;
3449                 p.y = origin.y;
3450                 q.x = last.x;
3451                 q.y = last.y;
3452         } else {
3453                 p.x = last.x;
3454                 p.y = last.y;
3455                 q.x = origin.x;
3456                 q.y = origin.y;
3457         }
3458
3459         delta = terminal->pvt->screen->scroll_delta;
3460
3461         terminal->pvt->selection_start.x = p.x;
3462         terminal->pvt->selection_start.y = p.y + delta;
3463         terminal->pvt->selection_end.x = q.x;
3464         terminal->pvt->selection_end.y = q.y + delta;
3465
3466         top = MIN(o.y, MIN(p.y, q.y));
3467         height = MAX(o.y, MAX(p.y, q.y)) - top + 1;
3468
3469 #ifdef VTE_DEBUG
3470         fprintf(stderr, "selection is (%ld,%ld) to (%ld,%ld)\n",
3471                 terminal->pvt->selection_start.x,
3472                 terminal->pvt->selection_start.y,
3473                 terminal->pvt->selection_end.x,
3474                 terminal->pvt->selection_end.y);
3475         fprintf(stderr, "repainting rows %ld to %ld\n", top, top + height);
3476 #endif
3477
3478         vte_invalidate_cells(terminal, 0, terminal->column_count,
3479                              top + delta, height);
3480
3481         return FALSE;
3482 }
3483
3484 /* Place the selected text onto the clipboard. */
3485 static void
3486 vte_terminal_get_clipboard(GtkClipboard *clipboard,
3487                            GtkSelectionData *selection_data,
3488                            guint info,
3489                            gpointer owner)
3490 {
3491         VteTerminal *terminal;
3492         long x, y;
3493         struct _VteScreen *screen;
3494         struct vte_charcell *pcell;
3495         wchar_t *buffer;
3496         size_t length;
3497         char *ibuf, *obuf, *obufptr;
3498         size_t icount, ocount;
3499         iconv_t conv;
3500
3501         g_return_if_fail(VTE_IS_TERMINAL(owner));
3502         terminal = VTE_TERMINAL(owner);
3503
3504         /* Build a buffer with the selected wide chars. */
3505         screen = terminal->pvt->screen;
3506         buffer = g_malloc((terminal->column_count + 1) *
3507                           terminal->row_count * sizeof(wchar_t));
3508         length = 0;
3509         for (y = screen->scroll_delta;
3510              y < terminal->row_count + screen->scroll_delta;
3511              y++) {
3512                 x = 0;
3513                 do {
3514                         pcell = vte_terminal_find_charcell(terminal, y, x);
3515                         if (vte_cell_is_selected(terminal, y, x)) {
3516                                 if (pcell != NULL) {
3517                                         if (pcell->columns > 0) {
3518                                                 buffer[length++] = pcell->c;
3519                                         }
3520                                 } else {
3521                                         buffer[length++] = '\n';
3522                                 }
3523                         }
3524                         x++;
3525                 } while (pcell != NULL);
3526         }
3527         /* Now convert it all to UTF-8. */
3528         if (length > 0) {
3529                 icount = sizeof(wchar_t) * length;
3530                 ibuf = (char*) buffer;
3531                 ocount = (terminal->column_count + 1) *
3532                          terminal->row_count * sizeof(wchar_t);
3533                 obuf = obufptr = g_malloc0(ocount);
3534                 conv = iconv_open("UTF-8", "WCHAR_T");
3535                 if (conv) {
3536                         if (iconv(conv, &ibuf, &icount,
3537                                   &obuf, &ocount) != -1) {
3538 #ifdef VTE_DEBUG
3539                                 fprintf(stderr, "Passing `%*s' to clipboard.\n",
3540                                         obuf - obufptr, obufptr);
3541 #endif
3542                                 gtk_selection_data_set_text(selection_data,
3543                                                             obufptr,
3544                                                             obuf - obufptr);
3545                         } else {
3546                                 g_warning("Conversion error in copy.");
3547                         }
3548                         iconv_close(conv);
3549                 } else {
3550                         g_warning("Error initializing for conversion.");
3551                 }
3552                 g_free(obufptr);
3553         }
3554         g_free(buffer);
3555 }
3556
3557 /* The clipboard contents have been changed or cleared, so we'd better not
3558  * have the user thinking anything we selected is in there. */
3559 static void
3560 vte_terminal_clear_clipboard(GtkClipboard *clipboard,
3561                              gpointer owner)
3562 {
3563         g_return_if_fail(VTE_IS_TERMINAL(owner));
3564 #ifdef VTE_DEBUG
3565         fprintf(stderr, "Clipboard has been modified.\n");
3566 #endif
3567         if (gtk_clipboard_get_owner(clipboard) != G_OBJECT(owner)) {
3568                 vte_terminal_deselect_all(VTE_TERMINAL(owner));
3569         }
3570 }
3571
3572 /* Set us up as the provider of data for the clipboard. */
3573 static void
3574 vte_terminal_copy(VteTerminal *terminal, GdkAtom board)
3575 {
3576         GtkClipboard *clipboard;
3577         GtkTargetEntry targets[] = {
3578                 {"UTF8_STRING", 0, 0,},
3579                 {"STRING", 0, 0,},
3580                 {"TEXT",  0, 0,},
3581                 {"COMPOUND_TEXT", 0, 0,},
3582         };
3583
3584         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3585         clipboard = gtk_clipboard_get(board);
3586
3587         if (gtk_clipboard_get_owner(clipboard) != G_OBJECT(terminal)) {
3588                 gtk_clipboard_set_with_owner(clipboard,
3589                                              targets, G_N_ELEMENTS(targets),
3590                                              vte_terminal_get_clipboard,
3591                                              vte_terminal_clear_clipboard,
3592                                              G_OBJECT(terminal));
3593         }
3594 }
3595
3596 /* Paste from the given clipboard. */
3597 static void
3598 vte_terminal_paste(VteTerminal *terminal, GdkAtom board)
3599 {
3600         GtkClipboard *clipboard;
3601         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3602         clipboard = gtk_clipboard_get(board);
3603         if (clipboard != NULL) {
3604                 gtk_clipboard_request_text(clipboard,
3605                                            vte_terminal_paste_cb,
3606                                            terminal);
3607         }
3608 }
3609
3610 /* Read and handle a pointing device buttonpress event. */
3611 static gint
3612 vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
3613 {
3614         VteTerminal *terminal;
3615         long height, width, delta;
3616
3617         g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE);
3618         terminal = VTE_TERMINAL(widget);
3619         height = terminal->char_height;
3620         width = terminal->char_width;
3621         delta = terminal->pvt->screen->scroll_delta;
3622
3623         if (event->type == GDK_BUTTON_PRESS) {
3624 #ifdef VTE_DEBUG
3625                 fprintf(stderr, "button %d pressed at (%lf,%lf)\n",
3626                         event->button, event->x, event->y);
3627 #endif
3628                 if (event->button == 1) {
3629                         if (!GTK_WIDGET_HAS_FOCUS(widget)) {
3630                                 gtk_widget_grab_focus(widget);
3631                         }
3632                         vte_terminal_deselect_all(terminal);
3633                         terminal->pvt->selection_origin.x = event->x;
3634                         terminal->pvt->selection_origin.y = event->y;
3635                         terminal->pvt->selection_type = selection_type_char;
3636                         return TRUE;
3637                 }
3638                 if (event->button == 2) {
3639                         vte_terminal_paste(terminal, GDK_SELECTION_PRIMARY);
3640                         return TRUE;
3641                 }
3642         }
3643         if (event->type == GDK_2BUTTON_PRESS) {
3644 #ifdef VTE_DEBUG
3645                 fprintf(stderr, "button %d double-clicked at (%lf,%lf)\n",
3646                         event->button, event->x, event->y);
3647 #endif
3648                 if (event->button == 1) {
3649                         if (!GTK_WIDGET_HAS_FOCUS(widget)) {
3650                                 gtk_widget_grab_focus(widget);
3651                         }
3652                         vte_terminal_deselect_all(terminal);
3653                         terminal->pvt->selection = TRUE;
3654                         terminal->pvt->selection_origin.x = event->x;
3655                         terminal->pvt->selection_origin.y = event->y;
3656                         terminal->pvt->selection_start.x = event->x / width;
3657                         terminal->pvt->selection_start.y = event->y / height +
3658                                                            delta;
3659                         terminal->pvt->selection_end =
3660                         terminal->pvt->selection_start;
3661                         terminal->pvt->selection_type = selection_type_word;
3662                         vte_invalidate_cells(terminal,
3663                                              0,
3664                                              terminal->column_count,
3665                                              terminal->pvt->selection_start.y,
3666                                              1);
3667                         return TRUE;
3668                 }
3669         }
3670         if (event->type == GDK_3BUTTON_PRESS) {
3671 #ifdef VTE_DEBUG
3672                 fprintf(stderr, "button %d triple-clicked at (%lf,%lf)\n",
3673                         event->button, event->x, event->y);
3674 #endif
3675                 if (event->button == 1) {
3676                         if (!GTK_WIDGET_HAS_FOCUS(widget)) {
3677                                 gtk_widget_grab_focus(widget);
3678                         }
3679                         vte_terminal_deselect_all(terminal);
3680                         terminal->pvt->selection = TRUE;
3681                         terminal->pvt->selection_origin.x = event->x;
3682                         terminal->pvt->selection_origin.y = event->y;
3683                         terminal->pvt->selection_start.x = event->x / width;
3684                         terminal->pvt->selection_start.y = event->y / height +
3685                                                            delta;
3686                         terminal->pvt->selection_end =
3687                         terminal->pvt->selection_start;
3688                         terminal->pvt->selection_type = selection_type_line;
3689                         vte_invalidate_cells(terminal,
3690                                              0,
3691                                              terminal->column_count,
3692                                              terminal->pvt->selection_start.y,
3693                                              1);
3694                         return TRUE;
3695                 }
3696         }
3697         return FALSE;
3698 }
3699
3700 /* Read and handle a pointing device buttonrelease event. */
3701 static gint
3702 vte_terminal_button_release(GtkWidget *widget, GdkEventButton *event)
3703 {
3704         VteTerminal *terminal;
3705
3706         g_return_val_if_fail(VTE_IS_TERMINAL(widget), FALSE);
3707         terminal = VTE_TERMINAL(widget);
3708
3709         if (event->type == GDK_BUTTON_RELEASE) {
3710 #ifdef VTE_DEBUG
3711                 fprintf(stderr, "button %d released at (%lf,%lf)\n",
3712                         event->button, event->x, event->y);
3713 #endif
3714                 if (event->button == 1) {
3715                         /* FIXME: if the cursor didn't move, don't replace
3716                          * the selection. */
3717                         vte_terminal_copy(terminal, GDK_SELECTION_PRIMARY);
3718                 }
3719         }
3720
3721         return FALSE;
3722 }
3723
3724 /* Handle receiving or losing focus. */
3725 static gint
3726 vte_terminal_focus_in(GtkWidget *widget, GdkEventFocus *event)
3727 {
3728         g_return_val_if_fail(GTK_IS_WIDGET(widget), 0);
3729         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
3730         return TRUE;
3731 }
3732
3733 static gint
3734 vte_terminal_focus_out(GtkWidget *widget, GdkEventFocus *event)
3735 {
3736         g_return_val_if_fail(GTK_WIDGET(widget), 0);
3737         GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
3738         return TRUE;
3739 }
3740
3741 /* Set the fontset used for rendering text into the widget. */
3742 static void
3743 vte_terminal_set_fontset(VteTerminal *terminal, const char *xlfds)
3744 {
3745         guint width, height, ascent, descent;
3746         GtkWidget *widget;
3747         XFontStruct **font_struct_list, font_struct;
3748         char **missing_charset_list, *def_string;
3749         int missing_charset_count;
3750         char **font_name_list;
3751
3752         g_return_if_fail(terminal != NULL);
3753         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3754         widget = GTK_WIDGET(terminal);
3755
3756         /* Choose default font metrics.  I like '10x20' as a terminal font. */
3757         if (xlfds == NULL) {
3758                 xlfds = "10x20";
3759         }
3760         width = 10;
3761         height = 20;
3762         descent = 0;
3763         ascent = height - descent;
3764
3765         /* Load the font set, freeing another one if we loaded one before. */
3766         if (terminal->pvt->fontset) {
3767                 XFreeFontSet(GDK_DISPLAY(), terminal->pvt->fontset);
3768         }
3769         terminal->pvt->fontset = XCreateFontSet(GDK_DISPLAY(),
3770                                                 xlfds,
3771                                                 &missing_charset_list,
3772                                                 &missing_charset_count,
3773                                                 &def_string);
3774         g_return_if_fail(terminal->pvt->fontset != NULL);
3775         XFreeStringList(missing_charset_list);
3776         missing_charset_list = NULL;
3777         /* Read the font metrics. */
3778         if (XFontsOfFontSet(terminal->pvt->fontset,
3779                             &font_struct_list,
3780                             &font_name_list)) {
3781                 if (font_struct_list) {
3782                         if (font_struct_list[0]) {
3783                                 font_struct = font_struct_list[0][0];
3784                                 width = font_struct.max_bounds.width;
3785                                 ascent = font_struct.max_bounds.ascent;
3786                                 descent = font_struct.max_bounds.descent;
3787                                 height = ascent + descent;
3788                         }
3789                 }
3790                 XFreeStringList(font_name_list);
3791                 font_name_list = NULL;
3792         }
3793
3794 #ifdef HAVE_XFT
3795         if (terminal->pvt->use_xft) {
3796                 if (terminal->pvt->ftfont != NULL) {
3797                         XftFontClose(GDK_DISPLAY(), terminal->pvt->ftfont);
3798                 }
3799                 terminal->pvt->ftfont = XftFontOpen(GDK_DISPLAY(),
3800                                                     gdk_x11_get_default_screen(),
3801                                                     XFT_FAMILY, XftTypeString, "courier",
3802
3803                                                     XFT_SIZE, XftTypeDouble, 16.0,
3804                                                     0);
3805                 if (terminal->pvt->ftfont != NULL) {
3806                         ascent = terminal->pvt->ftfont->ascent;
3807                         descent = terminal->pvt->ftfont->descent;
3808                         height = terminal->pvt->ftfont->height;
3809                         width = terminal->pvt->ftfont->max_advance_width;
3810                 } else {
3811                         g_warning("Error allocating Xft font, disabling Xft.");
3812                         terminal->pvt->use_xft = FALSE;
3813                 }
3814         }
3815 #endif
3816
3817         /* Now save the values. */
3818         terminal->char_width = width;
3819         terminal->char_height = height;
3820         terminal->char_ascent = ascent;
3821         terminal->char_descent = descent;
3822
3823         /* Emit a signal that the font changed. */
3824         g_signal_emit_by_name(terminal,
3825                               "char_size_changed",
3826                               terminal->char_width,
3827                               terminal->char_height);
3828 }
3829
3830 /* A comparison function which helps sort quarks. */
3831 static gint
3832 vte_compare_direct(gconstpointer a, gconstpointer b)
3833 {
3834         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3835 }
3836
3837 /* Read and refresh our perception of the size of the PTY. */
3838 static void
3839 vte_terminal_size_get(VteTerminal *terminal)
3840 {
3841         struct winsize size;
3842         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3843         if (terminal->pvt->pty_master != -1) {
3844                 /* Use an ioctl to read the size of the terminal. */
3845                 if (ioctl(terminal->pvt->pty_master, TIOCGWINSZ, &size) != 0) {
3846                         g_warning("Error reading PTY size, using defaults: "
3847                                   "%s.", strerror(errno));
3848                 } else {
3849                         terminal->row_count = size.ws_row;
3850                         terminal->column_count = size.ws_col;
3851                 }
3852         }
3853 }
3854
3855 /* Set the size of the PTY. */
3856 void
3857 vte_terminal_size_set(VteTerminal *terminal, guint columns, guint rows)
3858 {
3859         struct winsize size;
3860         g_return_if_fail(VTE_IS_TERMINAL(terminal));
3861         if (terminal->pvt->pty_master != -1) {
3862                 size.ws_row = rows;
3863                 size.ws_col = columns;
3864                 /* Try to set the terminal size. */
3865                 if (ioctl(terminal->pvt->pty_master, TIOCSWINSZ, &size) != 0) {
3866                         g_warning("Error setting PTY size: %s.",
3867                                   strerror(errno));
3868          &