aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/file.c131
-rw-r--r--src/file.h15
-rw-r--r--src/image.c97
-rw-r--r--src/image.h25
-rw-r--r--src/input.c206
-rw-r--r--src/input.h13
-rw-r--r--src/main.c51
-rw-r--r--src/window.c105
-rw-r--r--src/window.h12
9 files changed, 655 insertions, 0 deletions
diff --git a/src/file.c b/src/file.c
new file mode 100644
index 0000000..dcbcbf0
--- /dev/null
+++ b/src/file.c
@@ -0,0 +1,131 @@
+#define _DEFAULT_SOURCE
+#include "file.h"
+#include "image.h"
+#include <dirent.h>
+#include <gtk-3.0/gtk/gtk.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+static char **extensions = NULL;
+
+char *get_filename_ext(const char *file_name)
+{
+ char *dot = strrchr(file_name, '.');
+ if (!dot || dot == file_name)
+ return "";
+ return dot + 1;
+}
+
+int is_file_format_supported(const char *file_name)
+{
+ char *file_ext = get_filename_ext(file_name);
+ char **it = extensions;
+ while (*it != NULL) {
+ if (!strcmp(*it++, file_ext)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void scan_supported_formats()
+{
+ GSList *list, *it;
+ GdkPixbufFormat *format;
+ gchar **exts, **ex_it;
+ int i = 0;
+
+ list = gdk_pixbuf_get_formats();
+ if (list != NULL) {
+ for (it = list; it->next != NULL; it = it->next) {
+ format = (GdkPixbufFormat *)it->data;
+ exts = gdk_pixbuf_format_get_extensions(format);
+ if (exts != NULL) {
+ ex_it = exts;
+ while (*ex_it != NULL) {
+ extensions = realloc(extensions, sizeof(char *) * (i + 1));
+ extensions[i++] = strdup(*ex_it);
+ g_free(*ex_it);
+ ex_it++;
+ }
+ g_free(exts);
+ }
+ }
+ g_slist_free(list);
+ extensions[i] = NULL;
+ }
+}
+
+int image_filter(const struct dirent *dir)
+{
+ if (dir->d_type == DT_REG && is_file_format_supported(dir->d_name)) {
+ return 1;
+ }
+ return 0;
+}
+
+int scan(const char *file_name)
+{
+ scan_supported_formats();
+ struct dirent **name_list;
+ char *dir_name = dirname(strdup(file_name));
+ file_list_count = scandir(dir_name, &name_list, image_filter, alphasort);
+ if (file_list_count < 0) {
+ return -1;
+ }
+
+ file_list = malloc(file_list_count * sizeof(char *));
+ char *file_basename = basename(strdup(file_name));
+ int i = 0;
+ while (i < file_list_count) {
+ if (!strcmp(file_basename, name_list[i]->d_name))
+ curr_file_index = i;
+ file_list[i] = g_build_filename(dir_name, name_list[i]->d_name, NULL);
+ free(name_list[i]);
+ i++;
+ }
+ free(name_list);
+
+ return 0;
+}
+
+char *get_next_file()
+{
+ if (curr_file_index >= file_list_count - 1)
+ return NULL;
+ return file_list[++curr_file_index];
+}
+
+char *get_prev_file()
+{
+ if (curr_file_index <= 0)
+ return NULL;
+ return file_list[--curr_file_index];
+}
+
+char *get_first_file()
+{
+ if (curr_file_index == 0)
+ return NULL;
+ curr_file_index = 0;
+ return file_list[curr_file_index];
+}
+
+char *get_last_file()
+{
+ if (curr_file_index == file_list_count - 1)
+ return NULL;
+ curr_file_index = file_list_count - 1;
+ return file_list[curr_file_index];
+}
+
+void clean()
+{
+ for (int i = 0; i < file_list_count; ++i) {
+ g_free(file_list[i]);
+ }
+ g_free(file_list);
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000..7e8c167
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,15 @@
+#ifndef __FILE_H
+#define __FILE_H
+
+char **file_list;
+char *curr_file_name;
+int curr_file_index, file_list_count;
+
+int scan(const char *file_name);
+char *get_next_file();
+char *get_prev_file();
+char *get_first_file();
+char *get_last_file();
+void clean();
+
+#endif
diff --git a/src/image.c b/src/image.c
new file mode 100644
index 0000000..d4765fd
--- /dev/null
+++ b/src/image.c
@@ -0,0 +1,97 @@
+#include "image.h"
+#include "window.h"
+#include <gtk-3.0/gtk/gtk.h>
+
+GtkWidget *new_image()
+{
+ image = gtk_image_new();
+ return image;
+}
+
+int load_image(char *file_name)
+{
+ GError *error = NULL;
+ if (pixbuf != NULL)
+ g_object_unref(pixbuf);
+ pixbuf = gdk_pixbuf_new_from_file(file_name, &error);
+ if (error)
+ return -1;
+ curr_pixbuf_width = pixbuf_width = gdk_pixbuf_get_width(GDK_PIXBUF(pixbuf));
+ curr_pixbuf_height = pixbuf_height =
+ gdk_pixbuf_get_height(GDK_PIXBUF(pixbuf));
+ aspect_ratio = (double)pixbuf_width / (double)pixbuf_height;
+ curr_zoom = 1.0;
+ fit_image();
+ set_window_title(g_path_get_basename(file_name));
+ return 1;
+}
+
+void update_pixbuf()
+{
+ if (curr_pixbuf != NULL)
+ g_object_unref(curr_pixbuf);
+ if (pixbuf == NULL)
+ return;
+ curr_pixbuf =
+ gdk_pixbuf_scale_simple(GDK_PIXBUF(pixbuf), curr_pixbuf_width,
+ curr_pixbuf_height, GDK_INTERP_BILINEAR);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), GDK_PIXBUF(curr_pixbuf));
+}
+
+void fit_image()
+{
+ gint win_width, win_height;
+ get_curr_win_size(&win_width, &win_height);
+ curr_scale_mod = fit;
+ if (pixbuf == NULL || win_width < 1 || win_height < 1)
+ return;
+ if (win_width < pixbuf_width && win_height > pixbuf_height) {
+ curr_pixbuf_width = win_width;
+ curr_pixbuf_height = (double)curr_pixbuf_width / aspect_ratio;
+ } else if (win_width > pixbuf_width && win_height < pixbuf_height) {
+ curr_pixbuf_height = win_height;
+ curr_pixbuf_width = (double)curr_pixbuf_height * aspect_ratio;
+ } else if (win_width < pixbuf_width && win_height < pixbuf_height) {
+ if (((double)win_width / (double)win_height) > aspect_ratio) {
+ curr_pixbuf_height = win_height;
+ curr_pixbuf_width = ((double)curr_pixbuf_height * aspect_ratio);
+ } else {
+ curr_pixbuf_width = win_width;
+ curr_pixbuf_height = (double)curr_pixbuf_width / aspect_ratio;
+ }
+ } else {
+ curr_pixbuf_width = pixbuf_width;
+ curr_pixbuf_height = pixbuf_height;
+ }
+
+ curr_zoom = (double)curr_pixbuf_width / (double)pixbuf_width;
+ if (curr_pixbuf_width < 1 || curr_pixbuf_height < 1)
+ return;
+
+ update_pixbuf();
+}
+
+void zoom(int type)
+{
+ curr_scale_mod = zoomed;
+ if (pixbuf == NULL)
+ return;
+ if (type == 0) {
+ if (curr_zoom == 1.0)
+ return;
+ curr_zoom = 1.0;
+ } else if (type < 0) {
+ if (curr_zoom < 0.2)
+ return;
+ curr_zoom -= 0.1;
+ } else if (type > 0) {
+ if (curr_zoom > 2)
+ return;
+ curr_zoom += 0.1;
+ }
+
+ curr_pixbuf_width = curr_zoom * (double)pixbuf_width;
+ curr_pixbuf_height = curr_zoom * (double)pixbuf_height;
+
+ update_pixbuf();
+}
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000..dfbd9ff
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,25 @@
+#ifndef __IMAGE_H
+#define __IMAGE_H
+
+#include <gtk-3.0/gtk/gtk.h>
+
+#define ZOOM_FACTOR 0.1
+
+enum scale_mode {
+ fit = 0,
+ zoomed = 1,
+} curr_scale_mod;
+
+GtkWidget *image;
+GdkPixbuf *pixbuf;
+GdkPixbuf *curr_pixbuf;
+int pixbuf_width, pixbuf_height, curr_pixbuf_width, curr_pixbuf_height;
+double aspect_ratio;
+double curr_zoom;
+
+GtkWidget *new_image();
+int load_image(char *file_name);
+void fit_image();
+void zoom(int type);
+
+#endif
diff --git a/src/input.c b/src/input.c
new file mode 100644
index 0000000..1d21ef2
--- /dev/null
+++ b/src/input.c
@@ -0,0 +1,206 @@
+#include "input.h"
+#include "file.h"
+#include "image.h"
+#include "window.h"
+#include <gtk-3.0/gtk/gtk.h>
+
+#define TIMEOUT 20
+#define SCROLLAMMOUNT 30
+
+int grabbed = 0;
+gdouble start_x, start_y;
+
+void next()
+{
+ char *name;
+ if ((name = get_next_file()) != NULL)
+ load_image(name);
+}
+
+void prev()
+{
+ char *name;
+ if ((name = get_prev_file()) != NULL)
+ load_image(name);
+}
+
+void first()
+{
+ char *name;
+ if ((name = get_first_file()) != NULL)
+ load_image(name);
+}
+
+void last()
+{
+ char *name;
+ if ((name = get_last_file()) != NULL)
+ load_image(name);
+}
+
+void handle_key_press(GdkEvent *event)
+{
+ GdkModifierType state;
+ gdk_event_get_state(event, &state);
+
+ switch (event->key.keyval) {
+ case GDK_KEY_q:
+ quit();
+ case GDK_KEY_w:
+ fit_image();
+ break;
+ case GDK_KEY_plus:
+ case GDK_KEY_KP_Add:
+ zoom(1);
+ break;
+ case GDK_KEY_minus:
+ case GDK_KEY_KP_Subtract:
+ zoom(-1);
+ break;
+ case GDK_KEY_equal:
+ zoom(0);
+ break;
+ case GDK_KEY_n:
+ next();
+ break;
+ case GDK_KEY_p:
+ prev();
+ break;
+ case GDK_KEY_Up:
+ if (curr_scale_mod == fit || state & GDK_SHIFT_MASK)
+ prev();
+ else
+ scroll_window(0, -SCROLLAMMOUNT);
+ break;
+ case GDK_KEY_Down:
+ if (curr_scale_mod == fit || state & GDK_SHIFT_MASK)
+ next();
+ else
+ scroll_window(0, SCROLLAMMOUNT);
+ break;
+ case GDK_KEY_Right:
+ if (curr_scale_mod == fit || state & GDK_SHIFT_MASK)
+ next();
+ else
+ scroll_window(SCROLLAMMOUNT, 0);
+ break;
+ case GDK_KEY_Left:
+ if (curr_scale_mod == fit || state & GDK_SHIFT_MASK)
+ prev();
+ else
+ scroll_window(-SCROLLAMMOUNT, 0);
+ break;
+ case GDK_KEY_j:
+ if (curr_scale_mod == fit)
+ next();
+ else
+ scroll_window(0, SCROLLAMMOUNT);
+ break;
+ case GDK_KEY_J:
+ next();
+ break;
+ case GDK_KEY_k:
+ if (curr_scale_mod == fit)
+ prev();
+ else
+ scroll_window(0, -SCROLLAMMOUNT);
+ break;
+ case GDK_KEY_K:
+ prev();
+ break;
+ case GDK_KEY_l:
+ if (curr_scale_mod == fit)
+ next();
+ else
+ scroll_window(SCROLLAMMOUNT, 0);
+ break;
+ case GDK_KEY_L:
+ next();
+ break;
+ case GDK_KEY_h:
+ if (curr_scale_mod == fit)
+ prev();
+ else
+ scroll_window(-SCROLLAMMOUNT, 0);
+ break;
+ case GDK_KEY_H:
+ prev();
+ break;
+ case GDK_KEY_g:
+ case GDK_KEY_Home:
+ first();
+ break;
+ case GDK_KEY_G:
+ case GDK_KEY_End:
+ last();
+ break;
+ default:
+ break;
+ }
+}
+
+void handle_button_press(GdkEvent *event)
+{
+ grabbed = 1;
+ start_x = event->motion.x;
+ start_y = event->motion.y;
+}
+
+void handle_button_release()
+{
+ grabbed = 0;
+}
+
+void handle_mouse_move(gdouble x, gdouble y)
+{
+ if (grabbed) {
+ gdouble diff_x = start_x - x;
+ gdouble diff_y = start_y - y;
+ start_x = x;
+ start_y = y;
+ scroll_window(diff_x, diff_y);
+ }
+}
+
+void handle_scroll(GdkEvent *event)
+{
+ GdkModifierType state;
+ gdk_event_get_state(event, &state);
+
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ if (state & GDK_CONTROL_MASK) {
+ zoom(1);
+ } else {
+ prev();
+ }
+ break;
+ case GDK_SCROLL_DOWN:
+ if (state & GDK_CONTROL_MASK) {
+ zoom(-1);
+ } else {
+ next();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+gboolean resize_done(gpointer data)
+{
+ guint *id = data;
+ *id = 0;
+ fit_image();
+ return FALSE;
+}
+
+void handle_resize()
+{
+ if (curr_scale_mod != fit)
+ return;
+ static guint id = 0;
+ if (id)
+ g_source_remove(id);
+ id = g_timeout_add(TIMEOUT, resize_done, &id);
+}
diff --git a/src/input.h b/src/input.h
new file mode 100644
index 0000000..ff50b0c
--- /dev/null
+++ b/src/input.h
@@ -0,0 +1,13 @@
+#ifndef __INPUT_H
+#define __INPUT_H
+
+#include <gtk-3.0/gtk/gtk.h>
+
+void handle_key_press(GdkEvent *event);
+void handle_button_press(GdkEvent *event);
+void handle_button_release();
+void handle_mouse_move(gdouble x, gdouble y);
+void handle_scroll(GdkEvent *event);
+void handle_resize();
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b5eb616
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,51 @@
+#include "file.h"
+#include "image.h"
+#include "input.h"
+#include "window.h"
+#include <gtk-3.0/gtk/gtk.h>
+
+void print_help()
+{
+ printf("usage: qwe [filename]\n");
+}
+
+static void activate(GtkApplication *app, gpointer user_data)
+{
+ print_help();
+}
+
+static void open(GApplication *app, GFile **files, gint n_files,
+ const gchar *hint)
+{
+ if (n_files != 1) {
+ print_help();
+ return;
+ }
+
+ create_main_window(app);
+
+ char *curr_filename = g_file_get_path(files[0]);
+ int i = scan(curr_filename);
+ if (i < 0) {
+ printf("failed to load file\n");
+ return;
+ }
+
+ i = load_image(curr_filename);
+ if (i < 0) {
+ printf("failed to load file\n");
+ quit();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ GtkApplication *app;
+ int status;
+ app = gtk_application_new("org.gtk.qwe", G_APPLICATION_HANDLES_OPEN);
+ g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
+ g_signal_connect(app, "open", G_CALLBACK(open), NULL);
+ status = g_application_run(G_APPLICATION(app), argc, argv);
+ g_object_unref(app);
+ return status;
+}
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 0000000..c659114
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,105 @@
+#include "window.h"
+#include "file.h"
+#include "image.h"
+#include "input.h"
+#include <gtk-3.0/gtk/gtk.h>
+
+GtkWidget *window;
+GtkWidget *scrolled_window;
+
+static gboolean key_press(GtkWindow *window, GdkEvent *event, gpointer data)
+{
+ handle_key_press(event);
+ return FALSE;
+}
+
+static gboolean button_press(GtkWindow *window, GdkEvent *event, gpointer data)
+{
+ handle_button_press(event);
+ return TRUE;
+}
+
+static gboolean button_release(GtkWindow *window, GdkEvent *event,
+ gpointer data)
+{
+ handle_button_release();
+ return TRUE;
+}
+
+static gboolean motion_notify(GtkWindow *window, GdkEvent *event, gpointer data)
+{
+ handle_mouse_move(event->motion.x, event->motion.y);
+ return TRUE;
+}
+
+static gboolean scroll_callback(GtkWindow *window, GdkEvent *event,
+ gpointer data)
+{
+ handle_scroll(event);
+ return TRUE;
+}
+
+static gboolean configure_callback(GtkWindow *window, GdkEvent *event,
+ gpointer data)
+{
+ handle_resize();
+ return TRUE;
+}
+
+void create_main_window(GApplication *app)
+{
+ window = gtk_application_window_new(GTK_APPLICATION(app));
+ gtk_window_set_title(GTK_WINDOW(window), "qwe");
+ gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
+ g_signal_connect(G_OBJECT(window), "configure-event",
+ G_CALLBACK(configure_callback), NULL);
+ g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press),
+ NULL);
+ g_signal_connect(G_OBJECT(window), "button-press-event",
+ G_CALLBACK(button_press), NULL);
+ g_signal_connect(G_OBJECT(window), "button-release-event",
+ G_CALLBACK(button_release), NULL);
+ g_signal_connect(G_OBJECT(window), "motion-notify-event",
+ G_CALLBACK(motion_notify), NULL);
+ gtk_widget_add_events(GTK_WIDGET(window), GDK_SCROLL_MASK);
+ g_signal_connect(G_OBJECT(window), "scroll-event",
+ G_CALLBACK(scroll_callback), NULL);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(new_image()));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(scrolled_window));
+ gtk_widget_show_all(GTK_WIDGET(window));
+}
+
+void scroll_window(gdouble x, gdouble y)
+{
+ GtkAdjustment *h_adj = gtk_scrolled_window_get_hadjustment(
+ GTK_SCROLLED_WINDOW(scrolled_window));
+ GtkAdjustment *v_adj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(scrolled_window));
+ gtk_adjustment_set_value(h_adj, gtk_adjustment_get_value(h_adj) + x);
+ gtk_adjustment_set_value(v_adj, gtk_adjustment_get_value(v_adj) + y);
+ gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window),
+ h_adj);
+ gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolled_window),
+ v_adj);
+}
+
+void set_window_title(const char *title)
+{
+ gtk_window_set_title(GTK_WINDOW(window), title);
+}
+
+void get_curr_win_size(gint *width, gint *height)
+{
+ gtk_window_get_size(GTK_WINDOW(window), width, height);
+}
+
+void quit()
+{
+ clean();
+ g_application_quit(
+ G_APPLICATION(gtk_window_get_application(GTK_WINDOW(window))));
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 0000000..8b8a7fe
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,12 @@
+#ifndef __WINDOW_H
+#define __WINDOW_H
+
+#include <gtk-3.0/gtk/gtk.h>
+
+void create_main_window(GApplication *app);
+void get_curr_win_size(gint *width, gint *height);
+void scroll_window(gdouble x, gdouble y);
+void set_window_title(const char *title);
+void quit();
+
+#endif