#include #include #include #include #include #include #include #include #include #include #include #include static FILE* tty; static int tty_fd; static struct termios tty_tio; static int char_width = 8; static int char_height = 15; static int window_columns = 25; static int window_rows = 80; static int window_width = 8 * 80; static int window_height = 15 * 25; static bool init_terminal(int fd); static void close_terminal(); static bool init_terminal(int fd) { const char *name_of_tty = ttyname(fd); if (!name_of_tty) { return FALSE; } tty = fopen(name_of_tty, "r+"); if (tty == NULL) { g_printerr("Cannot open terminal %s", name_of_tty); return FALSE; } tty_fd = fileno(tty); tcgetattr(tty_fd, &tty_tio); struct termios tio = tty_tio; tio.c_lflag &= ~(ICANON | ECHO); tio.c_cc[VMIN] = 4; tio.c_cc[VTIME] = 1; tcsetattr(tty_fd, TCSADRAIN, &tio); // Try to get window width and height in pixels using a xterm command fprintf(tty, "\033[14;t"); fflush(tty); if (fscanf(tty, "\033[4;%d;%dt", &window_height, &window_width) != 2) { close_terminal(); return FALSE; } // Also use a xterm command to get the window size in chars, // (could also use termios...) fprintf(tty, "\033[18;t"); fflush(tty); if (fscanf(tty, "\033[8;%d;%dt", &window_rows, &window_columns) != 2) { close_terminal(); return FALSE; } // Now calculate char height & width based on the above data char_width = window_width / window_columns; char_height = window_height / window_rows; return TRUE; } static void close_terminal() { tcsetattr(tty_fd, TCSADRAIN, &tty_tio); fclose(tty); } static gboolean view_pixbuf(GdkPixbuf *pixbuf) { const gint max_width = window_width; const gint max_height = window_height - char_height; gint width = gdk_pixbuf_get_width(pixbuf); gint height = gdk_pixbuf_get_height(pixbuf); GdkPixbuf *scaled; if (width > max_width || height > max_height) { if (width > height) { height = floor((height / (double)width) * max_width); width = max_width; } else { width = floor((width / (double)height) * max_height); height = max_height; } scaled = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); } else { scaled = GDK_PIXBUF(g_object_ref(pixbuf)); } GdkPixdata pixdata; gpointer pixdata_p = gdk_pixdata_from_pixbuf(&pixdata, scaled, TRUE); if (pixdata_p == NULL) { g_object_unref(scaled); return FALSE; } guint size; guint8 *data = gdk_pixdata_serialize(&pixdata, &size); gchar *str = g_base64_encode(data, size); fprintf(tty, "\033]v;%s\007", str); int rows = ceil(height / (double)char_height); while (rows > 0) { fprintf(tty, "\n"); rows--; } fflush(tty); g_free(str); g_free(data); g_free(pixdata.pixel_data); g_object_unref(scaled); return TRUE; } int main(int argc, char * argv[]) { GError *error = NULL; if (!isatty(STDOUT_FILENO)) { g_printerr("Refusing to display image on not-terminal\n"); return EXIT_FAILURE; } if (!init_terminal((STDOUT_FILENO))) { g_printerr("Refusing to display image on non-graphical terminal\n"); return EXIT_FAILURE; } if (argc > 1) { int i; for (i = 1; i < argc; i++) { GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[i], &error); if (pixbuf == NULL) { g_printerr("Failed to open image '%s': %s\n", argv[i], error->message); close_terminal(); return EXIT_FAILURE; } if (!view_pixbuf(pixbuf)) { g_printerr("Failed to view image '%s'\n", argv[i]); close_terminal(); return EXIT_FAILURE; } g_object_unref(pixbuf); } } else { GInputStream *input = g_unix_input_stream_new(STDIN_FILENO, FALSE); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream(input, NULL, &error); if (pixbuf == NULL) { g_printerr("Failed to open stdin image: %s\n", error->message); close_terminal(); return EXIT_FAILURE; } if (!view_pixbuf(pixbuf)) { g_printerr("Failed to view stdin image\n"); close_terminal(); return EXIT_FAILURE; } g_object_unref(pixbuf); } close_terminal(); return EXIT_SUCCESS; }