/* * T.c * * T is a lean Terminal emulator. * * This file is part of T. * * T is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * T 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * T. If not, see . */ #include #include #include #include #include #include "config.h" static int window_count; /* Event callbacks */ static gboolean window_delete_event_callback(GtkWidget *widget, GdkEvent *event, gpointer data); static void window_destroy_callback(GtkWidget *widget, gpointer data); static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data); static void terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data); /* Helper setup functions */ static void setup_pty(VteTerminal *terminal, GPid *child_pid); static void set_app_preferences(VteTerminal *terminal); static void setup_terminal(GtkWindow *window, VteTerminal *terminal); static void setup_window(GtkWindow *window); static void spawn_window(void); static gboolean window_delete_event_callback(GtkWidget *widget, GdkEvent *event, gpointer data) { /* TODO: always false? */ return FALSE; } static void window_destroy_callback(GtkWidget *widget, gpointer data) { window_count--; if (window_count == 0) gtk_main_quit(); } static gboolean terminal_key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) { GdkEventKey *key_event = (GdkEventKey *) event; /* VteTerminal *terminal = (VteTerminal *) widget; */ if ((key_event->state & GDK_SHIFT_MASK) == 0 || (key_event->state & GDK_CONTROL_MASK) == 0) return FALSE; switch (key_event->keyval) { case GDK_KEY_C: case GDK_KEY_c: /* * This was: * vte_terminal_copy_clipboard(terminal); * break; * But now we just turn this event into a Control+Insert and delegate it */ key_event->keyval = GDK_KEY_Insert; key_event->state &= ~GDK_SHIFT_MASK; return FALSE; case GDK_KEY_V: case GDK_KEY_v: /* * This was: * vte_terminal_paste_clipboard(terminal); * break; * But now we just turn this event into a Shift+Insert and delegate it */ key_event->keyval = GDK_KEY_Insert; key_event->state &= ~GDK_CONTROL_MASK; return FALSE; case GDK_KEY_N: case GDK_KEY_n: spawn_window(); break; } return TRUE; } static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data) { GtkWindow *window; window = (GtkWindow *) data; gtk_widget_destroy(GTK_WIDGET(window)); } static void terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data) { GtkWindow *window; window = (GtkWindow *) data; gtk_window_set_title(window, vte_terminal_get_window_title(terminal)); } static void setup_pty(VteTerminal *terminal, GPid *child_pid) { VtePty* pty; char *argv[] = { NULL, NULL }; /* TODO: error handling */ argv[0] = vte_get_user_shell(); pty = vte_terminal_pty_new(terminal, VTE_PTY_DEFAULT, NULL); vte_terminal_set_pty_object(terminal, pty); vte_terminal_fork_command_full(terminal, VTE_PTY_DEFAULT, ".", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, child_pid, NULL); } static void set_app_preferences(VteTerminal *terminal) { /* * Options set here can (and should) be configured through config.h */ /* * These are static because this function is called every time a new window * is created and we don't want to parse the colors all over again */ static gboolean colors_parsed = FALSE; static GdkColor palette[CONFIG_PALETTE_SIZE], bg_color, fg_color; if (!colors_parsed) { int i; gdk_color_parse(CONFIG_FOREGROUND_COLOR, &fg_color); gdk_color_parse(CONFIG_BACKGROUND_COLOR, &bg_color); for (i = 0; i < CONFIG_PALETTE_SIZE; ++i) gdk_color_parse(CONFIG_COLOR_PALETTE[i], &palette[i]); colors_parsed = TRUE; } /* Set preferences */ vte_terminal_set_audible_bell(terminal, CONFIG_AUDIBLE_BELL); vte_terminal_set_colors(terminal, &fg_color, &bg_color, palette, CONFIG_PALETTE_SIZE); vte_terminal_set_font_from_string(terminal, CONFIG_FONT_NAME); vte_terminal_set_mouse_autohide(terminal, CONFIG_MOUSE_AUTOHIDE); vte_terminal_set_scrollback_lines(terminal, CONFIG_SCROLLBACK_LINES); vte_terminal_set_visible_bell(terminal, CONFIG_VISIBLE_BELL); } static void setup_terminal(GtkWindow *window, VteTerminal *terminal) { GPid child_pid; setup_pty(terminal, &child_pid); vte_terminal_watch_child(terminal, child_pid); g_signal_connect(terminal, "child-exited", G_CALLBACK(terminal_child_exited_callback), window); g_signal_connect(terminal, "window-title-changed", G_CALLBACK(terminal_window_title_changed_callback), window); g_signal_connect(terminal, "key-press-event", G_CALLBACK(terminal_key_press_callback), window); set_app_preferences(terminal); } static void setup_window(GtkWindow *window) { GdkGeometry hints; VteTerminal *terminal; terminal = (VteTerminal *) vte_terminal_new(); setup_terminal(window, terminal); hints.base_width = vte_terminal_get_char_width(terminal); hints.base_height = vte_terminal_get_char_height(terminal); hints.min_width = hints.base_width * CONFIG_MIN_WIDTH; hints.min_height = hints.base_height * CONFIG_MIN_HEIGHT; hints.width_inc = hints.base_width; hints.height_inc = hints.base_height; gtk_window_set_geometry_hints(window, GTK_WIDGET(terminal), &hints, GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE); g_signal_connect(window, "delete-event", G_CALLBACK(window_delete_event_callback), NULL); g_signal_connect(window, "destroy", G_CALLBACK(window_destroy_callback), NULL); gtk_window_set_icon_name(window, "utilities-terminal"); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(terminal)); gtk_widget_show(GTK_WIDGET(terminal)); gtk_widget_show(GTK_WIDGET(window)); } static void spawn_window(void) { GtkWindow *window; window = (GtkWindow *) gtk_window_new(GTK_WINDOW_TOPLEVEL); setup_window(window); window_count++; } int main(int argc, char *argv[]) { gtk_init(&argc, &argv); /* Initial state: single window */ window_count = 0; spawn_window(); /* * TODO * Find out a nice and clean way of opening more windows when the user * does not have focus on a T window. Maybe fire spawn_window() when we * detect a running T process? */ gtk_main(); return 0; }