Parent Directory | Revision Log
GPS focus enable, portrait support and some attribute icon work
1 | /* |
2 | * Copyright (C) 2009 Till Harbaum <till@harbaum.org>. |
3 | * |
4 | * This file is part of Maep. |
5 | * |
6 | * Maep is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * Maep is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with Maep. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | /* |
21 | * This version focusses on low power consumption. This means that |
22 | * a component can register for GPS callbacks for certain events |
23 | * (e.g. position change, altitude change, track change ...) and it |
24 | * will only get notified if one of these changes. Also it's been |
25 | * verified that the change in question exceed a certain limit. |
26 | */ |
27 | |
28 | #include <stdio.h> |
29 | #include <string.h> |
30 | #include <math.h> |
31 | #include <stdlib.h> |
32 | |
33 | #ifdef USE_MAEMO |
34 | #ifdef ENABLE_GPSBT |
35 | #include <gpsbt.h> |
36 | #include <gpsmgr.h> |
37 | #endif |
38 | #include <errno.h> |
39 | #else |
40 | #ifdef USE_LIBGPS |
41 | #include <gps.h> |
42 | #include <locale.h> |
43 | #endif |
44 | #endif |
45 | |
46 | #include "gps.h" |
47 | |
48 | static void gps_unregister_all(gps_state_t *gps_state); |
49 | |
50 | /* limits below which a fix is considered different */ |
51 | #define TRACK_PRECISION (1.0) // one degree |
52 | #define POS_PRECISION (1.0/60000.0) // 0.001 minute |
53 | #define EPH_PRECISION (2.0) // 2 meters |
54 | #define ALTITUDE_PRECISION (1.0) // 1 meters |
55 | |
56 | static gboolean |
57 | track_differs(struct gps_t *fix1, struct gps_t *fix2) { |
58 | return( fabsf(fix1->track - fix2->track) >= TRACK_PRECISION); |
59 | } |
60 | |
61 | static gboolean |
62 | latlon_differs(struct gps_t *fix1, struct gps_t *fix2) { |
63 | if( fabsf(fix1->latitude - fix2->latitude) >= POS_PRECISION) |
64 | return TRUE; |
65 | |
66 | if( fabsf(fix1->longitude - fix2->longitude) >= POS_PRECISION) |
67 | return TRUE; |
68 | |
69 | return FALSE; |
70 | } |
71 | |
72 | static gboolean |
73 | eph_differs(struct gps_t *fix1, struct gps_t *fix2) { |
74 | return( fabsf(fix1->eph - fix2->eph) >= EPH_PRECISION); |
75 | } |
76 | |
77 | static gboolean |
78 | altitude_differs(struct gps_t *fix1, struct gps_t *fix2) { |
79 | return( fabsf(fix1->altitude - fix2->altitude) >= ALTITUDE_PRECISION); |
80 | } |
81 | |
82 | static void gps_cb_func(gpointer data, gpointer user_data) { |
83 | gps_callback_t *callback = (gps_callback_t*)data; |
84 | gps_state_t *gps_state = (gps_state_t*)user_data; |
85 | |
86 | /* we can save some energy here if we make sure that */ |
87 | /* we really only wake up the main app whenever the */ |
88 | /* gps state changed in a way that the map needs to */ |
89 | /* be updated */ |
90 | |
91 | /* for all data check if its status has changed (data is available */ |
92 | /* or not) or if the data itself changed */ |
93 | |
94 | /* clear "changed" bits */ |
95 | gps_state->set &= ~CHANGED_MASK; |
96 | |
97 | /* check for changes in position */ |
98 | if(callback->mask & LATLON_CHANGED && |
99 | (((gps_state->set & FIX_LATLON_SET) != |
100 | (gps_state->last.set & FIX_LATLON_SET)) || |
101 | ((gps_state->set & FIX_LATLON_SET) && |
102 | latlon_differs(&gps_state->last.fix, &gps_state->fix)))) |
103 | gps_state->set |= LATLON_CHANGED; |
104 | |
105 | /* check for changes in eph */ |
106 | if(callback->mask & HERR_CHANGED && |
107 | (((gps_state->set & FIX_HERR_SET) != |
108 | (gps_state->last.set & FIX_HERR_SET)) || |
109 | ((gps_state->set & FIX_HERR_SET) && |
110 | eph_differs(&gps_state->last.fix, &gps_state->fix)))) |
111 | gps_state->set |= HERR_CHANGED; |
112 | |
113 | /* check for changes in track */ |
114 | if(callback->mask & TRACK_CHANGED && |
115 | (((gps_state->set & FIX_TRACK_SET) != |
116 | (gps_state->last.set & FIX_TRACK_SET)) || |
117 | ((gps_state->set & FIX_TRACK_SET) && |
118 | track_differs(&gps_state->last.fix, &gps_state->fix)))) |
119 | gps_state->set |= TRACK_CHANGED; |
120 | |
121 | /* check for changes in altitude */ |
122 | if(callback->mask & ALTITUDE_CHANGED && |
123 | (((gps_state->set & FIX_ALTITUDE_SET) != |
124 | (gps_state->last.set & FIX_ALTITUDE_SET)) || |
125 | ((gps_state->set & FIX_ALTITUDE_SET) && |
126 | altitude_differs(&gps_state->last.fix, &gps_state->fix)))) |
127 | gps_state->set |= ALTITUDE_CHANGED; |
128 | |
129 | if(gps_state->set & CHANGED_MASK) |
130 | callback->cb(gps_state->set, &gps_state->fix, callback->data); |
131 | } |
132 | |
133 | #ifndef ENABLE_LIBLOCATION |
134 | |
135 | static gboolean gps_notify(gpointer data) { |
136 | gps_state_t *gps_state = (gps_state_t*)data; |
137 | |
138 | if(gps_state->callbacks) { |
139 | g_mutex_lock(gps_state->mutex); |
140 | |
141 | /* tell all clients */ |
142 | g_slist_foreach(gps_state->callbacks, gps_cb_func, gps_state); |
143 | |
144 | g_mutex_unlock(gps_state->mutex); |
145 | } |
146 | |
147 | /* remember last state reported */ |
148 | gps_state->last.set = gps_state->set; |
149 | gps_state->last.fix = gps_state->fix; |
150 | |
151 | return FALSE; |
152 | } |
153 | |
154 | #ifndef USE_LIBGPS |
155 | |
156 | /* maybe user configurable later on ... */ |
157 | #define GPSD_HOST "127.0.0.1" |
158 | #define GPSD_PORT 2947 |
159 | |
160 | static int gps_connect(gps_state_t *gps_state) { |
161 | GnomeVFSResult vfs_result; |
162 | |
163 | #ifdef USE_MAEMO |
164 | char errstr[256] = ""; |
165 | |
166 | if(!gps_state) { |
167 | printf("No gps state\n"); |
168 | return -1; |
169 | } |
170 | |
171 | /* We need to start gpsd (via gpsbt) first. */ |
172 | memset(&gps_state->context, 0, sizeof(gpsbt_t)); |
173 | errno = 0; |
174 | |
175 | if(gpsbt_start(NULL, 0, 0, 0, errstr, sizeof(errstr), |
176 | 0, &gps_state->context) < 0) { |
177 | printf("gps: Error connecting to GPS receiver: (%d) %s (%s)\n", |
178 | errno, strerror(errno), errstr); |
179 | } |
180 | #endif |
181 | |
182 | /************** from here down pure gnome/gtk/gpsd ********************/ |
183 | |
184 | /* try to connect to gpsd */ |
185 | /* Create a socket to interact with GPSD. */ |
186 | |
187 | int retries = 5; |
188 | while(retries && |
189 | (GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create( |
190 | &gps_state->iconn, GPSD_HOST, GPSD_PORT, NULL)))) { |
191 | printf("gps: Error creating connection to GPSD, retrying ...\n"); |
192 | |
193 | retries--; |
194 | sleep(1); |
195 | } |
196 | |
197 | if(!retries) { |
198 | printf("gps: Finally failed ...\n"); |
199 | return -1; |
200 | } |
201 | |
202 | retries = 5; |
203 | while(retries && ((gps_state->socket = |
204 | gnome_vfs_inet_connection_to_socket(gps_state->iconn)) == NULL)) { |
205 | printf("gps: Error connecting GPSD socket, retrying ...\n"); |
206 | |
207 | retries--; |
208 | sleep(1); |
209 | } |
210 | |
211 | if(!retries) { |
212 | printf("gps: Finally failed ...\n"); |
213 | return -1; |
214 | } |
215 | |
216 | GTimeVal timeout = { 10, 0 }; |
217 | if(GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout( |
218 | gps_state->socket, &timeout, NULL))) { |
219 | printf("gps: Error setting GPSD timeout\n"); |
220 | return -1; |
221 | } |
222 | |
223 | printf("gps: GPSD connected ...\n"); |
224 | |
225 | return 0; |
226 | } |
227 | |
228 | static double parse_double(char *val) { |
229 | if(val[0] == '?') return NAN; |
230 | return g_ascii_strtod(val, NULL); |
231 | } |
232 | |
233 | /* unpack a daemon response into a status structure */ |
234 | static void gps_unpack(char *buf, gps_state_t *gps_state) { |
235 | char *ns, *sp, *tp; |
236 | int j; |
237 | |
238 | for(ns = buf; ns; ns = strstr(ns+1, "GPSD")) { |
239 | if(strncmp(ns, "GPSD", 4) == 0) { |
240 | /* the following should execute each time we have a good next sp */ |
241 | for (sp = ns + 5; *sp != '\0'; sp = tp+1) { |
242 | tp = sp + strcspn(sp, ",\r\n"); |
243 | if (*tp == '\0') tp--; |
244 | else *tp = '\0'; |
245 | |
246 | switch (*sp) { |
247 | case 'O': |
248 | if (sp[2] == '?') { |
249 | gps_state->fix.latitude = NAN; |
250 | gps_state->fix.longitude = NAN; |
251 | gps_state->fix.altitude = NAN; |
252 | gps_state->fix.track = NAN; |
253 | gps_state->fix.eph = NAN; |
254 | } else { |
255 | char tag[MAXTAGLEN+1], alt[20]; |
256 | char eph[20], track[20],speed[20]; |
257 | char lat[20], lon[20]; |
258 | if(sscanf(sp+2, |
259 | "%8s %*s %*s %19s %19s " |
260 | "%19s %19s %*s %19s %19s %*s " |
261 | "%*s %*s %*s %*s", |
262 | tag, lat, lon, |
263 | alt, eph, track, speed) == 7) { |
264 | |
265 | gps_state->fix.latitude = parse_double(lat); |
266 | gps_state->fix.longitude = parse_double(lon); |
267 | gps_state->fix.altitude = parse_double(alt); |
268 | gps_state->fix.eph = parse_double(eph); |
269 | gps_state->fix.track = parse_double(track); |
270 | |
271 | gps_state->set |= FIX_LATLON_SET; |
272 | |
273 | if (!isnan(gps_state->fix.eph)) |
274 | gps_state->set |= FIX_HERR_SET; |
275 | |
276 | if (!isnan(gps_state->fix.track)) |
277 | gps_state->set |= FIX_TRACK_SET; |
278 | |
279 | if(!isnan(gps_state->fix.altitude)) |
280 | gps_state->set |= FIX_ALTITUDE_SET; |
281 | } |
282 | } |
283 | break; |
284 | |
285 | case 'Y': |
286 | gps_state->fix.sat_num = 0; |
287 | |
288 | if (sp[2] != '?') { |
289 | (void)sscanf(sp, "Y=%*s %*s %d ", &gps_state->fix.sat_num); |
290 | |
291 | /* clear all slots */ |
292 | for (j = 0; j < gps_state->fix.sat_num; j++) |
293 | gps_state->fix.sat_data[j].prn = |
294 | gps_state->fix.sat_data[j].ss = |
295 | gps_state->fix.sat_data[j].used = 0; |
296 | |
297 | for (j = 0; j < gps_state->fix.sat_num; j++) { |
298 | if ((sp != NULL) && ((sp = strchr(sp, ':')) != NULL)) { |
299 | sp++; |
300 | (void)sscanf(sp, "%d %*d %*d %d %d", |
301 | &gps_state->fix.sat_data[j].prn, |
302 | &gps_state->fix.sat_data[j].ss, |
303 | &gps_state->fix.sat_data[j].used); |
304 | } |
305 | } |
306 | } |
307 | gps_state->set |= FIX_SATELLITE_SET; |
308 | break; |
309 | } |
310 | } |
311 | } |
312 | } |
313 | } |
314 | |
315 | static gpointer gps_thread(gpointer data) { |
316 | gps_state_t *gps_state = (gps_state_t*)data; |
317 | |
318 | GnomeVFSFileSize bytes_read; |
319 | GnomeVFSResult vfs_result; |
320 | char str[512]; |
321 | |
322 | gps_state->set = 0; |
323 | |
324 | gboolean connected = FALSE; |
325 | |
326 | int cnt=1000; |
327 | |
328 | const char *msg_pos = "o\r\n"; /* pos request */ |
329 | const char *msg_sat = "y\r\n"; /* sat request */ |
330 | |
331 | while(1) { |
332 | /* just lock and unlock the control mutex. This stops the thread */ |
333 | /* while the main process locks this mutex */ |
334 | g_mutex_lock(gps_state->control_mutex); |
335 | g_mutex_unlock(gps_state->control_mutex); |
336 | g_mutex_lock(gps_state->global_mutex); |
337 | g_mutex_unlock(gps_state->global_mutex); |
338 | |
339 | if(!connected) { |
340 | printf("gps: trying to connect\n"); |
341 | |
342 | if(gps_connect(gps_state) < 0) |
343 | sleep(10); |
344 | else |
345 | connected = TRUE; |
346 | |
347 | } else { |
348 | const char *msg; |
349 | if(!cnt) msg = msg_sat; |
350 | else msg = msg_pos; |
351 | |
352 | if(GNOME_VFS_OK == |
353 | (vfs_result = gnome_vfs_socket_write(gps_state->socket, |
354 | msg, strlen(msg)+1, &bytes_read, NULL))) { |
355 | |
356 | /* update every second, wait here to make sure a complete */ |
357 | /* reply is received */ |
358 | if(cnt <= 1) usleep(500000); |
359 | else sleep(1); |
360 | |
361 | if(bytes_read == (strlen(msg)+1)) { |
362 | vfs_result = gnome_vfs_socket_read(gps_state->socket, |
363 | str, sizeof(str)-1, &bytes_read, NULL); |
364 | |
365 | if(vfs_result == GNOME_VFS_OK) { |
366 | str[bytes_read] = 0; |
367 | |
368 | g_mutex_lock(gps_state->mutex); |
369 | |
370 | /* assume we could't parse anything ... */ |
371 | if(!cnt) gps_state->set &= ~FIX_SATELLITE_SET; |
372 | else gps_state->set &= |
373 | ~(FIX_LATLON_SET|FIX_HERR_SET|FIX_ALTITUDE_SET|FIX_TRACK_SET); |
374 | |
375 | gps_unpack(str, gps_state); |
376 | |
377 | /* notify applications of state if useful */ |
378 | g_idle_add(gps_notify, gps_state); |
379 | |
380 | g_mutex_unlock(gps_state->mutex); |
381 | } |
382 | } |
383 | |
384 | /* if we have clients requesting sat information return to */ |
385 | /* state 0 which will request them. Otherwise loop in states */ |
386 | /* 1 - 4 which don't request them */ |
387 | if(cnt++ >= 5) { |
388 | if(gps_state->sat_requests) cnt = 0; |
389 | else cnt = 1; |
390 | } |
391 | } |
392 | } |
393 | } |
394 | |
395 | printf("gps: thread ended???\n"); |
396 | return NULL; |
397 | } |
398 | |
399 | gps_state_t *gps_init(void) { |
400 | gps_state_t *gps_state = g_new0(gps_state_t, 1); |
401 | |
402 | /* start a new thread to listen to gpsd */ |
403 | gps_state->mutex = g_mutex_new(); |
404 | gps_state->control_mutex = g_mutex_new(); |
405 | gps_state->global_mutex = g_mutex_new(); |
406 | gps_state->thread_p = |
407 | g_thread_create(gps_thread, gps_state, FALSE, NULL); |
408 | |
409 | return gps_state; |
410 | } |
411 | |
412 | void gps_release(gps_state_t *gps_state) { |
413 | gps_unregister_all(gps_state); |
414 | |
415 | #ifdef USE_MAEMO |
416 | gpsbt_stop(&gps_state->context); |
417 | #endif |
418 | |
419 | g_free(gps_state); |
420 | } |
421 | |
422 | #else // USE_LIBGPS |
423 | |
424 | static gpointer gps_thread(gpointer data) { |
425 | gps_state_t *gps_state = (gps_state_t*)data; |
426 | |
427 | /* the following is required for libgps to be able to parse */ |
428 | /* the gps messages. Unfortunately this also affect the rest of */ |
429 | /* the program and the main thread */ |
430 | setlocale(LC_NUMERIC, "C"); |
431 | |
432 | while(1) { |
433 | /* just lock and unlock the control mutex. This stops the thread */ |
434 | /* while the main process locks this mutex */ |
435 | g_mutex_lock(gps_state->control_mutex); |
436 | g_mutex_unlock(gps_state->control_mutex); |
437 | |
438 | gps_poll(gps_state->data); |
439 | |
440 | g_mutex_lock(gps_state->mutex); |
441 | |
442 | /* assume we could't parse anything ... */ |
443 | gps_state->set = 0; |
444 | |
445 | if(gps_state->data->fix.mode >= MODE_2D) { |
446 | /* latlon valid */ |
447 | gps_state->set |= FIX_LATLON_SET; |
448 | gps_state->fix.latitude = gps_state->data->fix.latitude; |
449 | gps_state->fix.longitude = gps_state->data->fix.longitude; |
450 | |
451 | gps_state->fix.eph = |
452 | gps_state->data->fix.epy > gps_state->data->fix.epx ? |
453 | gps_state->data->fix.epy : gps_state->data->fix.epx; |
454 | if(!isnan(gps_state->fix.eph)) gps_state->set |= FIX_HERR_SET; |
455 | |
456 | gps_state->fix.track = gps_state->data->fix.track; |
457 | if(!isnan(gps_state->fix.track)) gps_state->set |= FIX_TRACK_SET; |
458 | } |
459 | |
460 | if(gps_state->data->fix.mode >= MODE_3D) { |
461 | /* altitude valid */ |
462 | gps_state->set |= FIX_ALTITUDE_SET; |
463 | gps_state->fix.altitude = gps_state->data->fix.altitude; |
464 | } else |
465 | gps_state->fix.altitude = NAN; |
466 | |
467 | #if MAXCHANNELS > GPS_MAXCHANNELS |
468 | #error "Channel number mismatch!" |
469 | #endif |
470 | |
471 | /* process sat info */ |
472 | if(gps_state->data->set | SATELLITE_SET) { |
473 | int i; |
474 | |
475 | gps_state->set |= FIX_SATELLITE_SET; |
476 | |
477 | gps_state->fix.sat_num = 0; |
478 | for(i=0;i<GPS_MAXCHANNELS;i++) |
479 | gps_state->fix.sat_data[i].used = 0; |
480 | |
481 | for(i=0;i<MAXCHANNELS;i++) { |
482 | if(gps_state->data->used[i]) { |
483 | gps_state->fix.sat_data[gps_state->fix.sat_num].used = 1; |
484 | gps_state->fix.sat_data[gps_state->fix.sat_num].prn = |
485 | gps_state->data->PRN[i]; |
486 | gps_state->fix.sat_data[gps_state->fix.sat_num].ss = |
487 | gps_state->data->ss[i]; |
488 | gps_state->fix.sat_num++; |
489 | } |
490 | } |
491 | |
492 | } |
493 | |
494 | /* notify applications of state if useful */ |
495 | g_idle_add(gps_notify, gps_state); |
496 | |
497 | g_mutex_unlock(gps_state->mutex); |
498 | |
499 | } |
500 | |
501 | printf("gps: thread ended???\n"); |
502 | return NULL; |
503 | } |
504 | |
505 | gps_state_t *gps_init(void) { |
506 | gps_state_t *gps_state = g_new0(gps_state_t, 1); |
507 | |
508 | gps_state->mutex = g_mutex_new(); |
509 | gps_state->control_mutex = g_mutex_new(); |
510 | |
511 | gps_state->data = gps_open("127.0.0.1", DEFAULT_GPSD_PORT ); |
512 | if(!gps_state->data) |
513 | perror("gps_open()"); |
514 | |
515 | (void)gps_stream(gps_state->data, WATCH_ENABLE, NULL); |
516 | |
517 | gps_state->thread_p = |
518 | g_thread_create(gps_thread, gps_state, FALSE, NULL); |
519 | |
520 | return gps_state; |
521 | } |
522 | |
523 | void gps_release(gps_state_t *gps_state) { |
524 | gps_unregister_all(gps_state); |
525 | gps_close(gps_state->data); |
526 | g_free(gps_state); |
527 | } |
528 | |
529 | #endif // USE_LIBGPS |
530 | |
531 | static void gps_background_enable(gps_state_t *gps_state, gboolean enable) { |
532 | printf("GPS: %sable background process\n", enable?"en":"dis"); |
533 | |
534 | /* start and stop gps thread by locking and unlocking the control mutex */ |
535 | if(enable) g_mutex_unlock(gps_state->control_mutex); |
536 | else g_mutex_lock(gps_state->control_mutex); |
537 | } |
538 | |
539 | void gps_enable(gps_state_t *gps_state, gboolean enable) { |
540 | /* just ignore request if we are already in that state */ |
541 | if(gps_state->stopped == !enable) return; |
542 | |
543 | if(!enable) g_mutex_lock(gps_state->global_mutex); |
544 | else g_mutex_unlock(gps_state->global_mutex); |
545 | |
546 | gps_state->stopped = !enable; |
547 | } |
548 | |
549 | |
550 | #else |
551 | |
552 | static void |
553 | location_changed(LocationGPSDevice *device, gps_state_t *gps_state) { |
554 | |
555 | gps_state->set = 0; |
556 | |
557 | if(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET) { |
558 | gps_state->set |= FIX_LATLON_SET | FIX_HERR_SET; |
559 | gps_state->fix.latitude = device->fix->latitude; |
560 | gps_state->fix.longitude = device->fix->longitude; |
561 | gps_state->fix.eph = device->fix->eph/100.0; // we want eph in meters |
562 | } |
563 | |
564 | if(device->fix->fields & LOCATION_GPS_DEVICE_ALTITUDE_SET) { |
565 | gps_state->set |= FIX_ALTITUDE_SET; |
566 | gps_state->fix.altitude = device->fix->altitude; |
567 | } else |
568 | gps_state->fix.altitude = NAN; |
569 | |
570 | if(device->fix->fields & LOCATION_GPS_DEVICE_TRACK_SET) { |
571 | gps_state->set |= FIX_TRACK_SET; |
572 | gps_state->fix.track = device->fix->track; |
573 | } |
574 | |
575 | /* tell all clients */ |
576 | g_slist_foreach(gps_state->callbacks, gps_cb_func, gps_state); |
577 | |
578 | /* remember last state reported */ |
579 | gps_state->last.set = gps_state->set; |
580 | gps_state->last.fix = gps_state->fix; |
581 | } |
582 | |
583 | gps_state_t *gps_init(void) { |
584 | gps_state_t *gps_state = g_new0(gps_state_t, 1); |
585 | |
586 | gps_state->device = g_object_new(LOCATION_TYPE_GPS_DEVICE, NULL); |
587 | if(!gps_state->device) { |
588 | printf("gps: Unable to connect to liblocation\n"); |
589 | g_free(gps_state); |
590 | return NULL; |
591 | } |
592 | |
593 | gps_state->idd_changed = |
594 | g_signal_connect(gps_state->device, "changed", |
595 | G_CALLBACK(location_changed), gps_state); |
596 | gps_state->connected = TRUE; |
597 | |
598 | gps_state->control = location_gpsd_control_get_default(); |
599 | |
600 | if(gps_state->control |
601 | #if MAEMO_VERSION_MAJOR < 5 |
602 | && gps_state->control->can_control |
603 | #endif |
604 | ) { |
605 | |
606 | printf("gps: Having control over GPSD and GPS is to be enabled, starting it\n"); |
607 | location_gpsd_control_start(gps_state->control); |
608 | } |
609 | return gps_state; |
610 | } |
611 | |
612 | void gps_release(gps_state_t *gps_state) { |
613 | gps_unregister_all(gps_state); |
614 | |
615 | if(gps_state->control |
616 | #if MAEMO_VERSION_MAJOR < 5 |
617 | && gps_state->control->can_control |
618 | #endif |
619 | ) { |
620 | printf("gps: Having control over GPSD, stopping it\n"); |
621 | location_gpsd_control_stop(gps_state->control); |
622 | } |
623 | |
624 | /* Disconnect signal */ |
625 | g_signal_handler_disconnect(gps_state->device, gps_state->idd_changed); |
626 | gps_state->connected = FALSE; |
627 | |
628 | g_free(gps_state); |
629 | } |
630 | |
631 | static void gps_control(gps_state_t *gps_state, |
632 | gboolean enable0, gboolean enable1) { |
633 | printf("GPS: control(stp: %d, bg: %d)\n", enable0, enable1); |
634 | |
635 | if(!gps_state->connected) { |
636 | /* both "enable" signals have to be true */ |
637 | if(enable0 && enable1) { |
638 | printf("GPS: connecting\n"); |
639 | gps_state->idd_changed = |
640 | g_signal_connect(gps_state->device, "changed", |
641 | G_CALLBACK(location_changed), gps_state); |
642 | gps_state->connected = TRUE; |
643 | } |
644 | } else { |
645 | /* at least one "enable" is false */ |
646 | if(!enable0 || !enable1) { |
647 | printf("GPS: disconnecting\n"); |
648 | /* Disconnect signal */ |
649 | g_signal_handler_disconnect(gps_state->device, gps_state->idd_changed); |
650 | gps_state->connected = FALSE; |
651 | } |
652 | } |
653 | } |
654 | |
655 | /* enable/disable due to all windows being in background */ |
656 | void gps_background_enable(gps_state_t *gps_state, gboolean enable) { |
657 | if(gps_state->backgrounded == !enable) return; |
658 | |
659 | gps_state->backgrounded = !enable; |
660 | gps_control(gps_state, !gps_state->stopped, !gps_state->backgrounded); |
661 | } |
662 | |
663 | /* enable/disable on app request */ |
664 | void gps_enable(gps_state_t *gps_state, gboolean enable) { |
665 | if(gps_state->stopped == !enable) return; |
666 | |
667 | gps_state->stopped = !enable; |
668 | gps_control(gps_state, !gps_state->stopped, !gps_state->backgrounded); |
669 | } |
670 | |
671 | #endif // USE_LIBLOCATION |
672 | |
673 | static gint compare(gconstpointer a, gconstpointer b) { |
674 | return ((gps_callback_t*)a)->cb != b; |
675 | } |
676 | |
677 | void gps_register_callback(gps_state_t *gps_state, int mask, |
678 | gps_cb cb, void *data) { |
679 | /* make sure the callback isn't already registered */ |
680 | GSList *list = g_slist_find_custom(gps_state->callbacks, cb, compare); |
681 | if(list) { |
682 | printf("GPS register: ignoring duplicate\n"); |
683 | return; |
684 | } |
685 | |
686 | gps_callback_t *callback = g_new0(gps_callback_t, 1); |
687 | |
688 | g_assert(mask & CHANGED_MASK); |
689 | g_assert(!(mask & ~CHANGED_MASK)); |
690 | |
691 | callback->mask = mask; |
692 | callback->cb = cb; |
693 | callback->data = data; |
694 | |
695 | #if !defined(ENABLE_LIBLOCATION) && !defined(USE_LIBGPS) |
696 | /* the gpsd solution needs to know the number of clients */ |
697 | /* requesting sat information */ |
698 | if(mask & SATELLITE_CHANGED) |
699 | gps_state->sat_requests++; |
700 | #endif |
701 | |
702 | gps_state->callbacks = g_slist_append(gps_state->callbacks, callback); |
703 | |
704 | if(g_slist_length(gps_state->callbacks) == 1) |
705 | gps_background_enable(gps_state, TRUE); |
706 | } |
707 | |
708 | void gps_unregister_callback(gps_state_t *gps_state, gps_cb cb) { |
709 | /* find callback in list */ |
710 | GSList *list = g_slist_find_custom(gps_state->callbacks, cb, compare); |
711 | g_assert(list); |
712 | |
713 | #if !defined(ENABLE_LIBLOCATION) && !defined(USE_LIBGPS) |
714 | /* the gpsd solution needs to know the number of clients */ |
715 | /* requesting sat information */ |
716 | gps_callback_t *cb_s = list->data; |
717 | if(cb_s->mask & SATELLITE_CHANGED) |
718 | gps_state->sat_requests--; |
719 | #endif |
720 | |
721 | /* and de-chain and free it */ |
722 | g_free(list->data); |
723 | gps_state->callbacks = g_slist_remove(gps_state->callbacks, list->data); |
724 | |
725 | if(g_slist_length(gps_state->callbacks) == 0) |
726 | gps_background_enable(gps_state, FALSE); |
727 | } |
728 | |
729 | static void gps_unregister_all(gps_state_t *gps_state) { |
730 | g_slist_foreach(gps_state->callbacks, (GFunc)g_free, NULL); |
731 | g_slist_free(gps_state->callbacks); |
732 | } |