2 * Copyright (C) 2001,2002 Red Hat, Inc.
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.
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.
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.
20 #include "../config.h"
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/param.h>
37 #include <glib-object.h>
39 #include <gdk/gdkkeysyms.h>
49 #include <X11/Xft/Xft.h>
52 #define VTE_TAB_WIDTH 8
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 */
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 */
70 /* Input data queues. */
71 iconv_t pending_conv; /* narrow/wide conversion state */
72 wchar_t *pending; /* pending output characters */
74 char *narrow_pending; /* pending output characters */
75 size_t n_narrow_pending;
76 iconv_t outgoing_conv; /* narrow/wide conversion state */
78 /* Data used when rendering the text. */
79 gboolean palette_initialized;
81 guint16 red, green, blue;
95 /* Emulation state. */
98 /* Screen data. We support the normal screen, and an alternate
99 * screen, which seems to be a DEC-specific feature. */
101 GArray *row_data; /* row data, arranged as a GArray of
102 vte_charcell structures */
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 */
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
119 } normal_screen, alternate_screen, *screen;
122 /* A function which can handle a terminal control sequence. */
123 typedef void (*VteTerminalSequenceHandler)(VteTerminal *terminal,
126 GValueArray *params);
128 static void vte_sequence_handler_clear_screen(VteTerminal *terminal,
131 GValueArray *params);
132 static void vte_sequence_handler_ho(VteTerminal *terminal,
135 GValueArray *params);
136 static void vte_sequence_handler_do(VteTerminal *terminal,
139 GValueArray *params);
140 static void vte_sequence_handler_up(VteTerminal *terminal,
143 GValueArray *params);
145 /* Allocate a new line. */
149 return g_array_new(FALSE, FALSE, sizeof(struct vte_charcell));
152 /* Reset defaults for character insertion. */
154 vte_terminal_set_default_attributes(VteTerminal *terminal)
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; */
169 /* Cause certain cells to be updated. */
171 vte_invalidate_cells(VteTerminal *terminal,
172 glong column_start, gint column_count,
173 glong row_start, gint row_count)
176 GtkWidget *widget = GTK_WIDGET(terminal);
178 g_return_if_fail(VTE_IS_TERMINAL(terminal));
180 /* Subtract the scrolling offset from the row start so that the
181 * resulting rectangle is relative to the visible portion of the
183 row_start -= terminal->pvt->screen->scroll_delta;
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;
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;
196 /* Invalidate the rectangle. */
197 gdk_window_invalidate_rect(widget->window, &rect, TRUE);
200 /* Update the adjustment field of the widget. This function should be called
201 * whenever we add rows to the history or switch screens. */
203 vte_terminal_adjust_adjustments(VteTerminal *terminal)
208 /* Adjust the vertical, uh, adjustment. */
210 /* The lower value should always be zero. */
211 if (terminal->adjustment->lower != 0) {
212 terminal->adjustment->lower = 0;
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;
223 /* The step increment should always be one. */
224 if (terminal->adjustment->step_increment != 1) {
225 terminal->adjustment->step_increment = 1;
228 /* Set the number of rows the user sees to the number of rows the
230 page_size = terminal->row_count;
231 if (terminal->adjustment->page_size != page_size) {
232 terminal->adjustment->page_size = page_size;
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;
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);
248 /* If anything changed, signal that there was a change. */
249 if (changed == TRUE) {
250 gtk_adjustment_changed(terminal->adjustment);
254 /* Call another function, offsetting any long arguments by the given
255 * increment value. */
257 vte_sequence_handler_offset(VteTerminal *terminal,
262 VteTerminalSequenceHandler handler)
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);
274 g_value_set_long(value, val);
277 handler(terminal, match, match_quark, params);
280 /* Call another function a given number of times, or once. */
282 vte_sequence_handler_multiple(VteTerminal *terminal,
286 VteTerminalSequenceHandler handler)
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);
297 for (i = 0; i < val; i++) {
298 handler(terminal, match, match_quark, NULL);
302 /* Insert a blank line at an arbitrary position. */
304 vte_insert_line_int(VteTerminal *terminal, long position)
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);
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);
318 g_array_append_val(terminal->pvt->screen->row_data, array);
322 /* Remove a line at an arbitrary position. */
324 vte_remove_line_int(VteTerminal *terminal, long position)
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,
332 g_array_remove_index(terminal->pvt->screen->row_data, position);
333 g_array_free(array, TRUE);
337 /* Change the encoding used for the terminal to the given codeset, or the
338 * locale default if NULL is passed in. */
340 vte_terminal_set_encoding(VteTerminal *terminal, const char *codeset)
342 if (codeset == NULL) {
343 codeset = nl_langinfo(CODESET);
346 if (terminal->pvt->pending_conv != NULL) {
347 iconv_close(terminal->pvt->pending_conv);
349 terminal->pvt->pending_conv = iconv_open("WCHAR_T", codeset);
351 if (terminal->pvt->outgoing_conv != NULL) {
352 iconv_close(terminal->pvt->outgoing_conv);
354 terminal->pvt->outgoing_conv = iconv_open(codeset, "WCHAR_T");
356 terminal->pvt->encoding = g_quark_to_string(g_quark_from_string(codeset));
358 g_print("Set encoding to `%s'.\n", terminal->pvt->encoding);
362 /* End alternate character set. */
364 vte_sequence_handler_ae(VteTerminal *terminal,
369 g_return_if_fail(VTE_IS_TERMINAL(terminal));
370 terminal->pvt->screen->defaults.alternate = 0;
373 /* Add a line at the current cursor position. */
375 vte_sequence_handler_al(VteTerminal *terminal,
380 struct _VteScreen *screen;
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;
388 start = screen->insert_delta;
389 end = screen->insert_delta + terminal->row_count - 1;
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,
399 /* Add N lines at the current cursor position. */
401 vte_sequence_handler_AL(VteTerminal *terminal,
406 g_return_if_fail(VTE_IS_TERMINAL(terminal));
407 vte_sequence_handler_multiple(terminal, match, match_quark, params,
408 vte_sequence_handler_al);
411 /* Start using alternate character set. */
413 vte_sequence_handler_as(VteTerminal *terminal,
418 g_return_if_fail(VTE_IS_TERMINAL(terminal));
419 terminal->pvt->screen->defaults.alternate = 1;
424 vte_sequence_handler_bl(VteTerminal *terminal,
429 g_return_if_fail(VTE_IS_TERMINAL(terminal));
433 /* Clear from the cursor position to the beginning of the line. */
435 vte_sequence_handler_cb(VteTerminal *terminal,
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,
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);
461 /* Repaint this row. */
462 vte_invalidate_cells(terminal,
463 0, terminal->column_count,
464 screen->cursor_current.row, 1);
468 /* Clear below the current line. */
470 vte_sequence_handler_cd(VteTerminal *terminal,
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;
485 /* Get the data for the row we're removing. */
486 rowdata = g_array_index(screen->row_data, GArray*, i);
488 while ((rowdata != NULL) && (rowdata->len > 0)) {
489 g_array_remove_index(rowdata, rowdata->len - 1);
491 /* Repaint this row. */
492 vte_invalidate_cells(terminal,
493 0, terminal->column_count,
498 /* Clear from the cursor position to the end of the line. */
500 vte_sequence_handler_ce(VteTerminal *terminal,
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);
519 /* Repaint this row. */
520 vte_invalidate_cells(terminal,
521 0, terminal->column_count,
522 screen->cursor_current.row, 1);
526 /* Move the cursor to the given column (horizontal position). */
528 vte_sequence_handler_ch(VteTerminal *terminal,
533 struct _VteScreen *screen;
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);
554 /* Clear the screen and home the cursor. */
556 vte_sequence_handler_cl(VteTerminal *terminal,
561 vte_sequence_handler_clear_screen(terminal, NULL, 0, NULL);
562 vte_sequence_handler_ho(terminal, NULL, 0, NULL);
565 /* Move the cursor to the given position. */
567 vte_sequence_handler_cm(VteTerminal *terminal,
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);
590 /* Clear from the current line. */
592 vte_sequence_handler_clear_current_line(VteTerminal *terminal,
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);
608 while (rowdata->len > 0) {
609 g_array_remove_index(rowdata, rowdata->len - 1);
611 /* Repaint this row. */
612 vte_invalidate_cells(terminal,
613 0, terminal->column_count,
614 screen->cursor_current.row, 1);
618 /* Carriage return. */
620 vte_sequence_handler_cr(VteTerminal *terminal,
625 g_return_if_fail(VTE_IS_TERMINAL(terminal));
626 terminal->pvt->screen->cursor_current.col = 0;
629 /* Restrict scrolling and updates to a subset of the visible lines. */
631 vte_sequence_handler_cs(VteTerminal *terminal,
636 long start, end, rows;
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;
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;
661 /* Move the cursor to the given row (vertical position). */
663 vte_sequence_handler_cv(VteTerminal *terminal,
668 struct _VteScreen *screen;
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);
689 /* Delete a line at the current cursor position. */
691 vte_sequence_handler_dl(VteTerminal *terminal,
696 struct _VteScreen *screen;
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;
703 end = screen->insert_delta + terminal->row_count - 1;
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,
710 terminal->column_count,
711 terminal->pvt->screen->insert_delta,
712 terminal->row_count);
715 /* Delete N lines at the current cursor position. */
717 vte_sequence_handler_DL(VteTerminal *terminal,
722 g_return_if_fail(VTE_IS_TERMINAL(terminal));
723 vte_sequence_handler_multiple(terminal, match, match_quark, params,
724 vte_sequence_handler_dl);
727 /* Scroll forward. */
729 vte_sequence_handler_do(VteTerminal *terminal,
735 long rows, col, row, start, end, delta;
736 struct _VteScreen *screen;
738 g_return_if_fail(VTE_IS_TERMINAL(terminal));
739 widget = GTK_WIDGET(terminal);
740 screen = terminal->pvt->screen;
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;
747 if (screen->scrolling_restricted) {
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);
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,
766 /* Move the cursor down. */
767 screen->cursor_current.row++;
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);
775 /* Update scroll bar adjustments. */
776 vte_terminal_adjust_adjustments(terminal);
778 /* Keep the cursor on-screen. */
779 if (floor(gtk_adjustment_get_value(terminal->adjustment)) != delta) {
780 gtk_adjustment_set_value(terminal->adjustment, delta);
787 vte_sequence_handler_DO(VteTerminal *terminal,
792 vte_sequence_handler_multiple(terminal, match, match_quark, params,
793 vte_sequence_handler_do);
796 /* End insert mode. */
798 vte_sequence_handler_ei(VteTerminal *terminal,
803 g_return_if_fail(VTE_IS_TERMINAL(terminal));
804 terminal->pvt->screen->insert = FALSE;
807 /* Move the cursor to the home position. */
809 vte_sequence_handler_ho(VteTerminal *terminal,
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;
821 /* Begin insert mode. */
823 vte_sequence_handler_im(VteTerminal *terminal,
828 g_return_if_fail(VTE_IS_TERMINAL(terminal));
829 terminal->pvt->screen->insert = TRUE;
834 vte_sequence_handler_le(VteTerminal *terminal,
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);
848 /* Move the cursor left N columns. */
850 vte_sequence_handler_LE(VteTerminal *terminal,
855 vte_sequence_handler_multiple(terminal, match, match_quark, params,
856 vte_sequence_handler_le);
861 vte_sequence_handler_mb(VteTerminal *terminal,
866 g_return_if_fail(VTE_IS_TERMINAL(terminal));
867 terminal->pvt->screen->defaults.blink = 1;
872 vte_sequence_handler_md(VteTerminal *terminal,
877 g_return_if_fail(VTE_IS_TERMINAL(terminal));
878 terminal->pvt->screen->defaults.bold = 1;
883 vte_sequence_handler_me(VteTerminal *terminal,
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;
900 vte_sequence_handler_mk(VteTerminal *terminal,
905 g_return_if_fail(VTE_IS_TERMINAL(terminal));
906 terminal->pvt->screen->defaults.invisible = 1;
911 vte_sequence_handler_mr(VteTerminal *terminal,
916 g_return_if_fail(VTE_IS_TERMINAL(terminal));
917 terminal->pvt->screen->defaults.reverse = 1;
922 vte_sequence_handler_nd(VteTerminal *terminal,
927 struct _VteScreen *screen;
928 g_return_if_fail(VTE_IS_TERMINAL(terminal));
929 screen = terminal->pvt->screen;
930 screen->cursor_current.col++;
933 /* Restore cursor (position). */
935 vte_sequence_handler_rc(VteTerminal *terminal,
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;
948 /* Cursor right N characters. */
950 vte_sequence_handler_RI(VteTerminal *terminal,
955 vte_sequence_handler_multiple(terminal, match, match_quark, params,
956 vte_sequence_handler_nd);
959 /* Save cursor (position). */
961 vte_sequence_handler_sc(VteTerminal *terminal,
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;
976 vte_sequence_handler_se(VteTerminal *terminal,
981 g_return_if_fail(VTE_IS_TERMINAL(terminal));
982 terminal->pvt->screen->defaults.standout = 0;
985 /* Standout start. */
987 vte_sequence_handler_so(VteTerminal *terminal,
992 g_return_if_fail(VTE_IS_TERMINAL(terminal));
993 terminal->pvt->screen->defaults.standout = 1;
996 /* Tab. FIXME: implement custom tabstop setting and the whole nine yards. */
998 vte_sequence_handler_ta(VteTerminal *terminal,
1001 GValueArray *params)
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;
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);
1020 terminal->pvt->screen->cursor_current.col = newcol;
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);
1028 /* Underline end. */
1030 vte_sequence_handler_ue(VteTerminal *terminal,
1033 GValueArray *params)
1035 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1036 terminal->pvt->screen->defaults.underline = 0;
1039 /* Cursor up, scrolling if need be. */
1041 vte_sequence_handler_up(VteTerminal *terminal,
1044 GValueArray *params)
1047 long col, row, start, end;
1048 struct _VteScreen *screen;
1050 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1051 widget = GTK_WIDGET(terminal);
1052 screen = terminal->pvt->screen;
1054 col = screen->cursor_current.col;
1055 row = screen->cursor_current.row;
1057 if (screen->scrolling_restricted) {
1058 start = screen->scrolling_region.start + screen->insert_delta;
1059 end = screen->scrolling_region.end + screen->insert_delta;
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);
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,
1079 start = terminal->pvt->screen->insert_delta;
1080 end = start + terminal->row_count - 1;
1082 /* Insert a blank line and remove one from the bottom,
1083 * to simulate a proper scroll without screwing up the
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);
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,
1105 vte_sequence_handler_UP(VteTerminal *terminal,
1108 GValueArray *params)
1110 vte_sequence_handler_multiple(terminal, match, match_quark, params,
1111 vte_sequence_handler_up);
1114 /* Underline start. */
1116 vte_sequence_handler_us(VteTerminal *terminal,
1119 GValueArray *params)
1121 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1122 terminal->pvt->screen->defaults.underline = 1;
1125 /* Cursor invisible. */
1127 vte_sequence_handler_vi(VteTerminal *terminal,
1130 GValueArray *params)
1132 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1133 terminal->pvt->screen->cursor_visible = FALSE;
1136 /* Cursor standout. */
1138 vte_sequence_handler_vs(VteTerminal *terminal,
1141 GValueArray *params)
1143 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1144 terminal->pvt->screen->cursor_visible = TRUE;
1147 /* Handle ANSI color setting and related stuffs (SGR). */
1149 vte_sequence_handler_character_attributes(VteTerminal *terminal,
1152 GValueArray *params)
1157 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1158 /* The default parameter is zero. */
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)) {
1167 param = g_value_get_long(value);
1170 vte_terminal_set_default_attributes(terminal);
1173 terminal->pvt->screen->defaults.bold = 1;
1176 terminal->pvt->screen->defaults.underline = 1;
1179 terminal->pvt->screen->defaults.blink = 1;
1182 terminal->pvt->screen->defaults.reverse = 1;
1185 terminal->pvt->screen->defaults.invisible = 1;
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;
1192 terminal->pvt->screen->defaults.underline = 0;
1195 terminal->pvt->screen->defaults.blink = 0;
1198 terminal->pvt->screen->defaults.reverse = 0;
1201 terminal->pvt->screen->defaults.invisible = 0;
1211 terminal->pvt->screen->defaults.fore = param - 30;
1214 /* default foreground, underscore */
1215 terminal->pvt->screen->defaults.fore = 7;
1216 terminal->pvt->screen->defaults.underline = 1;
1219 /* default foreground, no underscore */
1220 terminal->pvt->screen->defaults.fore = 7;
1221 terminal->pvt->screen->defaults.underline = 0;
1231 terminal->pvt->screen->defaults.back = param - 40;
1234 /* default background */
1235 terminal->pvt->screen->defaults.back = 0;
1245 terminal->pvt->screen->defaults.fore = param - 90;
1255 terminal->pvt->screen->defaults.back = param - 100;
1259 /* If we had no parameters, default to the defaults. */
1261 vte_terminal_set_default_attributes(terminal);
1265 /* Clear above the current line. */
1267 vte_sequence_handler_clear_above_current(VteTerminal *terminal,
1270 GValueArray *params)
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);
1284 while (rowdata->len > 0) {
1285 g_array_remove_index(rowdata, rowdata->len - 1);
1287 /* Repaint this row. */
1288 vte_invalidate_cells(terminal,
1289 0, terminal->column_count,
1295 /* Clear the entire screen. */
1297 vte_sequence_handler_clear_screen(VteTerminal *terminal,
1300 GValueArray *params)
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;
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);
1316 while (rowdata->len > 0) {
1317 g_array_remove_index(rowdata, rowdata->len - 1);
1319 /* Repaint this row. */
1320 vte_invalidate_cells(terminal,
1321 0, terminal->column_count,
1327 /* Move the cursor to the given position, 1-based. */
1329 vte_sequence_handler_cursor_position(VteTerminal *terminal,
1332 GValueArray *params)
1334 vte_sequence_handler_offset(terminal, match, match_quark, params,
1335 -1, vte_sequence_handler_cm);
1338 /* Set icon/window titles. */
1340 vte_sequence_handler_set_icon_title(VteTerminal *terminal,
1343 GValueArray *params)
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);
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));
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));
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));
1372 outbuf_len = sizeof(buf) - 1;
1373 if (iconv(conv, &inbuf, &inbuf_len,
1374 &outbuf, &outbuf_len) == -1) {
1375 memset(buf, 0, sizeof(buf));
1380 /* Emit the signal, passing the string. */
1381 g_signal_emit_by_name(terminal, "set_icon_title", buf);
1385 vte_sequence_handler_set_window_title(VteTerminal *terminal,
1388 GValueArray *params)
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);
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));
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));
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));
1417 outbuf_len = sizeof(buf) - 1;
1418 if (iconv(conv, &inbuf, &inbuf_len,
1419 &outbuf, &outbuf_len) == -1) {
1420 memset(buf, 0, sizeof(buf));
1426 /* Emit the signal, passing the string. */
1427 g_signal_emit_by_name(terminal, "set_window_title", buf);
1431 /* Set both the window and icon titles to the same string. */
1433 vte_sequence_handler_set_icon_and_window_title(VteTerminal *terminal,
1436 GValueArray *params)
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);
1444 /* Restrict the scrolling region. */
1446 vte_sequence_handler_set_scrolling_region(VteTerminal *terminal,
1449 GValueArray *params)
1451 vte_sequence_handler_offset(terminal, match, match_quark, params,
1452 -1, vte_sequence_handler_cs);
1455 /* Manipulate certain terminal attributes. */
1457 vte_sequence_handler_decset_internal(VteTerminal *terminal,
1460 GValueArray *params,
1466 if ((params == NULL) || (params->n_values == 0)) {
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)) {
1474 param = g_value_get_long(value);
1477 /* Set the application keypad. */
1478 terminal->pvt->keypad = set ?
1480 VTE_KEYPAD_APPLICATION;
1483 /* FIXME: reset alternate character sets to
1487 /* FIXME: set 132 (reset to 80) column mode. */
1490 /* FIXME: set or unset smooth-scrolling. */
1493 /* normal or reverse video. */
1494 terminal->pvt->screen->defaults.reverse = set;
1497 /* FIXME: origin or normal cursor mode. */
1500 /* FIXME: set or unset wraparound mode. */
1503 /* FIXME: set or unset autorepeat keys. */
1506 /* FIXME: send mouse X and Y on button. */
1509 /* FIXME: Tektronix/Xterm mode. */
1512 /* FIXME: Allow/disallow 80/132 column mode. */
1515 /* FIXME: more(1) fix. */
1518 /* FIXME: set/unset margin bell. */
1521 /* FIXME: set/unset reverse-wraparound mode. */
1524 /* FIXME(?): enable/disable logging. */
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,
1536 terminal->column_count,
1537 terminal->pvt->screen->scroll_delta,
1538 terminal->row_count);
1541 /* FIXME: send mouse X and Y on press and
1545 /* FIXME: use (or not) hilite mouse tracking. */
1553 /* Set the application or normal keypad. */
1555 vte_sequence_handler_application_keypad(VteTerminal *terminal,
1558 GValueArray *params)
1560 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1561 terminal->pvt->keypad = VTE_KEYPAD_APPLICATION;
1565 vte_sequence_handler_normal_keypad(VteTerminal *terminal,
1568 GValueArray *params)
1570 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1571 terminal->pvt->keypad = VTE_KEYPAD_NORMAL;
1574 /* Move the cursor. */
1576 vte_sequence_handler_character_position_absolute(VteTerminal *terminal,
1579 GValueArray *params)
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);
1586 vte_sequence_handler_line_position_absolute(VteTerminal *terminal,
1589 GValueArray *params)
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);
1596 /* Set certain terminal attributes. */
1598 vte_sequence_handler_decset(VteTerminal *terminal,
1601 GValueArray *params)
1603 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1604 vte_sequence_handler_decset_internal(terminal, match, match_quark,
1608 /* Unset certain terminal attributes. */
1610 vte_sequence_handler_decreset(VteTerminal *terminal,
1613 GValueArray *params)
1615 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1616 vte_sequence_handler_decset_internal(terminal, match, match_quark,
1620 /* Erase certain lines in the display. */
1622 vte_sequence_handler_erase_in_display(VteTerminal *terminal,
1625 GValueArray *params)
1630 /* The default parameter is 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)) {
1638 param = g_value_get_long(value);
1640 /* Clear the right area. */
1643 /* Clear below the current line. */
1644 vte_sequence_handler_cd(terminal, NULL, 0, NULL);
1647 /* Clear above the current line. */
1648 vte_sequence_handler_clear_above_current(terminal,
1654 /* Clear the entire screen. */
1655 vte_sequence_handler_clear_screen(terminal,
1665 /* Erase certain parts of the current line in the display. */
1667 vte_sequence_handler_erase_in_line(VteTerminal *terminal,
1670 GValueArray *params)
1675 /* The default parameter is 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)) {
1683 param = g_value_get_long(value);
1685 /* Clear the right area. */
1688 /* Clear to end of the line. */
1689 vte_sequence_handler_ce(terminal, NULL, 0, NULL);
1692 /* Clear to start of the line. */
1693 vte_sequence_handler_cb(terminal, NULL, 0, NULL);
1696 /* Clear the entire line. */
1697 vte_sequence_handler_clear_current_line(terminal,
1705 /* Insert a certain number of lines below the current cursor. */
1707 vte_sequence_handler_insert_lines(VteTerminal *terminal,
1710 GValueArray *params)
1713 struct _VteScreen *screen;
1714 long param, end, row;
1716 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1717 screen = terminal->pvt->screen;
1718 /* The default is one. */
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);
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);
1734 vte_insert_line_int(terminal, row);
1736 /* Refresh the modified area. */
1737 vte_invalidate_cells(terminal,
1738 0, terminal->column_count,
1739 row, end - row + 1);
1742 /* Delete certain lines from the scrolling region. */
1744 vte_sequence_handler_delete_lines(VteTerminal *terminal,
1747 GValueArray *params)
1750 struct _VteScreen *screen;
1751 long param, end, row;
1753 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1754 screen = terminal->pvt->screen;
1755 /* The default is one. */
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);
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);
1771 /* Remove the line at the top of the area. */
1772 vte_remove_line_int(terminal, row);
1774 /* Refresh the modified area. */
1775 vte_invalidate_cells(terminal,
1776 0, terminal->column_count,
1777 row, end - row + 1);
1780 /* Index. Move the cursor down a row, and if it's in a scrolling region,
1781 * scroll to keep it on the screen. */
1783 vte_sequence_handler_index(VteTerminal *terminal,
1786 GValueArray *params)
1788 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1789 vte_sequence_handler_DO(terminal, match, match_quark, params);
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. */
1795 vte_sequence_handler_reverse_index(VteTerminal *terminal,
1798 GValueArray *params)
1800 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1801 vte_sequence_handler_UP(terminal, match, match_quark, params);
1804 /* Set the terminal encoding. */
1806 vte_sequence_handler_iso8859_1(VteTerminal *terminal,
1809 GValueArray *params)
1811 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1812 vte_terminal_set_encoding(terminal, "ISO-8859-1");
1816 vte_sequence_handler_utf_8(VteTerminal *terminal,
1819 GValueArray *params)
1821 g_return_if_fail(VTE_IS_TERMINAL(terminal));
1822 vte_terminal_set_encoding(terminal, "UTF-8");
1825 /* The table of handlers. Primarily used at initialization time. */
1828 VteTerminalSequenceHandler handler;
1829 } vte_sequence_handlers[] = {
1892 {"al", vte_sequence_handler_al},
1893 {"AL", vte_sequence_handler_AL},
1895 {"ae", vte_sequence_handler_ae},
1896 {"as", vte_sequence_handler_as},
1899 {"bl", vte_sequence_handler_bl},
1902 {"cb", vte_sequence_handler_cb},
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},
1912 {"cv", vte_sequence_handler_cv},
1916 {"dl", vte_sequence_handler_dl},
1917 {"DL", vte_sequence_handler_DL},
1919 {"do", vte_sequence_handler_do},
1920 {"DO", vte_sequence_handler_DO},
1926 {"ei", vte_sequence_handler_ei},
1986 {"ho", vte_sequence_handler_ho},
1996 {"im", vte_sequence_handler_im},
2055 {"le", vte_sequence_handler_le},
2056 {"LE", vte_sequence_handler_LE},
2061 {"mb", vte_sequence_handler_mb},
2063 {"md", vte_sequence_handler_md},
2064 {"me", vte_sequence_handler_me},
2066 {"mk", vte_sequence_handler_mk},
2071 {"mr", vte_sequence_handler_mr},
2074 {"nd", vte_sequence_handler_nd},
2093 {"rc", vte_sequence_handler_rc},
2096 {"RI", vte_sequence_handler_RI},
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},
2120 {"ta", vte_sequence_handler_ta},
2126 {"ue", vte_sequence_handler_ue},
2127 {"up", vte_sequence_handler_up},
2128 {"UP", vte_sequence_handler_UP},
2129 {"us", vte_sequence_handler_us},
2133 {"vi", vte_sequence_handler_vi},
2134 {"vs", vte_sequence_handler_vs},
2140 {"character-attributes", vte_sequence_handler_character_attributes},
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},
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},
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},
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
2180 vte_terminal_new(void)
2182 return GTK_WIDGET(g_object_new(vte_terminal_get_type(), NULL));
2185 /* Reset palette defaults for character colors. */
2187 vte_terminal_set_default_palette(VteTerminal *terminal)
2193 GdkColormap *gcolormap;
2197 int bright, red, green, blue;
2199 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2200 if (terminal->pvt->palette_initialized) {
2203 memset(&color, 0, sizeof(color));
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);
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;
2231 /* Allocate a color from the colormap. */
2233 color.red = bright + red;
2234 color.green = bright + green;
2235 color.blue = bright + blue;
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;
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,
2253 &terminal->pvt->palette[i].rcolor,
2254 &terminal->pvt->palette[i].ftcolor)) {
2255 terminal->pvt->use_xft = FALSE;
2260 terminal->pvt->palette_initialized = TRUE;
2263 /* Insert a single character into the stored data array. */
2265 vte_terminal_insert_char(GtkWidget *widget, wchar_t c)
2267 VteTerminal *terminal;
2269 struct vte_charcell cell, *pcell;
2272 struct _VteScreen *screen;
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;
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);
2285 /* Get a handle on the array for the insertion row. */
2286 array = g_array_index(screen->row_data,
2288 screen->cursor_current.row);
2290 /* Figure out how many columns this character should occupy. */
2291 columns = wcwidth(c);
2293 /* Read the deltas. */
2294 for (i = 0; i < columns; i++) {
2295 col = terminal->pvt->screen->cursor_current.col;
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));
2305 while (array->len < col) {
2306 g_array_append_val(array, cell);
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,
2315 /* If we're in insert mode, insert a new cell here
2317 if (screen->insert) {
2318 memset(&cell, 0, sizeof(cell));
2323 g_array_insert_val(array, col, cell);
2324 pcell = &g_array_index(array,
2325 struct vte_charcell,
2328 /* We're in overtype mode, so use the existing
2330 pcell = &g_array_index(array,
2331 struct vte_charcell,
2336 /* Initialize the character cell with the proper data. */
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;
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);
2357 vte_invalidate_cells(terminal,
2358 screen->cursor_current.col - 1, 3,
2359 screen->cursor_current.row, 2);
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++;
2369 display_control_sequence(const char *name, GValueArray *params)
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. */
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);
2384 fprintf(stderr, ", ");
2386 if (G_VALUE_HOLDS_LONG(value)) {
2387 l = g_value_get_long(value);
2388 fprintf(stderr, "%ld", l);
2390 if (G_VALUE_HOLDS_STRING(value)) {
2391 s = g_value_get_string(value);
2392 fprintf(stderr, "\"%s\"", s);
2394 if (G_VALUE_HOLDS_POINTER(value)) {
2395 w = g_value_get_pointer(value);
2396 fprintf(stderr, "\"%ls\"", w);
2400 fprintf(stderr, ")\n");
2403 /* Handle a terminal control sequence and its parameters. */
2405 vte_terminal_handle_sequence(GtkWidget *widget,
2406 const char *match_s,
2408 GValueArray *params)
2410 VteTerminal *terminal;
2411 VteTerminalSequenceHandler handler;
2412 struct _VteScreen *screen;
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;
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);
2424 /* Find the handler for this control sequence. */
2425 handler = g_tree_lookup(terminal->pvt->sequences, GINT_TO_POINTER(match));
2427 display_control_sequence(match_s, params);
2429 if (handler != NULL) {
2430 /* Let the handler handle it. */
2431 handler(terminal, match_s, match, params);
2433 g_warning("No handler for control sequence `%s' defined.\n",
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);
2443 /* Handle an EOF from the client. */
2445 vte_terminal_eof(gint source, gpointer data)
2447 VteTerminal *terminal;
2449 g_return_if_fail(VTE_IS_TERMINAL(data));
2450 terminal = VTE_TERMINAL(data);
2452 /* Stop reading input. */
2453 gtk_input_remove(source);
2454 terminal->pvt->pty_input = -1;
2456 /* Emit a signal that we read an EOF. */
2457 g_signal_emit_by_name(terminal, "eof");
2460 /* Read and handle data from the child. */
2462 vte_terminal_io_read(GIOChannel *channel,
2463 GdkInputCondition condition,
2466 GValueArray *params;
2467 VteTerminal *terminal;
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;
2477 gboolean leave_open = TRUE;
2479 widget = GTK_WIDGET(data);
2480 terminal = VTE_TERMINAL(data);
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;
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);
2497 /* Convert any read bytes into wide characters. FIXME: handle
2498 * cases where the encoding changes mid-stream. */
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,
2506 &outbuf, &outbuf_len) != -1) {
2507 /* Save the resulting bytes as the narrow pending data
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);
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) *
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,
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];
2546 fprintf(stderr, "%ld = ", (long) c);
2549 fprintf(stderr, "^%lc\n",
2552 fprintf(stderr, "`%lc'\n", (wint_t)c);
2555 vte_terminal_insert_char(widget, c);
2557 terminal->pvt->n_pending = 0;
2558 } else if (match[0] != '\0') {
2559 /* A terminal sequence. */
2560 vte_terminal_handle_sequence(GTK_WIDGET(terminal),
2564 if (params != NULL) {
2565 g_value_array_free(params);
2567 terminal->pvt->n_pending = 0;
2569 /* It's a zero-length string, so we need to wait for
2570 * more data from the client. */
2574 /* Handle error conditions. */
2578 g_source_remove(terminal->pvt->pty_input);
2579 vte_terminal_eof(terminal->pvt->pty_input, data);
2584 g_source_remove(terminal->pvt->pty_input);
2585 vte_terminal_eof(terminal->pvt->pty_input,
2593 g_warning("Error reading from child: "
2594 "%s.\n", strerror(errno));
2602 /* Send some data to the child. */
2604 vte_terminal_send(VteTerminal *terminal, const guchar *data, size_t length)
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));
2614 /* Read and handle a keypress event. */
2616 vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
2618 VteTerminal *terminal;
2619 GdkModifierType modifiers;
2620 struct vte_termcap *termcap;
2622 unsigned char *normal = NULL;
2623 size_t normal_length = 0;
2624 unsigned char *special = NULL;
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);
2630 if (event->type == GDK_KEY_PRESS) {
2631 /* Read the modifiers. */
2632 if (gdk_event_get_state((GdkEvent*)event,
2633 &modifiers) == FALSE) {
2636 /* Map the key to a sequence name if we can. */
2637 switch (event->keyval) {
2705 if (modifiers & GDK_SHIFT_MASK) {
2706 fprintf(stderr, "Shift-PgUp\n");
2712 if (modifiers & GDK_SHIFT_MASK) {
2713 fprintf(stderr, "Shift-PgDn\n");
2719 if (modifiers & GDK_SHIFT_MASK) {
2722 normal = g_strdup("\t");
2726 /* The default is to just send the string. */
2728 if (event->string != NULL) {
2729 normal = g_strdup(event->string);
2730 normal_length = strlen(normal);
2734 /* If we got normal characters, send them to the child. */
2735 if (normal != NULL) {
2736 vte_terminal_send(terminal, normal, normal_length);
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,
2748 special = g_strdup_printf(normal, 1);
2749 vte_terminal_send(terminal, special, strlen(special));
2757 /* Read and handle a pointing device buttonpress event. */
2759 vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
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);
2771 /* Handle receiving or losing focus. */
2773 vte_terminal_focus_in(GtkWidget *widget, GdkEventFocus *event)
2775 g_return_val_if_fail(GTK_IS_WIDGET(widget), 0);
2776 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
2781 vte_terminal_focus_out(GtkWidget *widget, GdkEventFocus *event)
2783 g_return_val_if_fail(GTK_WIDGET(widget), 0);
2784 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
2788 /* Set the fontset used for rendering text into the widget. */
2790 vte_terminal_set_fontset(VteTerminal *terminal, const char *xlfds)
2792 guint width, height, ascent, descent;
2794 XFontStruct **font_struct_list, font_struct;
2795 char **missing_charset_list, *def_string;
2796 int missing_charset_count;
2797 char **font_name_list;
2799 g_return_if_fail(terminal != NULL);
2800 g_return_if_fail(VTE_IS_TERMINAL(terminal));
2801 widget = GTK_WIDGET(terminal);
2803 /* Choose default font metrics. I like '10x20' as a terminal font. */
2804 if (xlfds == NULL) {
2810 ascent = height - descent;
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);
2816 terminal->pvt->fontset = XCreateFontSet(GDK_DISPLAY(),
2818 &missing_charset_list,
2819 &missing_charset_count,
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,
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;
2837 XFreeStringList(font_name_list);
2838 font_name_list = NULL;
2842 if (terminal->pvt->use_xft) {
2843 if (terminal->pvt->ftfont != NULL) {
2844 XftFontClose(GDK_DISPLAY(), terminal->pvt->ftfont);
2846 terminal->pvt->ftfont = XftFontOpen(GDK_DISPLAY(),
2847 gdk_x11_get_default_screen(),
2848 XFT_FAMILY, XftTypeString, "courier",
2850 XFT_SIZE, XftTypeDouble, 16.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;
2858 g_warning("Error allocating Xft font, disabling Xft.");
2859 terminal->pvt->use_xft = FALSE;
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;
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);
2877 /* A comparison function which helps sort quarks. */
2879 vte_compare_direct(gconstpointer a, gconstpointer b)
2881 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
2884 /* Read and refresh our perception of the size of the PTY. */
2886 vte_terminal_pty_size_get(VteTerminal *terminal)
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.",
2895 terminal->row_count = 10;
2896 terminal->column_count = 60;
2898 terminal->row_count = size.ws_row;
2899 terminal->column_count = size.ws_col;
2903 /* Set the size of the PTY. */
2905 vte_terminal_pty_size_set(VteTerminal *terminal, guint columns, guint rows)
2907 struct winsize size;
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));
2916 /* Read the terminal size, in case something went awry. */
2917 vte_terminal_pty_size_get(terminal);
2920 /* Redraw the widget. */
2922 vte_handle_scroll(VteTerminal *terminal)
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) {
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;
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);
2946 /* Let the refreshing begin. */
2947 gdk_window_thaw_updates(widget->window);
2950 /* Set the adjustment objects used by the terminal widget. */
2952 vte_terminal_set_scroll_adjustment(VteTerminal *terminal,
2953 GtkAdjustment *adjustment)
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),
2965 g_object_unref(terminal->adjustment);
2967 /* Set the new adjustment object. */
2968 terminal->adjustment = adjustment;
2969 g_signal_connect_swapped(terminal->adjustment,
2971 G_CALLBACK(vte_handle_scroll),
2973 g_signal_connect_swapped(terminal->adjustment,
2975 G_CALLBACK(vte_handle_scroll),
2980 /* Set the type of terminal we're emulating. */
2982 vte_terminal_set_emulation(VteTerminal *terminal, const char *emulation)
2984 const char *code, *value;
2989 /* Set the emulation type, for reference. */
2990 if (emulation == NULL) {
2991 emulation = "xterm";
2993 quark = g_quark_from_string(emulation);
2994 terminal->pvt->terminal = g_quark_to_string(quark);
2996 g_print("Setting emulation to `%s'...", emulation);
2999 /* Create a trie to hold the control sequences. */
3000 if (terminal->pvt->trie) {
3001 vte_trie_free(terminal->pvt->trie);
3003 terminal->pvt->trie = vte_trie_new();
3005 /* Create a tree to hold the handlers. */
3006 if (terminal->pvt->sequences) {
3007 g_tree_destroy(terminal->pvt->sequences);
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);
3019 /* Load the known capability strings from the termcap structure into
3020 * the trie for recognition. */
3022 vte_terminal_capability_strings[i].capability != NULL;
3024 code = vte_terminal_capability_strings[i].capability;
3025 tmp = vte_termcap_find_string(terminal->pvt->termcap,
3026 terminal->pvt->terminal,
3028 if ((tmp != NULL) && (tmp[0] != '\0')) {
3029 vte_trie_add(terminal->pvt->trie, tmp, strlen(tmp),
3030 vte_terminal_capability_strings[i].capability,
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);
3047 /* Set the path to the termcap file we read, and read it in. */
3049 vte_terminal_set_termcap(VteTerminal *terminal, const char *path)
3052 path = "/etc/termcap";
3054 terminal->pvt->termcap_path = g_quark_to_string(g_quark_from_string(path));
3056 g_print("Loading termcap `%s'...", terminal->pvt->termcap_path);
3058 if (terminal->pvt->termcap) {
3059 vte_termcap_free(terminal->pvt->termcap);
3061 terminal->pvt->termcap = vte_termcap_new(path);
3065 vte_terminal_set_emulation(terminal, terminal->pvt->terminal);
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. */
3072 vte_terminal_init(VteTerminal *terminal)
3074 struct _VteTerminalPrivate *pvt;
3075 GtkAdjustment *adjustment;
3076 GIOChannel *channel;
3077 const char **env_add;
3080 g_return_if_fail(VTE_IS_TERMINAL(terminal));
3081 GTK_WIDGET_SET_FLAGS(GTK_WIDGET(terminal), GTK_CAN_FOCUS);
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;
3089 pvt->pending = NULL;
3091 pvt->palette_initialized = FALSE;
3092 pvt->keypad = VTE_KEYPAD_NORMAL;
3093 adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
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;
3105 vte_terminal_set_termcap(terminal, NULL);
3106 vte_terminal_set_emulation(terminal, NULL);
3107 vte_terminal_set_encoding(terminal, NULL);
3109 pvt->normal_screen.row_data = g_array_new(FALSE, TRUE,
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;
3120 pvt->alternate_screen.row_data = g_array_new(FALSE, TRUE,
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;
3131 pvt->screen = &terminal->pvt->alternate_screen;
3132 vte_terminal_set_default_attributes(terminal);
3134 pvt->screen = &terminal->pvt->normal_screen;
3135 vte_terminal_set_default_attributes(terminal);
3137 vte_terminal_set_scroll_adjustment(terminal, adjustment);
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);
3144 pvt->pty_master = vte_pty_open(&terminal->pvt->pty_pid,
3146 terminal->pvt->shell,
3148 g_free((char*)env_add[0]);
3149 g_free((char*)env_add[1]);
3150 g_free((char**)env_add);
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,
3158 vte_terminal_io_read,
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,
3167 vte_termcap_find_numeric(pvt->termcap,
3172 vte_terminal_set_fontset(terminal, NULL);
3175 /* Tell GTK+ how much space we need. */
3177 vte_terminal_size_request(GtkWidget *widget, GtkRequisition *requisition)
3179 VteTerminal *terminal;
3181 g_return_if_fail(widget != NULL);
3182 g_return_if_fail(VTE_IS_TERMINAL(widget));
3183 terminal = VTE_TERMINAL(widget);
3185 requisition->width = terminal->char_width * terminal->column_count;
3186 requisition->height = terminal->char_height * terminal->row_count;
3189 /* Accept a given size from GTK+. */
3191 vte_terminal_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
3193 VteTerminal *terminal;
3195 g_return_if_fail(widget != NULL);
3196 g_return_if_fail(VTE_IS_TERMINAL(widget));
3197 terminal = VTE_TERMINAL(widget);
3199 /* Set our allocation to match the structure. */
3200 widget->allocation = *allocation;
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;
3206 /* Set the size of the pseudo-terminal. */
3207 vte_terminal_pty_size_set(terminal,
3208 terminal->column_count,
3209 terminal->row_count);
3211 /* Resize the GDK window. */
3212 if (widget->window != NULL) {
3213 gdk_window_move_resize(widget->window,
3217 allocation->height);
3220 /* Adjust the adjustments. */
3221 vte_terminal_adjust_adjustments(terminal);
3224 /* The window is being destroyed. */
3226 vte_terminal_unrealize(GtkWidget *widget)
3228 VteTerminal *terminal;
3231 GdkColormap *gcolormap;
3237 g_return_if_fail(widget != NULL);
3238 g_return_if_fail(VTE_IS_TERMINAL(widget));
3239 terminal = VTE_TERMINAL(widget);
3241 /* Free the color palette. */
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,
3254 &terminal->pvt->palette[i].ftcolor);
3256 if (terminal->pvt->ftfont != NULL) {
3257 XftFontClose(display, terminal->pvt->ftfont);
3258 terminal->pvt->ftfont = NULL;
3262 /* Unmap the widget if it hasn't been already. */
3263 if (GTK_WIDGET_MAPPED(widget)) {
3264 gtk_widget_unmap(widget);
3267 /* Remove the GDK window. */
3268 if (widget->window != NULL) {
3269 gdk_window_destroy(widget->window);
3270 widget->window = NULL;
3273 /* Mark that we no longer have a GDK window. */
3274 GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
3276 /* Free some of our strings. */
3277 terminal->pvt->termcap_path = NULL;
3278 terminal->pvt->shell = NULL;
3279 terminal->pvt->terminal = NULL;
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);
3287 terminal->pvt->pty_pid = 0;
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;
3295 /* Discard any pending data. */
3296 g_free(terminal->pvt->pending);
3297 terminal->pvt->pending = NULL;
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;
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,
3312 g_array_free(array, TRUE);
3314 g_array_free(terminal->pvt->normal_screen.row_data, TRUE);
3315 terminal->pvt->normal_screen.row_data = NULL;
3317 for (i = 0; i < terminal->pvt->alternate_screen.row_data->len; i++) {
3318 array = g_array_index(terminal->pvt->alternate_screen.row_data,
3321 g_array_free(array, TRUE);
3323 g_array_free(terminal->pvt->alternate_screen.row_data, TRUE);
3324 terminal->pvt->alternate_screen.row_data = NULL;
3327 /* Handle realizing the widget. Most of this is copy-paste from GGAD. */
3329 vte_terminal_realize(GtkWidget *widget)
3331 VteTerminal *terminal = NULL;
3332 GdkWindowAttr attributes;
3333 GdkColor black = {0, 0, 0};
3334 int attributes_mask = 0;
3336 g_return_if_fail(widget != NULL);
3337 g_return_if_fail(VTE_IS_TERMINAL(widget));
3338 terminal = VTE_TERMINAL(widget);
3340 /* Create a GDK window for the widget. */
3341 attributes.window_type = GDK_WINDOW_CHILD;
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) |
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 |
3361 widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
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);
3372 /* Set up styles, backgrounds, and whatnot. */
3373 widget->style = gtk_style_attach(widget->style, widget->window);
3374 gtk_style_set_background(widget->style,
3377 gdk_window_set_background(widget->window, &black);
3379 /* Set the realized flag. */
3380 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
3382 /* Grab input focus. */
3383 gtk_widget_grab_focus(widget);
3386 /* Find the character in the given "virtual" position. */
3387 struct vte_charcell *
3388 vte_terminal_find_charcell(VteTerminal *terminal, long row, long col)
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);
3404 /* Draw the widget. */
3406 vte_terminal_paint(GtkWidget *widget, GdkRectangle *area)
3408 VteTerminal *terminal = NULL;
3409 struct _VteScreen *screen;
3411 GdkDrawable *gdrawable;
3413 GdkColormap *gcolormap;
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;
3422 XwcTextItem textitem;
3424 XftDraw *ftdraw = NULL;
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)) {
3435 screen = terminal->pvt->screen;
3437 /* Set up the default palette. */
3438 vte_terminal_set_default_palette(terminal);
3440 /* Get the X11 structures we need for the drawing area. */
3441 gdk_window_get_internal_paint_info(widget->window, &gdrawable,
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);
3452 if (terminal->pvt->use_xft) {
3453 gdk_window_get_internal_paint_info(widget->window, &gdrawable,
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;
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;
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,
3480 /* Now we're ready to draw the text. Iterate over the rows we
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. */
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);
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;
3503 cell = vte_terminal_find_charcell(terminal, drow, dcol);
3504 if (cell->columns > 0) {
3508 /* If we didn't find anything, bail. */
3513 /* Determine what the foreground and background
3514 * colors for rendering text should be. */
3515 if (cell->reverse) {
3522 if (cell->invisible) {
3528 if (cell->standout) {
3532 /* Set the textitem's fields. */
3533 textitem.chars = &cell->c;
3534 textitem.nchars = 1;
3536 textitem.font_set = terminal->pvt->fontset;
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,
3547 if (cell->alternate) {
3548 long xleft, ytop, xcenter, ycenter,
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;
3556 ycenter = ytop + ascent / 2;
3558 ycenter = (ytop + ybottom) / 2;
3560 /* Draw the alternate charset data. */
3561 XSetForeground(display, gc,
3562 terminal->pvt->palette[fore].pixel);
3565 /* drawing a blank */
3667 (ytop + ycenter) / 2,
3669 (ytop + ycenter) / 2);
3687 (ycenter + ybottom) / 2,
3689 (ycenter + ybottom) / 2);