Clean up the code a bit and add some more comments
[cteddy.git] / src / cteddy.cxx
blob15e7d6cfbdbd5608687a9212190fd03271c071c7
1 // Copyright 2008 Philip Allison <sane@not.co.uk>
3 // This file is part of cteddy.
4 //
5 // cteddy is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // cteddy is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with cteddy. If not, see <http://www.gnu.org/licenses/>.
20 // Includes
23 // Language headers
24 #include <iostream>
26 // Library headers
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
32 // Globals
35 // Main window
36 GtkWidget* window;
38 // GTK/GDK returned pointer
39 GError* gerr = NULL;
41 // Image to display and its dimensions
42 GdkPixbuf* pixbuf;
43 gint width, height;
47 // Implementation
50 // Re-paint the window when necessary
51 gboolean handle_expose(GtkWidget* widget, GdkEventExpose* event)
53 cairo_t* cairo = gdk_cairo_create(widget->window);
55 // Blank the surface
56 cairo_set_source_rgba (cairo, 1.0f, 1.0f, 1.0f, 0.0f);
57 cairo_set_operator (cairo, CAIRO_OPERATOR_SOURCE);
58 cairo_paint (cairo);
60 // Set cairo's source pattern from the desired image
61 gdk_cairo_set_source_pixbuf(cairo, pixbuf, 0.0f, 0.0f);
63 // Draw the image
64 cairo_rectangle(cairo, 0.0f, 0.0f,
65 static_cast<double>(width), static_cast<double>(height));
66 cairo_fill(cairo);
68 cairo_destroy(cairo);
70 return false;
73 // Handle mouse click events
74 gboolean window_clicked(GtkWidget* widget, GdkEventButton* event)
76 if (event->type == GDK_BUTTON_PRESS)
78 switch(event->button)
80 case 1:
81 // Move the window with the left mouse button
82 gtk_window_begin_move_drag(GTK_WINDOW(window),
83 event->button, static_cast<gint>(event->x_root),
84 static_cast<gint>(event->y_root), event->time);
85 break;
87 default:
88 // Quit the app with any other
89 gtk_main_quit();
90 break;
93 return true;
96 // Set up an RGBA colourmap for the window
97 void handle_screen(GtkWidget* window, GdkScreen* old, gpointer data)
99 GdkScreen* s = gtk_widget_get_screen(window);
100 GdkColormap* c = gdk_screen_get_rgba_colormap(s);
102 if (!c)
103 // Use standard RGB colourmap if we must. Window will have
104 // an ugly black border if this is the case.
105 c = gdk_screen_get_rgb_colormap(s);
107 gtk_widget_set_colormap(window, c);
110 // Handle key press events
111 void handle_key_press(GtkWidget *Window, GdkEventKey *theEvent)
113 /* TA: Boolean -noquit needed here. Wouldn't want to him to
114 * accidentally disappear. */
115 switch( theEvent->keyval )
117 case GDK_q:
118 // Quit on "q"
119 gtk_main_quit();
120 break;
122 default:
123 // Ignore all other keys
124 break;
129 // Entry point
130 int main (int argc, char* argv[])
132 gtk_init(&argc, &argv);
134 // Print usage message if no image path is supplied
135 if (argc < 2)
137 std::cerr << "Usage: " << argv[0] << " <image>" << std::endl;
138 return 1;
141 // Load the image from the supplied file path
142 pixbuf = gdk_pixbuf_new_from_file(argv[1], &gerr);
143 if (gerr)
145 std::cerr << "Cannot load image: " << gerr->message << std::endl;
146 g_error_free(gerr);
147 return 1;
150 // Create the window
151 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
153 // Top-level windows don't normally respond to mouse button events directly.
154 // We want this one to, though.
155 gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
157 // Set up event callbacks
158 g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(handle_expose), NULL);
159 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
160 g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(window_clicked), NULL);
161 g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(handle_screen), NULL);
162 g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(handle_key_press), NULL);
165 // Grap pixmap and alpha-thresholded bitmap from original pixmap
166 GdkPixmap* pixmap;
167 GdkBitmap* bitmap;
168 gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &bitmap, 128);
170 // Set window size to image size
171 gdk_drawable_get_size(pixmap, &width, &height);
172 gtk_widget_set_size_request(window, width, height);
174 // If we have an alpha channel on the image, set the window's input shape,
175 // clearing it completely first
176 if (bitmap)
178 gtk_widget_input_shape_combine_mask(window, NULL, 0, 0);
179 gtk_widget_input_shape_combine_mask(window, bitmap, 0, 0);
182 g_object_unref(G_OBJECT(pixmap));
183 g_object_unref(G_OBJECT(bitmap));
186 // Set up various other properties we're interested in
187 gtk_window_set_title(GTK_WINDOW(window), "cteddy");
188 gtk_window_set_resizable(GTK_WINDOW(window), false);
189 gtk_window_set_decorated(GTK_WINDOW(window), false);
190 gtk_widget_set_app_paintable(window, true);
192 // Trigger initial attempt to switch to an RGBA colourmap
193 handle_screen(window, NULL, NULL);
195 // Have to do this before we can use window->window as a GdkWindow*
196 gtk_widget_realize(window);
199 // Load in the heart-shaped cursor image and use it for our window's cursor
200 GdkPixbuf* heart = gdk_pixbuf_new_from_file(PKGDATADIR "/heart.png", &gerr);
201 if (gerr)
203 std::cerr << "Cannot load image: " << gerr->message << std::endl;
204 g_error_free(gerr);
205 return 1;
207 GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
208 heart, 12, 12);
209 gdk_window_set_cursor(window->window, cursor);
212 // Clear the window's default background
213 gdk_window_set_back_pixmap(window->window, NULL, false);
215 // Don't steal input focus when we start
216 g_object_set(G_OBJECT(window), "focus-on-map", false, NULL);
218 gtk_widget_show_all(window);
220 gtk_main();
222 return 0;