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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 76 - (show annotations)
Mon Aug 24 19:21:46 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 13602 byte(s)
started dpad-less osd option
1 /* -*- 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 /*
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 #include <stdlib.h> // abs
22 #include <math.h> // M_PI
23
24 /* parameters that can be overwritten from the config file: */
25 /* OSM_GPS_MAP_OSD_DIAMETER */
26
27 #ifndef USE_CAIRO
28 #error "OSD control display lacks a non-cairo implementation!"
29 #endif
30
31 #include <cairo.h>
32
33 #include "osm-gps-map.h"
34 #include "osm-gps-map-osd-classic.h"
35
36 //the osd controls
37 typedef struct {
38 /* the offscreen representation of the OSD */
39 cairo_surface_t *overlay;
40
41 GdkColor bg, fg, da;
42
43 } osd_priv_t;
44
45 /* position and extent of bounding box */
46 #define OSD_X (10)
47 #define OSD_Y (10)
48
49 /* parameters of the direction shape */
50 #ifndef OSM_GPS_MAP_OSD_DIAMETER
51 #define D_RAD (30) // diameter of dpad
52 #else
53 #define D_RAD (OSM_GPS_MAP_OSD_DIAMETER)
54 #endif
55 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
56 #define D_LEN (D_RAD/4) // length of arrow
57 #define D_WID (D_LEN) // width of arrow
58
59 /* parameters of the "zoom" pad */
60 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
61 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
62
63 #ifdef OSD_SHADOW_ENABLE
64 /* shadow also depends on control size */
65 #define OSD_SHADOW (D_RAD/6)
66 #else
67 #define OSD_SHADOW (0)
68 #endif
69
70 /* total width and height of controls incl. shadow */
71 #define OSD_W (2*D_RAD + OSD_SHADOW)
72 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
73
74 #ifdef OSD_SHADOW_ENABLE
75 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
76 #endif
77
78 #define Z_TOP (2 * D_RAD + Z_STEP)
79 #define Z_MID (Z_TOP + Z_RAD)
80 #define Z_BOT (Z_MID + Z_RAD)
81 #define Z_LEFT (Z_RAD)
82 #define Z_RIGHT (2 * D_RAD - Z_RAD)
83
84
85 /* create the cairo shape used for the zoom buttons */
86 static void
87 osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y)
88 {
89 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
90 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
91 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
92 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
93 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
94 }
95
96 #ifndef OSD_NO_DPAD
97 /* create the cairo shape used for the dpad */
98 static void
99 osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y)
100 {
101 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
102 }
103 #endif
104
105 static gboolean
106 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
107 {
108 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
109 }
110
111 #ifndef OSD_NO_DPAD
112 /* check whether x/y is within the dpad */
113 static osd_button_t
114 osm_gps_map_osd_check_dpad(gint x, gint y)
115 {
116 /* within entire dpad circle */
117 if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD))
118 {
119 /* convert into position relative to dpads centre */
120 x -= (OSD_X + D_RAD);
121 y -= (OSD_Y + D_RAD);
122
123 #ifdef OSD_GPS_BUTTON
124 /* check for dpad center goes here! */
125 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
126 return OSD_GPS;
127 #endif
128
129 if( y < 0 && abs(x) < abs(y))
130 return OSD_UP;
131
132 if( y > 0 && abs(x) < abs(y))
133 return OSD_DOWN;
134
135 if( x < 0 && abs(y) < abs(x))
136 return OSD_LEFT;
137
138 if( x > 0 && abs(y) < abs(x))
139 return OSD_RIGHT;
140
141 return OSD_BG;
142 }
143 return OSD_NONE;
144 }
145 #endif
146
147 /* check whether x/y is within the zoom pads */
148 static osd_button_t
149 osm_gps_map_osd_check_zoom(gint x, gint y) {
150 if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) {
151
152 /* within circle around (-) label */
153 if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD))
154 return OSD_OUT;
155
156 /* between center of (-) button and center of entire zoom control area */
157 if(x > OSD_LEFT && x < OSD_X + D_RAD)
158 return OSD_OUT;
159
160 /* within circle around (+) label */
161 if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD))
162 return OSD_IN;
163
164 /* between center of (+) button and center of entire zoom control area */
165 if(x < OSD_RIGHT && x > OSD_X + D_RAD)
166 return OSD_IN;
167 }
168
169 return OSD_NONE;
170 }
171
172 static osd_button_t
173 osm_gps_map_osd_check(gint x, gint y) {
174 osd_button_t but = OSD_NONE;
175
176 /* first do a rough test for the OSD area. */
177 /* this is just to avoid an unnecessary detailed test */
178 if(x > OSD_X && x < OSD_X + OSD_W &&
179 y > OSD_Y && y < OSD_Y + OSD_H) {
180 #ifndef OSD_NO_DPAD
181 but = osm_gps_map_osd_check_dpad(x, y);
182 #endif
183
184 if(but == OSD_NONE)
185 but = osm_gps_map_osd_check_zoom(x, y);
186 }
187
188 return but;
189 }
190
191 #ifdef OSD_SHADOW_ENABLE
192 static void
193 osm_gps_map_osd_shape_shadow(cairo_t *cr) {
194 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
195 cairo_fill (cr);
196 cairo_stroke (cr);
197 }
198 #endif
199
200 #ifndef OSD_COLOR
201 /* if no color has been specified we just use the gdks default colors */
202 static void
203 osm_gps_map_osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
204 gdk_cairo_set_source_color(cr, bg);
205 cairo_fill_preserve (cr);
206 gdk_cairo_set_source_color(cr, fg);
207 cairo_set_line_width (cr, 1);
208 cairo_stroke (cr);
209 }
210 #else
211 static void
212 osm_gps_map_osd_shape(cairo_t *cr) {
213 cairo_set_source_rgb (cr, OSD_COLOR_BG);
214 cairo_fill_preserve (cr);
215 cairo_set_source_rgb (cr, OSD_COLOR);
216 cairo_set_line_width (cr, 1);
217 cairo_stroke (cr);
218 }
219 #endif
220
221 #ifndef OSD_NO_DPAD
222 static void
223 osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) {
224 /* move reference to dpad center */
225 x += D_RAD;
226 y += D_RAD;
227
228 const static gint offset[][3][2] = {
229 /* left arrow/triangle */
230 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
231 /* right arrow/triangle */
232 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
233 /* top arrow/triangle */
234 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
235 /* bottom arrow/triangle */
236 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
237 };
238
239 int i;
240 for(i=0;i<4;i++) {
241 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
242 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
243 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
244 }
245 }
246 #endif
247
248 #ifdef OSD_GPS_BUTTON
249 /* draw the satellite dish icon in the center of the dpad */
250 #define GPS_V0 (D_RAD/8)
251 #define GPS_V1 (D_RAD/10)
252 #define GPS_V2 (D_RAD/5)
253
254 /* draw a satellite receiver dish */
255 static void
256 osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) {
257 /* move reference to dpad center */
258 x += D_RAD;
259 y += D_RAD + GPS_V0;
260
261 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
262 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
263 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
264 cairo_close_path (cr);
265
266 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
267 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
268 cairo_close_path (cr);
269
270 x += GPS_V1;
271 cairo_move_to (cr, x, y-GPS_V2);
272 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
273 }
274 #endif
275
276 #define Z_LEN (2*Z_RAD/3)
277
278 static void
279 osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) {
280 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
281 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
282
283 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
284 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
285 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
286 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
287 }
288
289 #ifndef OSD_COLOR
290 /* if no color has been specified we just use the gdks default colors */
291 static void
292 osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled,
293 GdkColor *fg, GdkColor *disabled) {
294 if(enabled) gdk_cairo_set_source_color(cr, fg);
295 else gdk_cairo_set_source_color(cr, disabled);
296 cairo_set_line_width (cr, width);
297 cairo_stroke (cr);
298 }
299 #else
300 static void
301 osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) {
302 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
303 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
304 cairo_set_line_width (cr, width);
305 cairo_stroke (cr);
306 }
307 #endif
308
309 #ifdef OSD_SHADOW_ENABLE
310 static void
311 osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
312 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
313 cairo_set_line_width (cr, width);
314 cairo_stroke (cr);
315 }
316 #endif
317
318 static void
319 osm_gps_map_osd_render(osm_gps_map_osd_t *osd) {
320 osd_priv_t *priv = (osd_priv_t*)osd->priv;
321
322 /* first fill with transparency */
323 cairo_t *cr = cairo_create(priv->overlay);
324 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
325 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
326 cairo_paint(cr);
327
328 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
329
330 /* --------- draw zoom and dpad shape shadow ----------- */
331 gint x = 0, y = 0;
332
333 #ifdef OSD_SHADOW_ENABLE
334 osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
335 osm_gps_map_osd_shape_shadow(cr);
336 #ifndef OSD_NO_DPAD
337 osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
338 osm_gps_map_osd_shape_shadow(cr);
339 #endif
340 #endif
341
342 /* --------- draw zoom and dpad shape ----------- */
343
344 osm_gps_map_osd_zoom_shape(cr, x, y);
345 #ifndef OSD_COLOR
346 osm_gps_map_osd_shape(cr, &priv->bg, &priv->fg);
347 #else
348 osm_gps_map_osd_shape(cr);
349 #endif
350 #ifndef OSD_NO_DPAD
351 osm_gps_map_osd_dpad_shape(cr, x, y);
352 #ifndef OSD_COLOR
353 osm_gps_map_osd_shape(cr, &priv->bg, &priv->fg);
354 #else
355 osm_gps_map_osd_shape(cr);
356 #endif
357 #endif
358
359 /* --------- draw zoom and dpad labels --------- */
360
361 #ifdef OSD_SHADOW_ENABLE
362 osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
363 #ifndef OSD_NO_DPAD
364 osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
365 #endif
366 osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE);
367 #ifdef OSD_GPS_BUTTON
368 osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
369 osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
370 #endif
371 #endif
372
373 osm_gps_map_osd_zoom_labels(cr, x, y);
374 #ifndef OSD_NO_DPAD
375 osm_gps_map_osd_dpad_labels(cr, x, y);
376 #endif
377 #ifndef OSD_COLOR
378 osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE, &priv->fg, &priv->da);
379 #else
380 osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE);
381 #endif
382 #ifdef OSD_GPS_BUTTON
383 osm_gps_map_osd_dpad_gps(cr, x, y);
384 #ifndef OSD_COLOR
385 osm_gps_map_osd_labels(cr, Z_RAD/6, osd->cb != NULL, &priv->fg, &priv->da);
386 #else
387 osm_gps_map_osd_labels(cr, Z_RAD/6, osd->cb != NULL);
388 #endif
389 #endif
390
391 cairo_destroy(cr);
392 }
393
394 static void
395 osm_gps_map_osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
396 {
397 osd_priv_t *priv = (osd_priv_t*)osd->priv;
398
399 /* OSD itself uses some off-screen rendering, so check if the */
400 /* offscreen buffer is present and create it if not */
401 if(!priv->overlay) {
402 /* create overlay ... */
403 priv->overlay =
404 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H);
405 /* ... and render it */
406 osm_gps_map_osd_render(osd);
407 }
408
409 // now draw this onto the original context
410 cairo_t *cr = gdk_cairo_create(drawable);
411 cairo_set_source_surface(cr, priv->overlay, OSD_X, OSD_Y);
412 cairo_paint(cr);
413 cairo_destroy(cr);
414 }
415
416 static void
417 osm_gps_map_osd_free(osm_gps_map_osd_t *osd)
418 {
419 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
420
421 if (priv->overlay)
422 cairo_surface_destroy(priv->overlay);
423
424 g_free(priv);
425 }
426
427 static osm_gps_map_osd_t osd_classic = {
428 .draw = osm_gps_map_osd_draw,
429 .check = osm_gps_map_osd_check,
430 .render = osm_gps_map_osd_render,
431 .free = osm_gps_map_osd_free,
432
433 .cb = NULL,
434 .data = NULL,
435
436 .priv = NULL
437 };
438
439 /* this is the only function that's externally visible */
440 void
441 osm_gps_map_osd_classic_init(OsmGpsMap *map)
442 {
443 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
444
445 osd_classic.priv = priv;
446
447 /* extract style info from the widget */
448 priv->bg = GTK_WIDGET(map)->style->bg[GTK_STATE_NORMAL];
449 priv->fg = GTK_WIDGET(map)->style->fg[GTK_STATE_NORMAL];
450 priv->da = GTK_WIDGET(map)->style->fg[GTK_STATE_INSENSITIVE];
451
452 osm_gps_map_register_osd(map, &osd_classic);
453 }
454
455 #ifdef OSD_GPS_BUTTON
456 /* below are osd specific functions which aren't used by osm-gps-map */
457 /* but instead are to be used by the main application */
458 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
459 gpointer data) {
460 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
461
462 g_return_if_fail (osd);
463
464 osd->cb = cb;
465 osd->data = data;
466
467 /* this may have changed the state of the gps button */
468 /* we thus re-render the overlay */
469 osd->render(osd);
470
471 osm_gps_map_redraw(map);
472 }
473 #endif