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