Contents of /trunk/src/osm-gps-map-osd-classic.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (hide annotations)
Fri Aug 21 13:20:40 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 12803 byte(s)
OSD
1 harbaum 71 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2     /* vim:set et sw=4 ts=4 cino=t0,(0: */
3 harbaum 70 /*
4     * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
5     *
6     * osm-gps-map is free software: you can redistribute it and/or modify it
7     * under the terms of the GNU General Public License as published by the
8     * Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * osm-gps-map is distributed in the hope that it will be useful, but
12     * WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14     * See the GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License along
17     * with this program. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "config.h"
21    
22 harbaum 71 /* parameters that can be overwritten from the config file: */
23     /* OSM_GPS_MAP_OSD_DIAMETER */
24    
25     #ifndef USE_CAIRO
26     #error "OSD control display lacks a non-cairo implementation!"
27 harbaum 70 #endif
28    
29 harbaum 71 #include <cairo.h>
30    
31     #include "osm-gps-map.h"
32    
33 harbaum 70 //the osd controls
34     typedef struct {
35     GdkPixmap *backup;
36     gint backup_x, backup_y;
37 harbaum 71
38     cairo_surface_t *overlay;
39    
40 harbaum 70 OsmGpsMapOsdGpsCallback cb;
41     gpointer data;
42     } osd_priv_t;
43    
44 harbaum 71 /* the osd structure mainly contains various callbacks */
45     /* required to draw and update the OSD */
46 harbaum 70 typedef struct {
47    
48 harbaum 71 void(enable_gps)(OsmGpsMap *, OsmGpsMapOsdGpsCallback, gpointer);
49     void(restore)(OsmGpsMap*);
50     void(draw)(OsmGpsMap *, gint, gint);
51     void(render)(OsmGpsMapPrivate *);
52     osd_button_t(*check)(gint, gint); /* check if x/y lies within OSD */
53     void(free)(gpointer);
54 harbaum 70
55     gpointer priv;
56     } osm_gps_map_osd_t;
57    
58 harbaum 71 static osm_gps_map_osd_t osd_classic = {
59     .restore = osm_gps_map_osd_restore,
60     .draw = osm_gps_map_osd_draw_controls,
61     .check = osm_gps_map_osd_check,
62     .render = osm_gps_map_osd_render,
63     .enable_gps = osm_gps_map_osd_enable_gps,
64     .free = osm_gps_map_osd_free,
65     .priv = NULL;
66     }
67    
68     /* this is the only function that's externally visible */
69     osm_gps_map_osd_t *
70     osm_gps_map_osd_classic_init(void)
71     {
72     return osd_classic;
73     }
74    
75     static void
76     osm_gps_map_osd_free(gpointer priv_ptr)
77     {
78     osd_priv_t *priv = (osd_priv_t *)priv_ptr;
79    
80    
81     g_free(priv);
82     }
83    
84 harbaum 70 /* position and extent of bounding box */
85     #define OSD_X (10)
86     #define OSD_Y (10)
87    
88     #define OSD_COLOR 0.5, 0.5, 1
89     #define OSD_COLOR_DISABLED 0.8, 0.8, 0.8
90    
91     /* parameters of the direction shape */
92     #ifndef OSM_GPS_MAP_OSD_DIAMETER
93     #define D_RAD (30) // diameter of dpad
94     #else
95     #define D_RAD (OSM_GPS_MAP_OSD_DIAMETER)
96     #endif
97     #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
98     #define D_LEN (D_RAD/4) // length of arrow
99     #define D_WID (D_LEN) // width of arrow
100    
101     /* parameters of the "zoom" pad */
102     #define Z_STEP (D_RAD/4) // distance between dpad and zoom
103     #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
104    
105     /* shadow also depends on control size */
106     #define OSD_SHADOW (D_RAD/6)
107    
108     /* total width and height of controls incl. shadow */
109     #define OSD_W (2*D_RAD + OSD_SHADOW)
110     #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
111    
112     #define OSD_LBL_SHADOW (OSD_SHADOW/2)
113    
114     #define Z_TOP (2 * D_RAD + Z_STEP)
115     #define Z_MID (Z_TOP + Z_RAD)
116     #define Z_BOT (Z_MID + Z_RAD)
117     #define Z_LEFT (Z_RAD)
118     #define Z_RIGHT (2 * D_RAD - Z_RAD)
119    
120 harbaum 71
121 harbaum 70 /* create the cairo shape used for the zoom buttons */
122     static void
123 harbaum 71 osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y)
124     {
125 harbaum 70 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
126     cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
127     cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
128     cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
129     cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
130     }
131    
132     /* create the cairo shape used for the dpad */
133     static void
134 harbaum 71 osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y)
135     {
136 harbaum 70 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
137     }
138    
139     static gboolean
140     osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
141     {
142     return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
143     }
144    
145     /* check whether x/y is within the dpad */
146     static osd_button_t
147     osm_gps_map_osd_check_dpad(gint x, gint y)
148     {
149     /* within entire dpad circle */
150     if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD))
151     {
152     /* convert into position relative to dpads centre */
153     x -= (OSD_X + D_RAD);
154     y -= (OSD_Y + D_RAD);
155    
156     /* check for dpad center goes here! */
157     if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
158     return OSD_GPS;
159    
160     if( y < 0 && abs(x) < abs(y))
161     return OSD_UP;
162    
163     if( y > 0 && abs(x) < abs(y))
164     return OSD_DOWN;
165    
166     if( x < 0 && abs(y) < abs(x))
167     return OSD_LEFT;
168    
169     if( x > 0 && abs(y) < abs(x))
170     return OSD_RIGHT;
171    
172     return OSD_BG;
173     }
174     return OSD_NONE;
175     }
176    
177     /* check whether x/y is within the zoom pads */
178     static osd_button_t
179     osm_gps_map_osd_check_zoom(gint x, gint y) {
180     if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) {
181    
182     /* within circle around (-) label */
183     if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD))
184     return OSD_OUT;
185    
186     /* between center of (-) button and center of entire zoom control area */
187     if(x > OSD_LEFT && x < OSD_X + D_RAD)
188     return OSD_OUT;
189    
190     /* within circle around (+) label */
191     if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD))
192     return OSD_IN;
193    
194     /* between center of (+) button and center of entire zoom control area */
195     if(x < OSD_RIGHT && x > OSD_X + D_RAD)
196     return OSD_IN;
197     }
198    
199     return OSD_NONE;
200     }
201    
202     static osd_button_t
203     osm_gps_map_osd_check(gint x, gint y) {
204     osd_button_t but = OSD_NONE;
205    
206     /* first do a rough test for the OSD area. */
207     /* this is just to avoid an unnecessary detailed test */
208     if(x > OSD_X && x < OSD_X + OSD_W &&
209     y > OSD_Y && y < OSD_Y + OSD_H) {
210     but = osm_gps_map_osd_check_dpad(x, y);
211    
212     if(but == OSD_NONE)
213     but = osm_gps_map_osd_check_zoom(x, y);
214     }
215    
216     return but;
217     }
218    
219     static void
220     osm_gps_map_osd_shape_shadow(cairo_t *cr) {
221     cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
222     cairo_fill (cr);
223     cairo_stroke (cr);
224     }
225    
226     static void
227     osm_gps_map_osd_shape(cairo_t *cr) {
228     cairo_set_source_rgb (cr, 1, 1, 1);
229     cairo_fill_preserve (cr);
230     cairo_set_source_rgb (cr, OSD_COLOR);
231     cairo_set_line_width (cr, 1);
232     cairo_stroke (cr);
233     }
234    
235     static void
236     osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) {
237     /* move reference to dpad center */
238     x += D_RAD;
239     y += D_RAD;
240    
241     const static gint offset[][3][2] = {
242     /* left arrow/triangle */
243     { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
244     /* right arrow/triangle */
245     { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
246     /* top arrow/triangle */
247     { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
248     /* bottom arrow/triangle */
249     { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
250     };
251    
252     int i;
253     for(i=0;i<4;i++) {
254     cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
255     cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
256     cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
257     }
258     }
259    
260     /* draw the sattelite dish icon in the center of the dpad */
261     #define GPS_V0 (D_RAD/8)
262     #define GPS_V1 (D_RAD/10)
263     #define GPS_V2 (D_RAD/5)
264    
265     /* draw a satellite receiver dish */
266     static void
267     osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) {
268     /* move reference to dpad center */
269     x += D_RAD;
270     y += D_RAD + GPS_V0;
271    
272     cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
273     cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
274     cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
275     cairo_close_path (cr);
276    
277     cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
278     cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
279     cairo_close_path (cr);
280    
281     x += GPS_V1;
282     cairo_move_to (cr, x, y-GPS_V2);
283     cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
284     }
285    
286     #define Z_LEN (2*Z_RAD/3)
287    
288     static void
289     osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) {
290     cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
291     cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
292    
293     cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
294     cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
295     cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
296     cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
297     }
298    
299     static void
300     osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) {
301     if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
302     else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
303     cairo_set_line_width (cr, width);
304     cairo_stroke (cr);
305     }
306    
307     static void
308     osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
309     cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
310     cairo_set_line_width (cr, width);
311     cairo_stroke (cr);
312     }
313    
314     static void
315     osm_gps_map_osd_render(OsmGpsMapPrivate *priv) {
316    
317     /* first fill with transparency */
318     cairo_t *cr = cairo_create(priv->osd.overlay);
319     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
320     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
321     cairo_paint(cr);
322    
323     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
324    
325     /* --------- draw zoom and dpad shape shadow ----------- */
326     gint x = 0, y = 0;
327    
328     osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
329     osm_gps_map_osd_shape_shadow(cr);
330     osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
331     osm_gps_map_osd_shape_shadow(cr);
332    
333     /* --------- draw zoom and dpad shape ----------- */
334    
335     osm_gps_map_osd_zoom_shape(cr, x, y);
336     osm_gps_map_osd_shape(cr);
337     osm_gps_map_osd_dpad_shape(cr, x, y);
338     osm_gps_map_osd_shape(cr);
339    
340     /* --------- draw zoom and dpad labels --------- */
341    
342     osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
343     osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
344     osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE);
345     osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
346     osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, priv->osd.cb != NULL);
347    
348     osm_gps_map_osd_zoom_labels(cr, x, y);
349     osm_gps_map_osd_dpad_labels(cr, x, y);
350     osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE);
351     osm_gps_map_osd_dpad_gps(cr, x, y);
352     osm_gps_map_osd_labels(cr, Z_RAD/6, priv->osd.cb != NULL);
353    
354     cairo_destroy(cr);
355     }
356    
357     static void
358     osm_gps_map_osd_draw_controls (OsmGpsMap *map, gint xoffset, gint yoffset)
359     {
360     OsmGpsMapPrivate *priv = map->priv;
361    
362     /* backup previous contents */
363     if(!priv->osd.backup)
364     priv->osd.backup = gdk_pixmap_new(priv->pixmap, OSD_W+2, OSD_H+2, -1);
365    
366     gint x = OSD_X + EXTRA_BORDER + xoffset;
367     gint y = OSD_Y + EXTRA_BORDER + yoffset;
368    
369     /* create backup of background */
370     gdk_draw_drawable(priv->osd.backup,
371     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
372     priv->pixmap, x-1, y-1, 0, 0, OSD_W+2, OSD_H+2);
373    
374     priv->osd.backup_x = x-1;
375     priv->osd.backup_y = y-1;
376    
377     /* OSD itself uses some off-screen rendering, so check if the */
378     /* offscreen buffer is present and create it if not */
379     if(!priv->osd.overlay) {
380     /* create overlay ... */
381     priv->osd.overlay =
382     cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H);
383     /* ... and render it */
384     osm_gps_map_osd_render(priv);
385     }
386    
387     // now draw this onto the original context
388     cairo_t *cr = gdk_cairo_create(priv->pixmap);
389     cairo_set_source_surface(cr, priv->osd.overlay, x, y);
390     cairo_paint(cr);
391     cairo_destroy(cr);
392     }
393    
394     static void
395     osm_gps_map_osd_restore (OsmGpsMap *map)
396     {
397     OsmGpsMapPrivate *priv = map->priv;
398    
399     /* restore backup of previous contents */
400     if(priv->osd.backup) {
401     /* create backup of background */
402     gdk_draw_drawable(priv->pixmap,
403     GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))],
404     priv->osd.backup, 0, 0,
405     priv->osd.backup_x, priv->osd.backup_y, OSD_W+2, OSD_H+2);
406     }
407     }
408    
409 harbaum 71 static void
410     osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdGpsCallback cb, gpointer data)
411 harbaum 70 {
412     OsmGpsMapPrivate *priv;
413    
414     g_return_if_fail (OSM_IS_GPS_MAP (map));
415     priv = map->priv;
416    
417     priv->osd.cb = cb;
418     priv->osd.data = data;
419    
420     /* this may have changed the state of the gps button */
421     /* we thus re-render the overlay */
422     osm_gps_map_osd_render(priv);
423    
424     osm_gps_map_map_redraw_idle(map);
425     }