diff options
author | Samuel Fadel <samuelfadel@gmail.com> | 2015-04-01 22:43:44 -0300 |
---|---|---|
committer | Samuel Fadel <samuelfadel@gmail.com> | 2015-04-01 22:43:44 -0300 |
commit | a605c88ba3a0180d5783722c2c877f2ab8b01659 (patch) | |
tree | 3ec8f1712b2549cf596ff4bf595a5b5bc767e29a | |
parent | 52913a6131fdc950065d33ec18f75a1bcd329896 (diff) |
Rewritten as a client-server application, similar to urxvt{d,c}.
-rw-r--r-- | Makefile | 45 | ||||
-rw-r--r-- | T.c (renamed from src/T.c) | 144 | ||||
-rw-r--r-- | T.h | 43 | ||||
-rw-r--r-- | Tc.c | 100 | ||||
-rw-r--r-- | Td.c | 142 | ||||
-rw-r--r-- | config.h (renamed from src/config.h) | 41 |
6 files changed, 399 insertions, 116 deletions
@@ -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 @@ -1,6 +1,5 @@ /* * T.c - * * T is a lean Terminal emulator. * * This file is part of T. @@ -23,42 +22,28 @@ #include <gtk/gtk.h> #include <unistd.h> #include <vte/vte.h> +#include <stdlib.h> -#include "config.h" +#include "T.h" -static int window_count; +static int window_count = 0; /* 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 change_font_size(VteTerminal *terminal, int delta); 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) { + /* Keeping track of the open windows */ window_count--; - - if (window_count == 0) - gtk_main_quit(); } static gboolean @@ -75,10 +60,10 @@ key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) switch (key_event->keyval) { case GDK_KEY_minus: - set_font_size(terminal, -1); + change_font_size(terminal, -1); return TRUE; case GDK_KEY_plus: - set_font_size(terminal, 1); + change_font_size(terminal, 1); return TRUE; } @@ -101,7 +86,7 @@ key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) break; case GDK_KEY_N: case GDK_KEY_n: - spawn_window(); + new_window(); return TRUE; } @@ -111,48 +96,31 @@ key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data) static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data) { - GtkWindow *window; - - window = GTK_WINDOW(data); - gtk_widget_destroy(GTK_WIDGET(window)); + /* 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; - - window = (GtkWindow *) data; + GtkWindow *window = GTK_WINDOW(data); gtk_window_set_title(window, vte_terminal_get_window_title(terminal)); } static void -set_font_size(VteTerminal *terminal, int diff) +change_font_size(VteTerminal *terminal, int delta) { PangoFontDescription *desc = (PangoFontDescription *) vte_terminal_get_font(terminal); - gint font_size = pango_font_description_get_size(desc) + diff * PANGO_SCALE; + 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 -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 + * Options set here can (and should) be configured in config.h */ /* @@ -161,6 +129,24 @@ set_preferences(VteTerminal *terminal) */ 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; @@ -186,10 +172,19 @@ set_preferences(VteTerminal *terminal) static void setup_terminal(GtkWindow *window, VteTerminal *terminal) { + char *argv[] = { vte_get_user_shell(), NULL }; GPid child_pid; - setup_pty(terminal, &child_pid); - vte_terminal_watch_child(terminal, 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); @@ -198,15 +193,20 @@ setup_terminal(GtkWindow *window, VteTerminal *terminal) set_preferences(terminal); } -static void -setup_window(GtkWindow *window) +int +num_open_windows() { - GdkGeometry hints; - VteTerminal *terminal; + return window_count; +} - terminal = (VteTerminal *) vte_terminal_new(); +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; @@ -214,44 +214,28 @@ setup_window(GtkWindow *window) 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(window_delete_event_callback), NULL); + g_signal_connect(window, "delete-event", G_CALLBACK(gtk_false), 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[]) +void +wrn(const char *message) { - 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(); + fprintf(stderr, "%s: %s\n", PROGRAM_NAME, message); +} - return 0; +void +err(const char *message, int ecode) +{ + wrn(message); + exit(ecode); } @@ -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 <http://www.gnu.org/licenses/>. + */ + +#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 */ @@ -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 <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} @@ -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 <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <vte/vte.h> + +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} @@ -1,6 +1,5 @@ /* * config.h - * * Configuration file. Modify these to customize T. * * This file is part of T. @@ -18,6 +17,12 @@ * T. If not, see <http://www.gnu.org/licenses/>. */ +/* 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 @@ -29,24 +34,22 @@ /* 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 */ -}; +#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] */ |