SourceXtractorPlusPlus
0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
SEMain
src
lib
ProgressNCurses.cpp
Go to the documentation of this file.
1
17
#include "AlexandriaKernel/memory_tools.h"
18
#include "
SEMain/ProgressNCurses.h
"
19
20
#include <poll.h>
21
#include <semaphore.h>
22
#include <ncurses.h>
23
#include <fcntl.h>
24
#include <readline/readline.h>
25
#include <csignal>
26
#include <chrono>
27
#include <iostream>
28
#include <iomanip>
29
#include <mutex>
30
#include <boost/algorithm/string/trim.hpp>
31
#include <boost/thread.hpp>
32
33
34
using
Euclid::make_unique
;
35
36
37
namespace
SourceXtractor
{
38
39
// Signal handlers
40
static
struct
sigaction
sigterm_action
;
41
static
struct
sigaction
sigstop_action
;
42
static
struct
sigaction
sigcont_action
;
43
static
struct
sigaction
sigwich_action
;
44
static
std::map<int, struct sigaction>
prev_signal
;
45
46
// Used for sending signals to the UI thread
47
static
int
signal_fds
[2];
48
49
// Used by the UI thread to notify that is is done
50
struct
ncurses_done_t
{
51
sem_t
m_semaphore
;
52
ncurses_done_t
() {
53
sem_init
(&
m_semaphore
, 0, 1);
54
}
55
};
56
static
ncurses_done_t
ncurses_done
;
57
58
// Forward declaration of signal handlers
59
static
void
handleTerminatingSignal
(
int
s);
60
static
void
handleStopSignal
(
int
s);
61
static
void
handleContinuationSignal
(
int
s);
62
static
void
handleResizeSignal
(
int
);
63
64
74
static
int
interceptFileDescriptor
(
int
old_fd
,
int
*
backup_fd
) {
75
int
pipe_fds
[2];
76
77
*
backup_fd
=
dup
(
old_fd
);
78
if
(*
backup_fd
< 0) {
79
throw
std::system_error
(
errno
,
std::generic_category
());
80
}
81
82
if
(
pipe
(
pipe_fds
) < 0) {
83
throw
std::system_error
(
errno
,
std::generic_category
());
84
}
85
86
int
flags =
fcntl
(
pipe_fds
[0],
F_GETFL
, 0);
87
if
(
fcntl
(
pipe_fds
[0],
F_SETFL
, flags |
O_NONBLOCK
) < 0) {
88
throw
std::system_error
(
errno
,
std::generic_category
());
89
}
90
91
if
(
dup2
(
pipe_fds
[1],
old_fd
) < 0) {
92
throw
std::system_error
(
errno
,
std::generic_category
());
93
}
94
close(
pipe_fds
[1]);
95
96
return
pipe_fds
[0];
97
}
98
107
static
void
override_rl_display
(
void
) {
108
}
109
113
class
Screen
:
public
boost::noncopyable {
114
public
:
115
123
Screen
(
FILE
*
outfd
,
FILE
*
infd
) {
124
if
(
pipe
(
signal_fds
) < 0) {
125
throw
std::system_error
(
errno
,
std::generic_category
());
126
}
127
128
m_old_redisplay
=
rl_redisplay_function
;
129
rl_redisplay_function
=
override_rl_display
;
130
131
// Tell readline to leave the terminal be
132
rl_catch_signals
= 0;
133
rl_deprep_term_function
=
nullptr
;
134
rl_prep_term_function
=
nullptr
;
135
136
// It seems like the readline in MacOSX is not the "real" readline, but a compatibility
137
// layer which misses some things, like the following:
138
#ifndef __APPLE__
139
rl_catch_sigwinch
= 0;
140
#endif
141
142
// Setup signal handlers
143
::memset
(&
sigterm_action
, 0,
sizeof
(
sigterm_action
));
144
::memset
(&
sigstop_action
, 0,
sizeof
(
sigstop_action
));
145
::memset
(&
sigcont_action
, 0,
sizeof
(
sigcont_action
));
146
::memset
(&
sigwich_action
, 0,
sizeof
(
sigwich_action
));
147
148
// Termination
149
sigterm_action
.sa_handler = &
handleTerminatingSignal
;
150
::sigaction(
SIGINT
, &
sigterm_action
, &
prev_signal
[
SIGINT
]);
151
::sigaction(
SIGTERM
, &
sigterm_action
, &
prev_signal
[
SIGTERM
]);
152
::sigaction(
SIGABRT
, &
sigterm_action
, &
prev_signal
[
SIGABRT
]);
153
::sigaction(
SIGSEGV
, &
sigterm_action
, &
prev_signal
[
SIGSEGV
]);
154
::sigaction(
SIGHUP
, &
sigterm_action
, &
prev_signal
[
SIGHUP
]);
155
156
// bg
157
sigstop_action
.sa_handler = &
handleStopSignal
;
158
::sigaction(
SIGTSTP
, &
sigstop_action
, &
prev_signal
[
SIGTSTP
]);
159
160
// fg
161
sigcont_action
.sa_handler = &
handleContinuationSignal
;
162
::sigaction(
SIGCONT
, &
sigcont_action
, &
prev_signal
[
SIGCONT
]);
163
164
// Resizing
165
// Some versions of ncurses handle this by themselves, some other do not, so
166
// we do it ourselves in anycase
167
sigwich_action
.sa_handler = &
handleResizeSignal
;
168
::sigaction(
SIGWINCH
, &
sigwich_action
, &
prev_signal
[
SIGWINCH
]);
169
170
// Enter ncurses
171
initscr
();
172
m_screen
=
newterm
(
nullptr
,
outfd
,
infd
);
173
set_term
(
m_screen
);
174
175
// Hide cursor
176
curs_set
(0);
177
178
// Get input without echo, but leave Ctrl+<Key> to the terminal
179
cbreak
();
180
keypad
(
stdscr
,
TRUE
);
181
noecho
();
182
183
// Setup colors
184
use_default_colors
();
185
start_color
();
186
}
187
191
virtual
~Screen
() {
192
// Exit ncurses
193
endwin
();
194
delscreen
(
m_screen
);
195
rl_redisplay_function
=
m_old_redisplay
;
196
// Restore signal handlers
197
::sigaction(
SIGINT
, &
prev_signal
[
SIGINT
],
nullptr
);
198
::sigaction(
SIGTERM
, &
prev_signal
[
SIGTERM
],
nullptr
);
199
::sigaction(
SIGABRT
, &
prev_signal
[
SIGABRT
],
nullptr
);
200
::sigaction(
SIGSEGV
, &
prev_signal
[
SIGSEGV
],
nullptr
);
201
::sigaction(
SIGHUP
, &
prev_signal
[
SIGHUP
],
nullptr
);
202
::sigaction(
SIGCONT
, &
prev_signal
[
SIGCONT
],
nullptr
);
203
::sigaction(
SIGWINCH
, &
prev_signal
[
SIGWINCH
],
nullptr
);
204
close(
signal_fds
[0]);
205
close(
signal_fds
[1]);
206
}
207
211
short
initColor
(
short
fg
,
short
bg
) {
212
init_pair
(
m_color_idx
,
fg
,
bg
);
213
return
m_color_idx
++;
214
}
215
216
private
:
217
short
m_color_idx
= 1;
218
SCREEN
*
m_screen
;
219
rl_voidfunc_t
*
m_old_redisplay
;
220
};
221
238
static
void
handleTerminatingSignal
(
int
s) {
239
// Restore handler (so if we get stuck somewhere, and second
240
// signal occurs, like a SIGTERM, the process is killed for good)
241
sigaction
(s, &
prev_signal
[s],
nullptr
);
242
243
// Notify
244
if
(write(
signal_fds
[1], &s,
sizeof
(s)) ==
sizeof
(s)) {
245
close(
signal_fds
[1]);
246
// Wait for UI thread to be done
247
#if _POSIX_C_SOURCE >= 200112L
248
timespec
timeout
;
249
clock_gettime
(
CLOCK_REALTIME
, &
timeout
);
250
timeout
.tv_sec += 5;
251
sem_timedwait
(&
ncurses_done
.m_semaphore, &
timeout
);
252
#else
253
// MacOSX does not have timedwait
254
int
timeout
= 5;
255
while
(
timeout
> 0 &&
sem_trywait
(&
ncurses_done
.m_semaphore) < 0) {
256
sleep
(1);
257
--
timeout
;
258
}
259
#endif
260
}
261
262
// Call the previous handler
263
raise
(s);
264
}
265
269
static
void
handleStopSignal
(
int
s) {
270
// Restore handler
271
sigaction
(s, &
prev_signal
[s],
nullptr
);
272
273
// Exit ncurses
274
endwin
();
275
276
// Trigger the previous handler
277
raise
(s);
278
}
279
283
static
void
handleContinuationSignal
(
int
) {
284
// Restore handlers
285
sigaction
(
SIGCONT
, &
sigcont_action
,
nullptr
);
286
sigaction
(
SIGTSTP
, &
sigstop_action
,
nullptr
);
287
}
288
292
static
void
handleResizeSignal
(
int
s) {
293
if
(write(
signal_fds
[1], &s,
sizeof
(s)) < 0) {
294
// Just ignore
295
}
296
}
297
301
class
LogWidget
{
302
private
:
303
WINDOW
*
m_pad
;
304
WINDOW
*
m_scroll
;
305
// Screen coordinates!
306
int
m_display_height
;
307
int
m_display_width
;
308
int
m_display_y
;
309
int
m_display_x
;
310
// Number of total lines being written so far
311
int
m_written_lines
= 0;
312
// Last line being *displayed*
313
int
m_active_line
= 0;
314
// Colors
315
int
m_scroll_bar_color
;
316
int
m_scroll_ind_color
;
317
318
static
const
int
BUFFER_INCREASE_STEP_SIZE
= 10;
319
static
const
int
BUFFER_MAX_SIZE
= 16384;
320
321
public
:
322
338
LogWidget
(
int
display_height
,
int
display_width
,
int
display_y
,
int
display_x
,
short
bar_color
,
short
ind_color
)
339
:
m_pad
(
newpad
(
BUFFER_INCREASE_STEP_SIZE
,
display_width
)),
340
m_scroll
(
newpad
(
display_height
, 1)),
341
m_display_height
(
display_height
),
m_display_width
(
display_width
),
m_display_y
(
display_y
),
m_display_x
(
display_x
),
342
m_scroll_bar_color
(
bar_color
),
m_scroll_ind_color
(
ind_color
) {
343
scrollok
(
m_pad
,
TRUE
);
344
}
345
349
virtual
~LogWidget
() {
350
delwin
(
m_pad
);
351
delwin
(
m_scroll
);
352
}
353
354
LogWidget
(
const
LogWidget
&) =
delete
;
355
const
LogWidget
&
operator=
(
const
LogWidget
&) =
delete
;
356
360
void
write
(
const
char
*data,
ssize_t
nchars
) {
361
while
(
nchars
> 0) {
362
if
(*data ==
'\n'
) {
363
// If the current line is the last one, follow the log
364
if
(
m_active_line
==
m_written_lines
) {
365
++
m_active_line
;
366
}
367
m_written_lines
=
std::min
({
m_written_lines
+ 1,
BUFFER_MAX_SIZE
});
368
// Increase buffer if we ran out of lines on the pad
369
if
(
getmaxy
(
m_pad
) <=
m_written_lines
) {
370
wresize
(
m_pad
,
m_written_lines
+
BUFFER_INCREASE_STEP_SIZE
,
m_display_width
);
371
}
372
}
373
waddch
(
m_pad
, *data);
374
++data;
375
--
nchars
;
376
}
377
drawLog
();
378
drawScroll
();
379
}
380
384
void
resize
(
int
display_height
,
int
display_width
) {
385
m_display_height
=
display_height
;
386
m_display_width
=
display_width
;
387
388
// Resize to make place for the new width only if it is bigger.
389
// Note that the pad height depends on the number of written lines, not displayed size!
390
if
(
display_width
>
getmaxx
(
m_pad
)) {
391
wresize
(
m_pad
,
getmaxy
(
m_pad
),
display_width
);
392
}
393
wresize
(
m_scroll
,
display_height
, 1);
394
drawLog
();
395
drawScroll
();
396
}
397
402
void
scrollText
(
int
d
) {
403
m_active_line
+=
d
;
404
if
(
m_active_line
>
getcury
(
m_pad
) + 1) {
405
m_active_line
=
getcury
(
m_pad
) + 1;
406
}
407
else
if
(
m_written_lines
>
m_display_height
&&
m_active_line
<
m_display_height
) {
408
m_active_line
=
m_display_height
;
409
}
410
else
if
(
m_written_lines
<
m_display_height
&&
m_active_line
<
m_written_lines
) {
411
m_active_line
=
m_written_lines
;
412
}
413
drawLog
();
414
drawScroll
();
415
}
416
420
void
handleKeyPress
(
int
key
) {
421
switch
(
key
) {
422
case
KEY_DOWN
:
423
scrollText
(1);
424
break
;
425
case
KEY_UP
:
426
scrollText
(-1);
427
break
;
428
case
KEY_NPAGE
:
429
scrollText
(
LINES
);
430
break
;
431
case
KEY_PPAGE
:
432
scrollText
(-
LINES
);
433
break
;
434
default
:
435
break
;
436
}
437
}
438
442
std::vector<std::string>
getText
() {
443
// Scan line by line
444
std::vector<std::string>
term_lines
;
445
for
(
int
i
= 0;
i
<
m_written_lines
; ++
i
) {
446
// Note: We do not want the '\0' to be part of the final string, so we use the string constructor to prune those
447
std::vector<char>
buffer
(
m_display_width
+ 1,
'\0'
);
448
mvwinnstr
(
m_pad
,
i
, 0,
buffer
.data(),
m_display_width
- 2);
449
term_lines
.emplace_back(
buffer
.data());
450
boost::algorithm::trim(
term_lines
.back());
451
}
452
// Prune trailing empty lines
453
while
(!
term_lines
.empty() &&
term_lines
.back().empty()) {
454
term_lines
.pop_back();
455
}
456
return
term_lines
;
457
}
458
459
private
:
463
void
drawScroll
()
const
{
464
werase
(
m_scroll
);
465
466
int
max_selectable_line
=
m_written_lines
;
467
int
min_selectable_line
=
std::min
(
m_written_lines
,
m_display_height
);
468
int
displayed_line_offset
=
m_active_line
-
min_selectable_line
;
469
float
p =
std::max
(0.f,
std::min
(1.f,
static_cast<
float
>
(
displayed_line_offset
) /
470
static_cast<
float
>
(
max_selectable_line
-
min_selectable_line
)));
471
472
auto
scroll_marker_pos
=
static_cast<
int
>
(p *
static_cast<
float
>
(
m_display_height
- 1));
473
for
(
int
i
= 0;
i
<
m_display_height
; ++
i
) {
474
if
(
i
==
scroll_marker_pos
)
475
waddch
(
m_scroll
,
ACS_CKBOARD
|
COLOR_PAIR
(
m_scroll_ind_color
));
476
else
477
waddch
(
m_scroll
,
'|'
|
COLOR_PAIR
(
m_scroll_bar_color
));
478
}
479
pnoutrefresh
(
m_scroll
,
480
0, 0,
481
m_display_y
,
m_display_x
+
m_display_width
- 1,
482
m_display_y
+
m_display_height
- 1,
m_display_x
+
m_display_width
- 1
483
);
484
}
485
489
void
drawLog
()
const
{
490
int
pad_y
=
std::max
(
m_active_line
-
m_display_height
, 0);
491
pnoutrefresh
(
m_pad
,
492
pad_y
, 0,
// Pad coordinates
493
m_display_y
,
m_display_x
,
// Start screen coordinates
494
m_display_y
+
m_display_height
- 1,
m_display_x
+
m_display_width
- 2
// End screen coordinates
495
);
496
}
497
};
498
502
class
ProgressWidget
:
public
boost::noncopyable {
503
public
:
519
ProgressWidget
(
int
height,
int
width,
int
y
,
int
x
,
short
done_color
,
short
progress_color
)
520
:
m_window
(
newwin
(height, width,
y
,
x
)),
m_done_color
(
done_color
),
m_progress_color
(
progress_color
) {}
521
525
~ProgressWidget
() {
526
delwin
(
m_window
);
527
}
528
536
void
move
(
int
y
,
int
x
) {
537
mvwin
(
m_window
,
y
,
x
);
538
wnoutrefresh
(
m_window
);
539
}
540
548
void
resize
(
int
height,
int
width) {
549
wresize
(
m_window
, height, width);
550
wnoutrefresh
(
m_window
);
551
}
552
556
unsigned
getHeight
()
const
{
557
return
getmaxy
(
m_window
);
558
}
559
563
void
update
(
const
std::list<ProgressInfo>
& info) {
564
// Precalculate layout, so labels are aligned
565
size_t
value_position
=
sizeof
(
"Elapsed"
);
566
567
for
(
auto
&
entry
: info) {
568
if
(
entry
.m_label.size() >
value_position
) {
569
value_position
=
entry
.m_label.size();
570
}
571
}
572
value_position
++;
// Plus space
573
574
// Width of the bar is the with of the windows - a space - two brackets []
575
int
bar_width
=
getmaxx
(
m_window
) - 2 -
static_cast<
int
>
(
value_position
);
576
577
// Elapsed
578
auto
now =
std::chrono::steady_clock::now
();
579
auto
elapsed
= now -
m_started
;
580
581
// Restore position to the beginning
582
werase
(
m_window
);
583
584
// Now, print the actual progress
585
int
line
= 0;
586
for
(
auto
&
entry
: info) {
587
drawProgressLine
(
static_cast<
int
>
(
value_position
),
bar_width
,
line
,
entry
.m_label,
entry
.m_total,
entry
.m_done);
588
++
line
;
589
}
590
591
// Elapsed time
592
drawElapsed
(
static_cast<
int
>
(
value_position
),
elapsed
,
line
);
593
594
// Flush
595
wnoutrefresh
(
m_window
);
596
}
597
598
private
:
602
void
drawElapsed
(
size_t
value_position
,
const
std::chrono::steady_clock::duration&
elapsed
,
int
line
)
const
{
603
auto
h
= std::chrono::duration_cast<std::chrono::hours>(
elapsed
);
604
auto
m = std::chrono::duration_cast<std::chrono::minutes>(
elapsed
-
h
);
605
auto
s = std::chrono::duration_cast<std::chrono::seconds>(
elapsed
-
h
- m);
606
std::ostringstream
elapsed_str
;
607
elapsed_str
.fill(
'0'
);
608
elapsed_str
<<
std::setw
(2) <<
h
.count() <<
':'
<<
std::setw
(2) << m.count() <<
':'
<<
std::setw
(2) << s.count();
609
610
wattron
(
m_window
,
A_BOLD
);
611
mvwaddstr
(
m_window
,
line
, 0,
"Elapsed"
);
612
wattroff
(
m_window
,
A_BOLD
);
613
mvwaddstr
(
614
m_window
,
615
line
,
value_position
+ 1,
616
elapsed_str
.str().c_str()
617
);
618
}
619
623
void
drawProgressLine
(
int
value_position
,
int
bar_width
,
int
line
,
const
std::string
&
label
,
624
int
total,
int
done)
const
{
625
// Label
626
wattron
(
m_window
,
A_BOLD
);
627
mvwaddstr
(
m_window
,
line
, 0,
label
.c_str());
628
wattroff
(
m_window
,
A_BOLD
);
629
630
// Total number is unknown
631
if
(total <= 0) {
632
mvwprintw
(
m_window
,
line
,
value_position
+ 1,
"%d"
, done);
633
return
;
634
}
635
636
// Otherwise, report progress as a bar
637
float
ratio
=
static_cast<
float
>
(done) /
static_cast<
float
>
(total);
638
// This can happens sometimes, as a measurement could be notified before the deblending, for instance
639
if
(
ratio
> 1)
640
ratio
= 1.;
641
642
// Build the report string
643
std::ostringstream
bar;
644
bar << done <<
" / "
<< total <<
" ("
<<
std::fixed
<<
std::setprecision
(2) <<
ratio
* 100. <<
"%)"
;
645
646
// Attach as many spaces as needed to fill the screen width, minus brackets
647
bar <<
std::string
(
bar_width
- bar.str().size(),
' '
);
648
649
// Print label
650
wattron
(
m_window
,
A_BOLD
);
651
mvwaddstr
(
m_window
,
line
, 0,
label
.c_str());
652
wattroff
(
m_window
,
A_BOLD
);
653
mvwaddch
(
m_window
,
line
,
value_position
,
'['
);
654
655
// Completed
656
auto
bar_content
= bar.str();
657
auto
completed
=
static_cast<
int
>
(
static_cast<
float
>
(
bar_content
.size()) *
ratio
);
658
659
wattron
(
m_window
,
COLOR_PAIR
(
m_done_color
));
660
waddstr
(
m_window
,
bar_content
.substr(0,
completed
).c_str());
661
wattroff
(
m_window
,
COLOR_PAIR
(
m_done_color
));
662
663
// Rest
664
wattron
(
m_window
,
COLOR_PAIR
(
m_progress_color
));
665
waddstr
(
m_window
,
bar_content
.substr(
completed
).c_str());
666
wattroff
(
m_window
,
COLOR_PAIR
(2));
667
668
// Closing bracket
669
waddch
(
m_window
,
']'
);
670
}
671
672
WINDOW
*
m_window
;
673
std::chrono::steady_clock::time_point
m_started
=
std::chrono::steady_clock::now
();
674
short
m_done_color
;
675
short
m_progress_color
;
676
};
677
688
class
ProgressNCurses::Dashboard
{
689
private
:
690
std::unique_ptr<boost::thread>
m_ui_thread
;
691
std::list<ProgressInfo>
m_progress_info
;
692
std::mutex
m_progress_info_mutex
;
693
694
// stderr intercept
695
int
m_stderr_original
;
696
int
m_stderr_pipe
;
697
FILE
*
m_stderr
;
698
// stdout intercept
699
int
m_stdout_original
;
700
int
m_stdout_pipe
;
701
702
// Used to recover log into the standard output
703
std::vector<std::string>
m_log_text
;
704
705
std::atomic_bool
m_trigger_resize
{
false
};
706
std::atomic_bool
m_exit_loop
{
false
};
707
711
void
uiThread
() {
712
sem_wait
(&
ncurses_done
.m_semaphore);
713
// SIGTERM, SIGINT and SIGHUP should not be handled by this thread, or we will not be able to properly
714
// exit ncurses.
715
// Hopefully there should be no SIGABRT or SIGSEGV here. If there were, we will exit but we will not be able
716
// to restore the terminal state. Having an abort or a segmentation fault is a bug anyway.
717
sigset_t
set
;
718
sigaddset
(&
set
,
SIGTERM
);
719
sigaddset
(&
set
,
SIGINT
);
720
sigaddset
(&
set
,
SIGHUP
);
721
pthread_sigmask
(
SIG_BLOCK
, &
set
,
nullptr
);
722
// Enter ncurses
723
ncursesMode
();
724
// Recover file descriptors
725
dup2
(
m_stderr_original
,
STDERR_FILENO
);
726
dup2
(
m_stdout_original
,
STDOUT_FILENO
);
727
// Dump recovered text
728
for
(
const
auto
&
line
:
m_log_text
) {
729
std::cerr
<<
line
<<
std::endl
;
730
}
731
sem_post
(&
ncurses_done
.m_semaphore);
732
}
733
734
void
handleSignal
(
const
struct
pollfd
&
poll_fd
,
LogWidget
&
logWidget
) {
735
if
(
poll_fd
.revents &
POLLIN
) {
736
int
signal_no
;
737
if
(read(
signal_fds
[0], &
signal_no
,
sizeof
(
signal_no
)) ==
sizeof
(
signal_no
) &&
signal_no
==
SIGWINCH
) {
738
m_trigger_resize
=
true
;
739
endwin
();
740
refresh();
741
clear();
742
}
743
else
{
744
char
buf
[64];
745
logWidget
.write(
buf
,
snprintf
(
buf
,
sizeof
(
buf
),
"Caught signal %s\n"
,
strsignal
(
signal_no
)));
746
m_exit_loop
=
true
;
747
}
748
}
749
}
750
751
void
pipeToLog
(
const
struct
pollfd
&
poll_fd
,
int
pipe
,
LogWidget
& out)
const
{
752
if
(
poll_fd
.revents &
POLLIN
) {
753
ssize_t
nbytes;
754
char
buf
[64];
755
while
((nbytes = read(
pipe
, &
buf
,
sizeof
(
buf
))) > 0) {
756
out.
write
(
buf
, nbytes);
757
}
758
}
759
}
760
761
void
handleKeyPress
(
const
struct
pollfd
&
poll_fd
,
LogWidget
&
logWidget
)
const
{
762
if
(
poll_fd
.revents &
POLLIN
) {
763
int
key
=
wgetch
(
stdscr
);
764
if
(
key
!=
KEY_RESIZE
) {
765
logWidget
.handleKeyPress(
key
);
766
}
767
}
768
}
769
773
void
ncursesMode
() {
774
Screen
screen
(
m_stderr
,
stdin
);
775
776
// Log area
777
LogWidget
logWidget
(
778
LINES
- 1,
COLS
, 0, 0,
779
screen
.initColor(
COLOR_WHITE
,
COLOR_BLACK
),
screen
.initColor(
COLOR_WHITE
,
COLOR_WHITE
)
780
);
781
782
// Progress widget
783
ProgressWidget
progressWidget
(
784
1,
COLS
,
LINES
- 1, 0,
785
screen
.initColor(
COLOR_WHITE
,
COLOR_GREEN
),
screen
.initColor(
COLOR_WHITE
,
COLOR_BLACK
)
786
);
787
788
// File descriptors to watch for
789
struct
pollfd
poll_fds
[] = {
790
{
m_stderr_pipe
,
POLLIN
, 0},
791
{
m_stdout_pipe
,
POLLIN
, 0},
792
{
STDIN_FILENO
,
POLLIN
, 0},
793
{
signal_fds
[0],
POLLIN
, 0}
794
};
795
796
// Event loop
797
m_exit_loop
=
false
;
798
799
do
{
800
// There has been a signal
801
handleSignal
(
poll_fds
[3],
logWidget
);
802
803
// Resize widgets if needed
804
if
(
m_trigger_resize
) {
805
std::lock_guard<std::mutex>
p_lock
(
m_progress_info_mutex
);
806
progressWidget
.move(
static_cast<
int
>
(
LINES
-
m_progress_info
.size() - 1), 0);
807
progressWidget
.resize(
static_cast<
int
>
(
m_progress_info
.size() + 1),
COLS
);
808
logWidget
.resize(
LINES
-
progressWidget
.getHeight(),
COLS
);
809
m_trigger_resize
=
false
;
810
}
811
812
// There is output/error to redirect
813
pipeToLog
(
poll_fds
[0],
m_stderr_pipe
,
logWidget
);
814
pipeToLog
(
poll_fds
[1],
m_stdout_pipe
,
logWidget
);
815
816
// There is a key to read
817
handleKeyPress
(
poll_fds
[2],
logWidget
);
818
819
{
820
std::lock_guard<std::mutex>
p_lock
(
m_progress_info_mutex
);
821
progressWidget
.update(
m_progress_info
);
822
}
823
824
// Update screen
825
doupdate
();
826
827
// Wait for events
828
if
(
poll
(
poll_fds
, 4, 1000) < 0) {
829
// poll may return with EINTR if a signal happened halfway
830
m_exit_loop
= (
errno
!=
EINTR
);
831
}
832
}
while
(!
m_exit_loop
&& !boost::this_thread::interruption_requested());
833
m_log_text
=
logWidget
.getText();
834
}
835
836
public
:
842
Dashboard
() {
843
m_stderr_pipe
=
interceptFileDescriptor
(
STDERR_FILENO
, &
m_stderr_original
);
844
m_stdout_pipe
=
interceptFileDescriptor
(
STDOUT_FILENO
, &
m_stdout_original
);
845
int
new_stderr_fd
=
dup
(
m_stderr_original
);
846
if
(
new_stderr_fd
< 0) {
847
throw
std::system_error
(
errno
,
std::generic_category
());
848
}
849
m_stderr
=
fdopen
(
new_stderr_fd
,
"w"
);
850
m_ui_thread
= Euclid::make_unique<boost::thread>(
std::bind
(&
Dashboard::uiThread
,
this
));
851
}
852
857
~Dashboard
() {
858
if
(
m_ui_thread
) {
859
try
{
860
m_ui_thread
->interrupt();
861
if
(
m_ui_thread
->joinable()) {
862
m_ui_thread
->join();
863
}
864
}
865
catch
(...) {
866
// Ignore
867
}
868
}
869
fclose
(
m_stderr
);
870
// Unneeded duplicates now
871
close(
m_stderr_original
);
872
close(
m_stdout_original
);
873
close(
m_stderr_pipe
);
874
close(
m_stdout_pipe
);
875
}
876
880
void
update
(
const
std::list<ProgressInfo>
& info) {
881
std::lock_guard<std::mutex>
p_lock
(
m_progress_info_mutex
);
882
m_trigger_resize
= (
m_progress_info
.size() != info.size()) ||
m_trigger_resize
;
883
m_progress_info
= info;
884
}
885
};
886
887
ProgressNCurses::ProgressNCurses
() {
888
m_dashboard
=
make_unique<Dashboard>
();
889
}
890
891
ProgressNCurses::~ProgressNCurses
() =
default
;
892
893
bool
ProgressNCurses::isTerminalCapable
() {
894
return
isatty(
STDERR_FILENO
);
895
}
896
897
void
ProgressNCurses::handleMessage
(
const
std::list<ProgressInfo>
& info) {
898
if
(
m_dashboard
)
899
m_dashboard
->update(info);
900
}
901
902
void
ProgressNCurses::handleMessage
(
const
bool
& done) {
903
if
(done &&
m_dashboard
)
904
m_dashboard
.reset(
nullptr
);
905
}
906
907
}
// end SourceXtractor
x
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
Definition
MoffatModelFittingTask.cpp:94
y
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
Definition
MoffatModelFittingTask.cpp:94
ProgressNCurses.h
std::cerr
std::ostringstream
std::string
std::bind
T bind(T... args)
std::FILE
SourceXtractor::LogWidget
Definition
ProgressNCurses.cpp:301
SourceXtractor::LogWidget::drawLog
void drawLog() const
Definition
ProgressNCurses.cpp:489
SourceXtractor::LogWidget::m_scroll_bar_color
int m_scroll_bar_color
Definition
ProgressNCurses.cpp:315
SourceXtractor::LogWidget::LogWidget
LogWidget(int display_height, int display_width, int display_y, int display_x, short bar_color, short ind_color)
Definition
ProgressNCurses.cpp:338
SourceXtractor::LogWidget::write
void write(const char *data, ssize_t nchars)
Definition
ProgressNCurses.cpp:360
SourceXtractor::LogWidget::BUFFER_INCREASE_STEP_SIZE
static const int BUFFER_INCREASE_STEP_SIZE
Definition
ProgressNCurses.cpp:318
SourceXtractor::LogWidget::drawScroll
void drawScroll() const
Definition
ProgressNCurses.cpp:463
SourceXtractor::LogWidget::m_display_x
int m_display_x
Definition
ProgressNCurses.cpp:309
SourceXtractor::LogWidget::getText
std::vector< std::string > getText()
Definition
ProgressNCurses.cpp:442
SourceXtractor::LogWidget::handleKeyPress
void handleKeyPress(int key)
Definition
ProgressNCurses.cpp:420
SourceXtractor::LogWidget::m_active_line
int m_active_line
Definition
ProgressNCurses.cpp:313
SourceXtractor::LogWidget::m_scroll_ind_color
int m_scroll_ind_color
Definition
ProgressNCurses.cpp:316
SourceXtractor::LogWidget::m_pad
WINDOW * m_pad
Definition
ProgressNCurses.cpp:303
SourceXtractor::LogWidget::m_display_height
int m_display_height
Definition
ProgressNCurses.cpp:306
SourceXtractor::LogWidget::scrollText
void scrollText(int d)
Definition
ProgressNCurses.cpp:402
SourceXtractor::LogWidget::resize
void resize(int display_height, int display_width)
Definition
ProgressNCurses.cpp:384
SourceXtractor::LogWidget::m_display_y
int m_display_y
Definition
ProgressNCurses.cpp:308
SourceXtractor::LogWidget::BUFFER_MAX_SIZE
static const int BUFFER_MAX_SIZE
Definition
ProgressNCurses.cpp:319
SourceXtractor::LogWidget::m_display_width
int m_display_width
Definition
ProgressNCurses.cpp:307
SourceXtractor::LogWidget::operator=
const LogWidget & operator=(const LogWidget &)=delete
SourceXtractor::LogWidget::~LogWidget
virtual ~LogWidget()
Definition
ProgressNCurses.cpp:349
SourceXtractor::LogWidget::m_scroll
WINDOW * m_scroll
Definition
ProgressNCurses.cpp:304
SourceXtractor::LogWidget::LogWidget
LogWidget(const LogWidget &)=delete
SourceXtractor::LogWidget::m_written_lines
int m_written_lines
Definition
ProgressNCurses.cpp:311
SourceXtractor::ProgressNCurses::Dashboard
Definition
ProgressNCurses.cpp:688
SourceXtractor::ProgressNCurses::Dashboard::~Dashboard
~Dashboard()
Definition
ProgressNCurses.cpp:857
SourceXtractor::ProgressNCurses::Dashboard::uiThread
void uiThread()
Definition
ProgressNCurses.cpp:711
SourceXtractor::ProgressNCurses::Dashboard::handleKeyPress
void handleKeyPress(const struct pollfd &poll_fd, LogWidget &logWidget) const
Definition
ProgressNCurses.cpp:761
SourceXtractor::ProgressNCurses::Dashboard::m_trigger_resize
std::atomic_bool m_trigger_resize
Definition
ProgressNCurses.cpp:705
SourceXtractor::ProgressNCurses::Dashboard::m_ui_thread
std::unique_ptr< boost::thread > m_ui_thread
Definition
ProgressNCurses.cpp:690
SourceXtractor::ProgressNCurses::Dashboard::m_exit_loop
std::atomic_bool m_exit_loop
Definition
ProgressNCurses.cpp:706
SourceXtractor::ProgressNCurses::Dashboard::m_progress_info
std::list< ProgressInfo > m_progress_info
Definition
ProgressNCurses.cpp:691
SourceXtractor::ProgressNCurses::Dashboard::m_stderr_pipe
int m_stderr_pipe
Definition
ProgressNCurses.cpp:696
SourceXtractor::ProgressNCurses::Dashboard::Dashboard
Dashboard()
Definition
ProgressNCurses.cpp:842
SourceXtractor::ProgressNCurses::Dashboard::update
void update(const std::list< ProgressInfo > &info)
Definition
ProgressNCurses.cpp:880
SourceXtractor::ProgressNCurses::Dashboard::m_stdout_pipe
int m_stdout_pipe
Definition
ProgressNCurses.cpp:700
SourceXtractor::ProgressNCurses::Dashboard::m_stdout_original
int m_stdout_original
Definition
ProgressNCurses.cpp:699
SourceXtractor::ProgressNCurses::Dashboard::pipeToLog
void pipeToLog(const struct pollfd &poll_fd, int pipe, LogWidget &out) const
Definition
ProgressNCurses.cpp:751
SourceXtractor::ProgressNCurses::Dashboard::handleSignal
void handleSignal(const struct pollfd &poll_fd, LogWidget &logWidget)
Definition
ProgressNCurses.cpp:734
SourceXtractor::ProgressNCurses::Dashboard::m_stderr_original
int m_stderr_original
Definition
ProgressNCurses.cpp:695
SourceXtractor::ProgressNCurses::Dashboard::m_stderr
FILE * m_stderr
Definition
ProgressNCurses.cpp:697
SourceXtractor::ProgressNCurses::Dashboard::m_log_text
std::vector< std::string > m_log_text
Definition
ProgressNCurses.cpp:703
SourceXtractor::ProgressNCurses::Dashboard::m_progress_info_mutex
std::mutex m_progress_info_mutex
Definition
ProgressNCurses.cpp:692
SourceXtractor::ProgressNCurses::Dashboard::ncursesMode
void ncursesMode()
Definition
ProgressNCurses.cpp:773
SourceXtractor::ProgressNCurses::m_dashboard
std::unique_ptr< Dashboard > m_dashboard
Definition
ProgressNCurses.h:64
SourceXtractor::ProgressNCurses::isTerminalCapable
static bool isTerminalCapable()
Definition
ProgressNCurses.cpp:893
SourceXtractor::ProgressNCurses::handleMessage
void handleMessage(const std::list< ProgressInfo > &info) override
Definition
ProgressNCurses.cpp:897
SourceXtractor::ProgressNCurses::~ProgressNCurses
virtual ~ProgressNCurses()
SourceXtractor::ProgressNCurses::ProgressNCurses
ProgressNCurses()
Definition
ProgressNCurses.cpp:887
SourceXtractor::ProgressWidget
Set of progress bars/information entries.
Definition
ProgressNCurses.cpp:502
SourceXtractor::ProgressWidget::~ProgressWidget
~ProgressWidget()
Definition
ProgressNCurses.cpp:525
SourceXtractor::ProgressWidget::drawElapsed
void drawElapsed(size_t value_position, const std::chrono::steady_clock::duration &elapsed, int line) const
Definition
ProgressNCurses.cpp:602
SourceXtractor::ProgressWidget::m_progress_color
short m_progress_color
Definition
ProgressNCurses.cpp:675
SourceXtractor::ProgressWidget::ProgressWidget
ProgressWidget(int height, int width, int y, int x, short done_color, short progress_color)
Definition
ProgressNCurses.cpp:519
SourceXtractor::ProgressWidget::m_window
WINDOW * m_window
Definition
ProgressNCurses.cpp:672
SourceXtractor::ProgressWidget::m_started
std::chrono::steady_clock::time_point m_started
Definition
ProgressNCurses.cpp:673
SourceXtractor::ProgressWidget::resize
void resize(int height, int width)
Definition
ProgressNCurses.cpp:548
SourceXtractor::ProgressWidget::m_done_color
short m_done_color
Definition
ProgressNCurses.cpp:674
SourceXtractor::ProgressWidget::move
void move(int y, int x)
Definition
ProgressNCurses.cpp:536
SourceXtractor::ProgressWidget::update
void update(const std::list< ProgressInfo > &info)
Definition
ProgressNCurses.cpp:563
SourceXtractor::ProgressWidget::drawProgressLine
void drawProgressLine(int value_position, int bar_width, int line, const std::string &label, int total, int done) const
Definition
ProgressNCurses.cpp:623
SourceXtractor::ProgressWidget::getHeight
unsigned getHeight() const
Definition
ProgressNCurses.cpp:556
SourceXtractor::Screen
Wrap the terminal into a singleton.
Definition
ProgressNCurses.cpp:113
SourceXtractor::Screen::m_old_redisplay
rl_voidfunc_t * m_old_redisplay
Definition
ProgressNCurses.cpp:219
SourceXtractor::Screen::m_screen
SCREEN * m_screen
Definition
ProgressNCurses.cpp:218
SourceXtractor::Screen::m_color_idx
short m_color_idx
Definition
ProgressNCurses.cpp:217
SourceXtractor::Screen::~Screen
virtual ~Screen()
Definition
ProgressNCurses.cpp:191
SourceXtractor::Screen::initColor
short initColor(short fg, short bg)
Definition
ProgressNCurses.cpp:211
SourceXtractor::Screen::Screen
Screen(FILE *outfd, FILE *infd)
Definition
ProgressNCurses.cpp:123
std::endl
T endl(T... args)
std::fclose
T fclose(T... args)
std::fixed
T fixed(T... args)
std::snprintf
T snprintf(T... args)
std::function
std::generic_category
T generic_category(T... args)
std::max
T max(T... args)
std::memset
T memset(T... args)
std::min
T min(T... args)
std::mutex
Euclid::make_unique
std::unique_ptr< T > make_unique(Args &&... args)
SourceXtractor
Definition
Aperture.h:30
SourceXtractor::handleResizeSignal
static void handleResizeSignal(int)
Definition
ProgressNCurses.cpp:292
SourceXtractor::sigterm_action
static struct sigaction sigterm_action
Definition
ProgressNCurses.cpp:40
SourceXtractor::sigcont_action
static struct sigaction sigcont_action
Definition
ProgressNCurses.cpp:42
SourceXtractor::override_rl_display
static void override_rl_display(void)
Definition
ProgressNCurses.cpp:107
SourceXtractor::prev_signal
static std::map< int, struct sigaction > prev_signal
Definition
ProgressNCurses.cpp:44
SourceXtractor::handleTerminatingSignal
static void handleTerminatingSignal(int s)
Definition
ProgressNCurses.cpp:238
SourceXtractor::sigwich_action
static struct sigaction sigwich_action
Definition
ProgressNCurses.cpp:43
SourceXtractor::handleStopSignal
static void handleStopSignal(int s)
Definition
ProgressNCurses.cpp:269
SourceXtractor::signal_fds
static int signal_fds[2]
Definition
ProgressNCurses.cpp:47
SourceXtractor::handleContinuationSignal
static void handleContinuationSignal(int s)
Definition
ProgressNCurses.cpp:283
SourceXtractor::interceptFileDescriptor
static int interceptFileDescriptor(int old_fd, int *backup_fd)
Definition
ProgressNCurses.cpp:74
SourceXtractor::sigstop_action
static struct sigaction sigstop_action
Definition
ProgressNCurses.cpp:41
SourceXtractor::ncurses_done
static ncurses_done_t ncurses_done
Definition
ProgressNCurses.cpp:56
std::chrono::steady_clock::now
T now(T... args)
std::raise
T raise(T... args)
std::ratio
std::set
std::setprecision
T setprecision(T... args)
std::setw
T setw(T... args)
SourceXtractor::ncurses_done_t
Definition
ProgressNCurses.cpp:50
SourceXtractor::ncurses_done_t::m_semaphore
sem_t m_semaphore
Definition
ProgressNCurses.cpp:51
SourceXtractor::ncurses_done_t::ncurses_done_t
ncurses_done_t()
Definition
ProgressNCurses.cpp:52
std::system_error
Generated by
1.10.0