mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
fix setting up and using the terminfo data
There should be just one place that calls `setupterm()`. While refactoring the code I also decided to not make initializing the curses subsystem a fatal error. We now try two fallback terminal names ("ansi" and "dumb") and if those can't be used we still end up with a usable shell. Fixes #3850
This commit is contained in:
parent
42a320064c
commit
6d02bec4c7
16 changed files with 282 additions and 237 deletions
|
@ -972,7 +972,7 @@ obj/builtin_set.o: config.h src/builtin.h src/common.h src/fallback.h
|
||||||
obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h
|
obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h
|
||||||
obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h
|
obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h
|
||||||
obj/builtin_set.o: src/wgetopt.h src/wutil.h
|
obj/builtin_set.o: src/wgetopt.h src/wutil.h
|
||||||
obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h
|
obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/env.h src/fallback.h
|
||||||
obj/builtin_set_color.o: src/signal.h src/color.h src/io.h src/output.h
|
obj/builtin_set_color.o: src/signal.h src/color.h src/io.h src/output.h
|
||||||
obj/builtin_set_color.o: src/proc.h src/parse_tree.h src/parse_constants.h
|
obj/builtin_set_color.o: src/proc.h src/parse_tree.h src/parse_constants.h
|
||||||
obj/builtin_set_color.o: src/tokenizer.h src/wgetopt.h src/wutil.h
|
obj/builtin_set_color.o: src/tokenizer.h src/wgetopt.h src/wutil.h
|
||||||
|
|
|
@ -93,7 +93,9 @@
|
||||||
{ symbol: ["uint32_t", "private", "<stdint.h>", "public"] },
|
{ symbol: ["uint32_t", "private", "<stdint.h>", "public"] },
|
||||||
{ symbol: ["intptr_t", "private", "<stdint.h>", "public"] },
|
{ symbol: ["intptr_t", "private", "<stdint.h>", "public"] },
|
||||||
{ symbol: ["tparm", "private", "<ncurses.h>", "public"] },
|
{ symbol: ["tparm", "private", "<ncurses.h>", "public"] },
|
||||||
|
{ symbol: ["tigetflag", "private", "<ncurses.h>", "public"] },
|
||||||
{ symbol: ["ERR", "private", "<ncurses.h>", "public"] },
|
{ symbol: ["ERR", "private", "<ncurses.h>", "public"] },
|
||||||
|
{ symbol: ["OK", "private", "<ncurses.h>", "public"] },
|
||||||
{ symbol: ["select", "private", "<sys/select.h>", "public"] },
|
{ symbol: ["select", "private", "<sys/select.h>", "public"] },
|
||||||
{ symbol: ["_LIBCPP_VERSION", "private", "<stddef.h>", "public"] },
|
{ symbol: ["_LIBCPP_VERSION", "private", "<stddef.h>", "public"] },
|
||||||
{ symbol: ["_LIBCPP_VERSION", "private", "<unistd.h>", "public"] },
|
{ symbol: ["_LIBCPP_VERSION", "private", "<unistd.h>", "public"] },
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#if HAVE_NCURSES_H
|
#if HAVE_NCURSES_H
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
@ -25,6 +24,7 @@
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "env.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
@ -52,6 +52,9 @@ static int set_color_builtin_outputter(char c) {
|
||||||
|
|
||||||
/// set_color builtin.
|
/// set_color builtin.
|
||||||
int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||||
|
// By the time this is called we should have initialized the curses subsystem.
|
||||||
|
assert(curses_initialized);
|
||||||
|
|
||||||
wgetopter_t w;
|
wgetopter_t w;
|
||||||
// Variables used for parsing the argument list.
|
// Variables used for parsing the argument list.
|
||||||
const struct woption long_options[] = {{L"background", required_argument, 0, 'b'},
|
const struct woption long_options[] = {{L"background", required_argument, 0, 'b'},
|
||||||
|
@ -66,7 +69,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||||
{0, 0, 0, 0}};
|
{0, 0, 0, 0}};
|
||||||
|
|
||||||
const wchar_t *short_options = L"b:hvoidrcu";
|
const wchar_t *short_options = L"b:hvoidrcu";
|
||||||
|
|
||||||
int argc = builtin_count_args(argv);
|
int argc = builtin_count_args(argv);
|
||||||
|
|
||||||
// Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a
|
// Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a
|
||||||
|
@ -77,7 +79,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||||
|
|
||||||
const wchar_t *bgcolor = NULL;
|
const wchar_t *bgcolor = NULL;
|
||||||
bool bold = false, underline = false, italics = false, dim = false, reverse = false;
|
bool bold = false, underline = false, italics = false, dim = false, reverse = false;
|
||||||
int errret;
|
|
||||||
|
|
||||||
// Parse options to obtain the requested operation and the modifiers.
|
// Parse options to obtain the requested operation and the modifiers.
|
||||||
w.woptind = 0;
|
w.woptind = 0;
|
||||||
|
@ -162,12 +163,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||||
return STATUS_BUILTIN_ERROR;
|
return STATUS_BUILTIN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the term exists.
|
|
||||||
if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR) {
|
|
||||||
streams.err.append_format(_(L"%ls: Could not set up terminal\n"), argv[0]);
|
|
||||||
return STATUS_BUILTIN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test if we have at least basic support for setting fonts, colors and related bits - otherwise
|
// Test if we have at least basic support for setting fonts, colors and related bits - otherwise
|
||||||
// just give up...
|
// just give up...
|
||||||
if (cur_term == NULL || !exit_attribute_mode) {
|
if (cur_term == NULL || !exit_attribute_mode) {
|
||||||
|
|
240
src/env.cpp
240
src/env.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -14,6 +15,13 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#if HAVE_NCURSES_H
|
||||||
|
#include <ncurses.h>
|
||||||
|
#elif HAVE_NCURSES_CURSES_H
|
||||||
|
#include <ncurses/curses.h>
|
||||||
|
#else
|
||||||
|
#include <curses.h>
|
||||||
|
#endif
|
||||||
#if HAVE_TERM_H
|
#if HAVE_TERM_H
|
||||||
#include <term.h>
|
#include <term.h>
|
||||||
#elif HAVE_NCURSES_TERM_H
|
#elif HAVE_NCURSES_TERM_H
|
||||||
|
@ -37,6 +45,7 @@
|
||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "input_common.h"
|
#include "input_common.h"
|
||||||
|
#include "output.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
|
@ -46,6 +55,8 @@
|
||||||
|
|
||||||
/// Value denoting a null string.
|
/// Value denoting a null string.
|
||||||
#define ENV_NULL L"\x1d"
|
#define ENV_NULL L"\x1d"
|
||||||
|
#define DEFAULT_TERM1 "ansi"
|
||||||
|
#define DEFAULT_TERM2 "dumb"
|
||||||
|
|
||||||
/// Some configuration path environment variables.
|
/// Some configuration path environment variables.
|
||||||
#define FISH_DATADIR_VAR L"__fish_datadir"
|
#define FISH_DATADIR_VAR L"__fish_datadir"
|
||||||
|
@ -62,20 +73,34 @@ extern char **environ;
|
||||||
size_t read_byte_limit = READ_BYTE_LIMIT;
|
size_t read_byte_limit = READ_BYTE_LIMIT;
|
||||||
|
|
||||||
bool g_use_posix_spawn = false; // will usually be set to true
|
bool g_use_posix_spawn = false; // will usually be set to true
|
||||||
|
bool curses_initialized = false;
|
||||||
|
|
||||||
/// Does the terminal have the "eat_newline_glitch".
|
/// Does the terminal have the "eat_newline_glitch".
|
||||||
bool term_has_xn = false;
|
bool term_has_xn = false;
|
||||||
|
|
||||||
/// List of all locale environment variable names.
|
/// This is used to ensure that we don't perform any callbacks from `react_to_variable_change()`
|
||||||
|
/// when we're importing environment variables in `env_init()`. That's because we don't have any
|
||||||
|
/// control over the order in which the vars are imported and some of them work in combination.
|
||||||
|
/// For example, `TERMINFO_DIRS` and `TERM`. If the user has set `TERM` to a custom value that is
|
||||||
|
/// found in `TERMINFO_DIRS` we don't to call `handle_curses()` before we've imported the latter.
|
||||||
|
static bool env_initialized = false;
|
||||||
|
|
||||||
|
/// List of all locale environment variable names that might trigger (re)initializing the locale
|
||||||
|
/// subsystem.
|
||||||
static const wchar_t *const locale_variable[] = {
|
static const wchar_t *const locale_variable[] = {
|
||||||
L"LANG", L"LANGUAGE", L"LC_ALL", L"LC_ADDRESS", L"LC_COLLATE",
|
L"LANG", L"LANGUAGE", L"LC_ALL", L"LC_ADDRESS", L"LC_COLLATE",
|
||||||
L"LC_CTYPE", L"LC_IDENTIFICATION", L"LC_MEASUREMENT", L"LC_MESSAGES", L"LC_MONETARY",
|
L"LC_CTYPE", L"LC_IDENTIFICATION", L"LC_MEASUREMENT", L"LC_MESSAGES", L"LC_MONETARY",
|
||||||
L"LC_NAME", L"LC_NUMERIC", L"LC_PAPER", L"LC_TELEPHONE", L"LC_TIME",
|
L"LC_NAME", L"LC_NUMERIC", L"LC_PAPER", L"LC_TELEPHONE", L"LC_TIME",
|
||||||
NULL};
|
NULL};
|
||||||
|
|
||||||
/// List of all curses environment variable names.
|
/// List of all curses environment variable names that might trigger (re)initializing the curses
|
||||||
|
/// subsystem.
|
||||||
static const wchar_t *const curses_variable[] = {L"TERM", L"TERMINFO", L"TERMINFO_DIRS", NULL};
|
static const wchar_t *const curses_variable[] = {L"TERM", L"TERMINFO", L"TERMINFO_DIRS", NULL};
|
||||||
|
|
||||||
|
// Some forward declarations to make it easy to logically group the code.
|
||||||
|
static void init_locale();
|
||||||
|
static void init_curses();
|
||||||
|
|
||||||
// Struct representing one level in the function variable stack.
|
// Struct representing one level in the function variable stack.
|
||||||
// Only our variable stack should create and destroy these
|
// Only our variable stack should create and destroy these
|
||||||
class env_node_t {
|
class env_node_t {
|
||||||
|
@ -105,7 +130,6 @@ class variable_entry_t {
|
||||||
static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static bool local_scope_exports(const env_node_t *n);
|
static bool local_scope_exports(const env_node_t *n);
|
||||||
static void handle_locale(const wchar_t *env_var_name);
|
|
||||||
|
|
||||||
// A class wrapping up a variable stack
|
// A class wrapping up a variable stack
|
||||||
// Currently there is only one variable stack in fish,
|
// Currently there is only one variable stack in fish,
|
||||||
|
@ -136,6 +160,8 @@ struct var_stack_t {
|
||||||
// Pops the top node if it's not global
|
// Pops the top node if it's not global
|
||||||
void pop();
|
void pop();
|
||||||
|
|
||||||
|
bool var_changed(wchar_t const *const vars[]);
|
||||||
|
|
||||||
// Returns the next scope to search for a given node, respecting the new_scope lag
|
// Returns the next scope to search for a given node, respecting the new_scope lag
|
||||||
// Returns NULL if we're done
|
// Returns NULL if we're done
|
||||||
env_node_t *next_scope_to_search(env_node_t *node);
|
env_node_t *next_scope_to_search(env_node_t *node);
|
||||||
|
@ -156,23 +182,24 @@ void var_stack_t::push(bool new_scope) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if one of the vars in the passed list was changed in the current var scope.
|
||||||
|
bool var_stack_t::var_changed(wchar_t const *const vars[]) {
|
||||||
|
for (auto v = vars; *v; v++) {
|
||||||
|
if (top->env.find(*v) != top->env.end()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void var_stack_t::pop() {
|
void var_stack_t::pop() {
|
||||||
// Don't pop the global
|
// Don't pop the top-most, global, level.
|
||||||
if (this->top.get() == this->global_env) {
|
if (top.get() == this->global_env) {
|
||||||
debug(0, _(L"Tried to pop empty environment stack."));
|
debug(0, _(L"Tried to pop empty environment stack."));
|
||||||
sanity_lose();
|
sanity_lose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *locale_changed = NULL;
|
bool locale_changed = this->var_changed(locale_variable);
|
||||||
|
bool curses_changed = this->var_changed(curses_variable);
|
||||||
for (int i = 0; locale_variable[i]; i++) {
|
|
||||||
var_table_t::iterator result = top->env.find(locale_variable[i]);
|
|
||||||
if (result != top->env.end()) {
|
|
||||||
locale_changed = locale_variable[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top->new_scope) { //!OCLINT(collapsible if statements)
|
if (top->new_scope) { //!OCLINT(collapsible if statements)
|
||||||
if (top->exportv || local_scope_exports(top->next.get())) {
|
if (top->exportv || local_scope_exports(top->next.get())) {
|
||||||
|
@ -196,8 +223,9 @@ void var_stack_t::pop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Move this to something general.
|
|
||||||
if (locale_changed) handle_locale(locale_changed);
|
if (locale_changed) init_locale();
|
||||||
|
if (curses_changed) init_curses();
|
||||||
}
|
}
|
||||||
|
|
||||||
const env_node_t *var_stack_t::next_scope_to_search(const env_node_t *node) const {
|
const env_node_t *var_stack_t::next_scope_to_search(const env_node_t *node) const {
|
||||||
|
@ -303,25 +331,27 @@ static bool var_is_locale(const wcstring &key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Properly sets all locale information.
|
/// Initialize the locale subsystem.
|
||||||
static void handle_locale(const wchar_t *env_var_name) {
|
static void init_locale() {
|
||||||
debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name);
|
|
||||||
// We have to make a copy because the subsequent setlocale() call to change the locale will
|
// We have to make a copy because the subsequent setlocale() call to change the locale will
|
||||||
// invalidate the pointer from the this setlocale() call.
|
// invalidate the pointer from the this setlocale() call.
|
||||||
char *old_msg_locale = strdup(setlocale(LC_MESSAGES, NULL));
|
char *old_msg_locale = strdup(setlocale(LC_MESSAGES, NULL));
|
||||||
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
|
|
||||||
|
for (const wchar_t *const *var_name = locale_variable; *var_name; var_name++) {
|
||||||
|
const env_var_t val = env_get_string(*var_name, ENV_EXPORT);
|
||||||
|
const std::string &name = wcs2string(*var_name);
|
||||||
const std::string &value = wcs2string(val);
|
const std::string &value = wcs2string(val);
|
||||||
const std::string &name = wcs2string(env_var_name);
|
|
||||||
debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
|
debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
|
||||||
if (val.empty()) {
|
if (val.empty()) {
|
||||||
unsetenv(name.c_str());
|
unsetenv(name.c_str());
|
||||||
} else {
|
} else {
|
||||||
setenv(name.c_str(), value.c_str(), 1);
|
setenv(name.c_str(), value.c_str(), 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char *locale = setlocale(LC_ALL, "");
|
char *locale = setlocale(LC_ALL, "");
|
||||||
fish_setlocale();
|
fish_setlocale();
|
||||||
debug(2, L"handle_locale() setlocale(): '%s'", locale);
|
debug(2, L"init_locale() setlocale(): '%s'", locale);
|
||||||
|
|
||||||
const char *new_msg_locale = setlocale(LC_MESSAGES, NULL);
|
const char *new_msg_locale = setlocale(LC_MESSAGES, NULL);
|
||||||
debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale);
|
debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale);
|
||||||
|
@ -380,15 +410,84 @@ static bool does_term_support_setting_title() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle changes to the TERM env var that do not involves the curses subsystem.
|
/// Updates our idea of whether we support term256 and term24bit (see issue #10222).
|
||||||
static void handle_term() { can_set_term_title = does_term_support_setting_title(); }
|
static void update_fish_color_support() {
|
||||||
|
// Detect or infer term256 support. If fish_term256 is set, we respect it;
|
||||||
|
// otherwise infer it from the TERM variable or use terminfo.
|
||||||
|
env_var_t fish_term256 = env_get_string(L"fish_term256");
|
||||||
|
env_var_t term = env_get_string(L"TERM");
|
||||||
|
bool support_term256 = false; // default to no support
|
||||||
|
if (!fish_term256.missing_or_empty()) {
|
||||||
|
support_term256 = from_string<bool>(fish_term256);
|
||||||
|
debug(2, L"256 color support determined by 'fish_term256'");
|
||||||
|
} else if (term.find(L"256color") != wcstring::npos) {
|
||||||
|
// TERM=*256color*: Explicitly supported.
|
||||||
|
support_term256 = true;
|
||||||
|
debug(2, L"256 color support enabled for '256color' in TERM");
|
||||||
|
} else if (term.find(L"xterm") != wcstring::npos) {
|
||||||
|
// Assume that all xterms are 256, except for OS X SnowLeopard
|
||||||
|
const env_var_t prog = env_get_string(L"TERM_PROGRAM");
|
||||||
|
const env_var_t progver = env_get_string(L"TERM_PROGRAM_VERSION");
|
||||||
|
if (prog == L"Apple_Terminal" && !progver.missing_or_empty()) {
|
||||||
|
// OS X Lion is version 300+, it has 256 color support
|
||||||
|
if (strtod(wcs2str(progver), NULL) > 300) {
|
||||||
|
support_term256 = true;
|
||||||
|
debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
support_term256 = true;
|
||||||
|
debug(2, L"256 color support enabled for TERM=xterm");
|
||||||
|
}
|
||||||
|
} else if (cur_term != NULL) {
|
||||||
|
// See if terminfo happens to identify 256 colors
|
||||||
|
support_term256 = (max_colors >= 256);
|
||||||
|
debug(2, L"256 color support: using %d colors per terminfo", max_colors);
|
||||||
|
} else {
|
||||||
|
debug(2, L"256 color support not enabled (yet)");
|
||||||
|
}
|
||||||
|
|
||||||
/// Push all curses/terminfo env vars into the global environment where they can be found by those
|
env_var_t fish_term24bit = env_get_string(L"fish_term24bit");
|
||||||
/// libraries.
|
bool support_term24bit;
|
||||||
static void handle_curses(const wchar_t *env_var_name) {
|
if (!fish_term24bit.missing_or_empty()) {
|
||||||
debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name);
|
support_term24bit = from_string<bool>(fish_term24bit);
|
||||||
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
|
debug(2, L"'fish_term24bit' preference: 24-bit color %s",
|
||||||
const std::string &name = wcs2string(env_var_name);
|
support_term24bit ? L"enabled" : L"disabled");
|
||||||
|
} else {
|
||||||
|
// We don't attempt to infer term24 bit support yet.
|
||||||
|
support_term24bit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
color_support_t support = (support_term256 ? color_support_term256 : 0) |
|
||||||
|
(support_term24bit ? color_support_term24bit : 0);
|
||||||
|
output_set_color_support(support);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to initialize the terminfo/curses subsystem using our fallback terminal name. Do not set
|
||||||
|
// `TERM` to our fallback. We're only doing this in the hope of getting a minimally functional
|
||||||
|
// shell. If we launch an external command that uses TERM it should get the same value we were
|
||||||
|
// given, if any.
|
||||||
|
static bool initialize_curses_using_fallback(const char *term) {
|
||||||
|
// If $TERM is already set to the fallback name we're about to use there isn't any point in
|
||||||
|
// seeing if the fallback name can be used.
|
||||||
|
const char *term_env = wcs2str(env_get_string(L"TERM"));
|
||||||
|
if (!strcmp(term_env, DEFAULT_TERM1) || !strcmp(term_env, DEFAULT_TERM2)) return false;
|
||||||
|
|
||||||
|
if (is_interactive_session) {
|
||||||
|
debug(1, _(L"Using fallback terminal type '%s'."), term);
|
||||||
|
}
|
||||||
|
int err_ret;
|
||||||
|
if (setupterm((char *)term, STDOUT_FILENO, &err_ret) == OK) return true;
|
||||||
|
if (is_interactive_session) {
|
||||||
|
debug(1, _(L"Could not set up terminal using the fallback terminal type '%s'."), term);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the curses subsystem.
|
||||||
|
static void init_curses() {
|
||||||
|
for (const wchar_t *const *var_name = curses_variable; *var_name; var_name++) {
|
||||||
|
const env_var_t val = env_get_string(*var_name, ENV_EXPORT);
|
||||||
|
const std::string &name = wcs2string(*var_name);
|
||||||
const std::string &value = wcs2string(val);
|
const std::string &value = wcs2string(val);
|
||||||
debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
|
debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
|
||||||
if (val.empty()) {
|
if (val.empty()) {
|
||||||
|
@ -396,23 +495,45 @@ static void handle_curses(const wchar_t *env_var_name) {
|
||||||
} else {
|
} else {
|
||||||
setenv(name.c_str(), value.c_str(), 1);
|
setenv(name.c_str(), value.c_str(), 1);
|
||||||
}
|
}
|
||||||
// TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically
|
}
|
||||||
// changed. At the present time it can be called just once. Also, we should really only do this
|
|
||||||
// if the TERM var is set.
|
|
||||||
// input_init();
|
|
||||||
term_has_xn = tgetflag((char *)"xn") == 1; // does terminal have the eat_newline_glitch
|
|
||||||
|
|
||||||
|
int err_ret;
|
||||||
|
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
||||||
|
env_var_t term = env_get_string(L"TERM");
|
||||||
|
if (is_interactive_session) {
|
||||||
|
debug(1, _(L"Could not set up terminal."));
|
||||||
|
if (term.missing_or_empty()) {
|
||||||
|
debug(1, _(L"TERM environment variable not set."));
|
||||||
|
} else {
|
||||||
|
debug(1, _(L"TERM environment variable set to '%ls'."), term.c_str());
|
||||||
|
debug(1, _(L"Check that this terminal type is supported on this system."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialize_curses_using_fallback(DEFAULT_TERM1)) {
|
||||||
|
initialize_curses_using_fallback(DEFAULT_TERM2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
can_set_term_title = does_term_support_setting_title();
|
||||||
|
term_has_xn = tigetflag((char *)"xenl") == 1; // does terminal have the eat_newline_glitch
|
||||||
|
update_fish_color_support();
|
||||||
// Invalidate the cached escape sequences since they may no longer be valid.
|
// Invalidate the cached escape sequences since they may no longer be valid.
|
||||||
cached_esc_sequences.clear();
|
cached_esc_sequences.clear();
|
||||||
|
curses_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// React to modifying the given variable.
|
/// React to modifying the given variable.
|
||||||
static void react_to_variable_change(const wcstring &key) {
|
static void react_to_variable_change(const wcstring &key) {
|
||||||
|
// Don't do any of this until `env_init()` has run. We only want to do this in response to
|
||||||
|
// variables set by the user; e.g., in a script like *config.fish* or interactively.
|
||||||
|
if (!env_initialized) return;
|
||||||
|
|
||||||
if (var_is_locale(key)) {
|
if (var_is_locale(key)) {
|
||||||
handle_locale(key.c_str());
|
init_locale();
|
||||||
} else if (var_is_curses(key)) {
|
} else if (var_is_curses(key)) {
|
||||||
handle_curses(key.c_str());
|
init_curses();
|
||||||
if (key == L"TERM") handle_term();
|
init_input();
|
||||||
} else if (var_is_timezone(key)) {
|
} else if (var_is_timezone(key)) {
|
||||||
handle_timezone(key.c_str());
|
handle_timezone(key.c_str());
|
||||||
} else if (key == L"fish_term256" || key == L"fish_term24bit") {
|
} else if (key == L"fish_term256" || key == L"fish_term24bit") {
|
||||||
|
@ -468,7 +589,7 @@ static void setup_path() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the `COLUMNS` and `LINES` env vars if they don't already exist to reasonable
|
/// If they don't already exist initialize the `COLUMNS` and `LINES` env vars to reasonable
|
||||||
/// defaults. They will be updated later by the `get_current_winsize()` function if they need to be
|
/// defaults. They will be updated later by the `get_current_winsize()` function if they need to be
|
||||||
/// adjusted.
|
/// adjusted.
|
||||||
static void env_set_termsize() {
|
static void env_set_termsize() {
|
||||||
|
@ -533,6 +654,42 @@ static void setup_user(bool force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||||
|
/// routines.
|
||||||
|
void misc_init() {
|
||||||
|
env_set_read_limit();
|
||||||
|
|
||||||
|
// If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might
|
||||||
|
// be intermixed with `write()` calls and we need to ensure the writes are not reordered. See
|
||||||
|
// issue #3748.
|
||||||
|
if (isatty(STDOUT_FILENO)) {
|
||||||
|
fflush(stdout);
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef OS_IS_CYGWIN
|
||||||
|
// MS Windows tty devices do not currently have either a read or write timestamp. Those
|
||||||
|
// respective fields of `struct stat` are always the current time. Which means we can't
|
||||||
|
// use them. So we assume no external program has written to the terminal behind our
|
||||||
|
// back. This makes multiline promptusable. See issue #2859 and
|
||||||
|
// https://github.com/Microsoft/BashOnWindows/issues/545
|
||||||
|
has_working_tty_timestamps = false;
|
||||||
|
#else
|
||||||
|
// This covers preview builds of Windows Subsystem for Linux (WSL).
|
||||||
|
FILE *procsyskosrel;
|
||||||
|
if ((procsyskosrel = wfopen(L"/proc/sys/kernel/osrelease", "r"))) {
|
||||||
|
wcstring osrelease;
|
||||||
|
fgetws2(&osrelease, procsyskosrel);
|
||||||
|
if (osrelease.find(L"3.4.0-Microsoft") != wcstring::npos) {
|
||||||
|
has_working_tty_timestamps = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (procsyskosrel) {
|
||||||
|
fclose(procsyskosrel);
|
||||||
|
}
|
||||||
|
#endif // OS_IS_MS_WINDOWS
|
||||||
|
}
|
||||||
|
|
||||||
void env_init(const struct config_paths_t *paths /* or NULL */) {
|
void env_init(const struct config_paths_t *paths /* or NULL */) {
|
||||||
// These variables can not be altered directly by the user.
|
// These variables can not be altered directly by the user.
|
||||||
const wchar_t *const ro_keys[] = {
|
const wchar_t *const ro_keys[] = {
|
||||||
|
@ -585,6 +742,10 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
||||||
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL);
|
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_locale();
|
||||||
|
init_curses();
|
||||||
|
init_input();
|
||||||
|
|
||||||
// Set up the USER and PATH variables
|
// Set up the USER and PATH variables
|
||||||
setup_path();
|
setup_path();
|
||||||
setup_user(false);
|
setup_user(false);
|
||||||
|
@ -647,6 +808,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
||||||
// scope will persist throughout the lifetime of the fish process, and it will ensure that `set
|
// scope will persist throughout the lifetime of the fish process, and it will ensure that `set
|
||||||
// -l` commands run at the command-line don't affect the global scope.
|
// -l` commands run at the command-line don't affect the global scope.
|
||||||
env_push(false);
|
env_push(false);
|
||||||
|
env_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search all visible scopes in order for the specified key. Return the first scope in which it was
|
/// Search all visible scopes in order for the specified key. Return the first scope in which it was
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
extern size_t read_byte_limit;
|
extern size_t read_byte_limit;
|
||||||
|
extern bool curses_initialized;
|
||||||
|
|
||||||
// Flags that may be passed as the 'mode' in env_set / env_get_string.
|
// Flags that may be passed as the 'mode' in env_set / env_get_string.
|
||||||
enum {
|
enum {
|
||||||
|
@ -54,6 +55,10 @@ struct config_paths_t {
|
||||||
/// Initialize environment variable data.
|
/// Initialize environment variable data.
|
||||||
void env_init(const struct config_paths_t *paths = NULL);
|
void env_init(const struct config_paths_t *paths = NULL);
|
||||||
|
|
||||||
|
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||||
|
/// routines.
|
||||||
|
void misc_init();
|
||||||
|
|
||||||
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t mode);
|
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t mode);
|
||||||
|
|
||||||
class env_var_t : public wcstring {
|
class env_var_t : public wcstring {
|
||||||
|
|
44
src/fish.cpp
44
src/fish.cpp
|
@ -44,7 +44,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
#include "fish_version.h"
|
#include "fish_version.h"
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "input.h"
|
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
@ -318,42 +317,6 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
|
||||||
return optind;
|
return optind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
|
||||||
/// routines.
|
|
||||||
static void misc_init() {
|
|
||||||
env_set_read_limit();
|
|
||||||
|
|
||||||
// If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might
|
|
||||||
// be intermixed with `write()` calls and we need to ensure the writes are not reordered. See
|
|
||||||
// issue #3748.
|
|
||||||
if (isatty(STDOUT_FILENO)) {
|
|
||||||
fflush(stdout);
|
|
||||||
setvbuf(stdout, NULL, _IONBF, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef OS_IS_CYGWIN
|
|
||||||
// MS Windows tty devices do not currently have either a read or write timestamp. Those
|
|
||||||
// respective fields of `struct stat` are always the current time. Which means we can't
|
|
||||||
// use them. So we assume no external program has written to the terminal behind our
|
|
||||||
// back. This makes multiline promptusable. See issue #2859 and
|
|
||||||
// https://github.com/Microsoft/BashOnWindows/issues/545
|
|
||||||
has_working_tty_timestamps = false;
|
|
||||||
#else
|
|
||||||
// This covers preview builds of Windows Subsystem for Linux (WSL).
|
|
||||||
FILE *procsyskosrel;
|
|
||||||
if ((procsyskosrel = wfopen(L"/proc/sys/kernel/osrelease", "r"))) {
|
|
||||||
wcstring osrelease;
|
|
||||||
fgetws2(&osrelease, procsyskosrel);
|
|
||||||
if (osrelease.find(L"3.4.0-Microsoft") != wcstring::npos) {
|
|
||||||
has_working_tty_timestamps = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (procsyskosrel) {
|
|
||||||
fclose(procsyskosrel);
|
|
||||||
}
|
|
||||||
#endif // OS_IS_MS_WINDOWS
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int res = 1;
|
int res = 1;
|
||||||
int my_optind = 0;
|
int my_optind = 0;
|
||||||
|
@ -389,17 +352,14 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
|
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
|
||||||
|
env_init(&paths);
|
||||||
proc_init();
|
proc_init();
|
||||||
event_init();
|
event_init();
|
||||||
builtin_init();
|
builtin_init();
|
||||||
function_init();
|
function_init();
|
||||||
env_init(&paths);
|
misc_init();
|
||||||
reader_init();
|
reader_init();
|
||||||
history_init();
|
history_init();
|
||||||
// For set_color to support term256 in config.fish (issue #1022).
|
|
||||||
update_fish_color_support();
|
|
||||||
misc_init();
|
|
||||||
|
|
||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "fish_version.h"
|
#include "fish_version.h"
|
||||||
#include "highlight.h"
|
#include "highlight.h"
|
||||||
#include "input.h"
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
#include "parse_tree.h"
|
||||||
|
@ -347,7 +346,6 @@ int main(int argc, char *argv[]) {
|
||||||
// (e.g., "# -*- coding: <encoding-name> -*-").
|
// (e.g., "# -*- coding: <encoding-name> -*-").
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
env_init();
|
env_init();
|
||||||
input_init();
|
|
||||||
|
|
||||||
// Types of output we support.
|
// Types of output we support.
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -294,7 +294,6 @@ static void setup_and_process_keys(bool continuous_mode) {
|
||||||
setup_fork_guards();
|
setup_fork_guards();
|
||||||
env_init();
|
env_init();
|
||||||
reader_init();
|
reader_init();
|
||||||
input_init();
|
|
||||||
proc_push_interactive(1);
|
proc_push_interactive(1);
|
||||||
install_our_signal_handlers();
|
install_our_signal_handlers();
|
||||||
|
|
||||||
|
|
|
@ -921,7 +921,7 @@ static void test_utf82wchar(const char *src, size_t slen, const wchar_t *dst, si
|
||||||
size = utf8_to_wchar(src, slen, NULL, flags);
|
size = utf8_to_wchar(src, slen, NULL, flags);
|
||||||
} else {
|
} else {
|
||||||
mem = (wchar_t *)malloc(dlen * sizeof(*mem));
|
mem = (wchar_t *)malloc(dlen * sizeof(*mem));
|
||||||
if (mem == NULL) {
|
if (!mem) {
|
||||||
err(L"u2w: %s: MALLOC FAILED\n", descr);
|
err(L"u2w: %s: MALLOC FAILED\n", descr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -969,7 +969,7 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si
|
||||||
|
|
||||||
if (dst) {
|
if (dst) {
|
||||||
mem = (char *)malloc(dlen);
|
mem = (char *)malloc(dlen);
|
||||||
if (mem == NULL) {
|
if (!mem) {
|
||||||
err(L"w2u: %s: MALLOC FAILED", descr);
|
err(L"w2u: %s: MALLOC FAILED", descr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1673,8 +1673,13 @@ struct pager_layout_testcase_t {
|
||||||
|
|
||||||
wcstring text = sd.line(0).to_string();
|
wcstring text = sd.line(0).to_string();
|
||||||
if (text != expected) {
|
if (text != expected) {
|
||||||
fwprintf(stderr, L"width %zu got <%ls>, expected <%ls>\n", this->width,
|
fwprintf(stderr, L"width %zu got %d<%ls>, expected %d<%ls>\n", this->width,
|
||||||
text.c_str(), expected.c_str());
|
text.length(), text.c_str(), expected.length(), expected.c_str());
|
||||||
|
for (size_t i = 0; i < std::max(text.length(), expected.length()); i++) {
|
||||||
|
fwprintf(stderr, L"i %zu got <%lx> expected <%lx>\n", i,
|
||||||
|
i >= text.length() ? 0xffff : text[i],
|
||||||
|
i >= expected.length() ? 0xffff : expected[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
do_test(text == expected);
|
do_test(text == expected);
|
||||||
}
|
}
|
||||||
|
@ -4178,7 +4183,7 @@ int main(int argc, char **argv) {
|
||||||
function_init();
|
function_init();
|
||||||
builtin_init();
|
builtin_init();
|
||||||
env_init();
|
env_init();
|
||||||
|
misc_init();
|
||||||
reader_init();
|
reader_init();
|
||||||
|
|
||||||
// Set default signal handlers, so we can ctrl-C out of this.
|
// Set default signal handlers, so we can ctrl-C out of this.
|
||||||
|
|
129
src/input.cpp
129
src/input.cpp
|
@ -2,19 +2,9 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
#if HAVE_NCURSES_H
|
|
||||||
#include <ncurses.h>
|
|
||||||
#elif HAVE_NCURSES_CURSES_H
|
|
||||||
#include <ncurses/curses.h>
|
|
||||||
#else
|
|
||||||
#include <curses.h>
|
|
||||||
#endif
|
|
||||||
#if HAVE_TERM_H
|
#if HAVE_TERM_H
|
||||||
#include <term.h>
|
#include <term.h>
|
||||||
#elif HAVE_NCURSES_TERM_H
|
#elif HAVE_NCURSES_TERM_H
|
||||||
|
@ -33,14 +23,12 @@
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "input_common.h"
|
#include "input_common.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "output.h"
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "signal.h" // IWYU pragma: keep
|
#include "signal.h" // IWYU pragma: keep
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#define DEFAULT_TERM L"ansi"
|
|
||||||
#define MAX_INPUT_FUNCTION_ARGS 20
|
#define MAX_INPUT_FUNCTION_ARGS 20
|
||||||
|
|
||||||
/// Struct representing a keybinding. Returned by input_get_mappings.
|
/// Struct representing a keybinding. Returned by input_get_mappings.
|
||||||
|
@ -201,11 +189,11 @@ static std::vector<terminfo_mapping_t> terminfo_mappings;
|
||||||
/// List of all terminfo mappings.
|
/// List of all terminfo mappings.
|
||||||
static std::vector<terminfo_mapping_t> mappings;
|
static std::vector<terminfo_mapping_t> mappings;
|
||||||
|
|
||||||
/// Set to one when the input subsytem has been initialized.
|
/// Set to true when the input subsytem has been initialized.
|
||||||
static bool is_init = false;
|
bool input_initialized = false;
|
||||||
|
|
||||||
/// Initialize terminfo.
|
/// Initialize terminfo.
|
||||||
static void input_terminfo_init();
|
static void init_input_terminfo();
|
||||||
|
|
||||||
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
||||||
static bool input_function_status;
|
static bool input_function_status;
|
||||||
|
@ -300,87 +288,11 @@ static int interrupt_handler() {
|
||||||
return R_NULL;
|
return R_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_fish_color_support(void) {
|
/// Set up arrays used by readch to detect escape sequences for special keys and perform related
|
||||||
// Detect or infer term256 support. If fish_term256 is set, we respect it;
|
/// initializations for our input subsystem.
|
||||||
// otherwise infer it from the TERM variable or use terminfo.
|
void init_input() {
|
||||||
env_var_t fish_term256 = env_get_string(L"fish_term256");
|
|
||||||
env_var_t term = env_get_string(L"TERM");
|
|
||||||
bool support_term256 = false; // default to no support
|
|
||||||
if (!fish_term256.missing_or_empty()) {
|
|
||||||
support_term256 = from_string<bool>(fish_term256);
|
|
||||||
debug(2, L"256 color support determined by 'fish_term256'");
|
|
||||||
} else if (term.find(L"256color") != wcstring::npos) {
|
|
||||||
// TERM=*256color*: Explicitly supported.
|
|
||||||
support_term256 = true;
|
|
||||||
debug(2, L"256 color support enabled for '256color' in TERM");
|
|
||||||
} else if (term.find(L"xterm") != wcstring::npos) {
|
|
||||||
// Assume that all xterms are 256, except for OS X SnowLeopard
|
|
||||||
const env_var_t prog = env_get_string(L"TERM_PROGRAM");
|
|
||||||
const env_var_t progver = env_get_string(L"TERM_PROGRAM_VERSION");
|
|
||||||
if (prog == L"Apple_Terminal" && !progver.missing_or_empty()) {
|
|
||||||
// OS X Lion is version 300+, it has 256 color support
|
|
||||||
if (strtod(wcs2str(progver), NULL) > 300) {
|
|
||||||
support_term256 = true;
|
|
||||||
debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
support_term256 = true;
|
|
||||||
debug(2, L"256 color support enabled for TERM=xterm");
|
|
||||||
}
|
|
||||||
} else if (cur_term != NULL) {
|
|
||||||
// See if terminfo happens to identify 256 colors
|
|
||||||
support_term256 = (max_colors >= 256);
|
|
||||||
debug(2, L"256 color support: using %d colors per terminfo", max_colors);
|
|
||||||
} else {
|
|
||||||
debug(2, L"256 color support not enabled (yet)");
|
|
||||||
}
|
|
||||||
|
|
||||||
env_var_t fish_term24bit = env_get_string(L"fish_term24bit");
|
|
||||||
bool support_term24bit;
|
|
||||||
if (!fish_term24bit.missing_or_empty()) {
|
|
||||||
support_term24bit = from_string<bool>(fish_term24bit);
|
|
||||||
debug(2, L"'fish_term24bit' preference: 24-bit color %s",
|
|
||||||
support_term24bit ? L"enabled" : L"disabled");
|
|
||||||
} else {
|
|
||||||
// We don't attempt to infer term24 bit support yet.
|
|
||||||
support_term24bit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
color_support_t support = (support_term256 ? color_support_term256 : 0) |
|
|
||||||
(support_term24bit ? color_support_term24bit : 0);
|
|
||||||
output_set_color_support(support);
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_init() {
|
|
||||||
if (is_init) return 1;
|
|
||||||
is_init = true;
|
|
||||||
input_common_init(&interrupt_handler);
|
input_common_init(&interrupt_handler);
|
||||||
|
init_input_terminfo();
|
||||||
int err_ret;
|
|
||||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
|
||||||
debug(0, _(L"Could not set up terminal"));
|
|
||||||
env_var_t term = env_get_string(L"TERM");
|
|
||||||
if (term.missing_or_empty()) {
|
|
||||||
debug(0, _(L"TERM environment variable not set"));
|
|
||||||
} else {
|
|
||||||
debug(0, _(L"TERM environment variable set to '%ls'"), term.c_str());
|
|
||||||
debug(0, _(L"Check that this terminal type is supported on this system"));
|
|
||||||
}
|
|
||||||
|
|
||||||
env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT);
|
|
||||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
|
||||||
debug(0,
|
|
||||||
_(L"Could not set up terminal using the fallback terminal type '%ls' - exiting"),
|
|
||||||
DEFAULT_TERM);
|
|
||||||
exit_without_destructors(1);
|
|
||||||
} else {
|
|
||||||
debug(0, _(L"Using fallback terminal type '%ls'"), DEFAULT_TERM);
|
|
||||||
}
|
|
||||||
fputwc(L'\n', stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
input_terminfo_init();
|
|
||||||
update_fish_color_support();
|
|
||||||
|
|
||||||
// If we have no keybindings, add a few simple defaults.
|
// If we have no keybindings, add a few simple defaults.
|
||||||
if (mapping_list.empty()) {
|
if (mapping_list.empty()) {
|
||||||
|
@ -393,17 +305,14 @@ int input_init() {
|
||||||
input_mapping_add(L"\x5", L"bind");
|
input_mapping_add(L"\x5", L"bind");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
input_initialized = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_destroy() {
|
void input_destroy() {
|
||||||
if (!is_init) return;
|
if (!input_initialized) return;
|
||||||
is_init = false;
|
input_initialized = false;
|
||||||
input_common_destroy();
|
input_common_destroy();
|
||||||
|
|
||||||
if (del_curterm(cur_term) == ERR) {
|
|
||||||
debug(0, _(L"Error while closing terminfo"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_function_push_arg(wchar_t arg) {
|
void input_function_push_arg(wchar_t arg) {
|
||||||
|
@ -678,8 +587,10 @@ bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add all terminfo mappings.
|
/// Add all terminfo mappings and cache other terminfo facts we care about.
|
||||||
static void input_terminfo_init() {
|
static void init_input_terminfo() {
|
||||||
|
assert(curses_initialized);
|
||||||
|
if (!cur_term) return; // setupterm() failed so we can't referency any key definitions
|
||||||
const terminfo_mapping_t tinfos[] = {
|
const terminfo_mapping_t tinfos[] = {
|
||||||
TERMINFO_ADD(key_a1),
|
TERMINFO_ADD(key_a1),
|
||||||
TERMINFO_ADD(key_a3),
|
TERMINFO_ADD(key_a3),
|
||||||
|
@ -843,13 +754,12 @@ static void input_terminfo_init() {
|
||||||
|
|
||||||
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
assert(input_initialized);
|
||||||
|
CHECK(name, 0);
|
||||||
|
|
||||||
const char *res = 0;
|
const char *res = 0;
|
||||||
int err = ENOENT;
|
int err = ENOENT;
|
||||||
|
|
||||||
CHECK(name, 0);
|
|
||||||
input_init();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||||
const terminfo_mapping_t &m = terminfo_mappings.at(i);
|
const terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||||
if (!wcscmp(name, m.name)) {
|
if (!wcscmp(name, m.name)) {
|
||||||
|
@ -869,7 +779,7 @@ bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
||||||
input_init();
|
assert(input_initialized);
|
||||||
|
|
||||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||||
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||||
|
@ -889,11 +799,10 @@ bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
||||||
|
assert(input_initialized);
|
||||||
wcstring_list_t result;
|
wcstring_list_t result;
|
||||||
result.reserve(terminfo_mappings.size());
|
result.reserve(terminfo_mappings.size());
|
||||||
|
|
||||||
input_init();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||||
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||||
|
|
||||||
|
|
14
src/input.h
14
src/input.h
|
@ -15,11 +15,12 @@
|
||||||
|
|
||||||
wcstring describe_char(wint_t c);
|
wcstring describe_char(wint_t c);
|
||||||
|
|
||||||
/// Initialize the terminal by calling setupterm, and set up arrays used by readch to detect escape
|
/// Set to true when the input subsytem has been initialized.
|
||||||
/// sequences for special keys.
|
extern bool input_initialized;
|
||||||
///
|
|
||||||
/// Before calling input_init, terminfo is not initialized and MUST not be used.
|
/// Set up arrays used by readch to detect escape sequences for special keys and perform related
|
||||||
int input_init();
|
/// initializations for our input subsystem.
|
||||||
|
void init_input();
|
||||||
|
|
||||||
/// free up memory used by terminal functions.
|
/// free up memory used by terminal functions.
|
||||||
void input_destroy();
|
void input_destroy();
|
||||||
|
@ -101,7 +102,4 @@ wchar_t input_function_get_code(const wcstring &name);
|
||||||
/// Returns a list of all existing input function names.
|
/// Returns a list of all existing input function names.
|
||||||
wcstring_list_t input_function_get_names(void);
|
wcstring_list_t input_function_get_names(void);
|
||||||
|
|
||||||
/// Updates our idea of whether we support term256 and term24bit.
|
|
||||||
void update_fish_color_support();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -96,6 +96,7 @@ static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool write_foreground_color(unsigned char idx) {
|
static bool write_foreground_color(unsigned char idx) {
|
||||||
|
if (!cur_term) return false;
|
||||||
if (set_a_foreground && set_a_foreground[0]) {
|
if (set_a_foreground && set_a_foreground[0]) {
|
||||||
return write_color_escape(set_a_foreground, idx, true);
|
return write_color_escape(set_a_foreground, idx, true);
|
||||||
} else if (set_foreground && set_foreground[0]) {
|
} else if (set_foreground && set_foreground[0]) {
|
||||||
|
@ -105,6 +106,7 @@ static bool write_foreground_color(unsigned char idx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool write_background_color(unsigned char idx) {
|
static bool write_background_color(unsigned char idx) {
|
||||||
|
if (!cur_term) return false;
|
||||||
if (set_a_background && set_a_background[0]) {
|
if (set_a_background && set_a_background[0]) {
|
||||||
return write_color_escape(set_a_background, idx, false);
|
return write_color_escape(set_a_background, idx, false);
|
||||||
} else if (set_background && set_background[0]) {
|
} else if (set_background && set_background[0]) {
|
||||||
|
@ -115,6 +117,7 @@ static bool write_background_color(unsigned char idx) {
|
||||||
|
|
||||||
// Exported for builtin_set_color's usage only.
|
// Exported for builtin_set_color's usage only.
|
||||||
bool write_color(rgb_color_t color, bool is_fg) {
|
bool write_color(rgb_color_t color, bool is_fg) {
|
||||||
|
if (!cur_term) return false;
|
||||||
bool supports_term24bit =
|
bool supports_term24bit =
|
||||||
static_cast<bool>(output_get_color_support() & color_support_term24bit);
|
static_cast<bool>(output_get_color_support() & color_support_term24bit);
|
||||||
if (!supports_term24bit || !color.is_rgb()) {
|
if (!supports_term24bit || !color.is_rgb()) {
|
||||||
|
@ -168,6 +171,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
||||||
debug(3, "set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
|
debug(3, "set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
|
||||||
#endif
|
#endif
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
if (!cur_term) return;
|
||||||
|
|
||||||
const rgb_color_t normal = rgb_color_t::normal();
|
const rgb_color_t normal = rgb_color_t::normal();
|
||||||
static rgb_color_t last_color = rgb_color_t::normal();
|
static rgb_color_t last_color = rgb_color_t::normal();
|
||||||
|
|
|
@ -1539,10 +1539,10 @@ static bool check_for_orphaned_process(unsigned long loop_count, pid_t shell_pgi
|
||||||
|
|
||||||
/// Initialize data for interactive use.
|
/// Initialize data for interactive use.
|
||||||
static void reader_interactive_init() {
|
static void reader_interactive_init() {
|
||||||
|
assert(input_initialized);
|
||||||
// See if we are running interactively.
|
// See if we are running interactively.
|
||||||
pid_t shell_pgid;
|
pid_t shell_pgid;
|
||||||
|
|
||||||
input_init();
|
|
||||||
kill_init();
|
kill_init();
|
||||||
shell_pgid = getpgrp();
|
shell_pgid = getpgrp();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
// IWYU pragma: no_include <cstddef>
|
// IWYU pragma: no_include <cstddef>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -552,7 +553,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
||||||
// Note that this is required to avoid some visual glitches in iTerm (issue #1448).
|
// Note that this is required to avoid some visual glitches in iTerm (issue #1448).
|
||||||
bool use_multi =
|
bool use_multi =
|
||||||
multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str);
|
multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str);
|
||||||
if (use_multi) {
|
if (use_multi && cur_term) {
|
||||||
char *multi_param = tparm(multi_str, abs(x_steps));
|
char *multi_param = tparm(multi_str, abs(x_steps));
|
||||||
writembs(multi_param);
|
writembs(multi_param);
|
||||||
} else {
|
} else {
|
||||||
|
@ -900,7 +901,10 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if we are using a dumb terminal.
|
/// Returns true if we are using a dumb terminal.
|
||||||
static bool is_dumb(void) { return !cursor_up || !cursor_down || !cursor_left || !cursor_right; }
|
static bool is_dumb(void) {
|
||||||
|
if (!cur_term) return true;
|
||||||
|
return !cursor_up || !cursor_down || !cursor_left || !cursor_right;
|
||||||
|
}
|
||||||
|
|
||||||
struct screen_layout_t {
|
struct screen_layout_t {
|
||||||
// The left prompt that we're going to use.
|
// The left prompt that we're going to use.
|
||||||
|
@ -1232,7 +1236,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
||||||
// We do `>` rather than `>=` because the code below might require one extra space.
|
// We do `>` rather than `>=` because the code below might require one extra space.
|
||||||
if (screen_width > non_space_width) {
|
if (screen_width > non_space_width) {
|
||||||
bool justgrey = true;
|
bool justgrey = true;
|
||||||
if (enter_dim_mode) {
|
if (cur_term && enter_dim_mode) {
|
||||||
std::string dim = tparm(enter_dim_mode);
|
std::string dim = tparm(enter_dim_mode);
|
||||||
if (!dim.empty()) {
|
if (!dim.empty()) {
|
||||||
// Use dim if they have it, so the color will be based on their actual normal
|
// Use dim if they have it, so the color will be based on their actual normal
|
||||||
|
@ -1241,7 +1245,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
||||||
justgrey = false;
|
justgrey = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (justgrey && set_a_foreground) {
|
if (cur_term && justgrey && set_a_foreground) {
|
||||||
if (max_colors >= 238) {
|
if (max_colors >= 238) {
|
||||||
// draw the string in a particular grey
|
// draw the string in a particular grey
|
||||||
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 237)));
|
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 237)));
|
||||||
|
@ -1254,15 +1258,18 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
||||||
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 0)));
|
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abandon_line_string.push_back(omitted_newline_char);
|
abandon_line_string.push_back(omitted_newline_char);
|
||||||
|
|
||||||
if (exit_attribute_mode) {
|
if (cur_term && exit_attribute_mode) {
|
||||||
abandon_line_string.append(
|
abandon_line_string.append(
|
||||||
str2wcstring(tparm(exit_attribute_mode))); // normal text ANSI escape sequence
|
str2wcstring(tparm(exit_attribute_mode))); // normal text ANSI escape sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
int newline_glitch_width = term_has_xn ? 0 : 1;
|
int newline_glitch_width = term_has_xn ? 0 : 1;
|
||||||
abandon_line_string.append(screen_width - non_space_width - newline_glitch_width, L' ');
|
abandon_line_string.append(screen_width - non_space_width - newline_glitch_width, L' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
abandon_line_string.push_back(L'\r');
|
abandon_line_string.push_back(L'\r');
|
||||||
abandon_line_string.push_back(omitted_newline_char);
|
abandon_line_string.push_back(omitted_newline_char);
|
||||||
// Now we are certainly on a new line. But we may have dropped the omitted newline char on
|
// Now we are certainly on a new line. But we may have dropped the omitted newline char on
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
|
@ -220,7 +220,7 @@ static void handle_hup(int sig, siginfo_t *info, void *context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle sigterm. The only thing we do is restore the front process ID, then die.
|
/// Handle sigterm. The only thing we do is restore the front process ID, then die.
|
||||||
static void handle_term(int sig, siginfo_t *info, void *context) {
|
static void handle_sigterm(int sig, siginfo_t *info, void *context) {
|
||||||
UNUSED(sig);
|
UNUSED(sig);
|
||||||
UNUSED(info);
|
UNUSED(info);
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
|
@ -286,7 +286,7 @@ static void set_interactive_handlers() {
|
||||||
sigaction(SIGINT, &act, 0);
|
sigaction(SIGINT, &act, 0);
|
||||||
|
|
||||||
// SIGTERM restores the terminal controlling process before dying.
|
// SIGTERM restores the terminal controlling process before dying.
|
||||||
act.sa_sigaction = &handle_term;
|
act.sa_sigaction = &handle_sigterm;
|
||||||
act.sa_flags = SA_SIGINFO;
|
act.sa_flags = SA_SIGINFO;
|
||||||
sigaction(SIGTERM, &act, 0);
|
sigaction(SIGTERM, &act, 0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue