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

Parent Directory Parent Directory | Revision Log Revision Log


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