1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 Grig: Gtk+ user interface for the Hamradio Control Libraries.
4
5 Copyright (C) 2001-2007 Alexandru Csete.
6
7 Authors: Alexandru Csete <oz9aec@gmail.com>
8
9 Comments, questions and bugreports should be submitted via
10 http://sourceforge.net/projects/groundstation/
11 More details can be found at the project home page:
12
13 http://groundstation.sourceforge.net/
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, visit http://www.fsf.org/
27
28
29 */
30 /** \file main.c
31 * \ingroup main
32 * \bief Main program file.
33 *
34 * Add some more text.
35 *
36 * \bug What do we do if we don't have getopt.h? Change to glib getopt in 2.6
37 *
38 * \bug Debug level is not read from hamlib. Original debug level is
39 * overwritten if used on rpcrig.
40 */
41 #include <stdlib.h>
42 #include <signal.h>
43 #include <gtk/gtk.h>
44 #include <glib/gi18n.h>
45 #include <hamlib/rig.h>
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif
49 #ifdef HAVE_GETOPT_H
50 # include <getopt.h>
51 #endif
52 #include "compat.h"
53 #include "grig-config.h"
54 #include "rig-gui.h"
55 #include "grig-debug.h"
56 #include "rig-gui-message-window.h"
57 #include "rig-daemon.h"
58 #include "rig-data.h"
59 #include "rig-selector.h"
60 #include "key-press-handler.h"
61
62
63
64 /** \brief Main GUI application widget */
65 GtkWidget *grigapp;
66
67
68 /* command line arguments */
69 /* FIXME: These need not be global!
70 not in this baseline anyway.
71 */
72 static gint rignum = 0; /*!< Flag indicating which radio to use.*/
73 static gchar *rigfile = NULL; /*!< The port where the rig is atached. */
74 static gchar *civaddr = NULL; /*!< CI-V address for ICOM rig. */
75 static gchar *rigconf = NULL; /*!< Configuration parameter. */
76 static gint rigspeed = 0; /*!< Optional serial speed. */
77 static gboolean listrigs = FALSE; /*!< List supported radios and exit. */
78 gint debug = RIG_DEBUG_NONE; /*!< Hamlib debug level. Note: not static since menubar.c needs access. */
79 static gint delay = 0; /*!< Command delay. */
80 static gboolean nothread = FALSE; /*!< Don't use threads, just a regular gtk-timeout. */
81 static gboolean pstat = FALSE; /*!< Enable power status button. */
82 static gboolean ptt = FALSE; /*!< Enable PTT button. */
83 static gboolean version = FALSE; /*!< Show version and exit. */
84 static gboolean help = FALSE; /*!< Show help and exit. */
85 //static gchar *rigcfg = NULL; /*!< .radio file name. */
86
87 /* group those which take no arg */
88 /** \brief Short options. */
89 #define SHORT_OPTIONS "m:r:s:c:C:d:D:nlpPhv"
90
91 /** \brief Table of command line options. */
92 static struct option long_options[] =
93 {
94 {"model", 1, 0, 'm'},
95 {"rig-file", 1, 0, 'r'},
96 {"speed", 1, 0, 's'},
97 {"civaddr", 1, 0, 'c'},
98 {"set-conf", 1, 0, 'C'},
99 {"debug", 1, 0, 'd'},
100 {"delay", 1, 0, 'D'},
101 {"nothread", 0, 0, 'n'},
102 {"list", 0, 0, 'l'},
103 {"enable-ptt", 0, 0, 'p'},
104 {"enable-pwr", 0, 0, 'P'},
105 {"help", 0, 0, 'h'},
106 {"version", 0, 0, 'v'},
107 {NULL, 0, 0, 0}
108 };
109
110
111 /** \brief Radio info to be used by list-rigs. */
112 typedef struct {
113 gint id; /*!< Model ID. */
114 gchar *mfg; /*!< Manufacturer name (eg. KENWOOD). */
115 gchar *model; /*!< Radio model (eg. TS-440). */
116 gchar *version; /*!< Driver version (eg. 0.3.2) */
117 gint status; /*!< Driver status (0..5 use rig_strstatus). */
118 } grig_rig_info_t;
119
120
121 /* Private function prototypes */
122 static void grig_list_rigs (void);
123 static GtkWidget *grig_app_create (gint);
124 static gint grig_app_delete (GtkWidget *, GdkEvent *, gpointer);
125 static void grig_app_destroy (GtkWidget *, gpointer);
126 static void grig_show_help (void);
127 static void grig_show_version (void);
128 static gint grig_list_add (const struct rig_caps *, void *);
129 static gint grig_list_compare (gconstpointer, gconstpointer);
130 static void grig_sig_handler (int sig);
131
132
133 /** \bief Main program execution entry.
134 * \param argc The number o command line arguments.
135 * \param argv List of command line arguments.
136 * \return Execution status (non-zero mean error ocurred).
137 *
138 * Some description.
139 *
140 * \bug Add more text.
141 *
142 */
143 int
main(int argc,char * argv[])144 main (int argc, char *argv[])
145 {
146 gchar *fname;
147
148 /* Initialize NLS support */
149 #ifdef ENABLE_NLS
150 bindtextdomain (PACKAGE, PACKAGE_LOCALE_DIR);
151 bind_textdomain_codeset (PACKAGE, "UTF-8");
152 textdomain (PACKAGE);
153 #endif
154
155 gtk_set_locale ();
156 gtk_init (&argc, &argv);
157 /* setlocale (LCNUMERIC, "C"); */
158
159
160 /* check whether installation is complete
161 by looking for some pixmps. This way we
162 can avoid surprises later on, when exit
163 is not an option anymore.
164 */
165 //fname = g_strconcat (PACKAGE_PIXMAPS_DIR, G_DIR_SEPARATOR_S, "smeter.png", NULL);
166 fname = pixmap_file_name ("smeter.png");
167 if (!g_file_test (fname, G_FILE_TEST_EXISTS)) {
168
169 g_print ("\n\n");
170 g_print (_("Grig can not find some necessary data files.\n"));
171 g_print (_("This usually means that your installation is incomplete.\n"));
172 g_print (_("Sorry... but I can not continue..."));
173 g_print ("\n\n");
174 g_print ("%s\n\n",fname);
175 return 1;
176 }
177
178 g_free (fname);
179
180
181 #if !GLIB_CHECK_VERSION(2,32,0)
182 /* initialize threads; according to glib docs, this call will terminate
183 the program if threads are not supported... then why doesn''t it work
184 on FreeBSD?
185 */
186 if (!g_thread_supported ())
187 g_thread_init (NULL);
188 #endif
189
190
191 /* decode command line arguments; this part of the code only sets the
192 global flags and variables, whereafter we check each variable in
193 descending priority order. This way it is easy to exit the program
194 in case of -v -h and such.
195 */
196 while(1) {
197 int c;
198 int option_index = 0;
199
200 /* get next option */
201 c = getopt_long (argc, argv, SHORT_OPTIONS,
202 long_options, &option_index);
203
204 if (c == -1)
205 break;
206
207 switch (c) {
208
209 /* set rig model*/
210 case 'm':
211 if (!optarg) {
212 help = TRUE;
213 }
214 else {
215 rignum = atoi (optarg);
216 }
217 break;
218
219 /* set rig port */
220 case 'r':
221 if (!optarg) {
222 help = TRUE;
223 }
224 else {
225 rigfile = optarg;
226 }
227 break;
228
229 /* set serial speed */
230 case 's':
231 if (!optarg) {
232 help = TRUE;
233 }
234 else {
235 rigspeed = atoi (optarg);
236 }
237 break;
238
239 /* set CIV address */
240 case 'c':
241 if (!optarg) {
242 help = TRUE;
243 }
244 else {
245 civaddr = optarg;
246 }
247 break;
248
249 /* set configuration parameter */
250 case 'C':
251 if (!optarg) {
252 help = TRUE;
253 }
254 else {
255 rigconf = optarg;
256 }
257 break;
258
259 /* list supported radios */
260 case 'l':
261 listrigs = TRUE;
262 break;
263
264 /* set debug level */
265 case 'd':
266 if (!optarg) {
267 help = TRUE;
268 }
269 else {
270 debug = atoi (optarg);
271 }
272 break;
273
274 /* command delay */
275 case 'D':
276 if (!optarg) {
277 help = TRUE;
278 }
279 else {
280 delay = atoi (optarg);
281 }
282 break;
283
284 /* no threads */
285 case 'n':
286 nothread = TRUE;
287 break;
288
289 /* enable PTT button */
290 case 'p':
291 ptt = TRUE;
292 break;
293
294 /* enable POWER button */
295 case 'P':
296 pstat = TRUE;
297 break;
298
299 /* show help */
300 case 'h':
301 help = TRUE;
302 break;
303
304 /* show version */
305 case 'v':
306 version = TRUE;
307 break;
308
309 /* unknown option: show usage */
310 default:
311 help = TRUE;
312 break;
313 }
314 }
315
316
317 /* check command line option flags in decreasing
318 priority.
319 */
320 if (help) {
321 grig_show_help ();
322 return 0;
323 }
324
325 if (version) {
326 grig_show_version ();
327 return 0;
328 }
329
330 if (listrigs) {
331 grig_list_rigs ();
332 return 0;
333 }
334
335
336 /* we set hamlib debug level to TRACE while we fire up the daemon;
337 it will be reset when we create the menubar
338 */
339 grig_debug_set_level (RIG_DEBUG_TRACE);
340
341 /* initialise debug handler */
342 grig_debug_init (NULL);
343
344 /* check configuration */
345 if (!grig_config_check ()) {
346
347 g_print ("\n\n");
348 g_print (_("Grig configuration check failed!\n"));
349 g_print (_("This usually means that your configuration is broken.\n"));
350 g_print (_("Sorry... but I can not continue..."));
351 g_print ("\n\n");
352 g_print (_("Proposed solutions:\n"));
353 return 1;
354
355 }
356
357 /* At this point, configuration is OK. */
358
359
360 /* 1. prio: .grc file */
361
362 /* 2. prio: -m or --model */
363
364 /* 3. prio: run rig-selector */
365 //g_print ("SELECT: %s\n", rig_selector_execute ());
366
367 /* launch rig daemon and pass the relevant
368 command line options
369 */
370 if (rig_daemon_start (rignum,
371 rigfile,
372 rigspeed,
373 civaddr,
374 rigconf,
375 delay,
376 nothread,
377 ptt,
378 pstat))
379 {
380
381 return 1;
382 }
383
384 /* install key press event handler */
385 key_press_handler_init ();
386
387 /* check whether the debug level is something meaningful
388 (it could be set to something junk by user); if yes, set
389 debuglevel, otherwise use RIG_DEBUG_WARN.
390 */
391 if ((debug >= RIG_DEBUG_NONE) && (debug <= RIG_DEBUG_TRACE)) {
392 grig_debug_set_level (debug);
393 }
394 else {
395 grig_debug_set_level (RIG_DEBUG_WARN);
396 }
397
398 /* create application */
399 grigapp = grig_app_create (rignum);
400
401 /* add contents */
402 gtk_container_add (GTK_CONTAINER (grigapp), rig_gui_create ());
403 gtk_widget_show (grigapp);
404
405 gtk_main ();
406
407
408 return 0;
409 }
410
411
412
413
414
415 /** \brief Create and initialize main application window.
416 * \param rignum The index of the radio wich is controled by the app
417 * \return A new GtkWindow widget.
418 *
419 * This function creates and initializes a new GtkWindow which can be used
420 * by the main application to pack the rig controls in.
421 *
422 * \note This function creates no contents; that part is done by the
423 * rig_gui.c package.
424 */
425 static GtkWidget *
grig_app_create(gint rignum)426 grig_app_create (gint rignum)
427 {
428 GtkWidget *app; /* The main application */
429 gchar *title; /* the window title */
430 gchar *brand;
431 gchar *model;
432 gchar *icon;
433
434
435 brand = rig_daemon_get_brand ();
436 model = rig_daemon_get_model ();
437
438 /* construct title */
439 title = g_strdup_printf (_("GRIG: %s %s"), brand, model);
440
441 /* window icon file */
442 //icon = g_strconcat (PACKAGE_PIXMAPS_DIR, G_DIR_SEPARATOR_S, "ic910.png", NULL);
443 icon = pixmap_file_name ("ic910.png");
444
445 /* create application */
446 app = gtk_window_new (GTK_WINDOW_TOPLEVEL);
447 gtk_window_set_title (GTK_WINDOW (app), title);
448 gtk_window_set_icon_from_file (GTK_WINDOW (app), icon, NULL);
449
450 g_free (title);
451 g_free (brand);
452 g_free (model);
453 g_free (icon);
454
455 /* connect delete and destroy signals */
456 g_signal_connect (G_OBJECT (app), "delete_event",
457 G_CALLBACK (grig_app_delete), NULL);
458 g_signal_connect (G_OBJECT (app), "destroy",
459 G_CALLBACK (grig_app_destroy), NULL);
460
461 /* register UNIX signals as well so that we
462 have a chance to clean up hamlib.
463 */
464 signal (SIGTERM, (void *) grig_sig_handler);
465 signal (SIGINT, (void *) grig_sig_handler);
466 signal (SIGABRT, (void *) grig_sig_handler);
467
468 return app;
469 }
470
471
472 /** \brief Handle terminate signals.
473 * \param sig The signal that has been received.
474 *
475 * This function is used to handle termination signals received by the program.
476 * The currently caught signals are SIGTERM, SIGINT and SIGABRT. When one of
477 * these signals is received, the function sends an error message to hamlib and
478 * tries to make a clean exit.
479 */
grig_sig_handler(int sig)480 static void grig_sig_handler (int sig)
481 {
482
483 grig_debug_local (RIG_DEBUG_ERR,
484 _("Received signal %d\n"\
485 "Trying clean exit..."),
486 sig);
487
488 gtk_widget_destroy (grigapp);
489 }
490
491
492 /** \brief Handle delete events.
493 * \param widget The widget which received the delete event signal.
494 * \param event Data structure describing the event.
495 * \param data User data (NULL).
496 * \param return Always FALSE to indicate that the app should be destroyed.
497 *
498 * This function handles the delete event received by the main application
499 * window (eg. when the window is closed by the WM). This function simply
500 * returns FALSE indicating that the main application window should be
501 * destroyed by emiting the destroy signal.
502 *
503 */
504 static gint
grig_app_delete(GtkWidget * widget,GdkEvent * event,gpointer data)505 grig_app_delete (GtkWidget *widget,
506 GdkEvent *event,
507 gpointer data)
508 {
509
510 /* return FALSE so that Gtk+ will emit the destroy signal */
511 return FALSE;
512 }
513
514
515
516 /** \brief Handle destroy signals.
517 * \param widget The widget which received the signal.
518 * \param data User data (NULL).
519 *
520 * This function is called when the main application window receives the
521 * destroy signal, ie. it is destroyed. This function signals all daemons
522 * and other threads to stop and exits the Gtk+ main loop.
523 *
524 */
525 static void
grig_app_destroy(GtkWidget * widget,gpointer data)526 grig_app_destroy (GtkWidget *widget,
527 gpointer data)
528 {
529
530 /* set debug level to TRACE */
531 grig_debug_set_level (RIG_DEBUG_TRACE);
532
533 /* remove key press event handler */
534 key_press_handler_close ();
535
536 /* stop daemons */
537 rig_daemon_stop ();
538
539 /* GUI timers are stopped automatically */
540
541 /* stop timeouts */
542
543 /* shut down debug handler */
544 grig_debug_close ();
545
546 /* exit Gtk+ */
547 gtk_main_quit ();
548 }
549
550
551
552 /** \brief Show help message.
553 *
554 * This function displays a brief help message for grig.
555 */
556 static void
grig_show_help()557 grig_show_help ()
558 {
559
560 g_print (_("Usage: grig [OPTION]...\n\n"));
561 g_print (_(" -m, --model=ID "\
562 "select radio model number; see --list\n"));
563 g_print (_(" -r, --rig-file=DEVICE "\
564 "set device of the radio, eg. /dev/ttyS0\n"));
565 g_print (_(" -s, --speed=BAUD "\
566 "set transfer rate (serial port only)\n"));
567 g_print (_(" -c, --civaddr=ID "\
568 "set CI-V address (decimal, ICOM only)\n"));
569 g_print (_(" -C, --set-conf=param=val "\
570 "set config parameter (same as in rigctl)\n"));
571 g_print (_(" -d, --debug=LEVEL "\
572 "set hamlib debug level (0..5)\n"));
573 g_print (_(" -D, --delay=val "\
574 "set delay between commands in msec\n"));
575 g_print (_(" -n, --nothread "\
576 "start daemon without using threads\n"));
577 g_print (_(" -l, --list "\
578 "list supported radios and exit\n"));
579 g_print (_(" -p, --enable-ptt "\
580 "enable PTT button\n"));
581 g_print (_(" -P, --enable-pwr "\
582 "enable POWER button\n"));
583 g_print (_(" -h, --help "\
584 "show this help message and exit\n"));
585 g_print (_(" -v, --version "\
586 "show version information and exit\n"));
587 g_print ("\n");
588 g_print (_("Example:"));
589 g_print ("\n");
590 g_print (_("Start grig using YAESU FT-990 connected to the first "\
591 "serial port, using 4800 baud and debug level set to "\
592 "warning:"));
593 g_print ("\n\n");
594 g_print (" grig -m 116 -r /dev/ttyS0 -s 4800 -d 3");
595 g_print ("\n\n");
596 g_print (_("or if you prefer the long options:"));
597 g_print ("\n\n");
598 g_print (" grig --model=116 --rig-file=/dev/ttyS0 "\
599 "--speed=4800 --debug=3");
600 g_print ("\n\n");
601 g_print (_("It is usually enough to specify the model "\
602 "ID and the DEVICE."));
603 g_print ("\n\n");
604 g_print (_("If you start grig without any options it "\
605 "will use the Dummy backend "\
606 "and set the debug level to RIG_DEBUG_NONE. "\
607 "If you don't specify "\
608 "the transfer rate for the serial port, the "\
609 "default value will be "\
610 "used by the backend and even if you specify "\
611 "a value, it can be "\
612 "overridden by the backend."));
613 g_print ("\n\n");
614 g_print (_("Debug levels:"));
615 g_print ("\n\n");
616 g_print (_(" 0 No debug, keep quiet.\n"));
617 g_print (_(" 1 Serious bug.\n"));
618 g_print (_(" 2 Error case (e.g. protocol, memory allocation).\n"));
619 g_print (_(" 3 Warnings.\n"));
620 g_print (_(" 4 Verbose information.\n"));
621 g_print (_(" 5 Trace.\n"));
622 g_print ("\n\n");
623 }
624
625
626 /** \brief Show version info.
627 *
628 * This function shows the version information about grig.
629 */
630 static void
grig_show_version()631 grig_show_version ()
632 {
633 g_print (_("grig %s\n"), VERSION);
634 g_print (_("Graphical User Interface for the "\
635 "Hamradio Control Libraries."));
636 g_print ("\n\n");
637 g_print (_("Copyright (C) 2001-2007 Alexandru Csete."));
638 g_print ("\n");
639 g_print (_("This is free software; see the source for "\
640 "copying conditions. "));
641 g_print (_("There is NO warranty; not even for MERCHANTABILITY "
642 "or FITNESS FOR A PARTICULAR PURPOSE."));
643 g_print ("\n");
644
645 }
646
647
648
649 /** \brief List rigs.
650 *
651 * This function lists the radios suported by hamlib. It shows the
652 * manufacturer, model, driver version and driver status in a list
653 * sorted by model number.
654 *
655 * The list of radios is obtained using the rig_list_foreach hamlib
656 * function and storing each entry in a GArray. When all models have
657 * been stored, the list is sorted by model number and printed.
658 *
659 * \bug Header string is not translated.
660 *
661 * \bug Should check retcode returned by rig_list_foreach.
662 *
663 * \sa grig_list_add, grig_list_compare
664 */
665 static void
grig_list_rigs()666 grig_list_rigs ()
667 {
668 GArray *array;
669 gint i;
670 grig_rig_info_t *info;
671
672
673 /* create the array */
674 array = g_array_new (FALSE, FALSE, sizeof (grig_rig_info_t));
675
676 /* make hamlib quiet and load all backends */
677 grig_debug_set_level (RIG_DEBUG_NONE);
678 rig_load_all_backends();
679
680 /* fill list using rig_list_foreach */
681 rig_list_foreach (grig_list_add, (void *) array);
682
683 /* sort the array */
684 g_array_sort (array, grig_list_compare);
685
686 g_print ("\n");
687 g_print (_(" ID Manufacturer Model "\
688 "Ver. Status\n"));
689 g_print ("-----------------------------------------------"\
690 "----------------\n");
691
692 /* loop over each element of array; after printing one element
693 free the dynamically allocated strings because GArray does
694 not know about them
695 */
696 for (i = 0; i < array->len; i++) {
697
698 info = &g_array_index (array, grig_rig_info_t, i);
699
700 g_print ("%5d %-16s %-22s %-6s %s\n",
701 info->id,
702 info->mfg,
703 info->model,
704 info->version,
705 rig_strstatus (info->status));
706
707 /* free dynamic strings */
708 g_free (info->mfg);
709 g_free (info->model);
710 g_free (info->version);
711 }
712 g_print ("\n");
713
714 g_array_free (array,TRUE);
715 }
716
717
718 /** \brief Add new entry to list of radios.
719 * \param caps Structure with the capablities of thecurrent radio.
720 * \param array Pointer to the GArray into which the new entry should be
721 * stored.
722 * \return Always 1 to keep rig_list_foreach running.
723 *
724 * This function is called by the rig_list_foreach hamlib function for each
725 * supported radio. It copies the relevant data into a grig_rig_info_t
726 * structure and adds the new entry to the GArray containing the list of
727 * supported radios.
728 *
729 * \sa grig_list_rigs, grig_list_compare
730 */
731 static gint
grig_list_add(const struct rig_caps * caps,void * array)732 grig_list_add (const struct rig_caps *caps, void *array)
733 {
734 grig_rig_info_t *info;
735
736 /* create new entry */
737 info = g_malloc (sizeof (grig_rig_info_t));
738
739 /* fill values */
740 info->id = caps->rig_model;
741 info->mfg = g_strdup (caps->mfg_name);
742 info->model = g_strdup (caps->model_name);
743 info->version = g_strdup (caps->version);
744 info->status = caps->status;
745
746 /* append new element to array */
747 array = (void *) g_array_append_vals ((GArray *) array, info, 1);
748
749 /* keep on running */
750 return 1;
751 }
752
753
754
755 /** \brief Compare two rig info entries.
756 * \param a Pointer to the first entry.
757 * \param b Pointer to the second entry.
758 * \return Negative value if a < b; zero if a = b; positive value if a > b.
759 *
760 * This function is used to compare two rig entries in the list of radios
761 * when the list is sorted. It compares the model ID of the two radios.
762 *
763 * \sa grig_list_rigs, grig_list_add
764 */
765 static gint
grig_list_compare(gconstpointer a,gconstpointer b)766 grig_list_compare (gconstpointer a, gconstpointer b)
767 {
768 gint ida, idb;
769
770 ida = ((grig_rig_info_t *) a)->id;
771 idb = ((grig_rig_info_t *) b)->id;
772
773 if (ida < idb) {
774 return -1;
775 }
776 else if (ida > idb) {
777 return 1;
778 }
779 else {
780 return 0;
781 }
782
783 }
784