From a60d6a20bfb350dc9bdad58850988082c7ea2cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrico=20Tr=C3=B6ger?= Date: Sun, 27 Nov 2005 20:54:28 +0000 Subject: [PATCH] added support for a virtual terminal emulator widget git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@22 ea778897-0a13-0410-b9d1-a72fbfd435f5 --- src/Makefile.am | 2 + src/callbacks.c | 18 +++- src/geany.h | 4 +- src/main.c | 16 ++++ src/msgwindow.h | 6 +- src/vte.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++ src/vte.h | 84 +++++++++++++++++++ 7 files changed, 336 insertions(+), 6 deletions(-) create mode 100644 src/vte.c create mode 100644 src/vte.h diff --git a/src/Makefile.am b/src/Makefile.am index c68d8289d..205fbc956 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ ## Process this file with automake to produce Makefile.in +# $Id$ INCLUDES = \ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ @@ -29,6 +30,7 @@ geany_SOURCES = \ sciwrappers.c sciwrappers.h \ document.c document.h \ utils.c utils.h \ + vte.c vte.h \ support.c support.h \ interface.c interface.h \ callbacks.c callbacks.h diff --git a/src/callbacks.c b/src/callbacks.c index 126175884..0a0fabaf9 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * $Id$ */ @@ -47,6 +48,15 @@ # include "win32.h" #endif +// include vte.h on non-Win32 systems, else define fake vte_init +#if defined(GEANY_WIN32) || ! defined(HAVE_VTE) +# define vte_close() ; +#else +# include "vte.h" +#endif + + + // represents the word under the mouse pointer when right button(no. 3) is pressed static gchar current_word[128]; @@ -126,6 +136,7 @@ gint destroyapp(GtkWidget *widget, gpointer gdata) if (GTK_IS_WIDGET(app->open_fontsel)) gtk_widget_destroy(app->open_fontsel); if (GTK_IS_WIDGET(app->open_colorsel)) gtk_widget_destroy(app->open_colorsel); gtk_widget_destroy(app->window); + if (app->have_vte) vte_close(); g_free(app); @@ -184,7 +195,7 @@ on_exit_clicked (GtkWidget *widget, gpointer gdata) // signal handler (for SIGINT and SIGTERM) -void signal_cb(gint sig) +RETSIGTYPE signal_cb(gint sig) { on_exit_clicked(NULL, NULL); } @@ -827,6 +838,7 @@ on_file_save_save_button_clicked (GtkButton *button, if (doc_list[idx].file_name) g_free(doc_list[idx].file_name); doc_list[idx].file_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(app->save_filesel)); + utils_replace_filename(idx); document_save_file(idx); utils_build_show_hide(idx); @@ -945,8 +957,8 @@ on_window_configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data) { - GtkWidget *vpaned1 = lookup_widget(app->window, "vpaned1"); - gtk_paned_set_position(GTK_PANED(vpaned1), event->height - GEANY_MSGWIN_HEIGHT); + //GtkWidget *vpaned1 = lookup_widget(app->window, "vpaned1"); + //gtk_paned_set_position(GTK_PANED(vpaned1), event->height - GEANY_MSGWIN_HEIGHT); app->geometry[0] = event->x; app->geometry[1] = event->y; app->geometry[2] = event->width; diff --git a/src/geany.h b/src/geany.h index 369191246..d2df5417c 100644 --- a/src/geany.h +++ b/src/geany.h @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * $Id$ */ #ifdef HAVE_CONFIG_H @@ -61,7 +62,7 @@ #define GEANY_MAX_TAGS_COUNT 1000 #define GEANY_WORDCHARS "_#&abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" #define GEANY_MAX_AUTOCOMPLETE_WORDS 30 -#define GEANY_MSGWIN_HEIGHT 210 +#define GEANY_MSGWIN_HEIGHT 240 #define GEANY_STRING_UNTITLED _("untitled") @@ -104,6 +105,7 @@ typedef struct MyApp gint long_line_color; gint geometry[4]; gboolean debug_mode; + gboolean have_vte; gboolean ignore_global_tags; gboolean toolbar_visible; gboolean treeview_nb_visible; diff --git a/src/main.c b/src/main.c index baf2aa8cf..5dd3d3c0d 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include #include + #include "geany.h" #include "interface.h" @@ -38,10 +39,17 @@ #include "templates.h" #include "encodings.h" #include "treeviews.h" +// include vte.h on non-Win32 systems, else define fake vte_init +#if defined(GEANY_WIN32) || ! defined(HAVE_VTE) +# define vte_init() ; +#else +# include "vte.h" +#endif static gboolean debug_mode = FALSE; static gboolean ignore_global_tags = FALSE; +static gboolean no_vte = FALSE; static gboolean show_version = FALSE; static gchar *alternate_config = NULL; static GOptionEntry entries[] = @@ -49,6 +57,7 @@ static GOptionEntry entries[] = { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug_mode, "runs in debug mode (means being verbose)", NULL }, { "no-ctags", 'n', 0, G_OPTION_ARG_NONE, &ignore_global_tags, "don't load auto completion data (see documentation)", NULL }, { "config", 'c', 0, G_OPTION_ARG_FILENAME, &alternate_config, "use an alternate configuration directory", NULL }, + { "no-terminal", 't', 0, G_OPTION_ARG_NONE, &no_vte, "don't load terminal support", NULL }, { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "show version and exit", NULL }, { NULL } }; @@ -255,6 +264,11 @@ gint main(gint argc, gchar **argv) app->prefs_dialog = NULL; app->find_dialog = NULL; app->main_window_realized= FALSE; +#ifdef HAVE_VTE + app->have_vte = ! no_vte; +#else + app->have_vte = FALSE; +#endif app->ignore_global_tags = ignore_global_tags; app->tm_workspace = tm_get_workspace(); app->recent_queue = g_queue_new(); @@ -337,6 +351,7 @@ gint main(gint argc, gchar **argv) msgwindow.popup_status_menu = msgwin_create_message_popup_menu(3); msgwindow.popup_msg_menu = msgwin_create_message_popup_menu(4); msgwindow.popup_compiler_menu = msgwin_create_message_popup_menu(5); + vte_init(); dialogs_create_build_menu(); dialogs_create_recent_menu(); utils_create_insert_menu_items(); @@ -390,6 +405,7 @@ gint main(gint argc, gchar **argv) app->main_window_realized = TRUE; + //g_timeout_add(0, (GSourceFunc)destroyapp, NULL); // useful for start time tests gtk_main(); return 0; } diff --git a/src/msgwindow.h b/src/msgwindow.h index 6db3cabb2..1c6768296 100644 --- a/src/msgwindow.h +++ b/src/msgwindow.h @@ -36,11 +36,13 @@ enum MSG_STATUS = 0, MSG_COMPILER, MSG_MESSAGE, - MSG_SCRATCH + MSG_SCRATCH, + MSG_VTE }; -typedef struct msgwin { +typedef struct msgwin +{ GtkListStore *store_status; GtkListStore *store_msg; GtkListStore *store_compiler; diff --git a/src/vte.c b/src/vte.c new file mode 100644 index 000000000..f4bbef75b --- /dev/null +++ b/src/vte.c @@ -0,0 +1,212 @@ +/* + * vte.c - this file is part of Geany, a fast and lightweight IDE + * + * Copyright 2005 Enrico Troeger + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +#include "geany.h" + +#ifdef HAVE_VTE + +#include +#include +#include +#include +#include + +#include "vte.h" +#include "msgwindow.h" +#include "support.h" + + +extern gchar **environ; +static pid_t pid; +static GModule *module = NULL; +static struct vte_funcs *vf; + +#define VTE_TERMINAL(obj) (GTK_CHECK_CAST((obj), VTE_TYPE_TERMINAL, VteTerminal)) +#define VTE_TYPE_TERMINAL (vf->vte_terminal_get_type()) + + +/* taken from anjuta, thanks */ +gchar **vte_get_child_environment(GtkWidget *term) +{ + /* code from gnome-terminal, sort of. */ + gchar **p; + gint i; + gchar **retval; +#define EXTRA_ENV_VARS 5 + + /* count env vars that are set */ + for (p = environ; *p; p++); + + i = p - environ; + retval = g_new(gchar *, i + 1 + EXTRA_ENV_VARS); + + for (i = 0, p = environ; *p; p++) + { + /* Strip all these out, we'll replace some of them */ + if ((strncmp(*p, "COLUMNS=", 8) == 0) || + (strncmp(*p, "LINES=", 6) == 0) || + (strncmp(*p, "TERM=", 5) == 0)) + { + /* nothing: do not copy */ + } + else + { + retval[i] = g_strdup(*p); + ++i; + } + } + + retval[i] = g_strdup ("TERM=xterm"); + ++i; + + retval[i] = NULL; + + return retval; +} + + +void vte_init(void) +{ + + GtkWidget *vte, *scrollbar, *hbox, *frame; + module = g_module_open("libvte.so.4", G_MODULE_BIND_LAZY); + + if (module == NULL || app->have_vte == FALSE) + { + app->have_vte = FALSE; + geany_debug("Could not load libvte.so.4, terminal support disabled"); + return; + } + else + { + app->have_vte = TRUE; + vf = g_new(struct vte_funcs, 1); + vte_register_symbols(module); + } + + vte = vf->vte_terminal_new(); + scrollbar = gtk_vscrollbar_new(GTK_ADJUSTMENT(VTE_TERMINAL(vte)->adjustment)); + GTK_WIDGET_UNSET_FLAGS(scrollbar, GTK_CAN_FOCUS); + + frame = gtk_frame_new(NULL); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), hbox); + gtk_box_pack_start(GTK_BOX(hbox), vte, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0); + + vf->vte_terminal_set_mouse_autohide(VTE_TERMINAL(vte), TRUE); + vf->vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), 20); + vf->vte_terminal_set_scroll_on_keystroke(VTE_TERMINAL(vte), TRUE); + vf->vte_terminal_set_scroll_on_output(VTE_TERMINAL(vte), TRUE); + vf->vte_terminal_set_word_chars(VTE_TERMINAL(vte), GEANY_WORDCHARS); + vf->vte_terminal_set_size(VTE_TERMINAL(vte), 50, 1); + vf->vte_terminal_set_encoding(VTE_TERMINAL(vte), "UTF-8"); + vf->vte_terminal_set_font_from_string(VTE_TERMINAL(vte), "Monospace 10"); + + g_signal_connect(G_OBJECT(vte), "child-exited", G_CALLBACK(vte_start), NULL); + g_signal_connect(G_OBJECT(vte), "event", G_CALLBACK(vte_keypress), NULL); + + vte_start(vte, NULL); + + gtk_widget_show_all(frame); + gtk_notebook_insert_page(GTK_NOTEBOOK(msgwindow.notebook), frame, gtk_label_new(_("Terminal")), MSG_VTE); +} + + +void vte_close(void) +{ + g_module_close(module); + g_free(vf); +} + + +gboolean vte_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + /* Fixme: GDK_KEY_PRESS doesn't seem to be called for our keys */ + if (event->type != GDK_KEY_RELEASE) + return FALSE; + + /* ctrl-c or ctrl-d */ + if (event->keyval == GDK_c || + event->keyval == GDK_d || + event->keyval == GDK_C || + event->keyval == GDK_D) + { + /* Ctrl pressed */ + if (event->state & GDK_CONTROL_MASK) + { + kill(pid, SIGINT); + pid = 0; + vte_start(widget, NULL); + return TRUE; + } + } + return FALSE; +} + + +void vte_start(GtkWidget *widget, gpointer data) +{ + VteTerminal *vte = VTE_TERMINAL(widget); + struct passwd *pw; + const gchar *shell; + const gchar *dir; + gchar **env; + + pw = getpwuid(getuid()); + if (pw) + { + shell = pw->pw_shell; + dir = pw->pw_dir; + } + else + { + shell = "/bin/sh"; + dir = "/"; + } + + env = vte_get_child_environment(app->window); + pid = vf->vte_terminal_fork_command(VTE_TERMINAL(vte), shell, NULL, env, dir, TRUE, TRUE, TRUE); + g_strfreev(env); +} + + +void vte_register_symbols(GModule *mod) +{ + g_module_symbol(mod, "vte_terminal_new", (void*)&vf->vte_terminal_new); + g_module_symbol(mod, "vte_terminal_set_size", (void*)&vf->vte_terminal_set_size); + g_module_symbol(mod, "vte_terminal_fork_command", (void*)&vf->vte_terminal_fork_command); + g_module_symbol(mod, "vte_terminal_set_word_chars", (void*)&vf->vte_terminal_set_word_chars); + g_module_symbol(mod, "vte_terminal_set_mouse_autohide", (void*)&vf->vte_terminal_set_mouse_autohide); + g_module_symbol(mod, "vte_terminal_set_encoding", (void*)&vf->vte_terminal_set_encoding); + g_module_symbol(mod, "vte_terminal_reset", (void*)&vf->vte_terminal_reset); + g_module_symbol(mod, "vte_terminal_set_cursor_blinks", (void*)&vf->vte_terminal_set_cursor_blinks); + g_module_symbol(mod, "vte_terminal_get_type", (void*)&vf->vte_terminal_get_type); + g_module_symbol(mod, "vte_terminal_set_scroll_on_output", (void*)&vf->vte_terminal_set_scroll_on_output); + g_module_symbol(mod, "vte_terminal_set_scroll_on_keystroke", (void*)&vf->vte_terminal_set_scroll_on_keystroke); + g_module_symbol(mod, "vte_terminal_set_font_from_string", (void*)&vf->vte_terminal_set_font_from_string); + g_module_symbol(mod, "vte_terminal_set_scrollback_lines", (void*)&vf->vte_terminal_set_scrollback_lines); + g_module_symbol(mod, "vte_terminal_get_has_selection", (void*)&vf->vte_terminal_get_has_selection); +} + +#endif diff --git a/src/vte.h b/src/vte.h new file mode 100644 index 000000000..e4c59ec23 --- /dev/null +++ b/src/vte.h @@ -0,0 +1,84 @@ +/* + * vte.h - this file is part of Geany, a fast and lightweight IDE + * + * Copyright 2005 Enrico Troeger + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + + +#ifndef GEANY_VTE_H +#define GEANY_VTE_H 1 + +#include + + +void vte_init(void); + +gboolean vte_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data); + +void vte_start(GtkWidget *widget, gpointer data); + +void vte_register_symbols(GModule *module); + +void vte_close(void); + + + +/* taken from original vte.h to make my life easier ;-) */ + +typedef struct _VteTerminalPrivate VteTerminalPrivate; + +typedef struct _VteTerminal VteTerminal; +struct _VteTerminal +{ + GtkWidget widget; + GtkAdjustment *adjustment; + glong char_width, char_height; + glong char_ascent, char_descent; + glong row_count, column_count; + char *window_title; + char *icon_title; + VteTerminalPrivate *pvt; +}; + + + +/* store function pointers in a struct to avoid a strange segfault if they are stored directly + * if accessed directly, gdb says the segfault arrives at old_tab_width(prefs.c), don't ask me */ +struct vte_funcs +{ + GtkWidget* (*vte_terminal_new) (void); + pid_t (*vte_terminal_fork_command) (VteTerminal *terminal, const char *command, char **argv, + char **envv, const char *directory, gboolean lastlog, + gboolean utmp, gboolean wtmp); + void (*vte_terminal_set_size) (VteTerminal *terminal, glong columns, glong rows); + void (*vte_terminal_set_word_chars) (VteTerminal *terminal, const char *spec); + void (*vte_terminal_set_mouse_autohide) (VteTerminal *terminal, gboolean setting); + void (*vte_terminal_reset) (VteTerminal *terminal, gboolean full, gboolean clear_history); + void (*vte_terminal_set_encoding) (VteTerminal *terminal, const char *codeset); + void (*vte_terminal_set_cursor_blinks) (VteTerminal *terminal, gboolean blink); + GtkType (*vte_terminal_get_type) (void); + void (*vte_terminal_set_scroll_on_output) (VteTerminal *terminal, gboolean scroll); + void (*vte_terminal_set_scroll_on_keystroke) (VteTerminal *terminal, gboolean scroll); + void (*vte_terminal_set_font_from_string) (VteTerminal *terminal, const char *name); + void (*vte_terminal_set_scrollback_lines) (VteTerminal *terminal, glong lines); + gboolean (*vte_terminal_get_has_selection) (VteTerminal *terminal); +}; + + +#endif