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