/* * Copyright (C) 2001,2002 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "debug.h" #undef VTE_DISABLE_DEPRECATED #include #ifdef WITH_PCRE2 #include "vtepcre2.h" #endif #include #define DINGUS1 "(((gopher|news|telnet|nntp|file|http|ftp|https)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.]+(:[0-9]*)?" #define DINGUS2 DINGUS1 "/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\) ,\\\"]" static const char *builtin_dingus[] = { DINGUS1, DINGUS2, NULL }; static void window_title_changed(GtkWidget *widget, gpointer win) { GtkWindow *window; g_assert(VTE_TERMINAL(widget)); g_assert(GTK_IS_WINDOW(win)); window = GTK_WINDOW(win); gtk_window_set_title(window, vte_terminal_get_window_title(VTE_TERMINAL(widget))); } static void icon_title_changed(GtkWidget *widget, gpointer win) { g_assert(VTE_TERMINAL(widget)); g_assert(GTK_IS_WINDOW(win)); g_message("Icon title changed to \"%s\".\n", vte_terminal_get_icon_title(VTE_TERMINAL(widget))); } static void char_size_changed(GtkWidget *widget, guint width, guint height, gpointer data) { VteTerminal *terminal = VTE_TERMINAL(widget); GtkWindow *window = GTK_WINDOW(data); if (!gtk_widget_get_realized (widget)) return; vte_terminal_set_geometry_hints_for_window(terminal, window); } static void char_size_realized(GtkWidget *widget, gpointer data) { VteTerminal *terminal = VTE_TERMINAL(widget); GtkWindow *window = GTK_WINDOW(data); if (!gtk_widget_get_realized (widget)) return; vte_terminal_set_geometry_hints_for_window(terminal, window); } static void destroy_and_quit(VteTerminal *terminal, GtkWidget *window) { const char *output_file = g_object_get_data (G_OBJECT (terminal), "output_file"); if (output_file) { GFile *file; GOutputStream *stream; GError *error = NULL; file = g_file_new_for_commandline_arg (output_file); stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error)); if (stream) { vte_terminal_write_contents_sync (terminal, stream, VTE_WRITE_DEFAULT, NULL, &error); g_object_unref (stream); } if (error) { g_printerr ("%s\n", error->message); g_error_free (error); } g_object_unref (file); } gtk_widget_destroy (window); gtk_main_quit (); } static void delete_event(GtkWidget *window, GdkEvent *event, gpointer terminal) { destroy_and_quit(VTE_TERMINAL (terminal), window); } static void child_exited(GtkWidget *terminal, int status, gpointer window) { _vte_debug_print(VTE_DEBUG_MISC, "Child exited with status %x\n", status); destroy_and_quit(VTE_TERMINAL (terminal), GTK_WIDGET (window)); } static int button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer data) { VteTerminal *terminal; char *match; int tag; GtkBorder padding; int char_width, char_height; switch (event->button) { case 3: terminal = VTE_TERMINAL(widget); gtk_style_context_get_padding(gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget), &padding); char_width = vte_terminal_get_char_width (terminal); char_height = vte_terminal_get_char_height (terminal); match = vte_terminal_match_check(terminal, (event->x - padding.left) / char_width, (event->y - padding.top) / char_height, &tag); if (match != NULL) { g_print("Matched `%s' (%d).\n", match, tag); g_free(match); if (GPOINTER_TO_INT(data) != 0) { vte_terminal_match_remove(terminal, tag); } } break; case 1: case 2: default: break; } return FALSE; } static void iconify_window(GtkWidget *widget, gpointer data) { gtk_window_iconify(data); } static void deiconify_window(GtkWidget *widget, gpointer data) { gtk_window_deiconify(data); } static void raise_window(GtkWidget *widget, gpointer data) { GdkWindow *window; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(GTK_WIDGET(data)); if (window) { gdk_window_raise(window); } } } static void lower_window(GtkWidget *widget, gpointer data) { GdkWindow *window; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(GTK_WIDGET(data)); if (window) { gdk_window_lower(window); } } } static void maximize_window(GtkWidget *widget, gpointer data) { GdkWindow *window; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(GTK_WIDGET(data)); if (window) { gdk_window_maximize(window); } } } static void restore_window(GtkWidget *widget, gpointer data) { GdkWindow *window; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(GTK_WIDGET(data)); if (window) { gdk_window_unmaximize(window); } } } static void refresh_window(GtkWidget *widget, gpointer data) { GdkWindow *window; GtkAllocation allocation; GdkRectangle rect; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(widget); if (window) { gtk_widget_get_allocation(widget, &allocation); rect.x = rect.y = 0; rect.width = allocation.width; rect.height = allocation.height; gdk_window_invalidate_rect(window, &rect, TRUE); } } } static void resize_window(GtkWidget *widget, guint width, guint height, gpointer data) { VteTerminal *terminal; if ((GTK_IS_WINDOW(data)) && (width >= 2) && (height >= 2)) { gint owidth, oheight, char_width, char_height, column_count, row_count; GtkBorder padding; terminal = VTE_TERMINAL(widget); gtk_window_get_size(GTK_WINDOW(data), &owidth, &oheight); /* Take into account border overhead. */ char_width = vte_terminal_get_char_width (terminal); char_height = vte_terminal_get_char_height (terminal); column_count = vte_terminal_get_column_count (terminal); row_count = vte_terminal_get_row_count (terminal); gtk_style_context_get_padding(gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget), &padding); owidth -= char_width * column_count + padding.left + padding.right; oheight -= char_height * row_count + padding.top + padding.bottom; gtk_window_resize(GTK_WINDOW(data), width + owidth, height + oheight); } } static void move_window(GtkWidget *widget, guint x, guint y, gpointer data) { GdkWindow *window; if (GTK_IS_WIDGET(data)) { window = gtk_widget_get_window(GTK_WIDGET(data)); if (window) { gdk_window_move(window, x, y); } } } static void adjust_font_size(GtkWidget *widget, gpointer data, gdouble factor) { VteTerminal *terminal; gdouble scale; glong char_width, char_height; gint columns, rows, owidth, oheight; /* Read the screen dimensions in cells. */ terminal = VTE_TERMINAL(widget); columns = vte_terminal_get_column_count(terminal); rows = vte_terminal_get_row_count(terminal); /* Take into account padding and border overhead. */ gtk_window_get_size(GTK_WINDOW(data), &owidth, &oheight); char_width = vte_terminal_get_char_width (terminal); char_height = vte_terminal_get_char_height (terminal); owidth -= char_width * columns; oheight -= char_height * rows; scale = vte_terminal_get_font_scale(terminal); vte_terminal_set_font_scale(terminal, scale * factor); /* This above call will have changed the char size! */ char_width = vte_terminal_get_char_width (terminal); char_height = vte_terminal_get_char_height (terminal); gtk_window_resize(GTK_WINDOW(data), columns * char_width + owidth, rows * char_height + oheight); } static void increase_font_size(GtkWidget *widget, gpointer data) { adjust_font_size(widget, data, 1.2); } static void decrease_font_size(GtkWidget *widget, gpointer data) { adjust_font_size(widget, data, 1. / 1.2); } static gboolean read_and_feed(GIOChannel *source, GIOCondition condition, gpointer data) { char buf[2048]; gsize size; GIOStatus status; g_assert(VTE_IS_TERMINAL(data)); status = g_io_channel_read_chars(source, buf, sizeof(buf), &size, NULL); if ((status == G_IO_STATUS_NORMAL) && (size > 0)) { vte_terminal_feed(VTE_TERMINAL(data), buf, size); return TRUE; } return FALSE; } static void disconnect_watch(gpointer data) { g_source_remove(GPOINTER_TO_INT(data)); } static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer owner) { /* No-op. */ return; } static void take_xconsole_ownership(GtkWidget *widget, gpointer data) { char *name, hostname[255]; GdkAtom atom; GtkClipboard *clipboard; GtkTargetList *target_list; GtkTargetEntry *targets; int n_targets; target_list = gtk_target_list_new(NULL, 0); gtk_target_list_add_text_targets(target_list, 0); targets = gtk_target_table_new_from_list (target_list, &n_targets); gtk_target_list_unref(target_list); memset(hostname, '\0', sizeof(hostname)); gethostname(hostname, sizeof(hostname) - 1); name = g_strdup_printf("MIT_CONSOLE_%s", hostname); atom = gdk_atom_intern(name, FALSE); clipboard = gtk_clipboard_get_for_display(gtk_widget_get_display(widget), atom); g_free(name); gtk_clipboard_set_with_owner(clipboard, targets, n_targets, clipboard_get, (GtkClipboardClearFunc)gtk_main_quit, G_OBJECT(widget)); } static void add_weak_pointer(GObject *object, GtkWidget **target) { g_object_add_weak_pointer(object, (gpointer*)target); } static void terminal_notify_cb(GObject *object, GParamSpec *pspec, gpointer user_data) { GValue value = { 0, }; char *value_string; if (!pspec || pspec->owner_type != VTE_TYPE_TERMINAL) return; g_value_init(&value, pspec->value_type); g_object_get_property(object, pspec->name, &value); value_string = g_strdup_value_contents(&value); g_print("NOTIFY property \"%s\" value '%s'\n", pspec->name, value_string); g_free(value_string); g_value_unset(&value); } /* Derived terminal class */ typedef struct _VteappTerminal VteappTerminal; typedef struct _VteappTerminalClass VteappTerminalClass; struct _VteappTerminalClass { VteTerminalClass parent_class; }; struct _VteappTerminal { VteTerminal parent_instance; }; static GType vteapp_terminal_get_type(void); G_DEFINE_TYPE(VteappTerminal, vteapp_terminal, VTE_TYPE_TERMINAL) static void vteapp_terminal_class_init(VteappTerminalClass *klass) { } static void vteapp_terminal_init(VteappTerminal *terminal) { } static GtkWidget * vteapp_terminal_new(void) { return g_object_new(vteapp_terminal_get_type(), NULL); } /* Command line options */ static int parse_enum(GType type, const char *string) { GEnumClass *enum_klass; const GEnumValue *enum_value; int value = 0; enum_klass = (GEnumClass*)g_type_class_ref(type); enum_value = g_enum_get_value_by_nick(enum_klass, string); if (enum_value) value = enum_value->value; else g_warning("Unknown enum '%s'\n", string); g_type_class_unref(enum_klass); return value; } static guint parse_flags(GType type, const char *string) { GFlagsClass *flags_klass; guint value = 0; char **flags; guint i; flags = g_strsplit_set(string, ",|", -1); if (flags == NULL) return 0; flags_klass = (GFlagsClass*)g_type_class_ref(type); for (i = 0; flags[i] != NULL; ++i) { const GFlagsValue *flags_value; flags_value = g_flags_get_value_by_nick(flags_klass, flags[i]); if (flags_value) value |= flags_value->value; else g_warning("Unknown flag '%s'\n", flags[i]); } g_type_class_unref(flags_klass); return value; } static gboolean parse_color (const gchar *value, GdkRGBA *color) { if (!gdk_rgba_parse(color, value)) { g_printerr("Failed to parse value \"%s\" as color", value); return FALSE; } return TRUE; } static void add_dingus (VteTerminal *terminal, char **dingus, gboolean use_gregex) { const GdkCursorType cursors[] = { GDK_GUMBY, GDK_HAND1 }; int id, i; for (i = 0; dingus[i]; ++i) { GRegex *gregex = NULL; GError *error = NULL; #ifdef WITH_PCRE2 VteRegex *regex = NULL; if (!use_gregex) regex = vte_regex_new(dingus[i], -1, PCRE2_UTF | PCRE2_NO_UTF_CHECK, &error); else #endif gregex = g_regex_new(dingus[i], G_REGEX_OPTIMIZE, 0, &error); if (error) { g_warning("Failed to compile regex '%s': %s\n", dingus[i], error->message); g_error_free(error); continue; } #ifdef WITH_PCRE2 if (!use_gregex) id = vte_terminal_match_add_regex(terminal, regex, 0); else #endif id = vte_terminal_match_add_gregex(terminal, gregex, 0); #ifdef WITH_PCRE2 if (regex) vte_regex_unref(regex); #endif if (gregex) g_regex_unref (gregex); vte_terminal_match_set_cursor_type(terminal, id, cursors[i % G_N_ELEMENTS(cursors)]); } } int main(int argc, char **argv) { GdkScreen *screen; GdkVisual *visual; GtkWidget *window, *widget,*hbox = NULL, *scrollbar, *scrolled_window = NULL; VteTerminal *terminal; char *env_add[] = { #ifdef VTE_DEBUG (char *) "FOO=BAR", (char *) "BOO=BIZ", #endif NULL}; char *transparent = NULL; char *encoding = NULL; char *cjk_ambiguous_width = NULL; gboolean audible = FALSE, debug = FALSE, no_builtin_dingus = FALSE, dbuffer = TRUE, console = FALSE, keep = FALSE, icon_title = FALSE, shell = TRUE, reverse = FALSE, use_geometry_hints = TRUE, use_scrolled_window = FALSE, use_gregex = FALSE, show_object_notifications = FALSE, rewrap = TRUE; char *geometry = NULL; gint lines = -1; const char *message = "Launching interactive shell...\r\n"; const char *font = NULL; const char *command = NULL; const char *working_directory = NULL; const char *output_file = NULL; char *pty_flags_string = NULL; char *cursor_blink_mode_string = NULL; char *cursor_shape_string = NULL; char *scrollbar_policy_string = NULL; char *border_width_string = NULL; char *cursor_color_string = NULL; char *highlight_foreground_color_string = NULL; char *highlight_background_color_string = NULL; char **dingus = NULL; GdkRGBA fore, back; const GOptionEntry options[]={ { "console", 'C', 0, G_OPTION_ARG_NONE, &console, "Watch /dev/console", NULL }, { "no-builtin-dingus", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &no_builtin_dingus, "Highlight URLs inside the terminal", NULL }, { "dingu", 'D', 0, G_OPTION_ARG_STRING_ARRAY, &dingus, "Add regex highlight", NULL }, #ifdef WITH_PCRE2 { "gregex", 0, 0, G_OPTION_ARG_NONE, &use_gregex, "Use GRegex instead of PCRE2", NULL }, #endif { "no-rewrap", 'R', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &rewrap, "Disable rewrapping on resize", NULL }, { "shell", 'S', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &shell, "Disable spawning a shell inside the terminal", NULL }, { "transparent", 'T', 0, G_OPTION_ARG_STRING, &transparent, "Enable the use of a transparent background", "ALPHA" }, { "double-buffer", '2', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &dbuffer, "Disable double-buffering", NULL }, { "audible-bell", 'a', 0, G_OPTION_ARG_NONE, &audible, "Use audible terminal bell", NULL }, { "command", 'c', 0, G_OPTION_ARG_STRING, &command, "Execute a command in the terminal", NULL }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Enable various debugging checks", NULL }, { "font", 'f', 0, G_OPTION_ARG_STRING, &font, "Specify a font to use", NULL }, { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Set the size (in characters) and position", "GEOMETRY" }, { "highlight-foreground-color", 0, 0, G_OPTION_ARG_STRING, &highlight_foreground_color_string, "Enable distinct highlight foreground color for selection", NULL }, { "highlight-background-color", 0, 0, G_OPTION_ARG_STRING, &highlight_background_color_string, "Enable distinct highlight background color for selection", NULL }, { "icon-title", 'i', 0, G_OPTION_ARG_NONE, &icon_title, "Enable the setting of the icon title", NULL }, { "keep", 'k', 0, G_OPTION_ARG_NONE, &keep, "Live on after the window closes", NULL }, { "scrollback-lines", 'n', 0, G_OPTION_ARG_INT, &lines, "Specify the number of scrollback-lines", NULL }, { "cursor-blink", 0, 0, G_OPTION_ARG_STRING, &cursor_blink_mode_string, "Cursor blink mode (system|on|off)", "MODE" }, { "cursor-color", 0, 0, G_OPTION_ARG_STRING, &cursor_color_string, "Enable a colored cursor", NULL }, { "cursor-shape", 0, 0, G_OPTION_ARG_STRING, &cursor_shape_string, "Set cursor shape (block|underline|ibeam)", NULL }, { "encoding", 0, 0, G_OPTION_ARG_STRING, &encoding, "Specify the terminal encoding to use", NULL }, { "cjk-width", 0, 0, G_OPTION_ARG_STRING, &cjk_ambiguous_width, "Specify the cjk ambiguous width to use for UTF-8 encoding", "NARROW|WIDE" }, { "working-directory", 'w', 0, G_OPTION_ARG_FILENAME, &working_directory, "Specify the initial working directory of the terminal", NULL }, { "reverse", 0, 0, G_OPTION_ARG_NONE, &reverse, "Reverse foreground/background colors", NULL }, { "no-geometry-hints", 'G', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &use_geometry_hints, "Allow the terminal to be resized to any dimension, not constrained to fit to an integer multiple of characters", NULL }, { "scrolled-window", 'W', 0, G_OPTION_ARG_NONE, &use_scrolled_window, "Use a GtkScrolledWindow as terminal container", NULL }, { "scrollbar-policy", 'P', 0, G_OPTION_ARG_STRING, &scrollbar_policy_string, "Set the policy for the vertical scroolbar in the scrolled window (always|auto|never; default:always)", NULL }, { "object-notifications", 'N', 0, G_OPTION_ARG_NONE, &show_object_notifications, "Print VteTerminal object notifications", NULL }, { "output-file", 0, 0, G_OPTION_ARG_STRING, &output_file, "Save terminal contents to file at exit", NULL }, { "pty-flags", 0, 0, G_OPTION_ARG_STRING, &pty_flags_string, "PTY flags set from default|no-utmp|no-wtmp|no-lastlog|no-helper|no-fallback", NULL }, { "border-width", 0, 0, G_OPTION_ARG_STRING, &border_width_string, "Border with", "WIDTH" }, { NULL } }; GOptionContext *context; GError *error = NULL; VteCursorBlinkMode cursor_blink_mode = VTE_CURSOR_BLINK_SYSTEM; VteCursorShape cursor_shape = VTE_CURSOR_SHAPE_BLOCK; GtkPolicyType scrollbar_policy = GTK_POLICY_ALWAYS; VtePtyFlags pty_flags = VTE_PTY_DEFAULT; _vte_debug_init(); if (g_getenv("VTE_CJK_WIDTH")) { g_printerr("VTE_CJK_WIDTH is not supported anymore, use --cjk-width instead\n"); } /* Not interested in silly debug spew, bug #749195 */ if (g_getenv ("G_ENABLE_DIAGNOSTIC") == NULL) g_setenv ("G_ENABLE_DIAGNOSTIC", "0", TRUE); context = g_option_context_new (" - test VTE terminal emulation"); g_option_context_add_main_entries (context, options, NULL); g_option_context_add_group (context, gtk_get_option_group (TRUE)); g_option_context_parse (context, &argc, &argv, &error); g_option_context_free (context); if (error != NULL) { g_printerr ("Failed to parse command line arguments: %s\n", error->message); g_error_free (error); return 1; } if (cursor_blink_mode_string) { cursor_blink_mode = parse_enum(VTE_TYPE_CURSOR_BLINK_MODE, cursor_blink_mode_string); g_free(cursor_blink_mode_string); } if (cursor_shape_string) { cursor_shape = parse_enum(VTE_TYPE_CURSOR_SHAPE, cursor_shape_string); g_free(cursor_shape_string); } if (scrollbar_policy_string) { scrollbar_policy = parse_enum(GTK_TYPE_POLICY_TYPE, scrollbar_policy_string); g_free(scrollbar_policy_string); } if (pty_flags_string) { pty_flags |= parse_flags(VTE_TYPE_PTY_FLAGS, pty_flags_string); g_free(pty_flags_string); } if (!reverse) { back.red = back.green = back.blue = 1.0; back.alpha = 1.0; fore.red = fore.green = fore.blue = 0.0; fore.alpha = 1.0; } else { back.red = back.green = back.blue = 0.0; back.alpha = 1.0; fore.red = fore.green = fore.blue = 1.0; fore.alpha = 1.0; } gdk_window_set_debug_updates(debug); /* Create a window to hold the scrolling shell, and hook its * delete event to the quit function.. */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_container_set_resize_mode(GTK_CONTAINER(window), GTK_RESIZE_IMMEDIATE); if (border_width_string) { guint w; w = g_ascii_strtoull (border_width_string, NULL, 10); gtk_container_set_border_width(GTK_CONTAINER(window), w); g_free (border_width_string); } /* Set ARGB visual */ screen = gtk_widget_get_screen (window); visual = gdk_screen_get_rgba_visual(screen); if (visual) gtk_widget_set_visual(GTK_WIDGET(window), visual); if (use_scrolled_window) { scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_NEVER, scrollbar_policy); gtk_container_add(GTK_CONTAINER(window), scrolled_window); } else { /* Create a box to hold everything. */ hbox = gtk_hbox_new(0, FALSE); gtk_container_add(GTK_CONTAINER(window), hbox); } /* Create the terminal widget and add it to the scrolling shell. */ widget = vteapp_terminal_new(); terminal = VTE_TERMINAL (widget); if (!dbuffer) { gtk_widget_set_double_buffered(widget, dbuffer); } if (show_object_notifications) g_signal_connect(terminal, "notify", G_CALLBACK(terminal_notify_cb), NULL); if (use_scrolled_window) { gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(terminal)); } else { gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); } /* Connect to the "char_size_changed" signal to set geometry hints * whenever the font used by the terminal is changed. */ if (use_geometry_hints) { char_size_changed(widget, 0, 0, window); g_signal_connect(widget, "char-size-changed", G_CALLBACK(char_size_changed), window); g_signal_connect(widget, "realize", G_CALLBACK(char_size_realized), window); } /* Connect to the "window_title_changed" signal to set the main * window's title. */ g_signal_connect(widget, "window-title-changed", G_CALLBACK(window_title_changed), window); if (icon_title) { g_signal_connect(widget, "icon-title-changed", G_CALLBACK(icon_title_changed), window); } /* Connect to the "button-press" event. */ g_signal_connect(widget, "button-press-event", G_CALLBACK(button_pressed), widget); /* Connect to application request signals. */ g_signal_connect(widget, "iconify-window", G_CALLBACK(iconify_window), window); g_signal_connect(widget, "deiconify-window", G_CALLBACK(deiconify_window), window); g_signal_connect(widget, "raise-window", G_CALLBACK(raise_window), window); g_signal_connect(widget, "lower-window", G_CALLBACK(lower_window), window); g_signal_connect(widget, "maximize-window", G_CALLBACK(maximize_window), window); g_signal_connect(widget, "restore-window", G_CALLBACK(restore_window), window); g_signal_connect(widget, "refresh-window", G_CALLBACK(refresh_window), window); g_signal_connect(widget, "resize-window", G_CALLBACK(resize_window), window); g_signal_connect(widget, "move-window", G_CALLBACK(move_window), window); /* Connect to font tweakage. */ g_signal_connect(widget, "increase-font-size", G_CALLBACK(increase_font_size), window); g_signal_connect(widget, "decrease-font-size", G_CALLBACK(decrease_font_size), window); if (!use_scrolled_window) { /* Create the scrollbar for the widget. */ scrollbar = gtk_vscrollbar_new(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(terminal))); gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0); } /* Set some defaults. */ vte_terminal_set_audible_bell(terminal, audible); vte_terminal_set_cursor_blink_mode(terminal, cursor_blink_mode); vte_terminal_set_scroll_on_output(terminal, FALSE); vte_terminal_set_scroll_on_keystroke(terminal, TRUE); vte_terminal_set_scrollback_lines(terminal, lines); vte_terminal_set_mouse_autohide(terminal, TRUE); if (transparent != NULL) { back.alpha = g_ascii_strtod (transparent, NULL); g_free (transparent); } vte_terminal_set_colors(terminal, &fore, &back, NULL, 0); if (cursor_color_string) { GdkRGBA rgba; if (parse_color (cursor_color_string, &rgba)) vte_terminal_set_color_cursor(terminal, &rgba); g_free(cursor_color_string); } if (highlight_foreground_color_string) { GdkRGBA rgba; if (parse_color (highlight_foreground_color_string, &rgba)) vte_terminal_set_color_highlight_foreground(terminal, &rgba); g_free(highlight_foreground_color_string); } if (highlight_background_color_string) { GdkRGBA rgba; if (parse_color (highlight_background_color_string, &rgba)) vte_terminal_set_color_highlight(terminal, &rgba); g_free(highlight_background_color_string); } if (encoding != NULL) { if (!vte_terminal_set_encoding(terminal, encoding, &error)) { g_printerr("Failed to set encoding: %s\n", error->message); } g_free(encoding); } if (cjk_ambiguous_width != NULL) { int width = 1; if (g_ascii_strcasecmp(cjk_ambiguous_width, "narrow") == 0) width = 1; else if (g_ascii_strcasecmp(cjk_ambiguous_width, "wide") == 0) width = 2; else g_printerr("Unrecognised value \"%s\" for --cjk-width\n", cjk_ambiguous_width); g_free(cjk_ambiguous_width); vte_terminal_set_cjk_ambiguous_width(terminal, width); } vte_terminal_set_cursor_shape(terminal, cursor_shape); vte_terminal_set_rewrap_on_resize(terminal, rewrap); /* Set the default font. */ if (font) { PangoFontDescription *desc; desc = pango_font_description_from_string(font); vte_terminal_set_font(terminal, desc); pango_font_description_free(desc); } /* Match "abcdefg". */ if (!no_builtin_dingus) { add_dingus (terminal, (char **) builtin_dingus, use_gregex); } if (dingus) { add_dingus (terminal, dingus, use_gregex); g_strfreev (dingus); } if (console) { /* Open a "console" connection. */ int consolefd = -1, yes = 1, watch; GIOChannel *channel; consolefd = open("/dev/console", O_RDONLY | O_NOCTTY); if (consolefd != -1) { /* Assume failure. */ console = FALSE; #ifdef TIOCCONS if (ioctl(consolefd, TIOCCONS, &yes) != -1) { /* Set up a listener. */ channel = g_io_channel_unix_new(consolefd); watch = g_io_add_watch(channel, G_IO_IN, read_and_feed, widget); g_signal_connect_swapped(widget, "eof", G_CALLBACK(disconnect_watch), GINT_TO_POINTER(watch)); g_signal_connect_swapped(widget, "child-exited", G_CALLBACK(disconnect_watch), GINT_TO_POINTER(watch)); g_signal_connect(widget, "realize", G_CALLBACK(take_xconsole_ownership), NULL); #ifdef VTE_DEBUG vte_terminal_feed(terminal, "Console log for ...\r\n", -1); #endif /* Record success. */ console = TRUE; } #endif } else { /* Bail back to normal mode. */ g_warning(_("Could not open console.\n")); close(consolefd); console = FALSE; } } if (!console) { if (shell) { GError *err = NULL; char **command_argv = NULL; int command_argc; GPid pid = -1; char *free_me = NULL; _VTE_DEBUG_IF(VTE_DEBUG_MISC) vte_terminal_feed(terminal, message, -1); if (command == NULL || *command == '\0') command = free_me = vte_get_user_shell (); if (command == NULL || *command == '\0') command = g_getenv ("SHELL"); if (command == NULL || *command == '\0') command = "/bin/sh"; if (!g_shell_parse_argv(command, &command_argc, &command_argv, &err) || !vte_terminal_spawn_sync(terminal, pty_flags, NULL, command_argv, env_add, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL /* cancellable */, &err)) { g_warning("Failed to fork: %s\n", err->message); g_error_free(err); } else { g_print("Fork succeeded, PID %d\n", pid); } g_free (free_me); g_strfreev(command_argv); } else { #ifdef HAVE_FORK GError *err = NULL; VtePty *pty; pid_t pid; int i; pty = vte_pty_new_sync(VTE_PTY_DEFAULT, NULL, &err); if (pty == NULL) { g_printerr ("Failed to create PTY: %s\n", err->message); g_error_free(err); return 1; } pid = fork(); switch (pid) { case -1: /* abnormal */ g_warning("Error forking: %s", g_strerror(errno)); g_object_unref(pty); break; case 0: /* child */ vte_pty_child_setup(pty); for (i = 0; ; i++) { switch (i % 3) { case 0: case 1: g_print("%d\n", i); break; case 2: g_printerr("%d\n", i); break; } sleep(1); } _exit(0); break; default: vte_terminal_set_pty(terminal, pty); g_object_unref(pty); vte_terminal_watch_child(terminal, pid); g_print("Child PID is %d (mine is %d).\n", (int) pid, (int) getpid()); /* normal */ break; } #endif /* HAVE_FORK */ } } g_object_set_data (G_OBJECT (widget), "output_file", (gpointer) output_file); /* Go for it! */ g_signal_connect(widget, "child-exited", G_CALLBACK(child_exited), window); g_signal_connect(window, "delete-event", G_CALLBACK(delete_event), widget); add_weak_pointer(G_OBJECT(widget), &widget); add_weak_pointer(G_OBJECT(window), &window); gtk_widget_realize(widget); if (geometry) { if (!gtk_window_parse_geometry (GTK_WINDOW(window), geometry)) { g_warning (_("Could not parse the geometry spec passed to --geometry")); } } else { /* As of GTK+ 2.91.0, the default size of a window comes from its minimum * size not its natural size, so we need to set the right default size * explicitly */ gtk_window_set_default_geometry (GTK_WINDOW (window), vte_terminal_get_column_count (terminal), vte_terminal_get_row_count (terminal)); } gtk_widget_show_all(window); gtk_main(); g_assert(widget == NULL); g_assert(window == NULL); if (keep) { while (TRUE) { sleep(60); } } return 0; }