Compare commits
7 Commits
6f66aa3c06
...
2c595100ae
Author | SHA1 | Date | |
---|---|---|---|
2c595100ae | |||
e4af5b4147 | |||
c9bffc6046 | |||
292829852e | |||
db051698e6 | |||
51ed172d5d | |||
ffa5754b59 |
247
sdn.cpp
247
sdn.cpp
@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <sys/xattr.h>
|
#include <sys/xattr.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <acl/libacl.h>
|
#include <acl/libacl.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
@ -106,7 +108,19 @@ fun split (const string &s, const string &sep) -> vector<string> {
|
|||||||
vector<string> result; split (s, sep, result); return result;
|
vector<string> result; split (s, sep, result); return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun needs_shell_quoting (const string &v) -> bool {
|
||||||
|
// IEEE Std 1003.1 sh + the exclamation mark because of csh/bash
|
||||||
|
// history expansion, implicitly also the NUL character
|
||||||
|
for (auto c : v)
|
||||||
|
if (strchr ("|&;<>()$`\\\"' \t\n" "*?[#˜=%" "!", c))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
fun shell_escape (const string &v) -> string {
|
fun shell_escape (const string &v) -> string {
|
||||||
|
if (!needs_shell_quoting (v))
|
||||||
|
return v;
|
||||||
|
|
||||||
string result;
|
string result;
|
||||||
for (auto c : v)
|
for (auto c : v)
|
||||||
if (c == '\'')
|
if (c == '\'')
|
||||||
@ -143,6 +157,21 @@ template<class T> fun shift (vector<T> &v) -> T {
|
|||||||
auto front = v.front (); v.erase (begin (v)); return front;
|
auto front = v.front (); v.erase (begin (v)); return front;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun capitalize (const string &s) -> string {
|
||||||
|
string result;
|
||||||
|
for (auto c : s)
|
||||||
|
result += result.empty () ? toupper (c) : tolower (c);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Underlining for teletypes, also imitated in more(1) and less(1)
|
||||||
|
fun underline (const string& s) -> string {
|
||||||
|
string result;
|
||||||
|
for (auto c : s)
|
||||||
|
result.append ({c, 8, '_'});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
fun xdg_config_home () -> string {
|
fun xdg_config_home () -> string {
|
||||||
@ -173,8 +202,8 @@ fun xdg_config_find (const string &suffix) -> unique_ptr<ifstream> {
|
|||||||
using ncstring = basic_string<cchar_t>;
|
using ncstring = basic_string<cchar_t>;
|
||||||
|
|
||||||
fun cchar (chtype attrs, wchar_t c) -> cchar_t {
|
fun cchar (chtype attrs, wchar_t c) -> cchar_t {
|
||||||
cchar_t ch {};
|
cchar_t ch {}; wchar_t ws[] = {c, 0};
|
||||||
setcchar (&ch, &c, attrs, PAIR_NUMBER (attrs), nullptr);
|
setcchar (&ch, ws, attrs, PAIR_NUMBER (attrs), nullptr);
|
||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +250,13 @@ fun print (const ncstring &nc, int limit) -> int {
|
|||||||
return total_width;
|
return total_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun compute_width (const wstring &w) -> int {
|
||||||
|
int total = 0;
|
||||||
|
for (const auto &c : w)
|
||||||
|
total += wcwidth (c);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
fun compute_width (const ncstring &nc) -> int {
|
fun compute_width (const ncstring &nc) -> int {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (const auto &c : nc)
|
for (const auto &c : nc)
|
||||||
@ -269,21 +305,7 @@ enum { ALT = 1 << 24, SYM = 1 << 25 }; // Outside the range of Unicode
|
|||||||
#define KEY(name) (SYM | KEY_ ## name)
|
#define KEY(name) (SYM | KEY_ ## name)
|
||||||
#define CTRL 31 &
|
#define CTRL 31 &
|
||||||
|
|
||||||
struct entry {
|
#define ACTIONS(XX) XX(NONE) XX(CHOOSE) XX(CHOOSE_FULL) XX(HELP) XX(QUIT) \
|
||||||
string filename, target_path;
|
|
||||||
struct stat info = {}, target_info = {};
|
|
||||||
|
|
||||||
enum { MODES, USER, GROUP, SIZE, MTIME, FILENAME, COLUMNS };
|
|
||||||
ncstring cols[COLUMNS];
|
|
||||||
|
|
||||||
auto operator< (const entry &other) -> bool {
|
|
||||||
auto a = S_ISDIR (info.st_mode);
|
|
||||||
auto b = S_ISDIR (other.info.st_mode);
|
|
||||||
return (a && !b) || (a == b && filename < other.filename);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ACTIONS(XX) XX(NONE) XX(CHOOSE) XX(CHOOSE_FULL) XX(QUIT) \
|
|
||||||
XX(UP) XX(DOWN) XX(TOP) XX(BOTTOM) XX(PAGE_PREVIOUS) XX(PAGE_NEXT) \
|
XX(UP) XX(DOWN) XX(TOP) XX(BOTTOM) XX(PAGE_PREVIOUS) XX(PAGE_NEXT) \
|
||||||
XX(SCROLL_UP) XX(SCROLL_DOWN) XX(GO_START) XX(GO_HOME) \
|
XX(SCROLL_UP) XX(SCROLL_DOWN) XX(GO_START) XX(GO_HOME) \
|
||||||
XX(SEARCH) XX(RENAME) XX(RENAME_PREFILL) \
|
XX(SEARCH) XX(RENAME) XX(RENAME_PREFILL) \
|
||||||
@ -300,7 +322,7 @@ static const char *g_action_names[] = {ACTIONS(XX)};
|
|||||||
|
|
||||||
static map<wint_t, action> g_normal_actions = {
|
static map<wint_t, action> g_normal_actions = {
|
||||||
{ALT | '\r', ACTION_CHOOSE_FULL}, {ALT | KEY (ENTER), ACTION_CHOOSE_FULL},
|
{ALT | '\r', ACTION_CHOOSE_FULL}, {ALT | KEY (ENTER), ACTION_CHOOSE_FULL},
|
||||||
{'\r', ACTION_CHOOSE}, {KEY (ENTER), ACTION_CHOOSE},
|
{'\r', ACTION_CHOOSE}, {KEY (ENTER), ACTION_CHOOSE}, {'h', ACTION_HELP},
|
||||||
// M-o ought to be the same shortcut the navigator is launched with
|
// M-o ought to be the same shortcut the navigator is launched with
|
||||||
{ALT | 'o', ACTION_QUIT}, {'q', ACTION_QUIT},
|
{ALT | 'o', ACTION_QUIT}, {'q', ACTION_QUIT},
|
||||||
{'k', ACTION_UP}, {CTRL 'p', ACTION_UP}, {KEY (UP), ACTION_UP},
|
{'k', ACTION_UP}, {CTRL 'p', ACTION_UP}, {KEY (UP), ACTION_UP},
|
||||||
@ -347,10 +369,30 @@ struct stringcaseless {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct entry {
|
||||||
|
string filename, target_path;
|
||||||
|
struct stat info = {}, target_info = {};
|
||||||
|
|
||||||
|
enum { MODES, USER, GROUP, SIZE, MTIME, FILENAME, COLUMNS };
|
||||||
|
ncstring cols[COLUMNS];
|
||||||
|
|
||||||
|
auto operator< (const entry &other) -> bool {
|
||||||
|
auto a = S_ISDIR (info.st_mode);
|
||||||
|
auto b = S_ISDIR (other.info.st_mode);
|
||||||
|
return (a && !b) || (a == b && filename < other.filename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct level {
|
||||||
|
int offset, cursor; ///< Scroll offset and cursor position
|
||||||
|
string path, filename; ///< Level path and filename at cursor
|
||||||
|
};
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
string cwd; ///< Current working directory
|
string cwd; ///< Current working directory
|
||||||
string start_dir; ///< Starting directory
|
string start_dir; ///< Starting directory
|
||||||
vector<entry> entries; ///< Current directory entries
|
vector<entry> entries; ///< Current directory entries
|
||||||
|
vector<level> levels; ///< Upper directory levels
|
||||||
int offset, cursor; ///< Scroll offset and cursor position
|
int offset, cursor; ///< Scroll offset and cursor position
|
||||||
bool full_view; ///< Show extended information
|
bool full_view; ///< Show extended information
|
||||||
int max_widths[entry::COLUMNS]; ///< Column widths
|
int max_widths[entry::COLUMNS]; ///< Column widths
|
||||||
@ -361,8 +403,10 @@ static struct {
|
|||||||
int inotify_fd, inotify_wd = -1; ///< File watch
|
int inotify_fd, inotify_wd = -1; ///< File watch
|
||||||
bool out_of_date; ///< Entries may be out of date
|
bool out_of_date; ///< Entries may be out of date
|
||||||
|
|
||||||
wchar_t editor; ///< Prompt character for editing
|
const wchar_t *editor; ///< Prompt string for editing
|
||||||
wstring editor_line; ///< Current user input
|
wstring editor_line; ///< Current user input
|
||||||
|
void (*editor_on_change) (); ///< Callback on editor change
|
||||||
|
void (*editor_on_confirm) (); ///< Callback on editor confirmation
|
||||||
|
|
||||||
enum { AT_CURSOR, AT_BAR, AT_CWD, AT_INPUT, AT_COUNT };
|
enum { AT_CURSOR, AT_BAR, AT_CWD, AT_INPUT, AT_COUNT };
|
||||||
chtype attrs[AT_COUNT] = {A_REVERSE, 0, A_BOLD, 0};
|
chtype attrs[AT_COUNT] = {A_REVERSE, 0, A_BOLD, 0};
|
||||||
@ -372,7 +416,9 @@ static struct {
|
|||||||
map<string, chtype> ls_exts; ///< LS_COLORS file extensions
|
map<string, chtype> ls_exts; ///< LS_COLORS file extensions
|
||||||
bool ls_symlink_as_target; ///< ln=target in dircolors
|
bool ls_symlink_as_target; ///< ln=target in dircolors
|
||||||
|
|
||||||
map<string, wint_t, stringcaseless> key_names;
|
map<string, wint_t, stringcaseless> name_to_key;
|
||||||
|
map<wint_t, string> key_to_name;
|
||||||
|
string action_names[ACTION_COUNT]; ///< Stylized action names
|
||||||
|
|
||||||
// Refreshed by reload():
|
// Refreshed by reload():
|
||||||
|
|
||||||
@ -455,7 +501,7 @@ fun make_entry (const struct dirent *f) -> entry {
|
|||||||
entry e;
|
entry e;
|
||||||
e.filename = f->d_name;
|
e.filename = f->d_name;
|
||||||
e.info.st_mode = DTTOIF (f->d_type);
|
e.info.st_mode = DTTOIF (f->d_type);
|
||||||
auto& info = e.info;
|
auto &info = e.info;
|
||||||
|
|
||||||
// TODO: benchmark just readdir() vs. lstat(), also on dead mounts;
|
// TODO: benchmark just readdir() vs. lstat(), also on dead mounts;
|
||||||
// it might make sense to stat asynchronously in threads
|
// it might make sense to stat asynchronously in threads
|
||||||
@ -559,7 +605,7 @@ fun update () {
|
|||||||
attrset (g.attrs[g.AT_INPUT]);
|
attrset (g.attrs[g.AT_INPUT]);
|
||||||
if (g.editor) {
|
if (g.editor) {
|
||||||
move (LINES - 1, 0);
|
move (LINES - 1, 0);
|
||||||
auto p = apply_attrs ({g.editor, L' ', L'\0'}, 0);
|
auto p = apply_attrs (wstring (g.editor) + L": ", 0);
|
||||||
move (LINES - 1, print (p + apply_attrs (g.editor_line, 0), COLS - 1));
|
move (LINES - 1, print (p + apply_attrs (g.editor_line, 0), COLS - 1));
|
||||||
curs_set (1);
|
curs_set (1);
|
||||||
} else
|
} else
|
||||||
@ -608,6 +654,71 @@ fun reload () {
|
|||||||
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
|
(IN_ALL_EVENTS | IN_ONLYDIR | IN_EXCL_UNLINK) & ~(IN_ACCESS | IN_OPEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: we should be able to signal failures to the user
|
||||||
|
fun run_pager (FILE *contents) {
|
||||||
|
// We don't really need to set O_CLOEXEC, so we're not going to
|
||||||
|
rewind (contents);
|
||||||
|
endwin ();
|
||||||
|
|
||||||
|
switch (pid_t child = fork ()) {
|
||||||
|
int status;
|
||||||
|
case -1:
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
// Put the child in a new foreground process group...
|
||||||
|
setpgid (0, 0);
|
||||||
|
tcsetpgrp (STDOUT_FILENO, getpgid (0));
|
||||||
|
dup2 (fileno (contents), STDIN_FILENO);
|
||||||
|
|
||||||
|
// Behaviour copies man-db's man(1), similar to POSIX man(1)
|
||||||
|
for (auto pager : {(const char *) getenv ("PAGER"), "pager", "cat"})
|
||||||
|
if (pager) execl ("/bin/sh", "/bin/sh", "-c", pager, NULL);
|
||||||
|
_exit (EXIT_FAILURE);
|
||||||
|
default:
|
||||||
|
// ...and make sure of it in the parent as well
|
||||||
|
(void) setpgid (child, child);
|
||||||
|
waitpid (child, &status, 0);
|
||||||
|
tcsetpgrp (STDOUT_FILENO, getpgid (0));
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh ();
|
||||||
|
update ();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encode_key (wint_t key) -> string {
|
||||||
|
string encoded;
|
||||||
|
if (key & ALT)
|
||||||
|
encoded.append ("M-");
|
||||||
|
wchar_t bare = key & ~ALT;
|
||||||
|
if (g.key_to_name.count (bare))
|
||||||
|
encoded.append (capitalize (g.key_to_name.at (bare)));
|
||||||
|
else if (bare < 32)
|
||||||
|
encoded.append ("C-").append ({char (tolower (bare + 64))});
|
||||||
|
else
|
||||||
|
encoded.append (to_mb ({bare}));
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show_help () {
|
||||||
|
FILE *contents = tmpfile ();
|
||||||
|
if (!contents)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto &kv : g_binding_contexts) {
|
||||||
|
fprintf (contents, "%s\n",
|
||||||
|
underline (capitalize (kv.first + " key bindings")).c_str ());
|
||||||
|
for (const auto &kv : *kv.second) {
|
||||||
|
auto key = encode_key (kv.first);
|
||||||
|
key.append (max (0, 10 - compute_width (to_wide (key))), ' ');
|
||||||
|
fprintf (contents, "%s %s\n",
|
||||||
|
key.c_str (), g.action_names[kv.second].c_str ());
|
||||||
|
}
|
||||||
|
fprintf (contents, "\n");
|
||||||
|
}
|
||||||
|
run_pager (contents);
|
||||||
|
fclose (contents);
|
||||||
|
}
|
||||||
|
|
||||||
fun search (const wstring &needle) {
|
fun search (const wstring &needle) {
|
||||||
int best = g.cursor, best_n = 0;
|
int best = g.cursor, best_n = 0;
|
||||||
for (int i = 0; i < int (g.entries.size ()); i++) {
|
for (int i = 0; i < int (g.entries.size ()); i++) {
|
||||||
@ -621,13 +732,40 @@ fun search (const wstring &needle) {
|
|||||||
g.cursor = best;
|
g.cursor = best;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun change_dir (const string& path) {
|
fun is_ancestor_dir (const string &ancestor, const string &of) -> bool {
|
||||||
|
if (strncmp (ancestor.c_str (), of.c_str (), ancestor.length ()))
|
||||||
|
return false;
|
||||||
|
return of.c_str ()[ancestor.length ()] == '/'
|
||||||
|
|| (ancestor == "/" && ancestor != of);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun change_dir (const string &path) {
|
||||||
if (chdir (path.c_str ())) {
|
if (chdir (path.c_str ())) {
|
||||||
beep ();
|
beep ();
|
||||||
} else {
|
return;
|
||||||
// TODO: remember cursor going down, then restore going up
|
}
|
||||||
g.cursor = 0;
|
|
||||||
|
level last {g.offset, g.cursor, g.cwd, g.entries[g.cursor].filename};
|
||||||
reload ();
|
reload ();
|
||||||
|
|
||||||
|
if (is_ancestor_dir (last.path, g.cwd)) {
|
||||||
|
g.levels.push_back (last);
|
||||||
|
g.offset = g.cursor = 0;
|
||||||
|
} else {
|
||||||
|
string anchor;
|
||||||
|
auto i = g.levels.rbegin ();
|
||||||
|
while (i != g.levels.rend () && !is_ancestor_dir (i->path, g.cwd)) {
|
||||||
|
if (i->path == g.cwd) {
|
||||||
|
g.offset = i->offset;
|
||||||
|
g.cursor = i->cursor;
|
||||||
|
anchor = i->filename;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
g.levels.pop_back ();
|
||||||
|
}
|
||||||
|
if (!anchor.empty () && (g.cursor >= g.entries.size ()
|
||||||
|
|| g.entries[g.cursor].filename != anchor))
|
||||||
|
search (to_wide (anchor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,21 +781,17 @@ fun choose (const entry &entry) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun handle_editor (wint_t c) {
|
fun handle_editor (wint_t c) {
|
||||||
// FIXME: do not check editor actions by the prompt letter
|
|
||||||
auto i = g_input_actions.find (c);
|
auto i = g_input_actions.find (c);
|
||||||
switch (i == g_input_actions.end () ? ACTION_NONE : i->second) {
|
switch (i == g_input_actions.end () ? ACTION_NONE : i->second) {
|
||||||
|
case ACTION_INPUT_CONFIRM:
|
||||||
|
if (g.editor_on_confirm)
|
||||||
|
g.editor_on_confirm ();
|
||||||
|
// Fall-through
|
||||||
case ACTION_INPUT_ABORT:
|
case ACTION_INPUT_ABORT:
|
||||||
g.editor_line.clear ();
|
g.editor_line.clear ();
|
||||||
g.editor = 0;
|
g.editor = 0;
|
||||||
break;
|
g.editor_on_change = nullptr;
|
||||||
case ACTION_INPUT_CONFIRM:
|
g.editor_on_confirm = nullptr;
|
||||||
if (g.editor == L'e') {
|
|
||||||
auto mb = to_mb (g.editor_line);
|
|
||||||
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
|
||||||
reload ();
|
|
||||||
}
|
|
||||||
g.editor_line.clear ();
|
|
||||||
g.editor = 0;
|
|
||||||
break;
|
break;
|
||||||
case ACTION_INPUT_B_DELETE:
|
case ACTION_INPUT_B_DELETE:
|
||||||
if (!g.editor_line.empty ())
|
if (!g.editor_line.empty ())
|
||||||
@ -668,9 +802,8 @@ fun handle_editor (wint_t c) {
|
|||||||
beep ();
|
beep ();
|
||||||
} else {
|
} else {
|
||||||
g.editor_line += c;
|
g.editor_line += c;
|
||||||
if (g.editor == L'/'
|
if (g.editor_on_change)
|
||||||
|| g.editor == L's')
|
g.editor_on_change ();
|
||||||
search (g.editor_line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -693,6 +826,9 @@ fun handle (wint_t c) -> bool {
|
|||||||
if (choose (current))
|
if (choose (current))
|
||||||
break;
|
break;
|
||||||
return false;
|
return false;
|
||||||
|
case ACTION_HELP:
|
||||||
|
show_help ();
|
||||||
|
return true;
|
||||||
case ACTION_QUIT:
|
case ACTION_QUIT:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -733,13 +869,21 @@ fun handle (wint_t c) -> bool {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_SEARCH:
|
case ACTION_SEARCH:
|
||||||
g.editor = c;
|
g.editor = L"search";
|
||||||
|
g.editor_on_change = [] {
|
||||||
|
search (g.editor_line);
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
case ACTION_RENAME_PREFILL:
|
case ACTION_RENAME_PREFILL:
|
||||||
g.editor_line = to_wide (current.filename);
|
g.editor_line = to_wide (current.filename);
|
||||||
// Fall-through
|
// Fall-through
|
||||||
case ACTION_RENAME:
|
case ACTION_RENAME:
|
||||||
g.editor = c & ~ALT;
|
g.editor = L"rename";
|
||||||
|
g.editor_on_confirm = [] {
|
||||||
|
auto mb = to_mb (g.editor_line);
|
||||||
|
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
||||||
|
reload ();
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_TOGGLE_FULL:
|
case ACTION_TOGGLE_FULL:
|
||||||
@ -895,7 +1039,6 @@ fun parse_key (const string &key_name) -> wint_t {
|
|||||||
c |= ALT;
|
c |= ALT;
|
||||||
p += 2;
|
p += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp (p, "C-", 2)) {
|
if (!strncmp (p, "C-", 2)) {
|
||||||
p += 2;
|
p += 2;
|
||||||
if (*p < 32) {
|
if (*p < 32) {
|
||||||
@ -904,9 +1047,8 @@ fun parse_key (const string &key_name) -> wint_t {
|
|||||||
}
|
}
|
||||||
c |= CTRL *p;
|
c |= CTRL *p;
|
||||||
p += 1;
|
p += 1;
|
||||||
} else if (g.key_names.count (p)) {
|
} else if (g.name_to_key.count (p)) {
|
||||||
c |= g.key_names.at (p);
|
return c | g.name_to_key.at (p);
|
||||||
p += strlen (p);
|
|
||||||
} else {
|
} else {
|
||||||
wchar_t w; mbstate_t mb {};
|
wchar_t w; mbstate_t mb {};
|
||||||
auto len = strlen (p) + 1, res = mbrtowc (&w, p, len, &mb);
|
auto len = strlen (p) + 1, res = mbrtowc (&w, p, len, &mb);
|
||||||
@ -928,8 +1070,13 @@ fun parse_key (const string &key_name) -> wint_t {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun learn_named_key (const string &name, wint_t key) {
|
||||||
|
g.name_to_key[g.key_to_name[key] = name] = key;
|
||||||
|
}
|
||||||
|
|
||||||
fun load_bindings () {
|
fun load_bindings () {
|
||||||
g.key_names["space"] = ' ';
|
learn_named_key ("space", ' ');
|
||||||
|
learn_named_key ("escape", 0x1b);
|
||||||
for (int kc = KEY_MIN; kc < KEY_MAX; kc++) {
|
for (int kc = KEY_MIN; kc < KEY_MAX; kc++) {
|
||||||
const char *name = keyname (kc);
|
const char *name = keyname (kc);
|
||||||
if (!name)
|
if (!name)
|
||||||
@ -941,7 +1088,7 @@ fun load_bindings () {
|
|||||||
if (*name != '(' && *name != ')')
|
if (*name != '(' && *name != ')')
|
||||||
filtered += *name;
|
filtered += *name;
|
||||||
}
|
}
|
||||||
g.key_names[filtered] = kc;
|
learn_named_key (filtered, SYM | kc);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto config = xdg_config_find ("/" PROJECT_NAME "/bindings");
|
auto config = xdg_config_find ("/" PROJECT_NAME "/bindings");
|
||||||
@ -950,11 +1097,12 @@ fun load_bindings () {
|
|||||||
|
|
||||||
// Stringization in the preprocessor is a bit limited, we want lisp-case
|
// Stringization in the preprocessor is a bit limited, we want lisp-case
|
||||||
map<string, action> actions;
|
map<string, action> actions;
|
||||||
int a;
|
int a = 0;
|
||||||
for (auto p : g_action_names) {
|
for (auto p : g_action_names) {
|
||||||
string name;
|
string name;
|
||||||
for (; *p; p++)
|
for (; *p; p++)
|
||||||
name += *p == '_' ? '-' : *p + 'a' - 'A';
|
name += *p == '_' ? '-' : *p + 'a' - 'A';
|
||||||
|
g.action_names[a] = name;
|
||||||
actions[name] = action (a++);
|
actions[name] = action (a++);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1001,6 +1149,9 @@ int main (int argc, char *argv[]) {
|
|||||||
auto output_fd = dup (STDOUT_FILENO);
|
auto output_fd = dup (STDOUT_FILENO);
|
||||||
dup2 (STDIN_FILENO, STDOUT_FILENO);
|
dup2 (STDIN_FILENO, STDOUT_FILENO);
|
||||||
|
|
||||||
|
// So that the neither us nor our children stop on tcsetpgrp()
|
||||||
|
signal (SIGTTOU, SIG_IGN);
|
||||||
|
|
||||||
if ((g.inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0) {
|
if ((g.inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0) {
|
||||||
cerr << "cannot initialize inotify" << endl;
|
cerr << "cannot initialize inotify" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user