From a605c88ba3a0180d5783722c2c877f2ab8b01659 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Wed, 1 Apr 2015 22:43:44 -0300 Subject: Rewritten as a client-server application, similar to urxvt{d,c}. --- Makefile | 45 +++++++---- T.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ T.h | 43 ++++++++++ Tc.c | 100 +++++++++++++++++++++++ Td.c | 142 +++++++++++++++++++++++++++++++++ config.h | 71 +++++++++++++++++ src/T.c | 257 ----------------------------------------------------------- src/config.h | 68 ---------------- 8 files changed, 625 insertions(+), 342 deletions(-) create mode 100644 T.c create mode 100644 T.h create mode 100644 Tc.c create mode 100644 Td.c create mode 100644 config.h delete mode 100644 src/T.c delete mode 100644 src/config.h diff --git a/Makefile b/Makefile index 3b0eb6a..debd6de 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,38 @@ DESTDIR = / PREFIX = usr -INCS = `pkg-config --cflags gtk+-2.0 vte` -LIBS = `pkg-config --libs gtk+-2.0 vte` -CFLAGS = -ansi -pedantic -Wall -O2 ${INCS} +INCS = `pkg-config --cflags gtk+-2.0 gdk-2.0 vte` +LIBS = `pkg-config --libs gtk+-2.0 gdk-2.0 vte` +CFLAGS = -std=c99 -pedantic -Wall -O2 ${INCS} LDFLAGS = -s ${LIBS} -VER = 0.7 +VER = 0.9 CC = cc -SRC = src/T.c +SRC = T.c Tc.c Td.c OBJ = ${SRC:.c=.o} -all: T +all: Tc Td -.c.o: +%.o: %.c @echo CC $< @${CC} -c ${CFLAGS} -o $@ $< -${OBJ}: src/config.h +${OBJ}: config.h T.h -T: ${OBJ} +Tc: T.o Tc.o @echo CC -o $@ - @${CC} -o $@ ${OBJ} ${LDFLAGS} + @${CC} -o $@ $^ ${LDFLAGS} + +Td: T.o Td.o + @echo CC -o $@ + @${CC} -o $@ $^ ${LDFLAGS} clean: @echo cleaning - @rm -f T ${OBJ} T-${VER}.tar.bz2 + @rm -f Tc Td ${OBJ} T-${VER}.tar.bz2 dist: clean + #TODO: remove .desktop file? @echo creating distributable tarball @mkdir T-${VER} @cp -R src/ T.desktop Makefile T-${VER} @@ -35,9 +40,15 @@ dist: clean @rm -rf T-${VER} install: all - @echo installing executable file in ${DESTDIR}${PREFIX}/bin - @install -D -m755 T ${DESTDIR}${PREFIX}/bin/T - @echo installing desktop file in ${DESTDIR}${PREFIX}/share/applications - @install -D -m644 T.desktop ${DESTDIR}${PREFIX}/share/applications/T.desktop - -.PHONY: all clean dist install + @echo installing executable files in ${DESTDIR}${PREFIX}/bin + @install -D -m755 Tc ${DESTDIR}${PREFIX}/bin/Tc + @install -D -m755 Td ${DESTDIR}${PREFIX}/bin/Td + #@echo installing desktop file in ${DESTDIR}${PREFIX}/share/applications + #@install -D -m644 T.desktop ${DESTDIR}${PREFIX}/share/applications/T.desktop + +uninstall: + @echo removing executables files from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/Tc + @rm -f ${DESTDIR}${PREFIX}/bin/Td + +.PHONY: all clean dist install uninstall diff --git a/T.c b/T.c new file mode 100644 index 0000000..dc62c3d --- /dev/null +++ b/T.c @@ -0,0 +1,241 @@ +/* + * 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 + +#include "T.h" + +static int window_count = 0; + +/* Event callbacks */ +static void window_destroy_callback(GtkWidget *widget, gpointer data); +static gboolean key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data); +static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data); +static void terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data); + +/* Helper functions */ +static void change_font_size(VteTerminal *terminal, int delta); +static void set_preferences(VteTerminal *terminal); +static void setup_terminal(GtkWindow *window, VteTerminal *terminal); + +static void +window_destroy_callback(GtkWidget *widget, gpointer data) +{ + /* Keeping track of the open windows */ + window_count--; +} + +static gboolean +key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + GdkEventKey *key_event = (GdkEventKey *) event; + VteTerminal *terminal = VTE_TERMINAL(widget); + + gboolean has_shift = (key_event->state & GDK_SHIFT_MASK) != 0; + gboolean has_control = (key_event->state & GDK_CONTROL_MASK) != 0; + + if (!has_control) + return FALSE; + + switch (key_event->keyval) { + case GDK_KEY_minus: + change_font_size(terminal, -1); + return TRUE; + case GDK_KEY_plus: + change_font_size(terminal, 1); + return TRUE; + } + + if (!has_shift) + return FALSE; + + /* Handles ctrl + shift + {c,v,n} */ + switch (key_event->keyval) { + case GDK_KEY_C: + case GDK_KEY_c: + /* turn this event into a Control+Insert and delegate it */ + key_event->keyval = GDK_KEY_Insert; + key_event->state &= ~GDK_SHIFT_MASK; + break; + case GDK_KEY_V: + case GDK_KEY_v: + /* turn this event into a Shift+Insert and delegate it */ + key_event->keyval = GDK_KEY_Insert; + key_event->state &= ~GDK_CONTROL_MASK; + break; + case GDK_KEY_N: + case GDK_KEY_n: + new_window(); + return TRUE; + } + + return FALSE; +} + +static void +terminal_child_exited_callback(VteTerminal *terminal, gpointer data) +{ + /* The forked process exited; issue destruction of the terminal window */ + gtk_widget_destroy(GTK_WIDGET(GTK_WINDOW(data))); +} + +static void +terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data) +{ + GtkWindow *window = GTK_WINDOW(data); + gtk_window_set_title(window, vte_terminal_get_window_title(terminal)); +} + +static void +change_font_size(VteTerminal *terminal, int delta) +{ + PangoFontDescription *desc = (PangoFontDescription *) vte_terminal_get_font(terminal); + gint font_size = pango_font_description_get_size(desc) + delta * PANGO_SCALE; + pango_font_description_set_size(desc, font_size); + vte_terminal_set_font(terminal, desc); +} + +static void +set_preferences(VteTerminal *terminal) +{ + /* + * Options set here can (and should) be configured in 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; + static const char *CONFIG_COLOR_PALETTE[CONFIG_PALETTE_SIZE] = { + CONFIG_PALLETE_0, + CONFIG_PALLETE_1, + CONFIG_PALLETE_2, + CONFIG_PALLETE_3, + CONFIG_PALLETE_4, + CONFIG_PALLETE_5, + CONFIG_PALLETE_6, + CONFIG_PALLETE_7, + CONFIG_PALLETE_8, + CONFIG_PALLETE_9, + CONFIG_PALLETE_10, + CONFIG_PALLETE_11, + CONFIG_PALLETE_12, + CONFIG_PALLETE_13, + CONFIG_PALLETE_14, + CONFIG_PALLETE_15, + }; + + 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_cursor_blink_mode(terminal, CONFIG_CURSOR_BLINK_MODE); + 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) +{ + char *argv[] = { vte_get_user_shell(), NULL }; + GPid child_pid; + + vte_terminal_fork_command_full(terminal, + VTE_PTY_DEFAULT, + NULL, /* wd; NULL for cwd */ + argv, /* the program to fork into and its args */ + NULL, /* env vars */ + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, /* setup func */ + NULL, /* custom data to setup func */ + &child_pid, + NULL); /* TODO: error handling */ + + g_signal_connect(terminal, "child-exited", G_CALLBACK(terminal_child_exited_callback), window); + g_signal_connect(terminal, "key-press-event", G_CALLBACK(key_press_callback), window); + g_signal_connect(terminal, "window-title-changed", G_CALLBACK(terminal_window_title_changed_callback), window); + + set_preferences(terminal); +} + +int +num_open_windows() +{ + return window_count; +} + +void +new_window() +{ + GtkWindow *window = (GtkWindow *) gtk_window_new(GTK_WINDOW_TOPLEVEL); + VteTerminal *terminal = (VteTerminal *) vte_terminal_new(); + setup_terminal(window, terminal); + + GdkGeometry hints; + 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); + gtk_window_set_icon_name(window, "utilities-terminal"); + + g_signal_connect(window, "delete-event", G_CALLBACK(gtk_false), NULL); + g_signal_connect(window, "destroy", G_CALLBACK(window_destroy_callback), NULL); + + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(terminal)); + + gtk_widget_show(GTK_WIDGET(terminal)); + gtk_widget_show(GTK_WIDGET(window)); + + window_count++; +} + +void +wrn(const char *message) +{ + fprintf(stderr, "%s: %s\n", PROGRAM_NAME, message); +} + +void +err(const char *message, int ecode) +{ + wrn(message); + exit(ecode); +} diff --git a/T.h b/T.h new file mode 100644 index 0000000..a891982 --- /dev/null +++ b/T.h @@ -0,0 +1,43 @@ +/* + * T.h + * Common definitions and utilities. + * + * 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 . + */ + +#ifndef T_H +#define T_H + +#include "config.h" + +#define PROGRAM_NAME "T" + +#define SOCKPATH CONFIG_SOCKDIR "/" CONFIG_SOCKNAME +#define SOCKPATH_MAXLEN sizeof((SOCKPATH)) + +typedef enum { + MSG_NEWWIN, + MSG_EXIT, + MSG_OK, + MSG_ERROR +} TMessage; + +int num_open_windows(); +void new_window(); + +void wrn(const char *message); +void err(const char *message, int ecode); + +#endif /* T_H */ diff --git a/Tc.c b/Tc.c new file mode 100644 index 0000000..f54a2a2 --- /dev/null +++ b/Tc.c @@ -0,0 +1,100 @@ +/* + * Tc.c + * T client. Merely issues commands to the T daemon. + * + * 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 +#include + +#include "T.h" + +#define ARG_NEWWIN "-n" +#define ARG_EXIT "-x" + +static int open_conn(); +static TMessage parse_args(int argc, char *argv[]); +static void usage(); + +static int +open_conn() +{ + struct sockaddr_un sock_addr; + + int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) + err("Failed to create socket", EXIT_FAILURE); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sun_family = AF_UNIX; + strncpy(sock_addr.sun_path, SOCKPATH, sizeof(sock_addr.sun_path) - 1); + + if (connect(sock_fd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) + err("Failed to connect to daemon", EXIT_FAILURE); + + return sock_fd; +} + +static TMessage +parse_args(int argc, char *argv[]) +{ + if (argc == 1) /* No args means new window */ + return MSG_NEWWIN; + if (argc > 2) /* More than 1 arg means derp */ + return MSG_ERROR; + if (strcmp(argv[1], ARG_NEWWIN) == 0) + return MSG_NEWWIN; + if (strcmp(argv[1], ARG_EXIT) == 0) + return MSG_EXIT; + + return MSG_ERROR; +} + +static void +usage() +{ + puts("usage:"); + puts("\tTc [ARG]"); + puts("\nwhere ARG is one of:"); + puts("\t" ARG_NEWWIN "\tRequests a new window; default if ommited"); + puts("\t" ARG_EXIT "\tRequests daemon termination; only successful if there are no open windows"); +} + +int +main(int argc, char *argv[]) +{ + TMessage msg = parse_args(argc, argv); + if (msg == MSG_ERROR) { + usage(); + return EXIT_FAILURE; + } + + int sock_fd = open_conn(); + if (write(sock_fd, &msg, sizeof(msg)) < 0) + err("Failed to send data", EXIT_FAILURE); + if (recv(sock_fd, &msg, sizeof(msg), 0) < 0) + err("Failed to recv data", EXIT_FAILURE); + close(sock_fd); + + return msg == MSG_OK ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/Td.c b/Td.c new file mode 100644 index 0000000..22a847b --- /dev/null +++ b/Td.c @@ -0,0 +1,142 @@ +/* + * Td.c + * T daemon. Saves resources by uniting all T windows under the same process. + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "T.h" + +#define MSGBUF_MAXLEN 64 + +static int bind_socket(); +static TMessage handle_message(TMessage msg); +static gboolean socket_g_io_watch(GIOChannel *source, GIOCondition condition, gpointer data); + +static int +bind_socket() +{ + struct sockaddr_un sock_addr; + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sun_family = AF_UNIX; + strncpy(sock_addr.sun_path, SOCKPATH, sizeof(sock_addr.sun_path) - 1); + + /* Remove old socket */ + if (!access(sock_addr.sun_path, F_OK) && unlink(sock_addr.sun_path) < 0) + err("Failed to remove socket file at " SOCKPATH, EXIT_FAILURE); + if (!access(CONFIG_SOCKDIR, F_OK) && rmdir(CONFIG_SOCKDIR) < 0) + err("Failed to remove socket directory at " CONFIG_SOCKDIR, EXIT_FAILURE); + + /* Create a new directory */ + if (mkdir(CONFIG_SOCKDIR, S_IRWXU) < 0) + err("Failed to create socket directory at " CONFIG_SOCKDIR, EXIT_FAILURE); + + int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) + err("Failed to create socket", EXIT_FAILURE); + + if (bind(sock_fd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) + err("Failed to bind the socket to the file at " SOCKPATH, EXIT_FAILURE); + + return sock_fd; +} + +static TMessage +handle_message(TMessage msg) +{ + switch (msg) { + case MSG_NEWWIN: + new_window(); + return MSG_OK; + + case MSG_EXIT: + if (num_open_windows() == 0) { + gtk_main_quit(); + return MSG_OK; + } + return MSG_ERROR; + + default: + return MSG_ERROR; + } +} + +static gboolean +socket_g_io_watch(GIOChannel *source, GIOCondition condition, gpointer data) +{ + int sock_fd = g_io_channel_unix_get_fd(source); + int client_fd = accept(sock_fd, NULL, NULL); + if (client_fd < 0) { + wrn("accept() failed"); + return TRUE; + } + + TMessage msg; + int len = recv(client_fd, &msg, sizeof(msg), 0); + if (len < 0) { + wrn("recv() failed"); + goto socket_g_io_watch_cleanup; + } + + msg = handle_message(msg); + write(client_fd, &msg, sizeof(msg)); + +socket_g_io_watch_cleanup: + close(client_fd); + return TRUE; +} + +int +main(int argc, char *argv[]) +{ + int sock_fd; + + gtk_init(&argc, &argv); + + sock_fd = bind_socket(); + if (listen(sock_fd, SOMAXCONN) < 0) + err("Failed to listen to socket", EXIT_SUCCESS); + + /* poll the socket in the main loop */ + GIOChannel *sock_chan = g_io_channel_unix_new(sock_fd); + g_io_add_watch(sock_chan, G_IO_IN, socket_g_io_watch, NULL); + + gtk_main(); + + /* clean up */ + close(sock_fd); + unlink(SOCKPATH); + + return EXIT_SUCCESS; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..2afffe4 --- /dev/null +++ b/config.h @@ -0,0 +1,71 @@ +/* + * config.h + * Configuration file. Modify these to customize T. + * + * 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 . + */ + +/* The directory of the connection socket used by the T daemon. */ +#define CONFIG_SOCKDIR "/tmp/Td" + +/* The name of the connection socket used by the T daemon. */ +#define CONFIG_SOCKNAME "Td.sock" + +/* Minimum width/height, in characters */ +#define CONFIG_MIN_WIDTH 20 +#define CONFIG_MIN_HEIGHT 5 + +/* Font */ +#define CONFIG_FONT_NAME "Monospace 9" + +/* Color palette definition */ +/* PALETTE_SIZE must be 8, 16, 24 or between 25 and 255, inclusive */ +#define CONFIG_PALETTE_SIZE 16 + +#define CONFIG_PALLETE_0 "#000000" /* Black */ +#define CONFIG_PALLETE_1 "#95513a" /* Red */ +#define CONFIG_PALLETE_2 "#3a9551" /* Green */ +#define CONFIG_PALLETE_3 "#7e953a" /* Yellow */ +#define CONFIG_PALLETE_4 "#3d558d" /* Blue */ +#define CONFIG_PALLETE_5 "#953a7e" /* Magenta */ +#define CONFIG_PALLETE_6 "#3a7e95" /* Cyan */ +#define CONFIG_PALLETE_7 "#d9d9d9" /* White */ +#define CONFIG_PALLETE_8 "#262626" /* Black */ +#define CONFIG_PALLETE_9 "#c17860" /* Red */ +#define CONFIG_PALLETE_10 "#60c178" /* Green */ +#define CONFIG_PALLETE_11 "#a9c160" /* Yellow */ +#define CONFIG_PALLETE_12 "#5f7aba" /* Blue */ +#define CONFIG_PALLETE_13 "#c160a9" /* Magenta */ +#define CONFIG_PALLETE_14 "#60a9c1" /* Cyan */ +#define CONFIG_PALLETE_15 "#ffffff" /* White */ + +/* Foreground and background. */ +#define CONFIG_FOREGROUND_COLOR "#d9e6f2" /* COLOR_PALETTE[7] */ +#define CONFIG_BACKGROUND_COLOR "#0d1926" /* COLOR_PALETTE[0] */ + +/* Scrollback lines (0 means disabled; negative means "infinite") */ +#define CONFIG_SCROLLBACK_LINES 2000 + +/* Mouse auto-hide (TRUE or FALSE) */ +#define CONFIG_MOUSE_AUTOHIDE TRUE + +/* Audible bell (TRUE or FALSE) */ +#define CONFIG_AUDIBLE_BELL FALSE + +/* Visible bell (TRUE or FALSE) */ +#define CONFIG_VISIBLE_BELL FALSE + +/* Cursor blink mode. One of: VTE_CURSOR_BLINK_SYSTEM/ON/OFF */ +#define CONFIG_CURSOR_BLINK_MODE VTE_CURSOR_BLINK_OFF diff --git a/src/T.c b/src/T.c deleted file mode 100644 index c776983..0000000 --- a/src/T.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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 gboolean key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data); - -static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data); -static void terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data); - -/* Helper functions */ -static void set_font_size(VteTerminal *terminal, int diff); -static void setup_pty(VteTerminal *terminal, GPid *child_pid); -static void set_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 -key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) -{ - GdkEventKey *key_event = (GdkEventKey *) event; - VteTerminal *terminal = VTE_TERMINAL(widget); - - gboolean has_shift = (key_event->state & GDK_SHIFT_MASK) != 0; - gboolean has_control = (key_event->state & GDK_CONTROL_MASK) != 0; - - if (!has_control) - return FALSE; - - switch (key_event->keyval) { - case GDK_KEY_minus: - set_font_size(terminal, -1); - return TRUE; - case GDK_KEY_plus: - set_font_size(terminal, 1); - return TRUE; - } - - if (!has_shift) - return FALSE; - - /* Handles ctrl + shift + {c,v,n} */ - switch (key_event->keyval) { - case GDK_KEY_C: - case GDK_KEY_c: - /* turn this event into a Control+Insert and delegate it */ - key_event->keyval = GDK_KEY_Insert; - key_event->state &= ~GDK_SHIFT_MASK; - break; - case GDK_KEY_V: - case GDK_KEY_v: - /* turn this event into a Shift+Insert and delegate it */ - key_event->keyval = GDK_KEY_Insert; - key_event->state &= ~GDK_CONTROL_MASK; - break; - case GDK_KEY_N: - case GDK_KEY_n: - spawn_window(); - return TRUE; - } - - return FALSE; -} - -static void -terminal_child_exited_callback(VteTerminal *terminal, gpointer data) -{ - GtkWindow *window; - - window = GTK_WINDOW(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 -set_font_size(VteTerminal *terminal, int diff) -{ - PangoFontDescription *desc = (PangoFontDescription *) vte_terminal_get_font(terminal); - gint font_size = pango_font_description_get_size(desc) + diff * PANGO_SCALE; - pango_font_description_set_size(desc, font_size); - vte_terminal_set_font(terminal, desc); -} - -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_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_cursor_blink_mode(terminal, CONFIG_CURSOR_BLINK_MODE); - 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, "key-press-event", G_CALLBACK(key_press_callback), window); - g_signal_connect(terminal, "window-title-changed", G_CALLBACK(terminal_window_title_changed_callback), window); - - set_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. - */ - - gtk_main(); - - return 0; -} diff --git a/src/config.h b/src/config.h deleted file mode 100644 index ee466ee..0000000 --- a/src/config.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * config.h - * - * Configuration file. Modify these to customize T. - * - * 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 . - */ - -/* Minimum width/height, in characters */ -#define CONFIG_MIN_WIDTH 20 -#define CONFIG_MIN_HEIGHT 5 - -/* Font */ -#define CONFIG_FONT_NAME "Monospace 9" - -/* Color palette definition */ -/* PALETTE_SIZE must be 8, 16, 24 or between 25 and 255, inclusive */ -#define CONFIG_PALETTE_SIZE 16 - -static const char *CONFIG_COLOR_PALETTE[CONFIG_PALETTE_SIZE] = { - "#000000", /* 0 Black */ - "#95513a", /* 1 Red */ - "#3a9551", /* 2 Green */ - "#7e953a", /* 3 Yellow */ - "#3d558d", /* 4 Blue */ - "#953a7e", /* 5 Magenta */ - "#3a7e95", /* 6 Cyan */ - "#d9d9d9", /* 7 White */ - "#262626", /* 8 Black */ - "#c17860", /* 9 Red */ - "#60c178", /* 10 Green */ - "#a9c160", /* 11 Yellow */ - "#5f7aba", /* 12 Blue */ - "#c160a9", /* 13 Magenta */ - "#60a9c1", /* 14 Cyan */ - "#ffffff", /* 15 White */ -}; - -/* Foreground and background. */ -#define CONFIG_FOREGROUND_COLOR "#d9e6f2" /* COLOR_PALETTE[7] */ -#define CONFIG_BACKGROUND_COLOR "#0d1926" /* COLOR_PALETTE[0] */ - -/* Scrollback lines (0 means disabled; negative means "infinite") */ -#define CONFIG_SCROLLBACK_LINES 2000 - -/* Mouse auto-hide (TRUE or FALSE) */ -#define CONFIG_MOUSE_AUTOHIDE TRUE - -/* Audible bell (TRUE or FALSE) */ -#define CONFIG_AUDIBLE_BELL FALSE - -/* Visible bell (TRUE or FALSE) */ -#define CONFIG_VISIBLE_BELL FALSE - -/* Cursor blink mode. One of: VTE_CURSOR_BLINK_SYSTEM/ON/OFF */ -#define CONFIG_CURSOR_BLINK_MODE VTE_CURSOR_BLINK_OFF -- cgit v1.2.3