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