1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2014
3 // David Freese, W1HKJ
4 //
5 // This file is part of flrig.
6 //
7 // flrig is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // flrig is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <iostream>
25 #include <fstream>
26 #include <sstream>
27 #include <cstring>
28 #include <ctime>
29 #include <exception>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <errno.h>
35
36 #include <FL/Fl.H>
37 #include <FL/Enumerations.H>
38 #include <FL/Fl_Window.H>
39 #include <FL/Fl_Button.H>
40 #include <FL/Fl_Group.H>
41 #include <FL/Fl_Sys_Menu_Bar.H>
42 #include <FL/x.H>
43 #include <FL/Fl_Help_Dialog.H>
44 #include <FL/Fl_Menu_Item.H>
45 #include <FL/Fl_Text_Display.H>
46 #include <FL/Fl_Text_Buffer.H>
47
48 #ifdef WIN32
49 # include "flrigrc.h"
50 # include "compat.h"
51 # define dirent fl_dirent_no_thanks
52 #endif
53
54 #include <FL/filename.H>
55
56 #ifdef __MINGW32__
57 # if FLRIG_FLTK_API_MAJOR == 1 && FLRIG_FLTK_API_MINOR < 3
58 # undef dirent
59 # include <dirent.h>
60 # endif
61 #else
62 # include <dirent.h>
63 #endif
64
65 #include <FL/x.H>
66 #include <FL/Fl_Pixmap.H>
67 #include <FL/Fl_Image.H>
68
69 //#include "images/flrig.xpm"
70 #include "support.h"
71 #include "dialogs.h"
72 #include "rig.h"
73 #include "status.h"
74 #include "debug.h"
75 #include "util.h"
76 #include "gettext.h"
77 #include "xml_server.h"
78 //#include "xml_io.h"
79 #include "serial.h"
80 #include "ui.h"
81 #include "icons.h"
82 #include "cwio.h"
83 #include "cwioUI.h"
84
85 #include "flrig_icon.cxx"
86
87 int parse_args(int argc, char **argv, int& idx);
88
89 Fl_Double_Window *mainwindow = (Fl_Double_Window *)0;
90 Fl_Double_Window *tabs_dialog = (Fl_Double_Window *)0;
91 Fl_Double_Window *cwio_keyer_dialog = (Fl_Double_Window *)0;
92 Fl_Double_Window *cwio_editor = (Fl_Double_Window *)0;
93 Fl_Double_Window *cwio_configure = (Fl_Double_Window *)0;
94
95 string HomeDir;
96 string RigHomeDir;
97 string TempDir;
98 string defFileName;
99 string title;
100
101 pthread_t *serial_thread = 0;
102 pthread_t *digi_thread = 0;
103
104 pthread_mutex_t mutex_serial = PTHREAD_MUTEX_INITIALIZER;
105
106 //pthread_mutex_t mutex_xmlrpc = PTHREAD_MUTEX_INITIALIZER;
107
108 pthread_mutex_t mutex_vfoque = PTHREAD_MUTEX_INITIALIZER;
109
110 //pthread_mutex_t mutex_vfoque = PTHREAD_MUTEX_INITIALIZER;
111 //pthread_mutex_t mutex_vfoque = PTHREAD_MUTEX_INITIALIZER;
112
113 pthread_mutex_t mutex_ptt = PTHREAD_MUTEX_INITIALIZER;
114 pthread_mutex_t mutex_replystr = PTHREAD_MUTEX_INITIALIZER;
115
116 pthread_mutex_t mutex_srvc_reqs = PTHREAD_MUTEX_INITIALIZER;
117
118 pthread_mutex_t mutex_trace = PTHREAD_MUTEX_INITIALIZER;
119
120 int use_trace = 0;
121
122 bool EXPAND_CONTROLS = false;
123
124 int xmlport = 12345;
125
126 //----------------------------------------------------------------------
about()127 void about()
128 {
129 string msg = "\
130 %s\n\
131 Version %s\n\
132 copyright W1HKJ <w1hkj@@w1hkj.com>\n\
133 Developer: Dave, W1HKJ";
134 fl_message(msg.c_str(), PACKAGE_TARNAME, PACKAGE_VERSION);
135 }
136
visit_URL(void * arg)137 void visit_URL(void* arg)
138 {
139 const char* url = reinterpret_cast<const char *>(arg);
140 #ifndef __WOE32__
141 const char* browsers[] = {
142 # ifdef __APPLE__
143 getenv("FLDIGI_BROWSER"), // valid for any OS - set by user
144 "open" // OS X
145 # else
146 "fl-xdg-open", // Puppy Linux
147 "xdg-open", // other Unix-Linux distros
148 getenv("FLDIGI_BROWSER"), // force use of spec'd browser
149 getenv("BROWSER"), // most Linux distributions
150 "sensible-browser",
151 "firefox",
152 "mozilla" // must be something out there!
153 # endif
154 };
155 switch (fork()) {
156 case 0:
157 # ifndef NDEBUG
158 unsetenv("MALLOC_CHECK_");
159 unsetenv("MALLOC_PERTURB_");
160 # endif
161 for (size_t i = 0; i < sizeof(browsers)/sizeof(browsers[0]); i++)
162 if (browsers[i])
163 execlp(browsers[i], browsers[i], url, (char*)0);
164 exit(EXIT_FAILURE);
165 case -1:
166 fl_alert(_("Could not run a web browser:\n%s\n\n"
167 "Open this URL manually:\n%s"),
168 strerror(errno), url);
169 }
170 #else
171 // gurgle... gurgle... HOWL
172 // "The return value is cast as an HINSTANCE for backward
173 // compatibility with 16-bit Windows applications. It is
174 // not a true HINSTANCE, however. The only thing that can
175 // be done with the returned HINSTANCE is to cast it to an
176 // int and compare it with the value 32 or one of the error
177 // codes below." (Error codes omitted to preserve sanity).
178 if ((int)ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL) <= 32)
179 fl_alert(_("Could not open url:\n%s\n"), url);
180 #endif
181 }
182
183 //----------------------------------------------------------------------
184
185 extern void saveFreqList();
186
showEvents(void *)187 void showEvents(void *)
188 {
189 debug::show();
190 }
191
192 #if defined(__WIN32__) && defined(PTW32_STATIC_LIB)
ptw32_cleanup(void)193 static void ptw32_cleanup(void)
194 {
195 (void)pthread_win32_process_detach_np();
196 }
197
ptw32_init(void)198 void ptw32_init(void)
199 {
200 (void)pthread_win32_process_attach_np();
201 atexit(ptw32_cleanup);
202 }
203 #endif // __WIN32__
204
205 #define KNAME "Flrig"
206 #if !defined(__WIN32__) && !defined(__APPLE__)
207 Pixmap Rig_icon_pixmap;
208
make_pixmap(Pixmap * xpm,const char ** data)209 void make_pixmap(Pixmap *xpm, const char **data)
210 {
211 Fl_Window w(0,0, KNAME);
212 w.xclass(KNAME);
213 w.show();
214 w.make_current();
215 Fl_Pixmap icon(data);
216 int maxd = (icon.w() > icon.h()) ? icon.w() : icon.h();
217 *xpm = fl_create_offscreen(maxd, maxd);
218 fl_begin_offscreen(*xpm);
219 fl_color(FL_BACKGROUND_COLOR);
220 fl_rectf(0, 0, maxd, maxd);
221 icon.draw(maxd - icon.w(), maxd - icon.h());
222 fl_end_offscreen();
223 }
224
225 #endif
226
checkdirectories(void)227 static void checkdirectories(void)
228 {
229 struct {
230 string& dir;
231 const char* suffix;
232 void (*new_dir_func)(void);
233 } dirs[] = {
234 { RigHomeDir, 0, 0 }
235 };
236
237 int r;
238 for (size_t i = 0; i < sizeof(dirs)/sizeof(*dirs); i++) {
239 if (dirs[i].suffix)
240 dirs[i].dir.assign(RigHomeDir).append(dirs[i].suffix).append("/");
241
242 if ((r = mkdir(dirs[i].dir.c_str(), 0777)) == -1 && errno != EEXIST) {
243 cerr << _("Could not make directory") << ' ' << dirs[i].dir
244 << ": " << strerror(errno) << '\n';
245 exit(EXIT_FAILURE);
246 }
247 else if (r == 0 && dirs[i].new_dir_func)
248 dirs[i].new_dir_func();
249 }
250 }
251
exit_main(Fl_Widget * w)252 void exit_main(Fl_Widget *w)
253 {
254 if (Fl::event_key() == FL_Escape)
255 return;
256 cbExit();
257 }
258
expand_controls(void *)259 void expand_controls(void*)
260 {
261 show_controls();
262 }
263
close_controls(void *)264 void close_controls(void*)
265 {
266 switch (progStatus.UIsize) {
267 case wide_ui :
268 if (EXPAND_CONTROLS && selrig->has_extras) return;
269 btn_show_controls->label("@-22->");
270 btn_show_controls->redraw_label();
271 // grpTABS->hide();
272 // mainwindow->resizable(grpTABS);
273 mainwindow->size(progStatus.mainW, 150);
274 mainwindow->size_range(735, 150, 0, 150);
275 if (tabs_dialog && tabs_dialog->visible()) tabs_dialog->hide();
276 break;
277 case small_ui :
278 if (tabs_dialog && tabs_dialog->visible()) tabs_dialog->hide();
279 // if (EXPAND_CONTROLS && selrig->has_extras)
280 // Fl::add_timeout(1.0, expand_controls);
281 break;
282 case touch_ui :
283 default :
284 break;
285 }
286 }
287
startup(void *)288 void startup(void*)
289 {
290 initStatusConfigDialog();
291
292 switch (progStatus.UIsize) {
293 case touch_ui :
294 mainwindow->size(progStatus.mainW, TOUCH_MAINH);
295 mainwindow->size_range(TOUCH_MAINW, TOUCH_MAINH, 0, TOUCH_MAINH);
296 mainwindow->redraw();
297 break;
298 case wide_ui :
299 {
300 if (EXPAND_CONTROLS && selrig->has_extras) return;
301 btn_show_controls->label("@-22->");
302 btn_show_controls->redraw_label();
303 mainwindow->redraw();
304 }
305 case small_ui :
306 default :
307 break;
308 }
309 start_server(xmlport);
310 }
311
rotate_log(std::string filename)312 void rotate_log(std::string filename)
313 {
314 const int n = 5; // rename existing log files to keep up to 5 old versions
315 ostringstream oldfn, newfn;
316 ostringstream::streampos p;
317
318 oldfn << filename << '.';
319 newfn << filename << '.';
320 p = oldfn.tellp();
321
322 for (int i = n - 1; i > 0; i--) {
323 oldfn.seekp(p);
324 newfn.seekp(p);
325 oldfn << i;
326 newfn << i + 1;
327 rename(oldfn.str().c_str(), newfn.str().c_str());
328 }
329 rename(filename.c_str(), oldfn.str().c_str());
330 }
331
flrig_terminate()332 void flrig_terminate() {
333 std::cerr << "terminating" << std::endl;
334 fl_message("Closing flrig");
335 cbExit();
336 }
337
main(int argc,char * argv[])338 int main (int argc, char *argv[])
339 {
340 std::set_terminate(flrig_terminate);
341
342 int arg_idx;
343 HomeDir.clear();
344 RigHomeDir.clear();
345
346 Fl::args(argc, argv, arg_idx, parse_args);
347 Fl::set_fonts(0);
348
349 char dirbuf[FL_PATH_MAX + 1];
350 string appdir = argv[0];
351 size_t p;
352 #ifdef __WIN32__
353 p = appdir.rfind("flrig.exe");
354 if (p != string::npos) appdir.erase(p);
355 p = appdir.find("FL_APPS\\");
356 if (p != string::npos) {
357 HomeDir.assign(appdir.substr(0, p + 8));
358 RigHomeDir.assign(HomeDir).append("flrig.files\\");
359 } else if (RigHomeDir.empty()) {
360 fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$USERPROFILE/");
361 HomeDir = dirbuf;
362 RigHomeDir.assign(HomeDir).append("flrig.files\\");
363 }
364 #else
365 p = appdir.rfind("flrig");
366 if (p != std::string::npos)
367 appdir.erase(p);
368 p = appdir.find("FL_APPS/");
369 if (p != string::npos)
370 RigHomeDir = appdir.substr(0, p + 8);
371 if (RigHomeDir.empty()) {
372 fl_filename_expand(dirbuf, FL_PATH_MAX, "$HOME/");
373 HomeDir = dirbuf;
374 }
375
376 DIR *isdir = 0;
377 string test_dir;
378 test_dir.assign(HomeDir).append("flrig.files/");
379 isdir = opendir(test_dir.c_str());
380 if (isdir) {
381 RigHomeDir = test_dir;
382 closedir(isdir);
383 } else if (RigHomeDir.empty()) {
384 RigHomeDir.assign(HomeDir).append(".flrig/");
385 }
386
387 #endif
388 checkdirectories();
389
390 #if SERIAL_DEBUG
391 extern FILE *serlog;
392 std::string serlogname = RigHomeDir;
393 serlogname.append("serlog.txt");
394 serlog = fopen(serlogname.c_str(), "w");
395 #endif
396
397 RigSerial = new Cserial;
398 SepSerial = new Cserial;
399 AuxSerial = new Cserial;
400 cwio_serial = new Cserial();
401 morse = new Cmorse();
402
403 try {
404 std::string fname = RigHomeDir;
405 fname.append("debug_log.txt");
406 rotate_log(fname);
407 debug::start(fname.c_str());
408 time_t t = time(NULL);
409 LOG(debug::INFO_LEVEL, debug::LOG_OTHER, _("%s log started on %s"), PACKAGE_STRING, ctime(&t));
410 string trace_fname = RigHomeDir;
411 trace_fname.append("trace.txt");
412 rotate_log(trace_fname);
413
414 }
415 catch (const char* error) {
416 cerr << error << '\n';
417 debug::stop();
418 exit(1);
419 }
420
421 progStatus.loadLastState();
422
423 if (use_trace) progStatus.trace = true;
424
425 switch (progStatus.UIsize) {
426 case touch_ui :
427 mainwindow = touch_rig_window();
428 break;
429 case small_ui :
430 mainwindow = Small_rig_window();
431 tabs_dialog = tabs_window();
432 tabs_dialog->hide();
433 break;
434 case wide_ui :
435 default :
436 mainwindow = Wide_rig_window();
437 tabs_dialog = tabs_window();
438 tabs_dialog->hide();
439 }
440 mainwindow->callback(exit_main);
441
442 progStatus.UI_laststate();
443
444 cwio_keyer_dialog = cwio_window();
445 cwio_editor = make_message_editor();
446 cwio_configure = cwio_config_dialog();
447
448 fntbrowser = new Font_Browser;
449 dlgMemoryDialog = Memory_Dialog();
450 dlgDisplayConfig = DisplayDialog();
451
452 Fl::lock();
453
454 #if defined(__WIN32__) && defined(PTW32_STATIC_LIB)
455 ptw32_init();
456 #endif
457
458 bypass_serial_thread_loop = true;
459 serial_thread = new pthread_t;
460 if (pthread_create(serial_thread, NULL, serial_thread_loop, NULL)) {
461 perror("pthread_create");
462 exit(EXIT_FAILURE);
463 }
464
465 // start_server(xmlport);
466 if (start_cwio_thread() != 0)
467 return 1;
468
469 if (progStatus.cwioCONNECTED) {
470 if (!open_cwkey()) {
471 btn_cwioCONNECT->value(0);
472 btn_cwioCAT->activate();
473 btn_cwioAUX->activate();
474 btn_cwioSEP->activate();
475 progStatus.cwioCONNECTED = 0;
476 } else {
477 btn_cwioCONNECT->value(1);
478 btn_cwioCAT->value(0); btn_cwioCAT->deactivate();
479 btn_cwioAUX->value(0); btn_cwioAUX->deactivate();
480 btn_cwioSEP->value(0); btn_cwioSEP->deactivate();
481 progStatus.cwioCONNECTED = 1;
482 }
483 }
484
485 createXcvrDialog();
486
487 btnALC_SWR->image(image_swr);
488 sldrRcvSignal->clear();
489 sldrFwdPwr->clear();
490 sldrALC->clear();
491 sldrSWR->clear();
492
493 switch (progStatus.UIsize) {
494 case small_ui :
495 mainwindow->resize(
496 progStatus.mainX, progStatus.mainY,
497 mainwindow->w(), 150);//mainwindow->h());
498 grpInitializing->size(mainwindow->w(), mainwindow->h() - grpInitializing->y());
499 grpInitializing->redraw();
500 progress->position(progress->x(), grpInitializing->y() + grpInitializing->h()/2);
501 progress->redraw();
502 break;
503 case wide_ui :
504 mainwindow->resize(
505 progStatus.mainX, progStatus.mainY,
506 progStatus.mainW, btnVol->y() + btnVol->h() + 2);
507 break;
508 case touch_ui :
509 mainwindow->resize(
510 progStatus.mainX, progStatus.mainY,
511 progStatus.mainW, TOUCH_MAINH);
512 default :
513 break;
514 }
515 mainwindow->xclass(KNAME);
516
517 #if defined(__WOE32__)
518 # ifndef IDI_ICON
519 # define IDI_ICON 101
520 # endif
521 mainwindow->icon((char*)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
522 mainwindow->show (argc, argv);
523 #elif !defined(__APPLE__)
524 make_pixmap(&Rig_icon_pixmap, flrig_icon);
525 mainwindow->icon((char *)Rig_icon_pixmap);
526 mainwindow->show(argc, argv);
527 #else
528 mainwindow->show(argc, argv);
529 #endif
530
531 if (progStatus.UIsize != touch_ui) {
532 btn_show_controls->label("@-28->");
533 btn_show_controls->redraw_label();
534 }
535
536 Fl::add_timeout(0.1, startup);
537
538 return Fl::run();
539
540 }
541
cl_print(std::string cl)542 void cl_print(std::string cl)
543 {
544 Fl_Double_Window clwin(50,50,600,500, "Command line text");
545 Fl_Text_Display cldisplay(2,2,596,496);
546 Fl_Text_Buffer clbuff;
547
548 cldisplay.buffer(&clbuff);
549 cldisplay.textfont(FL_COURIER);
550
551 cldisplay.insert(cl.c_str());
552
553 clwin.end();
554 clwin.resizable(cldisplay);
555
556 clwin.show();
557
558 while (clwin.visible()) {
559 Fl::wait();
560 MilliSleep(50);
561 }
562 }
563
cb_xml_help(Fl_Menu_ *,void *)564 void cb_xml_help(Fl_Menu_*, void*)
565 {
566 cl_print(print_xmlhelp());
567 }
568
parse_args(int argc,char ** argv,int & idx)569 int parse_args(int argc, char **argv, int& idx)
570 {
571 std::string helpstr =
572 "Usage: \n\
573 --help this help text\n\
574 --version\n\
575 --config-dir [fully qualified pathname to <DIR>]\n\
576 --debug-level N (0..4)\n\
577 --trace\n\
578 --xml-help\n\
579 --xml-trace\n\
580 --exp (expand menu tab controls)";
581
582 if (strcasecmp("--help", argv[idx]) == 0) {
583 #ifdef __WIN32__
584 fl_alert2("%s", helpstr.c_str());
585 #else
586 std::cout << helpstr << std::endl;
587 #endif
588 exit(0);
589 }
590 if (strcasecmp("--version", argv[idx]) == 0) {
591 std::string ver = "Version: ";
592 ver.append(VERSION).append("\n");
593 std::cout << ver;
594 exit (0);
595 }
596 if (strcasecmp("--trace", argv[idx]) == 0) {
597 use_trace = true;
598 idx++;
599 return 1;
600 }
601 if (strcasecmp("--xml-help", argv[idx]) == 0) {
602 std::string help = print_xmlhelp();
603 std::cout << help;
604 exit(0);
605 }
606 if (strcasecmp("--debug-level", argv[idx]) == 0) {
607 string level = argv[idx + 1];
608 switch (level[0]) {
609 case '0': debug::level = debug::QUIET_LEVEL; break;
610 case '1': debug::level = debug::ERROR_LEVEL; break;
611 case '2': debug::level = debug::WARN_LEVEL; break;
612 case '3': debug::level = debug::INFO_LEVEL; break;
613 case '4': debug::level = debug::DEBUG_LEVEL; break;
614 default : debug::level = debug::WARN_LEVEL;
615 }
616 idx += 2;
617 return 1;
618 }
619 if (strcasecmp("--config-dir", argv[idx]) == 0) {
620 RigHomeDir = argv[idx + 1];
621 if (RigHomeDir[RigHomeDir.length()-1] != '/')
622 RigHomeDir += '/';
623 idx += 2;
624 return 1;
625 }
626 if (strcasecmp("--exp", argv[idx]) == 0) {
627 EXPAND_CONTROLS = true;
628 idx++;
629 return 1;
630 }
631 # ifdef __APPLE__
632 if (strncasecmp("-psn", argv[idx], 4) == 0) {
633 idx++;
634 return 1;
635 }
636 #endif
637 fl_alert2("Unknown command line parameter: \"%s\"\n\n%s", argv[idx], helpstr.c_str());
638 exit(0);
639
640 return 0;
641 }
642