2 * Sandcastle - Falling sand game for Maemo 5
3 * Copyright (c) 2009 Thomas Thurman <thomas.thurman@collabora.co.uk>
4 * This is a demonstration of the accelerometer.
5 * It's only a few hours' work and could stand to be improved in
7 * - use a sorted list of grains to speed up sand processing
8 * - add different colours of sand?
9 * - add various other stuff like plants, water, explosives...
10 * (as in the web game "Hell of Sand")
12 * I also haven't tracked down why it crashes sometimes when
13 * you run it from the launcher. Run it from a terminal as
14 * /usr/bin/sandcastle to get around this.
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License as
20 * published by the Free Software Foundation; either version 2 of the
21 * License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
35 #include <gdk-pixbuf/gdk-pixbuf.h>
36 #include <mce/mode-names.h>
37 #include <mce/dbus-names.h>
38 #include <hildon/hildon.h>
39 #include <dbus/dbus-glib.h>
42 GtkWidget *window, *area;
43 int width, height, rowstride;
44 int minx, maxx, miny, maxy;
45 DBusGProxy *dbus_proxy = NULL;
46 DBusGProxyCall *dbus_call = NULL;
47 guint32 *pixels = NULL;
50 const guint32 SAND = 0xff00ffff;
51 const guint32 WALL = 0xffffffff;
52 const guint32 EMPTY = 0xff000000;
66 int there_is_gravity = TRUE;
68 static inline guint32*
69 translate_pointer_by_direction(guint32 *src, int direction, int x, int y)
71 static guint32 edge = WALL;
73 switch (direction%8) {
75 if (y<=0) return &edge;
78 if (y<=0 || x>=width) return &edge;
79 return (src-rowstride)+1;
81 if (x>=width) return &edge;
84 if (x>=width || y>=height) return &edge;
85 return (src+rowstride)+1;
87 if (y>=height) return &edge;
90 if (x<=0 || y>=height) return &edge;
91 return (src+rowstride)-1;
93 if (x<=0) return &edge;
96 if (x<=0 || y<=0) return &edge;
97 return (src-rowstride)-1;
102 expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data)
104 gdk_draw_pixbuf (GDK_DRAWABLE(widget->window),
105 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
106 pixbuf, event->area.x, event->area.y,
107 event->area.x, event->area.y,
108 event->area.width, event->area.height,
109 GDK_RGB_DITHER_NONE, 0, 0);
114 tap_event (GtkWidget *widget,
115 GdkEventMotion *event,
120 for (rx=-5; rx<=5; rx++)
121 for (ry=-5; ry<=5; ry++)
123 guint32 *p = pixels + (ry+(int)event->y)*rowstride + (rx+(int)event->x);
125 if (*p==EMPTY) *p=SAND;
128 gtk_widget_queue_draw_area (area, ((int)event->x)-5, ((int)event->y)-5, 10, 10);
130 maxx=width; maxy=height;
134 orientation_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *data)
140 if (dbus_g_proxy_end_call (proxy, call, NULL,
149 if (x<=-500) facing |= 0x10;
150 else if (x<=0) facing |= 0x20;
151 else if (x<=500) facing |= 0x30;
154 if (y<=-500) facing |= 0x1;
155 else if (y<=0) facing |= 0x2;
156 else if (y<=500) facing |= 0x3;
159 g_free(s1); g_free(s2); g_free(s3);
161 there_is_gravity = TRUE;
164 case 0x34: case 0x36: case 0x24: gravity=UP; break;
165 case 0x13: case 0x14: gravity=UP_RIGHT; break;
166 case 0x12: gravity=RIGHT; break;
167 case 0x11: gravity=DOWN_RIGHT; break;
168 case 0x21: case 0x31: gravity=DOWN; break;
169 case 0x41: gravity=DOWN_LEFT; break;
170 case 0x42: case 0x43: gravity=LEFT; break;
171 case 0x44: gravity=UP_LEFT; break;
173 there_is_gravity=FALSE;
177 g_warning ("Couldn't end the call!\n");
183 sand_sort (gpointer sand1, gpointer sand2)
187 else if (sand1==sand1)
194 update_screen (gpointer data)
197 int newminx=width, newminy=height, newmaxx=0, newmaxy=0;
199 int xstart, xend, xdelta, ystart, yend, ydelta;
201 if (!dbus_call && dbus_proxy)
202 dbus_call = dbus_g_proxy_begin_call (
204 "get_device_orientation",
205 orientation_callback,
209 if (!there_is_gravity) return TRUE;
218 xstart=maxx; xend=minx; xdelta=-1;
223 xstart=minx; xend=maxx; xdelta=1;
234 ystart=miny; yend=maxy; ydelta=1;
241 ystart=maxy; yend=miny; ydelta=-1;
246 g_assert( (xstart-xend)*xdelta < 0);
247 g_assert( (ystart-yend)*ydelta < 0);
249 for (y=ystart; y!=yend; y+=ydelta) {
251 guint32 *p = pixels + y*rowstride + xstart;
253 for (x=xstart; x!=xend; x+=xdelta) {
256 const int directions[] = {0, 1, -1};
258 for (i=0; i<3; i++) {
259 guint32 *below = translate_pointer_by_direction(p, gravity+directions[i], x, y);
267 if (x<newminx) newminx=x;
268 if (x>newmaxx) newmaxx=x;
269 if (y<newminy) newminy=y;
270 if (y>newmaxy) newmaxy=y;
276 minx=newminx; if (minx>2) minx--; else minx=0;
277 maxx=newmaxx; if (maxx<width-2) maxx++; else maxx=width;
278 miny=newminy; if (miny>2) miny--; else miny=0;
279 maxy=newmaxy; if (maxy<height-2) maxy++; else maxy=height-2;
281 gtk_widget_queue_draw_area (area, minx, miny, maxx-minx, maxy-miny);
291 for (y=0; y<=height; y++) {
293 guint32 *p = pixels + y*rowstride;
295 for (x=0; x<width; x++) {
296 grains = g_list_append(grains, p+x);
300 g_warning ("There are %d grains.\n", g_list_length (grains));
304 main(int argc, char **argv)
306 hildon_gtk_init (&argc, &argv);
308 dbus_proxy = dbus_g_proxy_new_for_name_owner (
309 dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL),
311 "/com/nokia/mce/request",
312 "com.nokia.mce.request", NULL);
316 g_warning ("You have no accelerometer installed. Gravity will always be downwards.\n");
319 pixbuf = gdk_pixbuf_new_from_file ("/usr/share/sandcastle/sandcastle.png", NULL);
320 pixels = (guint32*) gdk_pixbuf_get_pixels (pixbuf);
322 maxx = width = gdk_pixbuf_get_width (pixbuf);
323 maxy = height = gdk_pixbuf_get_height (pixbuf);
325 rowstride = gdk_pixbuf_get_rowstride (pixbuf)/4;
329 window = hildon_stackable_window_new ();
330 gtk_window_set_title (GTK_WINDOW (window), "Sandcastle");
332 area = gtk_drawing_area_new ();
333 gtk_widget_add_events (area, GDK_BUTTON_MOTION_MASK|GDK_BUTTON_PRESS_MASK);
335 gtk_container_add (GTK_CONTAINER (window), area);
336 g_signal_connect (G_OBJECT (area), "expose_event", G_CALLBACK (expose_event), NULL);
337 g_signal_connect (G_OBJECT (area), "motion-notify-event", G_CALLBACK (tap_event), NULL);
338 g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
340 gtk_widget_show_all (window);
342 g_timeout_add (20, update_screen, NULL);