Parent Directory | Revision Log
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 |