Skip to content

Commit 2a67eaa

Browse files
committed
chafa: Optionally probe the ctty. Add --probe-mode to control this
Fixes #325 (GitHub).
1 parent 75bd00a commit 2a67eaa

File tree

6 files changed

+163
-27
lines changed

6 files changed

+163
-27
lines changed

chafa/chafa-term.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,12 @@ ensure_raw_mode_enabled (ChafaTerm *term, struct termios *saved_termios,
196196
if (t.c_lflag != saved_termios->c_lflag)
197197
{
198198
*termios_changed = TRUE;
199+
200+
/* Note tcsetattr() can block on tty fds when we're in the background. */
199201
tcsetattr (chafa_stream_reader_get_fd (term->reader), TCSANOW, &t);
202+
203+
/* FIXME: Should probably call tcgetattr() here to ensure things went
204+
* according to plan. */
200205
}
201206
else
202207
{
@@ -770,6 +775,17 @@ chafa_term_get_term_info (ChafaTerm *term)
770775
return term->term_info;
771776
}
772777

778+
void
779+
chafa_term_set_term_info (ChafaTerm *term, ChafaTermInfo *term_info)
780+
{
781+
if (term_info)
782+
chafa_term_info_ref (term_info);
783+
if (term->term_info)
784+
chafa_term_info_unref (term->term_info);
785+
786+
term->term_info = term_info;
787+
}
788+
773789
ChafaEvent *
774790
chafa_term_read_event (ChafaTerm *term, guint timeout_ms)
775791
{

chafa/chafa-term.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ void chafa_term_set_buffer_max (ChafaTerm *term, gint max);
4646

4747
CHAFA_AVAILABLE_IN_1_20
4848
ChafaTermInfo *chafa_term_get_term_info (ChafaTerm *term);
49+
CHAFA_AVAILABLE_IN_1_20
50+
void chafa_term_set_term_info (ChafaTerm *term, ChafaTermInfo *term_info);
4951

5052
CHAFA_AVAILABLE_IN_1_20
5153
ChafaEvent *chafa_term_read_event (ChafaTerm *term, guint timeout_ms);

docs/chafa.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ in seconds. Defaults to 5.0.
9393
</para></listitem>
9494
</varlistentry>
9595

96+
<varlistentry>
97+
<term><option>--probe-mode <replaceable>mode</replaceable></option></term>
98+
<listitem><para>
99+
How to probe the terminal [any, ctty, stdio]. "Any" is the default and will
100+
probe by any means available. "Ctty" will probe the controlling tty, usually
101+
/dev/tty, which is useful when chafa is part of a pipeline. This may block
102+
if done in the background. "Stdio" will only consider standard input/output.
103+
</para></listitem>
104+
</varlistentry>
105+
96106
<varlistentry>
97107
<term><option>--version</option></term>
98108
<listitem><para>

tests/chafa-tool-test-common.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ long="no"
6464
tool="${top_builddir}/tools/chafa/chafa"
6565
timeout_cmd="$(which timeout)"
6666
[ "x${timeout_cmd}" = "x" ] && timeout_cmd="$(which gtimeout)"
67+
[ "x${timeout_cmd}" = "x" ] && timeout_cmd="timeout"
68+
69+
# Foreground mode: allow access to tty. Not necessary currently.
70+
# timeout_cmd="${timeout_cmd} -f"
6771

6872
if [ "x$long" = "xyes" ]; then
6973
formats="symbol sixel kitty iterm"

tools/chafa/chicle-options.c

Lines changed: 122 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ print_summary (void)
218218
" on, off]. A positive real number denotes the maximum time\n"
219219
" to wait for a response, in seconds. Defaults to "
220220
G_STRINGIFY (CHICLE_PROBE_DURATION_DEFAULT) ".\n"
221+
" --probe-mode=ARG How to probe the terminal [any, ctty, stdio].\n"
221222
" --version Show version.\n"
222223
" -v, --verbose Be verbose.\n"
223224

@@ -431,6 +432,27 @@ parse_probe_arg (G_GNUC_UNUSED const gchar *option_name, const gchar *value, G_G
431432
return result;
432433
}
433434

435+
static gboolean
436+
parse_probe_mode_arg (G_GNUC_UNUSED const gchar *option_name, const gchar *value, G_GNUC_UNUSED gpointer data, GError **error)
437+
{
438+
gboolean result = TRUE;
439+
440+
if (!g_ascii_strcasecmp (value, "any"))
441+
options.probe_mode = CHICLE_PROBE_MODE_ANY;
442+
else if (!g_ascii_strcasecmp (value, "ctty"))
443+
options.probe_mode = CHICLE_PROBE_MODE_CTTY;
444+
else if (!g_ascii_strcasecmp (value, "stdio"))
445+
options.probe_mode = CHICLE_PROBE_MODE_STDIO;
446+
else
447+
{
448+
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
449+
"Probe mode must be one of [any, ctty, stdio].");
450+
result = FALSE;
451+
}
452+
453+
return result;
454+
}
455+
434456
static gboolean
435457
parse_colors_arg (G_GNUC_UNUSED const gchar *option_name, const gchar *value, G_GNUC_UNUSED gpointer data, GError **error)
436458
{
@@ -1756,6 +1778,59 @@ chicle_retire_passthrough_workarounds_tmux (void)
17561778
return result;
17571779
}
17581780

1781+
static ChafaTerm *
1782+
new_ctty_term (void)
1783+
{
1784+
ChafaTerm *ctty_term = NULL;
1785+
1786+
#if defined(L_ctermid) && !defined(G_OS_WIN32)
1787+
gchar dev_name [L_ctermid];
1788+
gint in_fd = -1, out_fd = -1;
1789+
pid_t proc_group;
1790+
1791+
ctermid (dev_name);
1792+
1793+
in_fd = open (dev_name, O_RDONLY);
1794+
out_fd = open (dev_name, O_WRONLY);
1795+
1796+
if (in_fd < 0 || out_fd < 0)
1797+
goto out;
1798+
1799+
/* Don't probe tty if we're definitely in the background. This mitigates
1800+
* issues with running under timeout(1) in CI and such, but does not
1801+
* guarantee that we won't be moved to background between now and
1802+
* ChafaTerm's call to tcsetattr(), causing it to block. Those cases are
1803+
* outside the scope of our handling.
1804+
*
1805+
* If the user explicitly requests ctty probing, we skip this check. */
1806+
1807+
if (options.probe_mode != CHICLE_PROBE_MODE_CTTY)
1808+
{
1809+
proc_group = getpgrp ();
1810+
1811+
if (tcgetpgrp (in_fd) != proc_group
1812+
|| tcgetpgrp (out_fd) != proc_group)
1813+
goto out;
1814+
}
1815+
1816+
ctty_term = chafa_term_new (NULL,
1817+
in_fd,
1818+
out_fd,
1819+
out_fd);
1820+
#endif
1821+
1822+
out:
1823+
if (!ctty_term)
1824+
{
1825+
if (in_fd >= 0)
1826+
close (in_fd);
1827+
if (out_fd >= 0)
1828+
close (out_fd);
1829+
}
1830+
1831+
return ctty_term;
1832+
}
1833+
17591834
gboolean
17601835
chicle_parse_options (int *argc, char **argv [])
17611836
{
@@ -1807,6 +1882,7 @@ chicle_parse_options (int *argc, char **argv [])
18071882
{ "polite", '\0', 0, G_OPTION_ARG_CALLBACK, parse_polite_arg, "Polite", NULL },
18081883
{ "preprocess", 'p', 0, G_OPTION_ARG_CALLBACK, parse_preprocess_arg, "Preprocessing", NULL },
18091884
{ "probe", '\0', 0, G_OPTION_ARG_CALLBACK, parse_probe_arg, "Terminal probing", NULL },
1885+
{ "probe-mode", '\0', 0, G_OPTION_ARG_CALLBACK, parse_probe_mode_arg, "Probe mode", NULL },
18101886
{ "relative", '\0', 0, G_OPTION_ARG_CALLBACK, parse_relative_arg, "Relative", NULL },
18111887
{ "scale", '\0', 0, G_OPTION_ARG_CALLBACK, parse_scale_arg, "Scale", NULL },
18121888
{ "size", 's', 0, G_OPTION_ARG_CALLBACK, parse_size_arg, "Output size", NULL },
@@ -1946,41 +2022,60 @@ chicle_parse_options (int *argc, char **argv [])
19462022

19472023
/* Synchronous probe for sixels, default colors, etc. */
19482024

1949-
if ((options.probe == CHICLE_TRISTATE_TRUE
1950-
|| options.probe == CHICLE_TRISTATE_AUTO)
1951-
&& options.probe_duration >= 0.0)
19522025
{
1953-
chafa_term_sync_probe (term, options.probe_duration * 1000);
2026+
ChafaTerm *ctty_term = NULL;
2027+
ChafaTerm *probe_term = NULL;
19542028

1955-
if (!options.pixel_mode_set)
1956-
{
1957-
options.pixel_mode = chafa_term_info_get_best_pixel_mode (
1958-
chafa_term_get_term_info (term));
1959-
}
2029+
if (options.probe_mode == CHICLE_PROBE_MODE_CTTY
2030+
|| (options.probe_mode == CHICLE_PROBE_MODE_ANY &&
2031+
(!isatty (STDIN_FILENO) || !isatty (STDOUT_FILENO))))
2032+
probe_term = ctty_term = new_ctty_term ();
19602033

1961-
if (!options.fg_color_set)
1962-
{
1963-
gint32 color = chafa_term_get_default_fg_color (term);
1964-
if (color >= 0)
1965-
options.fg_color = color;
1966-
}
2034+
if (!probe_term)
2035+
probe_term = term;
19672036

1968-
if (!options.bg_color_set)
2037+
if ((options.probe == CHICLE_TRISTATE_TRUE
2038+
|| options.probe == CHICLE_TRISTATE_AUTO)
2039+
&& options.probe_duration >= 0.0)
19692040
{
1970-
gint32 color = chafa_term_get_default_bg_color (term);
1971-
if (color >= 0)
1972-
options.bg_color = color;
2041+
chafa_term_sync_probe (probe_term, options.probe_duration * 1000);
2042+
2043+
if (!options.pixel_mode_set)
2044+
{
2045+
options.pixel_mode = chafa_term_info_get_best_pixel_mode (
2046+
chafa_term_get_term_info (probe_term));
2047+
}
2048+
2049+
if (!options.fg_color_set)
2050+
{
2051+
gint32 color = chafa_term_get_default_fg_color (probe_term);
2052+
if (color >= 0)
2053+
options.fg_color = color;
2054+
}
2055+
2056+
if (!options.bg_color_set)
2057+
{
2058+
gint32 color = chafa_term_get_default_bg_color (probe_term);
2059+
if (color >= 0)
2060+
options.bg_color = color;
2061+
}
19732062
}
1974-
}
19752063

1976-
/* Detect terminal geometry */
2064+
/* Detect terminal geometry */
2065+
2066+
chafa_term_get_size_px (probe_term,
2067+
&detected_term_size.width_pixels,
2068+
&detected_term_size.height_pixels);
2069+
chafa_term_get_size_cells (probe_term,
2070+
&detected_term_size.width_cells,
2071+
&detected_term_size.height_cells);
19772072

1978-
chafa_term_get_size_px (term,
1979-
&detected_term_size.width_pixels,
1980-
&detected_term_size.height_pixels);
1981-
chafa_term_get_size_cells (term,
1982-
&detected_term_size.width_cells,
1983-
&detected_term_size.height_cells);
2073+
chafa_term_set_term_info (term,
2074+
chafa_term_get_term_info (probe_term));
2075+
2076+
if (ctty_term)
2077+
chafa_term_destroy (ctty_term);
2078+
}
19842079

19852080
/* Fall back on COLUMNS. It's the best we can do in some environments (e.g. CI). */
19862081

tools/chafa/chicle-options.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ typedef enum
7171
}
7272
ChicleTristate;
7373

74+
typedef enum
75+
{
76+
CHICLE_PROBE_MODE_ANY = 0,
77+
CHICLE_PROBE_MODE_CTTY,
78+
CHICLE_PROBE_MODE_STDIO
79+
}
80+
ChicleProbeMode;
81+
7482
/* Dimensions are set to GRID_AUTO with --grid auto. This means the user
7583
* wants us to pick appropriate grid parameters based on the view size */
7684
#define CHICLE_GRID_AUTO -2
@@ -123,6 +131,7 @@ typedef struct
123131
gint grid_width, grid_height;
124132
gint margin_bottom, margin_right;
125133
ChicleTristate probe;
134+
ChicleProbeMode probe_mode;
126135
gdouble probe_duration;
127136
gdouble scale;
128137
gdouble font_ratio;

0 commit comments

Comments
 (0)