/******************************************************************************/ /* ____ ___ _ _ ____ ___ _ _____ */ /* / ___| / _ \ | \ | | / ___| / _ \ | | | ____| */ /* | | _ | | | | | \| | \___ \ | | | | | | | _| */ /* | |_| | | |_| | | |\ | ___) | | |_| | | |___ | |___ */ /* \____| \___/ |_| \_| |____/ \___/ |_____| |_____| */ /* */ /* By C2H5OH and Tito Houzy */ /* */ /* Tabbed terminal emulator in one source file. All the dirty job is */ /* performed smoothly by libvte so here we don't have much more to do, just */ /* organize. If you like it, use it; if you don't use it to make your own one */ /* at your like. That's what open source should be: clear code easy to */ /* modify because, in the end, all of this is for fun, isn't it? */ /******************************************************************************/ #include #include #include #include #include #include #include extern char **environ; /*************************** TUNABLE CONSTANTS ***************************/ #define HIDE_TABS_ON_SINGLE // Boolean. Undefine to disable #define BORDER_WIDTH 0 #define TAB_ORIENTATION GTK_POS_BOTTOM #define TAB_TITLE "Terminal" #define TERMINAL_FONT_NAME "monospace 10" /******************************** GLOBALS *********************************/ GtkWidget *MainWindow; GtkWidget *Notebook; int TabCount = 0; int FullScreen = 0; /************************** CALLBACK PROTOTYPES ****************************/ void tab_focused (GtkNotebook *, GtkNotebookPage *, gint, gpointer); void new_tab (void); void destroy_tab (VteTerminal *, gpointer); void next_tab (void); void previous_tab (void); void toggle_fullscreen (void); /***************************** MAIN FUNCTION ******************************/ int main (int argc, char **argv) { GtkAccelGroup *accel; gtk_init (&argc, &argv); /* Create the main window */ MainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width (GTK_CONTAINER (MainWindow), 0); gtk_window_set_title (GTK_WINDOW (MainWindow), "Gonsole"); /* Create the notebook as a child of the window */ Notebook = gtk_notebook_new (); gtk_container_set_border_width (GTK_CONTAINER (Notebook), BORDER_WIDTH); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (Notebook), TAB_ORIENTATION); gtk_notebook_set_scrollable (GTK_NOTEBOOK (Notebook), TRUE); #ifdef HIDE_TABS_ON_SINGLE gtk_notebook_set_show_tabs (GTK_NOTEBOOK (Notebook), FALSE); #endif gtk_container_add (GTK_CONTAINER (MainWindow), Notebook); /* Closures cannot be reused for connecting accelerators, we need to * create a new one for each. */ #define BIND_KEYCOMBO(accel, key, modifiers, callback) \ gtk_accel_group_connect((accel), (key), (modifiers), GTK_ACCEL_LOCKED,\ g_cclosure_new(G_CALLBACK(callback), NULL, NULL)) /* Now let's get into the keyboard shorcuts, or accelerators. */ accel = gtk_accel_group_new (); /* Ctrl + T --> new_tab() */ BIND_KEYCOMBO (accel, GDK_T, GDK_CONTROL_MASK, new_tab); /* Ctrl + Shift + N --> new_tab() */ BIND_KEYCOMBO (accel, GDK_N, GDK_CONTROL_MASK|GDK_SHIFT_MASK, new_tab); /* Shift + Right_Arrow --> next_tab() */ BIND_KEYCOMBO (accel, GDK_Right, GDK_SHIFT_MASK, next_tab); /* Shift + Left_Arrow --> prev_tab() */ BIND_KEYCOMBO (accel, GDK_Left, GDK_SHIFT_MASK, previous_tab); /* Ctrl + Shift + F --> toggle_fullscreen() */ BIND_KEYCOMBO (accel, GDK_F, GDK_CONTROL_MASK|GDK_SHIFT_MASK, toggle_fullscreen); /* F11 --> toggle_fullscreen () */ BIND_KEYCOMBO (accel, GDK_F11, 0, toggle_fullscreen); /* Attach acceleratros to the window and connect the rest of the signals. */ gtk_window_add_accel_group (GTK_WINDOW (MainWindow), accel); g_signal_connect (G_OBJECT (Notebook), "switch-page", G_CALLBACK (tab_focused), NULL); g_signal_connect (G_OBJECT (MainWindow), "destroy", G_CALLBACK (gtk_main_quit), NULL); /* Create a new tab initially */ new_tab (); /* Show everything and start event loop. We start with one tab. */ gtk_widget_show (Notebook); gtk_widget_show (MainWindow); gtk_main (); return 0; } /*************************** CALLBACK FUNCTIONS ***************************/ /* * tab_focused - When a tab is selected ("switch-page" signal) the focus has to * be passed to the child. Otherwise would be very uncomfortable * to click the VTE widget every time we change the tab to be able * to write on it. */ void tab_focused (GtkNotebook *nb, GtkNotebookPage *x, gint num, gpointer y) { GtkWidget *child; child = gtk_notebook_get_nth_page (nb, num); gtk_container_set_focus_child (GTK_CONTAINER (nb), child); } /* * new_tab - User requested tab creation. */ void new_tab (void) { int p; GtkWidget *terminal; /* Create the termianl widget. */ terminal = vte_terminal_new (); vte_terminal_set_font_from_string (VTE_TERMINAL (terminal), TERMINAL_FONT_NAME); /* Fork a shell and don't care too much about the pid. If you want to tune * the VTE objects, this is the place to do it. */ vte_terminal_fork_command (VTE_TERMINAL (terminal), getenv ("SHELL"), NULL, environ, getenv ("HOME"), FALSE, FALSE, FALSE); /* And look for SIGCHLD the Gtk, and VTE, way */ g_signal_connect (G_OBJECT (terminal), "eof", G_CALLBACK (destroy_tab), NULL); g_signal_connect (G_OBJECT (terminal), "child-exited", G_CALLBACK (destroy_tab), NULL); /* Make it visible and append it on a new selected tab. */ gtk_widget_show (terminal); p = gtk_notebook_append_page (GTK_NOTEBOOK (Notebook), terminal, gtk_label_new (TAB_TITLE)); gtk_notebook_set_current_page (GTK_NOTEBOOK (Notebook), p); gtk_widget_queue_draw (Notebook); TabCount++; #ifdef HIDE_TABS_ON_SINGLE if (TabCount == 2) gtk_notebook_set_show_tabs (GTK_NOTEBOOK (Notebook), TRUE); #endif } /* * destroy_tab - A shell terminated. Remove the corresponding tab. */ void destroy_tab (VteTerminal *terminal, gpointer x) { int page; TabCount--; if (TabCount == 0) { /* Last tab terminated. Goodbye. */ gtk_main_quit (); return; } /* Let's look for it */ page = gtk_notebook_page_num (GTK_NOTEBOOK (Notebook), GTK_WIDGET (terminal)); gtk_notebook_remove_page (GTK_NOTEBOOK (Notebook), page); #ifdef HIDE_TABS_ON_SINGLE if (TabCount == 1) gtk_notebook_set_show_tabs (GTK_NOTEBOOK (Notebook), FALSE); #endif } /* * next_tab - Show next tab. It doesn't cycle. */ void next_tab (void) { gtk_notebook_next_page (GTK_NOTEBOOK (Notebook)); } /* * previous_tab - Show previous tab. It doesn't cycle. */ void previous_tab (void) { gtk_notebook_prev_page (GTK_NOTEBOOK (Notebook)); } /* * toggle_fullscreen - Put the window fullscreen or restore it. */ void toggle_fullscreen (void) { if (FullScreen) { gtk_window_unfullscreen (GTK_WINDOW (MainWindow)); FullScreen = 0; } else { gtk_window_fullscreen (GTK_WINDOW (MainWindow)); FullScreen = 1; } }