1 // ----------------------------------------------------------------------------
2 //      debug.cxx
3 //
4 // Copyright (C) 2008, 2012
5 //              Stelios Bounanos, M0GLD, Dave Freese, W1HKJ
6 // ----------------------------------------------------------------------------
7 // Copyright (C) 2014
8 //              David Freese, W1HKJ
9 //
10 // This file is part of flmsg
11 //
12 // flrig is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // flrig is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 // ----------------------------------------------------------------------------
25 
26 
27 #include <cstdio>
28 #include <cstring>
29 #include <cstdarg>
30 #include <string>
31 #include <iostream>
32 #include <fstream>
33 
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <time.h>
38 
39 #include <FL/Fl.H>
40 #include <FL/Fl_Double_Window.H>
41 #include <FL/Fl_Slider.H>
42 #include <FL/Fl_Button.H>
43 #include <FL/Fl_Menu_Item.H>
44 #include <FL/Fl_Menu_Button.H>
45 
46 #include <FL/Fl_Browser.H>
47 
48 #include "debug.h"
49 #include "icons.h"
50 #include "gettext.h"
51 #include "flmsg.h"
52 
53 using namespace std;
54 
55 #define MAX_LINES 65536
56 
57 static FILE* wfile;
58 static FILE* rfile;
59 static int rfd;
60 
61 static Fl_Double_Window*	window;
62 static Fl_Browser*			btext;
63 static string dbg_buffer;
64 
65 debug* debug::inst = 0;
66 debug::level_e debug::level = debug::INFO_LEVEL;
67 uint32_t debug::mask = ~0u;
68 
69 const char* prefix[] = { _("Quiet"), _("Error"), _("Warning"), _("Info"), _("Debug") };
70 
71 static void slider_cb(Fl_Widget* w, void*);
72 static void clear_cb(Fl_Widget *w, void*);
73 static void save_cb(Fl_Widget *w, void*);
74 static void synctext(void *);
75 
start(const char * filename)76 void debug::start(const char* filename)
77 {
78 	if (debug::inst)
79 		return;
80 	inst = new debug(filename);
81 
82 	window = new Fl_Double_Window(600, 256, _("Event log"));
83 
84 	int pad = 2;
85 
86 	Fl_Slider* slider = new Fl_Slider(pad, pad, 128, 20, prefix[level]);
87 	slider->tooltip(_("Change log level"));
88 	slider->align(FL_ALIGN_RIGHT);
89 	slider->type(FL_HOR_NICE_SLIDER);
90 	slider->range(0.0, LOG_NLEVELS - 1);
91 	slider->step(1.0);
92 	slider->value(level);
93 	slider->callback(slider_cb);
94 
95 	Fl_Button* savebtn  = new Fl_Button(window->w() - 124, pad, 60, 20, "save");
96 	savebtn->callback(save_cb);
97 
98 	Fl_Button* clearbtn = new Fl_Button(window->w() - 60, pad, 60, 20, "clear");
99 	clearbtn->callback(clear_cb);
100 
101 	btext = new Fl_Browser(pad,  slider->h()+pad, window->w()-2*pad, window->h()-slider->h()-2*pad, 0);
102 	btext->textfont(FL_COURIER);
103 	window->resizable(btext);
104 
105 	dbg_buffer.clear();
106 
107 	window->end();
108 }
109 
stop(void)110 void debug::stop(void)
111 {
112 	delete inst;
113 	inst = 0;
114 	delete window;
115 }
116 
117 static char fmt[1024];
118 static char sztemp[1024];
119 static string estr = "";
120 bool   debug_in_use = false;
121 
log(level_e level,const char * func,const char * srcf,int line,const char * format,...)122 void debug::log(level_e level, const char* func, const char* srcf, int line, const char* format, ...)
123 {
124 	if (!inst)
125 		return;
126 
127 	snprintf(fmt, sizeof(fmt), "%c: %s: %s\n", *prefix[level], func, format);
128 
129     while(debug_in_use) MilliSleep(10);
130 
131 	va_list args;
132 	va_start(args, format);
133 
134 	vsnprintf(sztemp, sizeof(sztemp), fmt, args);
135 	estr.append(sztemp);
136 
137 	va_end(args);
138 
139 	fprintf(wfile, "%s", sztemp);
140 
141 	fflush(wfile);
142 
143 	Fl::awake(synctext, 0);
144 	MilliSleep(10);
145 }
146 
slog(level_e level,const char * func,const char * srcf,int line,const char * format,...)147 void debug::slog(level_e level, const char* func, const char* srcf, int line, const char* format, ...)
148 {
149 	if (!inst)
150 		return;
151 
152 	snprintf(fmt, sizeof(fmt), "%c:%s\n", *prefix[level], format);
153 
154     while(debug_in_use) MilliSleep(10);
155 
156 	va_list args;
157 	va_start(args, format);
158 
159 	vsnprintf(sztemp, sizeof(sztemp), fmt, args);
160 	estr.append(sztemp);
161 
162 	va_end(args);
163 	fflush(wfile);
164 
165 	Fl::awake(synctext, 0);
166 	MilliSleep(10);
167 }
168 
elog(const char * func,const char * srcf,int line,const char * text)169 void debug::elog(const char* func, const char* srcf, int line, const char* text)
170 {
171 	log(ERROR_LEVEL, func, srcf, line, "%s: %s", text, strerror(errno));
172 }
173 
show(void)174 void debug::show(void)
175 {
176 	window->show();
177 }
178 
sync_text(void * arg)179 void debug::sync_text(void* arg)
180 {
181     debug_in_use = true;
182 	size_t p0 = 0, p1 = estr.find('\n');
183 	while (p1 != string::npos) {
184 		btext->insert(1, estr.substr(p0,p1-p0).c_str());
185 		dbg_buffer.append(estr.substr(p0, p1 - p0)).append("\n");
186 		p0 = p1 + 1;
187 		p1 = estr.find('\n', p0);
188 	}
189     estr = "";
190     debug_in_use = false;
191 }
192 
debug(const char * filename)193 debug::debug(const char* filename)
194 {
195 	if ((wfile = fopen(filename, "w")) == NULL)
196 		throw strerror(errno);
197 	setvbuf(wfile, (char*)NULL, _IOLBF, 0);
198 
199 	if ((rfile = fopen(filename, "r")) == NULL)
200 		throw strerror(errno);
201 	rfd = fileno(rfile);
202 #ifndef __WIN32__
203 	int f;
204 	if ((f = fcntl(rfd, F_GETFL)) == -1)
205 		throw strerror(errno);
206 	if (fcntl(rfd, F_SETFL, f | O_NONBLOCK) == -1)
207 		throw strerror(errno);
208 #endif
209 }
210 
~debug()211 debug::~debug()
212 {
213 	fclose(wfile);
214 	fclose(rfile);
215 }
216 
synctext(void * d)217 static void synctext(void *d)
218 {
219     debug_in_use = true;
220 	size_t p0 = 0, p1 = estr.find('\n');
221 	while (p1 != string::npos) {
222 		btext->insert(1, estr.substr(p0,p1-p0).c_str());
223 		p0 = p1 + 1;
224 		p1 = estr.find('\n', p0);
225 	}
226     estr = "";
227     debug_in_use = false;
228 }
229 
slider_cb(Fl_Widget * w,void *)230 static void slider_cb(Fl_Widget* w, void*)
231 {
232 	debug::level = (debug::level_e)((Fl_Slider*)w)->value();
233 	w->label(prefix[debug::level]);
234 	w->parent()->redraw();
235 }
236 
clear_cb(Fl_Widget * w,void *)237 static void clear_cb(Fl_Widget* w, void*)
238 {
239 	btext->clear();
240 	dbg_buffer.clear();
241 }
242 
save_cb(Fl_Widget * w,void *)243 static void save_cb(Fl_Widget* w, void*)
244 {
245 	if (!btext->size()) return;
246 	string filename = FLMSG_log_dir;
247 	filename.append("events.txt");
248 	ofstream out;
249 	out.open(filename.c_str(), ios::app);
250 	out << dbg_buffer;
251 	out.close();
252 	fl_alert2("Saved in %s", filename.c_str());
253 }
254