From 0703ad53993b3ecfbbbaaaa643c52012dcafd60d Mon Sep 17 00:00:00 2001 From: Martinez Date: Mon, 6 Apr 2015 22:21:30 +0200 Subject: [PATCH] wip universal prompt renderer, using Powerline symbols --- .gitignore | 2 + fish-init | 5 + render.cpp | 316 +++++++++++++++++++++++++++++++++++++++++++++++++ screeenrc.over | 51 ++++++++ zsh-init | 10 +- 5 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 fish-init create mode 100644 render.cpp create mode 100644 screeenrc.over diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ab3a7b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +render +colors.sh diff --git a/fish-init b/fish-init new file mode 100644 index 0000000..9c29511 --- /dev/null +++ b/fish-init @@ -0,0 +1,5 @@ +#! /bin/fish + +function fish_prompt + ./render .1 .35 +end diff --git a/render.cpp b/render.cpp new file mode 100644 index 0000000..8bdb9e7 --- /dev/null +++ b/render.cpp @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include + +#define ATTR_NONE 0 +#define ATTR_BOLD 1 +#define ATTR_UNDER 4 +#define ATTR_BLINK 5 + +#define SYM_GIT "\ue0a0" +#define SYM_LN "\ue0a1" +#define SYM_LOCK "\ue0a2" +#define SYM_RIGHTBLK "\ue0b0" +#define SYM_RIGHT "\ue0b1" +#define SYM_LEFTBLK "\ue0b2" +#define SYM_LEFT "\ue0b3" + +#define PATH_MAX 4096 + +#ifndef uint +#define uint unsigned int +#endif + +/* -------------------------------------------------- */ + +void term_size(uint *columns, uint *rows) { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + if (columns) *columns = w.ws_col; + if (rows) *rows = w.ws_row; +} + +/* -------------------------------------------------- */ + +class Config { +public: + Config(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Usage: %s red_threshold yellow_threshold\n", argv[0]); + this->failed = true; + return; + } + + this->red_thresh = strtof(argv[1], NULL); + this->yellow_thresh = strtof(argv[2], NULL); + term_size(&(this->term_width), NULL); + this->failed = false; + } + + uint term_width; + float red_thresh; + float yellow_thresh; + + bool failed; +}; + +class Segment { +public: + Segment(const char *str, bool printable=true) { + this->raw_length = strlen(str); + this->raw_str = new char[(this->raw_length + 1) * sizeof(char)]; // +1 for null + strcpy(this->raw_str, str); + + if (printable) { + this->printable_length = u8_mbsnlen((const uint8_t*)str, this->raw_length); + } else { + this->printable_length = 0; + } + } + + ~Segment(void) { + delete[] this->raw_str; + } + + char *raw_str; + size_t raw_length; + size_t printable_length; +}; + +class Output { +public: + Output(void) { + this->printable_length = 0; + this->raw_length = 0; + this->ptr = 0; + this->max = 256; // a reasonable amount? + + this->segments = new Segment*[this->max]; + + this->set_colors(7, 0); + } + + ~Output(void) { + for (uint i = 0; i < this->ptr; i++) { + delete this->segments[i]; + } + + delete[] this->segments; + } + + void output(FILE *stream) { + for (uint i = 0; i < this->ptr; i++) { + fputs(this->segments[i]->raw_str, stream); + } + } + + void set_colors(uint fg, uint bg, const char *transition=NULL) { + if (transition) { + this->push(" "); + } + + this->_push_color(bg, true); + + if (transition) { + this->_push_color(this->col_bg); + this->push(transition); + this->push(" "); + } + + this->_push_color(fg); + + this->col_fg = fg; + this->col_bg = bg; + } + + void _push_color(uint color=ATTR_NONE, bool bg=false) { + if (color == ATTR_NONE) { + this->set_attr(); + } else { + char tmp[16]; + snprintf(tmp, 16, "\e[%d;5;%dm", (bg? 48:38), color); + + this->push(new Segment(tmp, false)); + } + } + + void set_attr(uint attr=ATTR_NONE) { + char tmp[10]; + snprintf(tmp, 10, "\e[%dm", attr); + + this->push(new Segment(tmp, false)); + } + + void push(Segment *segment) { + if (this->ptr + 1 == this->max) { + fprintf(stderr, "Output.segments full, this will probably fail.\n"); + } + + this->segments[this->ptr++] = segment; + this->printable_length += segment->printable_length; + this->raw_length += segment->raw_length; + } + + void push(const char *str) { + this->push(new Segment(str)); + } + +protected: + Segment **segments; + size_t ptr, max; + size_t raw_length; + size_t printable_length; + + uint col_fg, col_bg; +}; + +void render_screen(Output *output) { + char *tmp = getenv("WINDOW"); + + if (tmp) { + output->push(":"); + output->push(tmp); + } +} + +void render_path(Output *output) { + char buf[PATH_MAX]; + + getcwd(buf, PATH_MAX); + + // tokenize path TODO replace homes with ~ + char *ptr = buf; + char *ptr_end; + char *tokens[100]; // a sensible default? + char token[4096]; + uint token_count = 0; + uint token_length; + + while (*ptr) { // until NULL terminator + ptr = strchrnul(ptr, '/') + 1; + ptr_end = strchrnul(ptr, '/'); + token_length = ptr_end - ptr; + memcpy(token, ptr, token_length); + token[token_length] = 0; + + if (token_length) { + tokens[token_count] = new char[(token_length + 1) * sizeof(char)]; + memcpy(tokens[token_count], token, token_length); + tokens[token_count][token_length] = 0; + token_count++; + } + } + + // render path with increasing background brightness + uint fg = 202; + uint bg = 233; + + for (uint i = 0; i < token_count; i++) { + if (bg == 241) fg = 232; + + if (bg < 255) { + output->set_colors(fg, bg++, SYM_RIGHTBLK); + } else { + output->push(" "); + output->push(SYM_RIGHT); + output->push(" "); + } + + output->push(tokens[i]); + delete[] tokens[i]; + } + + // if cwd is not writable, show a warning + if (access(".", W_OK) != 0) { + output->set_colors(232, 88, SYM_RIGHTBLK); + output->push(SYM_LOCK); + } +} + +void render_git(Output *output) { + FILE *pipe; + char branch_name[1024]; + + pipe = popen("git rev-parse --abbrev-ref HEAD 2> /dev/null", "r"); + + if (pipe) { + if (fgets(branch_name, 1024, pipe)) { + branch_name[strlen(branch_name) - 1] = 0; // kill the newline + + // rebuild index + system("git update-index -q --ignore-submodules --refresh"); + + // so that I can check if there are unstaged changes + if (system("git diff-files --quiet --ignore-submodules")) { + output->set_colors(232, 88, SYM_RIGHTBLK); + } else { + output->set_colors(232, 46, SYM_RIGHTBLK); + } + + output->push(SYM_GIT); + output->push(" "); + output->push(branch_name); + } + + pclose(pipe); + } else { + fprintf(stderr, "--- failed to run git\n"); + } +} + +/* -------------------------------------------------- */ + +int main(int argc, char **argv) { + Config config(argc, argv); + if (config.failed) return 1; + + /* -------------------------------------------------- + * output init + */ + + Output top_left; + Output top_path; + Output top_right; + + /* -------------------------------------------------- + * user@host[:screen] + */ + + char buf[PATH_MAX]; + + if (geteuid() == 0) { + top_left.set_colors(232, 124); // red bg + } else { + top_left.set_colors(232, 202); // aware orange bg + } + + top_left.push(" "); + top_left.push(getlogin()); + + top_left.set_colors(202, 232, SYM_RIGHTBLK); + + gethostname(buf, 256); + top_left.push(buf); + + render_screen(&top_left); + render_path(&top_left); + render_git(&top_left); + + /* -------------------------------------------------- + * end + */ + + top_left.set_colors(7, 0, SYM_RIGHTBLK); + + /* -------------------------------------------------- + * finally, output the prompt + */ + + top_left.output(stdout); + + return 0; +} diff --git a/screeenrc.over b/screeenrc.over new file mode 100644 index 0000000..4deeeeb --- /dev/null +++ b/screeenrc.over @@ -0,0 +1,51 @@ +defutf8 on +autodetach on +startup_message off +defscrollback 10240 + +# scroll through scrollback +termcapinfo xterm* ti@:te@ + +# let's get high-tech with 256 colors over here +attrcolor b ".I" # allow bold colors - necessary for some reason +termcapinfo xterm "Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm" # tell screen how to set colors. AB = background, AF=foreground +defbce on # use current bg color for erased chars + +# remapping dangerous keybindings +bind k +bind ^k +bind . +bind ^\ +bind \\ +bind ^h +bind h +bind 'K' kill +bind 'I' login on +bind 'O' login off +bind '}' history + +# custom keybindings +bind '/' eval "scrollback 0" "scrollback 15000" + +# status line +#backtick 1 5 5 true +#termcapinfo rxvt* 'hs:ts=\E]2;:fs=\007:ds=\E]2;\007' +#hardstatus string "screen (%n: %t)" +#caption string "%{= kw}%Y-%m-%d;%c %{= kw}%-Lw%{= kG}%{+b}[%n %t]%{-b}%{= kw}%+Lw%1`" +#caption always + +# environment +setenv DISPLAY ":0" + +# start with a bunch of windows +#screen -t "Nice Rootshell" 0 sudo ionice -c 3 nice su - +#screen -t "Nice Rootshell" 1 sudo ionice -c 3 nice su - +#screen -t "Rootshell" 2 sudo su - +#screen -t "Shell" 3 zsh +#screen -t "Shell" 4 zsh +#screen -t "Shell" 5 zsh +#screen -t "Shell" 6 zsh +#screen -t "Dictionary" 7 /home/eridanus/Software/Development/Et/et-dictionary/screen-wrapper.sh +#screen -t "Equalizer" 8 alsamixer -D equal +#screen -t "Red Noise" 9 red-noise.sh +#screen -t "Syncthing" 10 syncthing -no-browser diff --git a/zsh-init b/zsh-init index f9c722c..99813c0 100755 --- a/zsh-init +++ b/zsh-init @@ -12,11 +12,11 @@ function strlen { local PLAIN PLAIN="$(echo $1 | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")" - echo $#PLAIN + echo ${#PLAIN} } function precmd { - local OVER_OPTS RAW_DATA LOGIN_PART STATS_PART DATA TOP_LEFT TOP_RIGHT PADDING PADDING_SIZE GIT_BRANCH COLOR + local CUT OVER_OPTS RAW_DATA LOGIN_PART STATS_PART DATA TOP_LEFT TOP_RIGHT PADDING PADDING_SIZE GIT_BRANCH COLOR set -A OVER_OPTS ${(s. .)OVER_PROMPT_OPTS} PS1="$(print "%(?.%{\e[1;36m%}.%{\e[1;31m%}%?%{\e[0m%}:%{\e[1;31m%})%(\!.#.$)%{\e[0m%} ")" @@ -51,6 +51,12 @@ function precmd { TOP_LEFT=$(print -P "%(\!.\e[1;31m.\e[1;32m)%n\e[0m@$LOGIN_PART") TOP_RIGHT=$(print -P "[ \e[1;36m%T\e[0m | $STATS_PART") PADDING_SIZE=$(($COLUMNS - $(strlen "$TOP_LEFT") - $(strlen "$TOP_RIGHT"))) + +# if [[ $PADDING_SIZE -lt 0 ]]; then +# CUT=$((0 - $PADDING_SIZE)) +# TOP_LEFT="${TOP_LEFT[$CUT,-1]}" +# fi + PADDING=$(printf " "%.0s {1..$PADDING_SIZE}) print "$TOP_LEFT$PADDING$TOP_RIGHT"