mirror of
https://gitlab.com/procps-ng/procps.git
synced 2024-11-27 03:54:06 +08:00
* Implemented key control. Keys don't interrupt running cmd. * Implemented ' ' (issue immediate cmd run, partial #240), 'q' (quit, #270), 's' (take screenshot). * -n limited to 31 days (limitation of select()). * Prepended all xerr()s with endwin() to have the msg displayed.
This commit is contained in:
parent
ba70e47c33
commit
bf66c76f6e
21
man/watch.1
21
man/watch.1
@ -11,7 +11,7 @@
|
||||
.\" (at your option) any later version.
|
||||
.\"
|
||||
.\"
|
||||
.TH WATCH 1 "2023-07-15" "procps-ng" "User Commands"
|
||||
.TH WATCH 1 "2023-07-28" "procps-ng" "User Commands"
|
||||
.SH NAME
|
||||
watch \- execute a program periodically, showing output fullscreen
|
||||
.SH SYNOPSIS
|
||||
@ -51,9 +51,9 @@ the cause of \fIcommand\fR termination, the exit code will be 128 + \fBn\fR.
|
||||
Exit when the output of \fIcommand\fR changes.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-interval\fR \fIseconds\fR
|
||||
Specify update interval. \fBwatch\fR will not allow quicker than 0.1 second
|
||||
interval, in which the smaller values are converted. Both '.' and ',' work for
|
||||
any locale. The \fBWATCH_INTERVAL\fR environment variable can be used to
|
||||
Specify update interval. Values smaller than 0.1 and larger than 2678400 (31
|
||||
days) are converted into these respective bounds. Both '.' and ',' work for any
|
||||
locale. The \fBWATCH_INTERVAL\fR environment variable can be used to
|
||||
persistently set a non-default interval (following the same rules and
|
||||
formatting).
|
||||
.TP
|
||||
@ -90,6 +90,19 @@ Display help text and exit.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Display version information and exit.
|
||||
.SH "KEY CONTROL"
|
||||
.TP
|
||||
.B spacebar
|
||||
Issue \fIcommand\fR immediately. If it's running at the moment, it is not
|
||||
interrupted and its next round will start without delay.
|
||||
.TP
|
||||
.B q
|
||||
Quit \fBwatch\fR. It currently does not interrupt a running \fIcommand\fR (as
|
||||
opposed to terminating signals, such as the SIGKILL following Ctrl+C).
|
||||
.TP
|
||||
.B s
|
||||
Take a screenshot. It is saved in the file \fBwatch-dump\fR in the working
|
||||
directory.
|
||||
.SH "EXIT STATUS"
|
||||
.TP
|
||||
.B 0
|
||||
|
376
src/watch.c
376
src/watch.c
@ -29,37 +29,33 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "c.h"
|
||||
#include "config.h"
|
||||
#include "fileutils.h"
|
||||
#include "nls.h"
|
||||
#include "strutils.h"
|
||||
#include "xalloc.h"
|
||||
#include "signals.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#ifdef WITH_WATCH8BIT
|
||||
# define _XOPEN_SOURCE_EXTENDED 1
|
||||
# include <wctype.h>
|
||||
# include <ncursesw/ncurses.h>
|
||||
#else
|
||||
# include <ctype.h>
|
||||
# include <ncurses.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#ifdef WITH_WATCH8BIT
|
||||
# define _XOPEN_SOURCE_EXTENDED 1
|
||||
# include <wctype.h>
|
||||
# include <ncursesw/ncurses.h>
|
||||
#else
|
||||
# include <ncurses.h>
|
||||
#endif /* WITH_WATCH8BIT */
|
||||
#include "c.h"
|
||||
#include "config.h"
|
||||
#include "fileutils.h"
|
||||
#include "nls.h"
|
||||
#include "signals.h"
|
||||
#include "strutils.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
#ifdef FORCE_8BIT
|
||||
# undef isprint
|
||||
@ -101,14 +97,17 @@
|
||||
|
||||
static uf16 flags;
|
||||
static int height, width;
|
||||
static bool first_screen = true, screen_size_changed;
|
||||
static bool first_screen = true, screen_size_changed, screen_changed;
|
||||
static long double interval_real = 2;
|
||||
static char *command;
|
||||
static int command_len;
|
||||
static char *const *command_argv;
|
||||
static const char *dumpfile = "watch-dump";
|
||||
|
||||
|
||||
|
||||
// don't use EXIT_FAILURE, it can be anything and manpage makes guarantees about
|
||||
// exitcodes
|
||||
static void __attribute__ ((__noreturn__)) usage(FILE * out)
|
||||
{
|
||||
fputs(USAGE_HEADER, out);
|
||||
@ -138,13 +137,9 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
|
||||
exit(out == stderr ? 1 : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// don't use EXIT_FAILURE, it can be anything and manpage makes guarantees about
|
||||
// exitcodes
|
||||
static void __attribute__ ((__noreturn__)) endwin_exit(int status)
|
||||
{
|
||||
endwin();
|
||||
exit(status);
|
||||
}
|
||||
#define endwin_xerr(...) do { endwin(); xerr(__VA_ARGS__); } while (0)
|
||||
#define endwin_xerrx(...) do { endwin(); xerrx(__VA_ARGS__); } while (0)
|
||||
#define endwin_exit(status) do { endwin(); exit(status); } while (0)
|
||||
|
||||
static void die(int notused __attribute__ ((__unused__)))
|
||||
{
|
||||
@ -396,11 +391,44 @@ static void process_ansi(FILE * fp)
|
||||
|
||||
|
||||
|
||||
typedef uf64 watch_usec_t;
|
||||
#define USECS_PER_SEC ((watch_usec_t)1000000) // same type intentional
|
||||
#define NSECS_PER_USEC ((watch_usec_t)1000)
|
||||
// TODO: gettext everywhere
|
||||
static void screenshot(void) {
|
||||
const int f = open(dumpfile, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
|
||||
if (f == -1)
|
||||
endwin_xerr(1, "open(%s)", dumpfile);
|
||||
|
||||
// TODO: range checks
|
||||
#ifdef WITH_WATCH8BIT
|
||||
const size_t bufsize = width*CCHARW_MAX*MB_CUR_MAX + 1;
|
||||
#else
|
||||
const size_t bufsize = width + 1;
|
||||
#endif
|
||||
char *const buf = xmalloc(bufsize);
|
||||
int yin, xout;
|
||||
for (int y=0; y<height; ++y) {
|
||||
yin = mvinnstr(y, 0, buf, bufsize-1);
|
||||
if (yin == ERR) // screen resized
|
||||
yin = 0;
|
||||
buf[yin] = '\n';
|
||||
for (int x=0; x<yin+1; x+=xout) {
|
||||
xout = write(f, buf+x, (yin+1)-x);
|
||||
if (xout == -1)
|
||||
endwin_xerr(1, "write(%s)", dumpfile);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
|
||||
if (close(f) == -1)
|
||||
endwin_xerr(1, "close(%s)", dumpfile);
|
||||
if (screen_size_changed)
|
||||
endwin_xerrx(1, "error taking screenshot");
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef uf64 watch_usec_t;
|
||||
#define USECS_PER_SEC ((watch_usec_t)1000000) // same type
|
||||
|
||||
/* get current time in usec */
|
||||
static inline watch_usec_t get_time_usec(void)
|
||||
{
|
||||
struct timeval now;
|
||||
@ -456,10 +484,8 @@ static void output_header(void)
|
||||
|
||||
// never freed for !WATCH8BIT
|
||||
lheader_len = asprintf(&lheader, _("Every %.1Lfs: "), interval_real);
|
||||
if (lheader_len == -1) {
|
||||
xerr(0, "%s()", __func__);
|
||||
endwin_exit(1);
|
||||
}
|
||||
if (lheader_len == -1)
|
||||
endwin_xerr(1, "%s()", __func__);
|
||||
#ifdef WITH_WATCH8BIT
|
||||
// never freed
|
||||
wlheader_wid = mbswidth(lheader, &wlheader);
|
||||
@ -564,11 +590,11 @@ static void output_lowheader(watch_usec_t span, uint8_t exitcode) {
|
||||
int skip;
|
||||
// TODO: gettext everywhere
|
||||
if (span > USECS_PER_SEC * 24 * 60 * 60)
|
||||
snprintf(s, sizeof(s), "%s >1 %s (%u)", "in", "day", (unsigned)exitcode);
|
||||
snprintf(s, sizeof(s), "%s >1 %s (%" PRIu8 ")", "in", "day", exitcode);
|
||||
// for the localized decimal point
|
||||
else if (span < 1000)
|
||||
snprintf(s, sizeof(s), "%s <%.3f%s (%u)", "in", 0.001, "s", (unsigned)exitcode);
|
||||
else snprintf(s, sizeof(s), "%s %.3Lf%s (%u)", "in", (long double)span/USECS_PER_SEC, "s", (unsigned)exitcode);
|
||||
snprintf(s, sizeof(s), "%s <%.3f%s (%" PRIu8 ")", "in", 0.001, "s", exitcode);
|
||||
else snprintf(s, sizeof(s), "%s %.3Lf%s (%" PRIu8 ")", "in", (long double)span/USECS_PER_SEC, "s", exitcode);
|
||||
|
||||
move(1, 0);
|
||||
clrtoeol();
|
||||
@ -728,28 +754,26 @@ static bool display_char(int y, int x, Xint c, int cwid) {
|
||||
|
||||
|
||||
|
||||
static inline void skiptoeol(FILE *f)
|
||||
static void skiptoeol(FILE *f)
|
||||
{
|
||||
Xint c;
|
||||
do c = Xgetc(f);
|
||||
while (c != XEOF && c != XL('\n'));
|
||||
}
|
||||
|
||||
static inline void skiptoeof(FILE *f) {
|
||||
static void skiptoeof(FILE *f) {
|
||||
unsigned char dummy[4096];
|
||||
while (! feof(f) && ! ferror(f))
|
||||
(void)!fread(dummy, sizeof(dummy), 1, f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline bool my_clrtoeol(int y, int x)
|
||||
static bool my_clrtoeol(int y, int x)
|
||||
{
|
||||
if (flags & WATCH_ALL_DIFF) {
|
||||
bool screen_changed = false;
|
||||
bool changed = false;
|
||||
while (x < width)
|
||||
screen_changed = display_char(y, x++, XL(' '), 1) || screen_changed;
|
||||
return screen_changed;
|
||||
changed = display_char(y, x++, XL(' '), 1) || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
// make sure color is preserved
|
||||
@ -758,17 +782,17 @@ static inline bool my_clrtoeol(int y, int x)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool my_clrtobot(int y, int x)
|
||||
static bool my_clrtobot(int y, int x)
|
||||
{
|
||||
if (flags & WATCH_ALL_DIFF) {
|
||||
bool screen_changed = false;
|
||||
bool changed = false;
|
||||
while (y < height) {
|
||||
while (x < width)
|
||||
screen_changed = display_char(y, x++, XL(' '), 1) || screen_changed;
|
||||
changed = display_char(y, x++, XL(' '), 1) || changed;
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
return screen_changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
// make sure color is preserved
|
||||
@ -779,34 +803,25 @@ static inline bool my_clrtobot(int y, int x)
|
||||
|
||||
|
||||
|
||||
#define RUNCMD_EXITCODE 0xff
|
||||
#define RUNCMD_SCRCHANGED 0x100
|
||||
|
||||
// Returns n, where n&RUNCMD_EXITCODE = cmd exitcode, n&RUNCMD_SCRCHANGED =
|
||||
// screen change status.
|
||||
//
|
||||
// When first_screen initially, SCRCHANGED returns false. Otherwise, when
|
||||
// WATCH_ALL_DIFF is false, return value is unspecified. Otherwise, returns
|
||||
// true <==> the screen changed.
|
||||
// Sets screen_changed: when first_screen, screen_changed=false. Otherwise, when
|
||||
// ! WATCH_ALL_DIFF, screen_changed will be unspecified. Otherwise,
|
||||
// screen_changed=true <==> the screen changed.
|
||||
//
|
||||
// Make sure not to leak system resources (incl. fds, processes). Suggesting
|
||||
// -D_XOPEN_SOURCE=600 and an EINTR loop around every fclose() as well.
|
||||
static uf16 run_command(void)
|
||||
static uint8_t run_command(void)
|
||||
{
|
||||
int pipefd[2], status; // [0] = output, [1] = input
|
||||
if (pipe(pipefd) < 0) {
|
||||
xerr(0, _("unable to create IPC pipes"));
|
||||
endwin_exit(2);
|
||||
}
|
||||
if (pipe(pipefd) < 0)
|
||||
endwin_xerr(2, _("unable to create IPC pipes"));
|
||||
// child will share buffered data, will print it at fclose()
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child < 0) {
|
||||
xerr(0, _("unable to fork process"));
|
||||
endwin_exit(2);
|
||||
} else if (child == 0) { /* in child */
|
||||
if (child < 0)
|
||||
endwin_xerr(2, _("unable to fork process"));
|
||||
else if (child == 0) { /* in child */
|
||||
// stdout/err can't be used here. Avoid xerr(), close_stdout(), ...
|
||||
// fclose() so as not to confuse _Exit().
|
||||
fclose(stdout);
|
||||
@ -848,16 +863,14 @@ static uf16 run_command(void)
|
||||
/* otherwise, we're in parent */
|
||||
|
||||
while (close(pipefd[1]) == -1 && errno == EINTR) ;
|
||||
FILE *p;
|
||||
if ((p = fdopen(pipefd[0], "r")) == NULL) {
|
||||
xerr(0, _("fdopen"));
|
||||
endwin_exit(2);
|
||||
}
|
||||
FILE *p = fdopen(pipefd[0], "r");
|
||||
if (! p)
|
||||
endwin_xerr(2, _("fdopen"));
|
||||
setvbuf(p, NULL, _IOFBF, BUFSIZ); // We'll getc() from it. A lot.
|
||||
|
||||
Xint c, carry = XEOF;
|
||||
int cwid, y, x; // cwid = character width in terminal columns
|
||||
bool screen_changed = false;
|
||||
screen_changed = false;
|
||||
|
||||
for (y = flags&WATCH_NOTITLE?0:2; y < height; ++y) {
|
||||
x = 0;
|
||||
@ -951,30 +964,14 @@ static uf16 run_command(void)
|
||||
|
||||
/* harvest child process and get status, propagated from command */
|
||||
// TODO: gettext string no longer used
|
||||
bool childgone = false;
|
||||
while (waitpid(child, &status, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
childgone = true;
|
||||
break;
|
||||
}
|
||||
if (errno != EINTR)
|
||||
return 0x7f;
|
||||
}
|
||||
|
||||
// The possibilities:
|
||||
// 1. with -x: execvp() never happens or fails
|
||||
// 2. with -x: execvp() succeeds
|
||||
// 3. without -x: the immediate child terminates, but not because it's
|
||||
// reporting result from system()
|
||||
// 4. without -x: the immediate child is reporting result from system()
|
||||
if (childgone)
|
||||
status = 0x7f;
|
||||
else if (WIFEXITED(status))
|
||||
status = WEXITSTATUS(status);
|
||||
else {
|
||||
assert(WIFSIGNALED(status));
|
||||
status = 0x80 + (WTERMSIG(status)&0x7f);
|
||||
}
|
||||
|
||||
return screen_changed << 8 | status;
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
assert(WIFSIGNALED(status));
|
||||
return 0x80 + (WTERMSIG(status) & 0x7f);
|
||||
}
|
||||
|
||||
|
||||
@ -1028,18 +1025,21 @@ static void get_terminal_size(void)
|
||||
putenv(env_col_buf);
|
||||
}
|
||||
}
|
||||
|
||||
assert(width > 0 && height > 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int optc;
|
||||
watch_usec_t last_tick = 0, interval, t;
|
||||
struct timespec tosleep;
|
||||
int i;
|
||||
watch_usec_t interval, last_tick = 0, t;
|
||||
long max_cycles = 1, cycle_count = 1;
|
||||
uf16 cmd_status;
|
||||
|
||||
fd_set select_stdin;
|
||||
uint8_t cmdexit;
|
||||
struct timeval tosleep;
|
||||
bool dontsleep, scrdumped;
|
||||
const struct option longopts[] = {
|
||||
{"color", no_argument, 0, 'c'},
|
||||
{"no-color", no_argument, 0, 'C'},
|
||||
@ -1059,6 +1059,8 @@ int main(int argc, char *argv[])
|
||||
{0}
|
||||
};
|
||||
|
||||
atexit(close_stdout);
|
||||
setbuf(stdin, NULL); // for select()
|
||||
#ifdef HAVE_PROGRAM_INVOCATION_NAME
|
||||
program_invocation_name = program_invocation_short_name;
|
||||
#endif
|
||||
@ -1068,7 +1070,6 @@ int main(int argc, char *argv[])
|
||||
setlocale(LC_ALL, "");
|
||||
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||
textdomain(PACKAGE);
|
||||
atexit(close_stdout);
|
||||
|
||||
#ifdef WITH_COLORWATCH
|
||||
flags |= WATCH_COLOR;
|
||||
@ -1078,10 +1079,8 @@ int main(int argc, char *argv[])
|
||||
if(interval_string != NULL)
|
||||
interval_real = strtod_nol_or_err(interval_string, _("Could not parse interval from WATCH_INTERVAL"));
|
||||
|
||||
while ((optc =
|
||||
getopt_long(argc, argv, "+bCced::ghq:n:prtwvx", longopts, (int *)0))
|
||||
!= EOF) {
|
||||
switch (optc) {
|
||||
while ((i = getopt_long(argc,argv,"+bCced::ghq:n:prtwvx",longopts,NULL)) != EOF) {
|
||||
switch (i) {
|
||||
case 'b':
|
||||
flags |= WATCH_BEEP;
|
||||
break;
|
||||
@ -1141,9 +1140,7 @@ int main(int argc, char *argv[])
|
||||
if (optind >= argc)
|
||||
usage(stderr);
|
||||
|
||||
/* save for later */
|
||||
command_argv = argv + optind;
|
||||
|
||||
command_argv = argv + optind; // for exec*()
|
||||
command_len = strlen(argv[optind]);
|
||||
command = xmalloc(command_len+1);
|
||||
memcpy(command, argv[optind++], command_len+1);
|
||||
@ -1158,30 +1155,27 @@ int main(int argc, char *argv[])
|
||||
command[command_len] = '\0';
|
||||
}
|
||||
|
||||
tzset();
|
||||
// interval_real must
|
||||
// * be >= 0.1 (program design)
|
||||
// * fit in time_t (in struct timeval), which may be 32b signed
|
||||
// * be <=31 days (limitation of select(), as per POSIX 2001)
|
||||
if (interval_real < 0.1)
|
||||
interval_real = 0.1;
|
||||
// interval [s] must fit in time_t (in struct timespec), which might be 32b
|
||||
// signed
|
||||
if (interval_real >= 1UL << 31)
|
||||
interval_real = (1UL << 31) - 1;
|
||||
if (interval_real > 60L * 60 * 24 * 31)
|
||||
interval_real = 60L * 60 * 24 * 31;
|
||||
interval = interval_real * USECS_PER_SEC;
|
||||
t = get_time_usec();
|
||||
// make sure to start
|
||||
if (interval > t)
|
||||
interval = t;
|
||||
tzset();
|
||||
|
||||
get_terminal_size();
|
||||
initscr();
|
||||
FD_ZERO(&select_stdin);
|
||||
|
||||
// Catch keyboard interrupts so we can put tty back in a sane state.
|
||||
// After initscr(). It may replace handlers.
|
||||
signal(SIGINT, die);
|
||||
signal(SIGTERM, die);
|
||||
signal(SIGHUP, die);
|
||||
signal(SIGWINCH, winch_handler);
|
||||
|
||||
/* Set up tty for curses use. */
|
||||
get_terminal_size();
|
||||
initscr(); // succeeds or exit()s, may install sig handlers
|
||||
if (flags & WATCH_COLOR) {
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
@ -1198,7 +1192,6 @@ int main(int argc, char *argv[])
|
||||
while (1) {
|
||||
reset_ansi();
|
||||
set_ansi_attribute(-1, NULL);
|
||||
|
||||
if (screen_size_changed) {
|
||||
screen_size_changed = false; // "atomic" test-and-set
|
||||
get_terminal_size();
|
||||
@ -1206,67 +1199,94 @@ int main(int argc, char *argv[])
|
||||
first_screen = true;
|
||||
}
|
||||
|
||||
output_header();
|
||||
t = get_time_usec();
|
||||
if (! (flags & WATCH_NORERUN) || t - last_tick >= interval) {
|
||||
output_header();
|
||||
if (flags & WATCH_PRECISE)
|
||||
last_tick = t;
|
||||
cmd_status = run_command();
|
||||
if (! (flags & WATCH_PRECISE)) {
|
||||
last_tick = get_time_usec();
|
||||
output_lowheader(last_tick - t, cmd_status & RUNCMD_EXITCODE);
|
||||
if (flags & WATCH_PRECISE)
|
||||
last_tick = t;
|
||||
cmdexit = run_command();
|
||||
if (flags & WATCH_PRECISE)
|
||||
output_lowheader(get_time_usec() - t, cmdexit);
|
||||
else {
|
||||
last_tick = get_time_usec();
|
||||
output_lowheader(last_tick - t, cmdexit);
|
||||
}
|
||||
|
||||
if (cmdexit) {
|
||||
if (flags & WATCH_BEEP)
|
||||
beep(); // doesn't require refresh()
|
||||
if (flags & WATCH_ERREXIT) {
|
||||
// TODO: Hard to see when there's cmd output around it. Add
|
||||
// spaces or move to lowheader.
|
||||
mvaddstr(height-1, 0, _("command exit with a non-zero status, press a key to exit"));
|
||||
i = fcntl(STDIN_FILENO, F_GETFL);
|
||||
if (i >= 0 && fcntl(STDIN_FILENO, F_SETFL, i|O_NONBLOCK) >= 0) {
|
||||
while (getchar() != EOF) ;
|
||||
fcntl(STDIN_FILENO, F_SETFL, i);
|
||||
}
|
||||
refresh();
|
||||
getchar();
|
||||
endwin_exit(cmdexit);
|
||||
}
|
||||
else output_lowheader(get_time_usec() - t, cmd_status & RUNCMD_EXITCODE);
|
||||
}
|
||||
|
||||
if (cmd_status & RUNCMD_EXITCODE) {
|
||||
if (flags & WATCH_BEEP)
|
||||
beep();
|
||||
if (flags & WATCH_ERREXIT) {
|
||||
int stdinfl = fcntl(STDIN_FILENO, F_GETFL);
|
||||
if ( stdinfl >= 0 &&
|
||||
fcntl(STDIN_FILENO, F_SETFL, stdinfl|O_NONBLOCK) >= 0
|
||||
) {
|
||||
while (getchar() != EOF) ;
|
||||
fcntl(STDIN_FILENO, F_SETFL, stdinfl);
|
||||
}
|
||||
|
||||
// TODO: Hard to see when there's cmd output around it. Add spaces
|
||||
// or move to lowheader.
|
||||
mvaddstr(height-1, 0, _("command exit with a non-zero status, press a key to exit"));
|
||||
refresh();
|
||||
getchar();
|
||||
endwin_exit(cmd_status & RUNCMD_EXITCODE);
|
||||
// [BUG] When screen resizes, its contents change, but not
|
||||
// necessarily because cmd output's changed. It may have, but that
|
||||
// event is lost. Prevents cycle_count from soaring while resizing.
|
||||
if (! first_screen) {
|
||||
if (flags & WATCH_CHGEXIT && screen_changed)
|
||||
break;
|
||||
if (flags & WATCH_EQUEXIT) {
|
||||
if (screen_changed)
|
||||
cycle_count = 1;
|
||||
else {
|
||||
if (cycle_count == max_cycles)
|
||||
break;
|
||||
++cycle_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [BUG] When screen resizes, its contents change, but not
|
||||
// necessarily because cmd output's changed. It may have, but that
|
||||
// event is lost. Prevents cycle_count from soaring while resizing.
|
||||
if (! first_screen) {
|
||||
if (flags & WATCH_CHGEXIT && cmd_status & RUNCMD_SCRCHANGED)
|
||||
refresh();
|
||||
first_screen = false;
|
||||
|
||||
// first process all available input, then respond to
|
||||
// screen_size_changed, then sleep and repeat
|
||||
dontsleep = screen_size_changed && ! (flags & WATCH_NORERUN);
|
||||
scrdumped = false;
|
||||
do {
|
||||
if (! dontsleep && (t = get_time_usec()-last_tick) < interval) {
|
||||
tosleep.tv_sec = (interval-t) / USECS_PER_SEC;
|
||||
tosleep.tv_usec = (interval-t) % USECS_PER_SEC;
|
||||
}
|
||||
else memset(&tosleep, 0, sizeof(tosleep));
|
||||
assert(FD_SETSIZE >= STDIN_FILENO && FD_SETSIZE >= 1);
|
||||
FD_SET(STDIN_FILENO, &select_stdin);
|
||||
i = select(1, &select_stdin, NULL, NULL, &tosleep);
|
||||
if (i == -1) {
|
||||
assert(errno == EINTR);
|
||||
dontsleep |= ! (flags & WATCH_NORERUN);
|
||||
}
|
||||
if (i > 0) {
|
||||
switch (getchar()) {
|
||||
case EOF:
|
||||
if (errno != EINTR)
|
||||
endwin_exit(1);
|
||||
dontsleep |= ! (flags & WATCH_NORERUN);
|
||||
break;
|
||||
if (flags & WATCH_EQUEXIT) {
|
||||
if (cmd_status & RUNCMD_SCRCHANGED)
|
||||
cycle_count = 1;
|
||||
else {
|
||||
if (cycle_count == max_cycles)
|
||||
break;
|
||||
++cycle_count;
|
||||
case 'q': // overrides all remaining keys
|
||||
endwin_exit(EXIT_SUCCESS);
|
||||
case ' ': // idempotent
|
||||
dontsleep = true;
|
||||
break;
|
||||
case 's': // idempotent
|
||||
if (! scrdumped) {
|
||||
screenshot();
|
||||
scrdumped = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
refresh();
|
||||
first_screen = false;
|
||||
}
|
||||
|
||||
t = get_time_usec() - last_tick;
|
||||
if (t < interval) {
|
||||
t = interval - t;
|
||||
tosleep.tv_sec = t / USECS_PER_SEC;
|
||||
tosleep.tv_nsec = t % USECS_PER_SEC * NSECS_PER_USEC;
|
||||
nanosleep(&tosleep, NULL);
|
||||
}
|
||||
} while (i);
|
||||
}
|
||||
|
||||
endwin_exit(EXIT_SUCCESS);
|
||||
|
Loading…
Reference in New Issue
Block a user