]> git.cworth.org Git - akamaru/blob - dock.c
37707a98fcb243eb81014b40af25ab76ff4622a6
[akamaru] / dock.c
1 /*                                           -*- mode: c; c-basic-offset: 2 -*-
2  */
3
4 #include <gtk/gtk.h>
5 #include <cairo.h>
6 #include <cairo-xlib.h>
7 #include <gdk/gdkx.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/time.h>
11 #include <math.h>
12 #include <librsvg/rsvg.h>
13 #include <librsvg/rsvg-cairo.h>
14
15 #include "akamaru.h"
16
17 typedef struct Closure Closure;
18 struct Closure {
19   Model model;
20   int num_icons;
21   GdkWindow **windows;
22   int drag_offset_x, drag_offset_y;
23   int anchor_x, anchor_y;
24   int spacing;
25 };
26
27 static gint
28 timeout_callback (gpointer data)
29 {
30   Closure *closure = data;
31   int i;
32
33   for (i = 0; i < closure->num_icons; i++) {
34     gdk_window_move (closure->windows[i],
35                      closure->model.objects[i + 1].position.x,
36                      closure->model.objects[i + 1].position.y);
37   }
38
39   model_step (&closure->model, 0.1);
40
41   return TRUE;
42 }
43
44 static GdkWindow *
45 create_window (GdkScreen *screen, int x, int y, int width, int height)
46 {
47   GdkWindowAttr attributes;
48   gint attributes_mask;
49
50   attributes.wclass = GDK_INPUT_OUTPUT;
51   attributes.visual = gdk_screen_get_rgba_visual (screen);
52   attributes.colormap = gdk_screen_get_rgba_colormap (screen);
53   attributes.window_type = GDK_WINDOW_TEMP;
54
55   attributes.x = x;
56   attributes.y = y;
57   attributes.width = width;
58   attributes.height = height;
59   attributes.event_mask =
60     GDK_EXPOSURE_MASK |
61     GDK_BUTTON_PRESS_MASK |
62     GDK_BUTTON_RELEASE_MASK |
63     GDK_ENTER_NOTIFY_MASK |
64     GDK_LEAVE_NOTIFY_MASK |
65     GDK_POINTER_MOTION_MASK |
66     GDK_POINTER_MOTION_HINT_MASK;
67
68   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
69   
70   return gdk_window_new (gdk_screen_get_root_window (screen),
71                          &attributes, attributes_mask);
72 }
73
74 static void
75 model_init_dock (Model *model, int num_items, int x, int y, int spacing)
76 {
77   const int num_objects = num_items + 1;
78   const int num_spacers = (num_objects - 1) * (num_objects - 2) / 2;
79   const int num_springs = num_objects - 1;
80   int i, j;
81   Object *object;
82   Spring *spring;
83   Spacer *spacer;
84
85   memset (model, 0, sizeof *model);
86   model->objects = g_new (Object, num_objects);
87   model->num_objects = num_objects;
88   model->springs = g_new (Spring, num_springs);
89   model->num_springs = num_springs;
90   model->spacers = g_new (Spacer, num_spacers);
91   model->num_spacers = num_spacers;
92   model->anchors = g_new (Anchor, 1);
93   model->num_anchors = 1;
94   model->k = 0.1;
95
96   model->polygons = g_new (Polygon, 1);
97   model->num_polygons = 1;
98   polygon_init_enclosing_rectangle (&model->polygons[0], 0, 0, 1024 - 50, y);
99
100   model->anchors[0].x = x;
101   model->anchors[0].y = y;
102   model->anchors[0].object = &model->objects[0];
103
104   object_init (&model->objects[0], x, y, 20);
105
106   object = &model->objects[1];
107   spring = model->springs;
108   spacer = model->spacers;
109
110   for (i = 1; i < num_objects; i++, object++) {
111     object_init (&model->objects[i], 200 + i * spacing / 2, 300, 3);
112     spring_init (spring++, &model->objects[0], object, spacing);
113     for (j = 1; j < num_objects - i; j++) {
114       spacer_init (spacer++, object, object + j, spacing);
115     }
116   }
117 }
118
119 static GdkFilterReturn
120 window_event (GdkXEvent *xevent, GdkEvent *event, gpointer data)
121 {
122   Closure *closure = data;
123   GdkModifierType state;
124   XEvent *ev = (XEvent *) xevent;
125   int x, y, i;
126   Object *object;
127
128   switch (ev->type) {
129   case ButtonPress:
130     closure->drag_offset_x = ev->xbutton.x;
131     closure->drag_offset_y = ev->xbutton.y;
132     for (i = 0; i < closure->num_icons; i++) {
133       if (closure->windows[i] == event->any.window) {
134         object = &closure->model.objects[i + 1];
135         closure->model.mouse_anchor.x = object->position.x;
136         closure->model.mouse_anchor.y = object->position.y;
137         closure->model.mouse_anchor.object = object;
138       }
139     }
140     break;
141
142   case ButtonRelease:
143     closure->model.mouse_anchor.object = NULL;
144     break;
145
146   case MotionNotify:
147     gdk_window_get_pointer (gdk_get_default_root_window(), &x, &y, &state);
148     closure->model.mouse_anchor.x = x + 0.5 - closure->drag_offset_x;
149     closure->model.mouse_anchor.y = y + 0.5 - closure->drag_offset_y;
150     if (closure->model.mouse_anchor.y > closure->anchor_y)
151       closure->model.mouse_anchor.y = closure->anchor_y;
152     break;
153
154   default:
155     break;
156   }
157
158   return GDK_FILTER_CONTINUE;
159 }
160
161 static const char *icons[] = {
162   "svg/applications-office.svg",
163   "svg/camera-video.svg",
164   "svg/email.svg",
165   "svg/firefox-logo.svg",
166   "svg/gnome-dev-disc-dvdrom.svg",
167   "svg/gnome-terminal.svg",
168   "svg/help-browser.svg",
169   "svg/internet-group-chat.svg"
170 };
171
172 int main (int argc, char *argv[])
173 {
174     Closure closure;
175     GdkScreen *screen;
176     const int num_icons = G_N_ELEMENTS (icons);
177     int x, y, width, height, i;
178     RsvgHandle *handle;
179     RsvgDimensionData dimension;
180     cairo_t *cr;
181     const int screen_width = 1024, screen_height = 768, spacing = 50;
182
183     gtk_init (&argc, &argv);
184
185     rsvg_init ();
186
187     screen = gdk_screen_get_default ();
188
189     closure.anchor_x = screen_width / 2;
190     closure.anchor_y = screen_height - 50;
191     closure.spacing = spacing;
192     closure.num_icons = num_icons;
193     closure.windows = g_new (GdkWindow *, num_icons);
194     for (i = 0; i < num_icons; i++) {
195
196       handle = rsvg_handle_new_from_file (icons[i], NULL);
197       rsvg_handle_get_dimensions (handle, &dimension);
198
199       x = (screen_width - spacing * num_icons) / 2 + i * spacing;
200       y = closure.anchor_y;
201       width = dimension.width;
202       height = dimension.height;
203       closure.windows[i] = create_window (screen, x, y, width, height);
204
205       gdk_window_show (closure.windows[i]);
206
207       cr = gdk_cairo_create (closure.windows[i]);
208       cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
209       cairo_paint (cr);
210       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
211       rsvg_handle_render_cairo (handle, cr);
212       rsvg_handle_free (handle);
213       cairo_destroy (cr);
214
215       gdk_window_add_filter (closure.windows[i], window_event, &closure);
216     }
217
218     model_init_dock (&closure.model, num_icons,
219                      closure.anchor_x, closure.anchor_y, spacing);
220     g_timeout_add (20, timeout_callback, &closure);
221
222     gtk_main ();
223
224     rsvg_term ();
225
226     return 0;
227 }