diff --git a/Xresources b/Xresources
index ebdba57..6a06d00 100644
--- a/Xresources
+++ b/Xresources
@@ -1,3 +1,4 @@
#include ".Xresources.d/xft"
#include ".Xresources.d/theme"
#include ".Xresources.d/xterm"
+#include ".Xresources.d/urxvt"
diff --git a/Xresources.d/.gitignore b/Xresources.d/.gitignore
new file mode 100644
index 0000000..62fd177
--- /dev/null
+++ b/Xresources.d/.gitignore
@@ -0,0 +1 @@
+theme
diff --git a/Xresources.d/theme b/Xresources.d/theme
deleted file mode 100644
index d60c7b3..0000000
--- a/Xresources.d/theme
+++ /dev/null
@@ -1,36 +0,0 @@
-! special
-*.foreground: #f1ebeb
-*.background: #272822
-*.cursorColor: #f1ebeb
-
-! black
-*.color0: #48483e
-*.color8: #76715e
-
-! red
-*.color1: #dc2566
-*.color9: #fa2772
-
-! green
-*.color2: #8fc029
-*.color10: #a7e22e
-
-! yellow
-*.color3: #d4c96e
-*.color11: #e7db75
-
-! blue
-*.color4: #55bcce
-*.color12: #66d9ee
-
-! magenta
-*.color5: #9358fe
-*.color13: #ae82ff
-
-! cyan
-*.color6: #56b7a5
-*.color14: #66efd5
-
-! white
-*.color7: #acada1
-*.color15: #cfd0c2
diff --git a/Xresources.d/urxvt b/Xresources.d/urxvt
new file mode 100644
index 0000000..e94c0e2
--- /dev/null
+++ b/Xresources.d/urxvt
@@ -0,0 +1,55 @@
+! Scrollback position
+! TODO Does not work
+
+! do not scroll with output
+URxvt*scrollTtyOutput: false
+
+! scroll in relation to buffer (with mouse scroll or Shift+Page Up)
+URxvt*scrollWithBuffer: true
+
+! scroll back to the bottom on keypress
+URxvt*scrollTtyKeypress: true
+
+
+! Scrollback buffer in secondary screen
+! TODO Does not work
+URxvt.secondaryScreen: 1
+URxvt.secondaryScroll: 0
+
+
+! No scroll bar
+URxvt*scrollBar: false
+
+
+! Font declaration
+URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true,xft:Symbola:size=12:antialias=true
+
+! Font spacing
+URxvt.letterSpace: 0
+
+! Disable Ctrl+Shift default bindings
+URxvt.iso14755: false
+URxvt.iso14755_52: false
+
+! Copy/Paste CLIPBOARD selection with Ctrl+Shift+C/V
+URxvt.keysym.C-S-C: eval:selection_to_clipboard
+URxvt.keysym.C-S-V: eval:paste_clipboard
+
+
+! Extensions
+URxvt.perl-ext-common: resize-font,bell-command,readline,selection
+
+! Changing font size on the fly (extension: resize-font, package: urxvt-resize-font-git)
+URxvt.keysym.C-KP_Subtract: resize-font:smaller
+URxvt.keysym.C-KP_Add: resize-font:bigger
+
+! Fake transparency (without compositing manager)
+urxvt*transparent: true
+urxvt*shading: 30
+
+! True transparency (with compositing manager)
+!urxvt*depth: 32
+!urxvt*background: rgba:2700/2800/2200/c800
+
+! Bell command (extension: bell-command)
+URxvt.bell-command: play -n synth sine C5 sine E4 remix 1-2 fade 0.1 0.2 0.1 &> /dev/null
diff --git a/bashrc b/bashrc
index 076bb9d..8544d4c 100644
--- a/bashrc
+++ b/bashrc
@@ -12,11 +12,6 @@ export BROWSER=qutebrowser
# Some programs need those changes
export PATH="/usr/lib/ccache/bin/:$PATH"
-GEM_HOME=$(ruby -e 'puts Gem.user_dir' 2> /dev/null)
-if [ $? -eq 0 ]; then
- GEM_PATH=$GEM_HOME
- export PATH="$PATH:$GEM_HOME/bin"
-fi
if [ -d /data/data/com.termux/ ]; then
export PATH="$HOME/.termux/scripts:$HOME/.termux/bin:$PATH"
fi
@@ -44,8 +39,10 @@ alias dd='dd status=progress'
alias rm='rm -Iv --one-file-system'
alias free='free -m'
alias df='df -h'
-alias pacman='pacman --color auto'
alias dmesg='dmesg --ctime'
+alias ffmpeg='ffmpeg -hide_banner'
+alias ffprobe='ffprobe -hide_banner'
+alias ffplay='ffplay -hide_banner'
# Frequent mistakes
alias sl=ls
@@ -62,17 +59,50 @@ alias po='eval $(proxy off)'
alias nw="sudo systemctl restart NetworkManager"
alias mc="machines"
alias tracefiles="strace -f -t -e trace=file"
+alias n='urxvtc &'
# Superseding commands with better ones if they are present
-if which vim &> /dev/null; then
- alias vi='vim'
-fi
-if which gopass &> /dev/null; then
- alias pass='gopass'
-fi
-if which wakeonlan &> /dev/null; then
- alias wol='wakeonlan'
-fi
+function _do_rank() { # executables... -- arguments...
+ for ex in "$@"
+ do
+ [ "$ex" == "--" ] && break
+ if which "$ex" &> /dev/null
+ then
+ for al in "$@"
+ do
+ shift
+ [ "$al" == "--" ] && break
+ alias "$al"="$ex"
+ done
+ "$ex" "$@"
+ return $?
+ fi
+ done
+ for ex in "$@"
+ do
+ [ "$al" == "--" ] && break
+ if -z "$list"
+ then
+ list=$ex
+ else
+ list=$list, $ex
+ fi
+ done
+ echo "Not installed: $list"
+}
+
+function _install_rank() { # executables...
+ for ex in "$@"
+ do
+ list=$@
+ alias "$ex"="_do_rank $list --"
+ done
+}
+
+_install_rank nvim vim vi
+_install_rank gopass pass
+_install_rank wakeonlan wol
+_install_rank neomutt mutt
# SHELL CUSTOMIZATION
@@ -89,7 +119,8 @@ shopt -s hostcomplete
export HISTSIZE=100000
export HISTFILESIZE=${HISTSIZE}
-export HISTCONTROL=ignoreboth
+export HISTCONTROL=ignorespace:erasedups
+export HISTTIMEFORMAT="%y-%m-%d %H:%M:%S "
# PROMPT CUSTOMIZATION
@@ -104,16 +135,28 @@ export PS1="\[\e]2;\u@\h \w\a\]\[\e[0;37m\][\[\e[0;${col}m\]\u\[\e[0;37m\]@\[\e[
export PS2="> "
export PS3="+ "
export PS4="- "
+export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}:${PWD}\007"'
# CUSTOM SCRIPTS
-export PATH="$HOME/.bin/:$HOME/.scripts/:$PATH"
+export PATH="$HOME/.local/bin/:$HOME/.scripts/:$PATH"
[ -f ~/.gscripts/gprofile ] && source ~/.gscripts/gprofile
# UTILITIES
+# Theme
+[ -f ~/.local/bin/colorSchemeApply ] && source ~/.local/bin/colorSchemeApply
+[ -f ~/.local/bin/colorSchemeApplyFzf ] && source ~/.local/bin/colorSchemeApplyFzf
+
# Bash completion
-[ -f /etc/bash_completion ] && . /etc/bash_completion
+[ -f /etc/bash_completion ] && source /etc/bash_completion
+
+# Fuzzy matching all the way
+export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --height 100% --layout=default"
+export FZF_CTRL_T_OPTS="--preview '[[ -d {} ]] && ls -l --color=always {} || [[ \$(file --mime {}) =~ binary ]] && echo {} is a binary file || (highlight -O ansi -l {} || coderay {} || rougify {} || cat {}) 2> /dev/null | head -500'"
+export FZF_COMPLETION_OPTS="${FZF_CTRL_T_OPTS}"
+[ -f /usr/share/fzf/completion.bash ] && source /usr/share/fzf/completion.bash
+[ -f /usr/share/fzf/key-bindings.bash ] && source /usr/share/fzf/key-bindings.bash
# Bad day mood-saver
function fuck {
diff --git a/bin/.gitignore b/bin/.gitignore
deleted file mode 100644
index d6b7ef3..0000000
--- a/bin/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/config/compton.conf b/config/compton.conf
deleted file mode 100644
index 34eb7df..0000000
--- a/config/compton.conf
+++ /dev/null
@@ -1,53 +0,0 @@
-shadow = false;
-no-dnd-shadow = true;
-no-dock-shadow = true;
-clear-shadow = true;
-
-menu-opacity = 0.9;
-inactive-opacity = 0.93;
-active-opacity = 1;
-alpha-step = 0.01;
-inactive-dim = 0.0;
-blur-background = false;
-blur-kern = "3x3box";
-
-fading = false;
-fade-delta = 1;
-fade-in-step = 0.03;
-fade-out-step = 0.03;
-fade-exclude = [ ];
-
-backend = "xrender";
-mark-wmwin-focused = true;
-mark-ovredir-focused = true;
-detect-rounded-corners = true;
-detect-client-opacity = true;
-unredir-if-possible = true;
-refresh-rate = 0;
-vsync = "none";
-dbe = false;
-paint-on-overlay = true;
-focus-exclude = [ "class_g = 'Cairo-clock'" ];
-detect-transient = true;
-detect-client-leader = true;
-invert-color-include = [ ];
-glx-copy-from-front = false;
-glx-swap-method = "undefined";
-
-wintypes :
-{
- tooltip :
- {
- fade = true;
- shadow = false;
- opacity = 0.75;
- focus = true;
- };
- fullscreen :
- {
- fade = true;
- shadow = false;
- opacity = 1;
- focus = true;
- };
-};
diff --git a/config/dunst/dunstrc b/config/dunst/dunstrc
index eead4c9..17278d9 100644
--- a/config/dunst/dunstrc
+++ b/config/dunst/dunstrc
@@ -1,300 +1,63 @@
[global]
- font = Cantarell 10
-
- # Allow a small subset of html markup:
- # bold
- # italic
- # strikethrough
- # underline
- #
- # For a complete reference see
- # .
- # If markup is not allowed, those tags will be stripped out of the
- # message.
- allow_markup = yes
-
- # The format of the message. Possible variables are:
- # %a appname
- # %s summary
- # %b body
- # %i iconname (including its path)
- # %I iconname (without its path)
- # %p progress value if set ([ 0%] to [100%]) or nothing
- # Markup is allowed
- format = "%s %p\n%b"
-
- # Sort messages by urgency.
- sort = no
-
- # Show how many messages are currently hidden (because of geometry).
- indicate_hidden = yes
-
- # Alignment of message text.
- # Possible values are "left", "center" and "right".
alignment = left
-
- # The frequency with wich text that is longer than the notification
- # window allows bounces back and forth.
- # This option conflicts with "word_wrap".
- # Set to 0 to disable.
- bounce_freq = 5
-
-
- # Show age of message if message is older than show_age_threshold
- # seconds.
- # Set to -1 to disable.
- show_age_threshold = 60
-
- # Split notifications into multiple lines if they don't fit into
- # geometry.
- word_wrap = no
-
- # Ignore newlines '\n' in notifications.
- ignore_newline = no
-
-
- # The geometry of the window:
- # [{width}]x{height}[+/-{x}+/-{y}]
- # The geometry of the message window.
- # The height is measured in number of notifications everything else
- # in pixels. If the width is omitted but the height is given
- # ("-geometry x2"), the message window expands over the whole screen
- # (dmenu-like). If width is 0, the window expands to the longest
- # message displayed. A positive x is measured from the left, a
- # negative from the right side of the screen. Y is measured from
- # the top and down respectevly.
- # The width can be negative. In this case the actual width is the
- # screen width minus the width defined in within the geometry option.
- geometry = "0x0-25+25"
-
- # Shrink window if it's smaller than the width. Will be ignored if
- # width is 0.
- shrink = yes
-
- # The transparency of the window. Range: [0; 100].
- # This option will only work if a compositing windowmanager is
- # present (e.g. xcompmgr, compiz, etc.).
- transparency = 17
-
- # Don't remove messages, if the user is idle (no mouse or keyboard input)
- # for longer than idle_threshold seconds.
- # Set to 0 to disable.
- # default 120
- idle_threshold = 120
-
- # Which monitor should the notifications be displayed on.
- monitor = 0
-
- # Display notification on focused monitor. Possible modes are:
- # mouse: follow mouse pointer
- # keyboard: follow window with keyboard focus
- # none: don't follow anything
- #
- # "keyboard" needs a windowmanager that exports the
- # _NET_ACTIVE_WINDOW property.
- # This should be the case for almost all modern windowmanagers.
- #
- # If this option is set to mouse or keyboard, the monitor option
- # will be ignored.
- follow = none
-
- # Should a notification popped up from history be sticky or timeout
- # as if it would normally do.
- sticky_history = yes
-
- # Maximum amount of notifications kept in history
- history_length = 20
-
- # Display indicators for URLs (U) and actions (A).
- show_indicators = yes
-
- # The height of a single line. If the height is smaller than the
- # font height, it will get raised to the font height.
- # This adds empty space above and under the text.
- line_height = 0
-
- # Draw a line of "separator_height" pixel height between two
- # notifications.
- # Set to 0 to disable.
- separator_height = 1
-
- # Padding between text and separator.
- # padding = 8
- padding = 8
-
- # Horizontal padding.
- horizontal_padding = 10
-
- # Define a color for the separator.
- # possible values are:
- # * auto: dunst tries to find a color fitting to the background;
- # * foreground: use the same color as the foreground;
- # * frame: use the same color as the frame;
- # * anything else will be interpreted as a X color.
- separator_color = #2ECC71
-
- # Print a notification on startup.
- # This is mainly for error detection, since dbus (re-)starts dunst
- # automatically after a crash.
- startup_notification = false
-
- # dmenu path.
+ always_run_script = true
+ browser = /usr/bin/qutebrowser
+ class = Dunst
dmenu = /usr/bin/dmenu -p dunst:
-
- # Browser for opening urls in context menu.
- browser = firefox
-
- # Align icons left/right/off
+ ellipsize = middle
+ follow = none
+ font = DejaVu Sans 10
+ force_xinerama = false
+ format = "%s %p\n%b"
+ frame_color = "#A6E22E"
+ frame_width = 3
+ geometry = "500x5-30+20"
+ hide_duplicate_count = false
+ history_length = 20
+ horizontal_padding = 8
+ icon_path = /usr/share/icons/gnome/256x256/actions/:/usr/share/icons/gnome/256x256/status/:/usr/share/icons/gnome/256x256/devices/
icon_position = left
-
- # Paths to default icons.
- icon_folders = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
-
-[frame]
- width = 1
- color = "#2ECC71"
-
+ idle_threshold = 120
+ ignore_newline = no
+ indicate_hidden = yes
+ line_height = 0
+ markup = full
+ max_icon_size = 48
+ monitor = 0
+ notification_height = 0
+ padding = 8
+ separator_color = frame
+ separator_height = 2
+ show_age_threshold = 60
+ show_indicators = yes
+ shrink = no
+ sort = yes
+ stack_duplicates = true
+ startup_notification = false
+ sticky_history = yes
+ title = Dunst
+ transparency = 0
+ verbosity = mesg
+ word_wrap = yes
+[experimental]
+ per_monitor_dpi = false
[shortcuts]
-
- # Shortcuts are specified as [modifier+][modifier+]...key
- # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2",
- # "mod3" and "mod4" (windows-key).
- # Xev might be helpful to find names for keys.
-
- # Close notification.
- close = mod4+n
-
- # Close all notifications.
- # close_all = ctrl+shift+space
close_all = ctrl+mod4+n
-
- # Redisplay last message(s).
- # On the US keyboard layout "grave" is normally above TAB and left
- # of "1".
- history = shift+mod4+n
-
- # Context menu.
+ close = mod4+n
context = mod1+mod4+n
-
+ history = shift+mod4+n
[urgency_low]
- # IMPORTANT: colors have to be defined in quotation marks.
- # Otherwise the "#" and following would be interpreted as a comment.
- background = "#000000"
- foreground = "#888888"
+ background = "#272822"
+ foreground = "#F8F8F2"
+ frame_color = "#A6E22E"
timeout = 10
-
[urgency_normal]
- background = "#000000"
- foreground = "#ffffff"
+ background = "#272822"
+ foreground = "#F8F8F2"
+ frame_color = "#F4BF75"
timeout = 10
-
[urgency_critical]
- background = "#900000"
- foreground = "#ffffff"
+ background = "#272822"
+ foreground = "#F8F8F2"
+ frame_color = "#F92672"
timeout = 0
-
-
-# Every section that isn't one of the above is interpreted as a rules to
-# override settings for certain messages.
-# Messages can be matched by "appname", "summary", "body", "icon", "category",
-# "msg_urgency" and you can override the "timeout", "urgency", "foreground",
-# "background", "new_icon" and "format".
-# Shell-like globbing will get expanded.
-#
-# SCRIPTING
-# You can specify a script that gets run when the rule matches by
-# setting the "script" option.
-# The script will be called as follows:
-# script appname summary body icon urgency
-# where urgency can be "LOW", "NORMAL" or "CRITICAL".
-#
-# NOTE: if you don't want a notification to be displayed, set the format
-# to "".
-# NOTE: It might be helpful to run dunst -print in a terminal in order
-# to find fitting options for rules.
-
-#[espeak]
-# summary = "*"
-# script = dunst_espeak.sh
-
-#[script-test]
-# summary = "*script*"
-# script = dunst_test.sh
-
-#[ignore]
-# # This notification will not be displayed
-# summary = "foobar"
-# format = ""
-
-#[signed_on]
-# appname = Pidgin
-# summary = "*signed on*"
-# urgency = low
-#
-#[signed_off]
-# appname = Pidgin
-# summary = *signed off*
-# urgency = low
-#
-#[says]
-# appname = Pidgin
-# summary = *says*
-# urgency = critical
-#
-#[twitter]
-# appname = Pidgin
-# summary = *twitter.com*
-# urgency = normal
-#
-#[Claws Mail]
-# appname = claws-mail
-# category = email.arrived
-# urgency = normal
-# background = "#2F899E"
-# foreground = "#FFA247"
-#
-#[mute.sh]
-# appname = mute
-# category = mute.sound
-# script = mute.sh
-#
-#[JDownloader]
-# appname = JDownloader
-# category = JD
-# background = "#FFA247"
-# foreground = "#FFFFFF"
-#
-#[newsbeuter]
-# summary = *Feeds*
-# background = "#A8EB41"
-# foreground = "#FFFFFF"
-#
-[irc]
- appname = weechat
- timeout = 0
- background = "#0033bb"
- foreground = "#dddddd"
-#
-[weechat hl]
- appname = weechat
- category = weechat.HL
- background = "#FF5C47"
- foreground = "#FFFFFF"
-#
-[weechat pn]
- appname = weechat
- category = weechat.PM
- background = "#D53B84"
- foreground = "#FFFFFF"
-#
-#[CMUS]
-# appname = CMUS
-# category = cmus
-# background = "#6C4AB7"
-# foreground = "#FFE756"
-#
-#
-# background = "#30AB70"
-# foreground = "#F67245"
-#
-# vim: ft=cfg
diff --git a/config/i3/ashuffle b/config/i3/ashuffle
new file mode 100755
index 0000000..77168ef
--- /dev/null
+++ b/config/i3/ashuffle
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+while true
+do
+ ashuffle
+ sleep 1
+done
diff --git a/config/i3/autorandrdefaultmenu b/config/i3/autorandrdefaultmenu
index 3f8a36e..d2f2d25 100755
--- a/config/i3/autorandrdefaultmenu
+++ b/config/i3/autorandrdefaultmenu
@@ -2,7 +2,7 @@
shopt -s nullglob globstar
-profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Default profile" "$@")
+profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Default profile" "$@")
[[ -n $profile ]] || exit
diff --git a/config/i3/autorandrloadmenu b/config/i3/autorandrloadmenu
index 930b044..81000eb 100755
--- a/config/i3/autorandrloadmenu
+++ b/config/i3/autorandrloadmenu
@@ -2,7 +2,7 @@
shopt -s nullglob globstar
-profile=$(echo -e "common\nhorizontal\nvertical\n$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Load profile" "$@")
+profile=$(echo -e "common\nhorizontal\nvertical\n$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Load profile" "$@")
[[ -n $profile ]] || exit
diff --git a/config/i3/autorandrremovemenu b/config/i3/autorandrremovemenu
index 7b12673..323d72c 100755
--- a/config/i3/autorandrremovemenu
+++ b/config/i3/autorandrremovemenu
@@ -2,7 +2,7 @@
shopt -s nullglob globstar
-profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Remove profile" "$@")
+profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Remove profile" "$@")
[[ -n $profile ]] || exit
diff --git a/config/i3/autorandrsavemenu b/config/i3/autorandrsavemenu
index bae7989..e24db48 100755
--- a/config/i3/autorandrsavemenu
+++ b/config/i3/autorandrsavemenu
@@ -2,7 +2,7 @@
shopt -s nullglob globstar
-profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Save profile" "$@")
+profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Save profile" "$@")
[[ -n $profile ]] || exit
diff --git a/config/i3/batteryNotify b/config/i3/batteryNotify
new file mode 100755
index 0000000..a6a1ed2
--- /dev/null
+++ b/config/i3/batteryNotify
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+BATT="/sys/class/power_supply/BAT0"
+LOW=10
+CRIT=3
+LASTSTATE="$HOME/.cache/batteryState"
+
+function setState() { # state [...notify-send arguments]
+ state="$1"
+ last="$(cat "$LASTSTATE" 2> /dev/null)"
+ shift
+
+ echo "Battery state: $state"
+
+ if [ "$state" != "$last" ]
+ then
+ notify-send "$@"
+ echo "$state" > "$LASTSTATE"
+ fi
+}
+
+function computeState() {
+ acpiStatus="$(cat "$BATT/status")"
+ acpiCapacity="$(cat "$BATT/capacity")"
+
+ if [ "$acpiStatus" == "Discharging" ]
+ then
+ if [ $acpiCapacity -le $CRIT ]
+ then
+ setState "CRIT" -u critical -i battery-caution "Battery level is critical" "$acpiCapacity %"
+ elif [ $acpiCapacity -le $LOW ]
+ then
+ setState "LOW" -u critical -i battery-low "Battery level is low" "$acpiCapacity %"
+ else
+ setState "DISCHARGING" -i battery-good "Battery is discharging" "$acpiCapacity %"
+ fi
+ elif [ "$acpiStatus" == "Charging" ]
+ then
+ setState "CHARGING" -u normal -i battery-good-charging "Battery is charging" "$acpiCapacity %"
+ elif [ "$acpiStatus" == "Full" ]
+ then
+ setState "FULL" -u low -i battery-full-charged "Battery is full" "$acpiCapacity %"
+ fi
+}
+
+if [ "$1" == "-d" ]
+then
+ while true
+ do
+ computeState
+ sleep 10
+ done
+else
+ computeState
+fi
diff --git a/config/i3/clipmenu b/config/i3/clipmenu
deleted file mode 100755
index 89935a1..0000000
--- a/config/i3/clipmenu
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/bash
-
-shopt -s nullglob
-
-# We use this to make sure the cache files are sorted bytewise
-LC_COLLATE=C
-
-# Some people copy/paste huge swathes of text that could slow down dmenu
-line_length_limit=500
-
-declare -A selections
-ordered_selections=()
-
-files=("$HOME/.clipmenu/"*)
-
-# We can't use `for ... in` here because we need to add files to
-# ordered_selections from last to first -- that is, newest to oldest. Incoming
-# clipboard entries have a ISO datetime prefixed to the front to aid in this.
-for (( i=${#files[@]}-1; i>=0; i-- )); do
- file=${files[$i]}
-
- # We look for the first line matching regex /./ here because we want the
- # first line that can provide reasonable context to the user. That is, if
- # you have 5 leading lines of whitespace, displaying " (6 lines)" is much
- # less useful than displaying "foo (6 lines)", where "foo" is the first
- # line in the entry with actionable context.
- first_line=$(sed -n '/./{p;q}' "$file" | cut -c1-"$line_length_limit")
- lines=$(wc -l < "$file")
-
- if (( lines > 1 )); then
- first_line+=" ($lines lines)"
- fi
-
- ordered_selections+=("$first_line")
- selections[$first_line]=$file
-done
-
-# It's okay to hardcode `-l 8` here as a sensible default without checking
-# whether `-l` is also in "$@", because the way that dmenu works allows a later
-# argument to override an earlier one. That is, if the user passes in `-l`, our
-# one will be ignored.
-chosen_line=$(printf '%s\n' "${ordered_selections[@]}" | uniq | $HOME/.config/i3/dmenu_cmd -l 8 -p "Copy" "$@")
-
-[[ $chosen_line ]] || exit 1
-
-for selection in clipboard primary; do
- if type -p xsel >/dev/null 2>&1; then
- xsel --logfile /dev/null -i --"$selection" < "${selections[$chosen_line]}"
- else
- xclip -sel "$selection" < "${selections[$chosen_line]}"
- fi
-done
diff --git a/config/i3/clipmenud b/config/i3/clipmenud
deleted file mode 100755
index 98edd08..0000000
--- a/config/i3/clipmenud
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/bin/bash
-
-hr_msg() {
- printf -- '\n--- %s ---\n\n' "$1" >&2
-}
-
-debug() {
- if (( DEBUG )); then
- printf '%s\n' "$@" >&2
- fi
-}
-
-print_debug_info() {
- # DEBUG comes from the environment
- if ! (( DEBUG )); then
- return
- fi
-
- local msg="${1?}"
-
- hr_msg "$msg"
-
- hr_msg Environment
- env | LC_ALL=C sort >&2
-
- cgroup_path=/proc/$$/cgroup
-
- if [[ -f $cgroup_path ]]; then
- hr_msg cgroup
- cat "$cgroup_path" >&2
- else
- hr_msg 'NO CGROUP'
- fi
-
- hr_msg 'Finished debug info'
-}
-
-print_debug_info 'Initialising'
-
-cache_dir=$HOME/.clipmenu/
-
-# It's ok that this only applies to the final directory.
-# shellcheck disable=SC2174
-mkdir -p -m0700 "$cache_dir"
-
-declare -A last_data
-declare -A last_filename
-
-while sleep "${CLIPMENUD_SLEEP:-0.5}"; do
- print_debug_info 'About to run selection'
-
- for selection in clipboard primary; do
- print_debug_info "About to do selection for '$selection'"
-
- if type -p xsel >/dev/null 2>&1; then
- debug 'Using xsel'
- data=$(xsel --logfile /dev/null -o --"$selection"; printf x)
- else
- debug 'Using xclip'
- data=$(xclip -o -sel "$selection"; printf x)
- fi
-
- debug "Data before stripping: $data"
-
- # We add and remove the x so that trailing newlines are not stripped.
- # Otherwise, they would be stripped by the very nature of how POSIX
- # defines command substitution.
- data=${data%x}
-
- debug "Data after stripping: $data"
-
- if [[ $data != *[^[:space:]]* ]]; then
- debug "Skipping as clipboard is only blank"
- continue
- fi
-
- if [[ ${last_data[$selection]} == "$data" ]]; then
- debug 'Skipping as last selection is the same as this one'
- continue
- fi
-
- # If we were in the middle of doing a selection when the previous poll
- # ran, then we may have got a partial clip.
- possible_partial=${last_data[$selection]}
- if [[ $possible_partial && $data == "$possible_partial"* ]]; then
- debug "$possible_partial is a possible partial of $data"
- debug "Removing ${last_filename[$selection]}"
- rm -- "${last_filename[$selection]}"
- fi
-
- filename="$cache_dir/$(LC_ALL=C date +%F-%T.%N)"
-
- last_data[$selection]=$data
- last_filename[$selection]=$filename
-
- debug "Writing $data to $filename"
- printf '%s' "$data" > "$filename"
-
- if ! (( NO_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then
- # Take ownership of the clipboard, in case the original application
- # is unable to serve the clipboard request (due to being suspended,
- # etc).
- #
- # Primary is excluded from the change of ownership as applications
- # sometimes act up if clipboard focus is taken away from them --
- # for example, urxvt will unhilight text, which is undesirable.
- #
- # We can't colocate this with the above copying code because
- # https://github.com/cdown/clipmenu/issues/34 requires knowing if
- # we would skip first.
- if type -p xsel >/dev/null 2>&1; then
- xsel --logfile /dev/null -o --"$selection" | xsel -i --"$selection"
- else
- xclip -o -sel "$selection" | xclip -i -sel "$selection"
- fi
- fi
-
- if ! (( NO_TRANSFER_CLIPBOARD )) && [[ $selection != primary ]]; then
- # Copy every clipboard content into primary clipboard
- if type -p xsel >/dev/null 2>&1; then
- xsel --logfile /dev/null -o --"$selection" | xsel -i --primary
- else
- xclip -o -sel "$selection" | xclip -i -sel primary
- fi
- fi
- done
-done
diff --git a/config/i3/config b/config/i3/config
index 04ffddd..65f223b 100644
--- a/config/i3/config
+++ b/config/i3/config
@@ -19,11 +19,15 @@ hide_edge_borders both
#bindsym $mod+y border pixel 2
#bindsym $mod+n border normal
+# Compatibility layer for people coming from other backgrounds
+bindsym Mod1+Tab exec --no-startup-id rofi -modi window -show window
+bindsym Mod1+F2 exec --no-startup-id rofi -modi drun -show drun
+bindsym Mod1+F4 kill
+
# Font for window titles. Will also be used by the bar unless a different font
# is used in the bar {} block below.
-font pango:Source Code Pro 8
-font pango:DejaVu Sans Mono 8
+font pango:DejaVu Sans 8
font pango:Sans 8
# Use Mouse+$mod to drag floating windows
@@ -31,23 +35,19 @@ floating_modifier $mod
# kill focused window
bindsym $mod+z kill
-#bindsym Mod1+F4 kill
bindsym button2 kill
-# start dmenu (a program launcher)
-bindsym $mod+F2 exec --no-startup-id ~/.config/i3/dmenu_run
-bindsym Mod1+F2 exec --no-startup-id ~/.config/i3/dmenu_run
-
-bindsym $mod+c exec --no-startup-id ~/.config/i3/passmenu --type
-#bindsym $mod+x exec --no-startup-id ~/.config/i3/clipmenu
-bindsym $mod+asterisk exec --no-startup-id ~/.config/i3/sshmenu
-bindsym $mod+dollar exec --no-startup-id ~/.config/i3/sshmenu root
+bindsym $mod+c exec --no-startup-id rofi-pass --last-used
+bindsym $mod+asterisk exec --no-startup-id rofi -modi ssh -show ssh
+bindsym $mod+dollar exec --no-startup-id rofi -modi ssh -show ssh -ssh-command '{terminal} -e {ssh-client} {host} -t "sudo -s -E"'
+bindsym $mod+Tab exec --no-startup-id rofi -modi window -show window
# start program launcher
-bindsym $mod+d exec --no-startup-id ~/.config/i3/dmenu_run
+bindsym $mod+d exec --no-startup-id rofi -modi run -show run
+bindsym $mod+Shift+d exec --no-startup-id rofi -modi drun -show drun
# Start Applications
-bindsym $mod+Return exec xterm
+bindsym $mod+Return exec urxvtc
bindsym $mod+p exec thunar
bindsym $mod+m exec qutebrowser --override-restore --backend=webengine
@@ -57,11 +57,11 @@ bindsym XF86AudioLowerVolume exec pactl set-sink-mute @DEFAULT_SINK@ false; exec
bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ true
bindsym $mod+F7 exec pactl suspend-sink @DEFAULT_SINK@ 1; exec pactl suspend-sink @DEFAULT_SINK@ 0
# Re-synchronize bluetooth headset
-bindsym $mod+F8 exec mpc prev
-bindsym $mod+F9 exec mpc toggle
-bindsym $mod+F10 exec mpc next
-bindsym $mod+F11 exec xterm -e 'pacmixer'
-bindsym $mod+F12 exec xterm -e 'pacmixer'
+bindsym XF86AudioPrev exec mpc prev
+bindsym XF86AudioPlay exec mpc toggle
+bindsym XF86AudioNext exec mpc next
+bindsym $mod+F11 exec urxvtc -e 'pacmixer'
+bindsym $mod+F12 exec urxvtc -e 'pacmixer'
#Brightness control
bindsym XF86MonBrightnessDown exec xbacklight -dec 20
@@ -144,19 +144,14 @@ set $WS10 10
# Workspace output
workspace "$WS1" output LVDS1
-workspace "$WS2" output VGA1
workspace "$WS2" output HDMI1
workspace "$WS3" output LVDS1
-workspace "$WS4" output VGA1
workspace "$WS4" output HDMI1
workspace "$WS5" output LVDS1
-workspace "$WS6" output VGA1
workspace "$WS6" output HDMI1
workspace "$WS7" output LVDS1
-workspace "$WS8" output VGA1
workspace "$WS8" output HDMI1
workspace "$WS9" output LVDS1
-workspace "$WS10" output VGA1
workspace "$WS10" output HDMI1
# switch to workspace
@@ -245,6 +240,8 @@ for_window [class="qutebrowser"] layout tabbed
for_window [window_role="pop-up"] floating enable
for_window [window_role="task_dialog"] floating enable
+for_window [ title="^pdfpc.*" window_role="presenter" ] move to output left, fullscreen
+for_window [ title="^pdfpc.*" window_role="presentation" ] move to output right, fullscreen
# switch to workspace with urgent window automatically
for_window [urgent=latest] focus
@@ -335,24 +332,24 @@ mode "$mode_temp" {
}
# Colors
-set $bg #272822
-set $fg #f1ebeb
-set $00 #48483e
-set $01 #dc2566
-set $02 #8fc029
-set $03 #d4c96e
-set $04 #55bcce
-set $05 #9358fe
-set $06 #56b7a5
-set $07 #acada1
-set $08 #76715e
-set $09 #fa2772
-set $10 #a7e22e
-set $11 #e7db75
-set $12 #66d9ee
-set $13 #ae82ff
-set $14 #66efd5
-set $15 #cfd0c2
+set_from_resource $background i3wm.background #272822
+set_from_resource $foreground i3wm.foreground #f1ebeb
+set_from_resource $color00 i3wm.color0 #48483e
+set_from_resource $color01 i3wm.color1 #dc2566
+set_from_resource $color02 i3wm.color2 #8fc029
+set_from_resource $color03 i3wm.color3 #d4c96e
+set_from_resource $color04 i3wm.color4 #55bcce
+set_from_resource $color05 i3wm.color5 #9358fe
+set_from_resource $color06 i3wm.color6 #56b7a5
+set_from_resource $color07 i3wm.color7 #acada1
+set_from_resource $color08 i3wm.color8 #76715e
+set_from_resource $color09 i3wm.color9 #fa2772
+set_from_resource $color10 i3wm.color10 #a7e22e
+set_from_resource $color11 i3wm.color11 #e7db75
+set_from_resource $color12 i3wm.color12 #66d9ee
+set_from_resource $color13 i3wm.color13 #ae82ff
+set_from_resource $color14 i3wm.color14 #66efd5
+set_from_resource $color15 i3wm.color15 #cfd0c2
# Inactivity settings
exec --no-startup-id xautolock -time 10 -locker 'xset dpms force standby' -killtime 1 -killer '$locker'
@@ -361,26 +358,26 @@ bindsym $mod+F5 exec --no-startup-id xautolock -enable
# Autostart applications
-exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 # Password remembering
-exec --no-startup-id gnome-keyring-daemon # Password remembering
+#exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 # Password remembering
+#exec --no-startup-id gnome-keyring-daemon # Password remembering
+exec --no-startup-id urxvtd -q -f # urxvt daemon
exec --no-startup-id numlockx on # Activate Num lock
-#exec --no-startup-id nm-applet # Network manager tray icon
-#exec --no-startup-id compton -b # Compositing manager
-exec --no-startup-id unclutter # Hide mouse cursor after some time
-exec --no-startup-id dunst # Notifications
+exec --no-startup-id unclutter -root # Hide mouse cursor after some time
+#exec --no-startup-id dunst # Notifications (handled by systemd)
exec --no-startup-id keynav # Keyboard cursor controller
-#exec --no-startup-id $HOME/.config/i3/clipmenud # Clipboard manager
-exec --no-startup-id mpd # Music Player Daemon
+#exec --no-startup-id mpd # Music Player Daemon (handled by systemd)
+exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill
exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it
+exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification
set $ignore #ff00000
# Theme colors
-# class border backgr. text indicator child_border
-client.focused $02 $02 $bg $07 $10
-client.focused_inactive $03 $03 $08 $15 $14
-client.unfocused $08 $08 $fg $11 $00
-client.urgent $01 $01 $fg $05 $09
-client.placeholder $ignore $06 $fg $ignore $14
+#class border backgr. text indicator child_border
+client.focused $color02 $color02 $background $color08 $color10
+client.focused_inactive $color03 $color03 $color08 $color15 $color14
+client.unfocused $color08 $color08 $color07 $color11 $color00
+client.urgent $color01 $color01 $color07 $foreground $color09
+client.placeholder $ignore $color06 $color07 $ignore $color14
-client.background $15
+client.background $color15
diff --git a/config/i3/dmenu_cmd b/config/i3/dmenu_cmd
index a0d3180..2806540 100755
--- a/config/i3/dmenu_cmd
+++ b/config/i3/dmenu_cmd
@@ -1,2 +1,2 @@
#!/bin/sh
-dmenu -fn 'DejaVu Sans Mono-10' -nb '#48483e' -nf '#f1ebeb' -sb '#8fc029' -sf '#272822' -l 8 -f "$@"
+dmenu -fn 'DejaVu Sans Mono-10' -nb '#48483e' -nf '#f1ebeb' -sb '#8fc029' -sf '#272822' -i -l 8 -f "$@"
diff --git a/config/i3/lock b/config/i3/lock
index e5d1829..be3bebd 100755
--- a/config/i3/lock
+++ b/config/i3/lock
@@ -1,4 +1,9 @@
-#!/usr/bin/env sh
+#!/usr/bin/env bash
+
+# Remove SSH and GPG keys from keystores
+ssh-add -D
+echo RELOADAGENT | gpg-connect-agent
+
dm-tool lock
if [ $? -ne 0 ]; then
diff --git a/config/i3/multimediaKey b/config/i3/multimediaKey
new file mode 100755
index 0000000..3a4a808
--- /dev/null
+++ b/config/i3/multimediaKey
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+# Send a key event to the current multimedia application or to MPD
+
+# Currently since I did not found a way to test if a keystroke
+# is grabbed by a windows or not, we test if MPD is playing
+
+echo 8 "$1" "$2" "$(xdotool getactivewindow)" >> /tmp/dbg
+
+if [ $# != 2 ]; then
+ echo "Usage: $0 KEY MPC_COMMAND"
+ exit 1
+fi
+
+if [ $(mpc status | wc -l) -ne 1 ]; then
+ # If mpd is running
+ mpc $2
+else
+ # If mpd is not running
+ # echo "$1" "$2" "$(xdotool getactivewindow)" >> /tmp/dbg
+ xdotool key --window $(xdotool getactivewindow) $1
+ echo xdotool key --window $(xdotool getactivewindow) $1 >> /tmp/dbg
+fi
+exit 0
+
+
diff --git a/config/i3/passmenu b/config/i3/passmenu
deleted file mode 100755
index 9a4bf22..0000000
--- a/config/i3/passmenu
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-
-shopt -s nullglob globstar
-
-typeit=0
-if [[ $1 == "--type" ]]; then
- typeit=1
- shift
-fi
-
-if PASS=$(which gopass 2> /dev/null); then
- password_files="$($PASS ls --flat)"
-elif PASS=$(which pass 2> /dev/null); then
- prefix=${PASSWORD_STORE_DIR-~/.password-store}
- password_files=( "$prefix"/**/*.gpg )
- password_files=( "${password_files[@]#"$prefix"/}" )
- password_files=( "${password_files[@]%.gpg}" )
-else
- exit
-fi
-
-password=$(printf '%s\n' "${password_files[@]}" | $HOME/.config/i3/dmenu_cmd -p "Passwords" "$@")
-
-[[ -n $password ]] || exit
-
-if [[ $typeit -eq 0 ]]; then
- $PASS show -c "$password"
-else
- xdotool - <<<"type --clearmodifiers -- $($PASS show "$password" | head -n 1)"
-fi
diff --git a/config/i3/sshmenu b/config/i3/sshmenu
deleted file mode 100755
index 7b3fdc5..0000000
--- a/config/i3/sshmenu
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-if [ "$1" == 'root' ]; then
- a=" (root)"
- b="-t 'sudo -s'"
-fi
-machine=$(cat ~/.ssh/config | grep '^Host ' | cut -d ' ' -f 2 | grep -v '*' | sort | $HOME/.config/i3/dmenu_cmd -p "SSH$a to ")
-if [ ! -z $machine ]; then
- xterm -e "ssh $machine $b"
-fi
diff --git a/config/i3/test b/config/i3/test
deleted file mode 100644
index b3fb195..0000000
--- a/config/i3/test
+++ /dev/null
@@ -1,5 +0,0 @@
-prefix=${PASSWORD_STORE_DIR-~/.password-store}
-password_files=( "$prefix"/**/*.gpg )
-password_files=( "${password_files[@]#"$prefix"/}" )
-password_files=( "${password_files[@]%.gpg}" )
-echo -e "${password_files[@]}"
diff --git a/config/khal/config b/config/khal/config
new file mode 100644
index 0000000..902d4fd
--- /dev/null
+++ b/config/khal/config
@@ -0,0 +1,34 @@
+[calendars]
+[[calendars]]
+path = ~/.vdirsyncer/calendars/*
+type = discover
+
+# [[birthdays]]
+# type=birthdays
+# path = ~/.vdirsyncer/contacts/contacts/
+# color = light magenta
+
+[locale]
+timeformat = %H:%M
+dateformat = %d/%m
+longdateformat = %d/%m/%Y
+datetimeformat = %d/%m %H:%M
+longdatetimeformat = %d/%m/%Y %H:%M
+local_timezone = Europe/Paris
+
+[default]
+default_calendar = "Personnel"
+default_command = calendar
+highlight_event_days = True
+show_all_days = True
+timedelta = 7d
+
+[highlight_days]
+
+[view]
+agenda_day_format = "{bold}{name}, {date-long}{reset}"
+agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol} | {location}{reset}"
+bold_for_light_color = False
+event_format = "{calendar-color}{cancelled}{start}-{end} {title}{repeat-symbol} | {location}{reset}"
+event_view_always_visible = True
+frame = color
diff --git a/config/khard/khard.conf b/config/khard/khard.conf
new file mode 100644
index 0000000..eee27d6
--- /dev/null
+++ b/config/khard/khard.conf
@@ -0,0 +1,43 @@
+# example configuration file for khard version >= 0.11.0
+# place it under $HOME/.config/khard/khard.conf
+
+[addressbooks]
+[[contacts]]
+path = ~/.vdirsyncer/contacts/contacts/
+
+[general]
+debug = no
+default_action = list
+editor = vim
+merge_editor = vimdiff
+
+[contact table]
+# display names by first or last name: first_name / last_name
+display = first_name
+# group by address book: yes / no
+group_by_addressbook = no
+# reverse table ordering: yes / no
+reverse = no
+# append nicknames to name column: yes / no
+show_nicknames = no
+# show uid table column: yes / no
+show_uids = yes
+# sort by first or last name: first_name / last_name
+sort = last_name
+# localize dates: yes / no
+localize_dates = yes
+
+[vcard]
+# extend contacts with your own private objects
+# these objects are stored with a leading "X-" before the object name in the vcard files
+# every object label may only contain letters, digits and the - character
+# example:
+# private_objects = Jabber, Skype, Twitter
+private_objects = Jabber, Skype, Twitter
+# preferred vcard version: 3.0 / 4.0
+preferred_version = 3.0
+# Look into source vcf files to speed up search queries: yes / no
+search_in_source_files = no
+# skip unparsable vcard files: yes / no
+skip_unparsable = no
+
diff --git a/config/nvim/init.vim b/config/nvim/init.vim
new file mode 100644
index 0000000..f182e5b
--- /dev/null
+++ b/config/nvim/init.vim
@@ -0,0 +1,3 @@
+set runtimepath^=~/.vim runtimepath+=~/.vim/after
+let &packpath = &runtimepath
+source ~/.vimrc
diff --git a/config/offlineimap.py b/config/offlineimap.py
new file mode 100644
index 0000000..3d80ac9
--- /dev/null
+++ b/config/offlineimap.py
@@ -0,0 +1,10 @@
+#! /usr/bin/env python2
+from subprocess import check_output
+
+
+def get_pass(account):
+ return check_output("pass " + account, shell=True).splitlines()[0]
+
+def beep():
+ check_output("play -n synth sine E4 sine A5 remix 1-2 fade 0.5 1.2 0.5 2", shell=True)
+
diff --git a/config/polybar/bbswitch b/config/polybar/bbswitch
new file mode 100755
index 0000000..1228ef3
--- /dev/null
+++ b/config/polybar/bbswitch
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+if [ -f /proc/acpi/bbswitch ]
+then
+ echo -n
+ state="$(grep -o '\w\+$' /proc/acpi/bbswitch)"
+ if [ "$state" == "ON" ]
+ then
+ echo ""
+ elif [ "$state" == "OFF" ]
+ then
+ echo ""
+ else
+ echo "?"
+ fi
+else
+ echo
+fi
diff --git a/config/polybar/config b/config/polybar/config
index e259ae7..a58ef5d 100644
--- a/config/polybar/config
+++ b/config/polybar/config
@@ -76,7 +76,7 @@ enable-ipc = true
inherit = bar/base
modules-center = mpd
-modules-right = vpncheck eth wlan bbswitch xbacklight volume battery date
+modules-right = mail todo vpncheck eth wlan bbswitch xbacklight volume battery shortdate
tray-position = right
tray-padding = 2
@@ -84,7 +84,7 @@ tray-transparent = false
[bar/secondary]
inherit = bar/base
-modules-right = cpu memory temperature vpncheck ethMore wlanMore filesystem bbswitch xbacklight volume date
+modules-right = cpu memory temperature keystore vpncheck ethMore wlanMore filesystem linuxmismatch bbswitch xbacklight volume date
[module/filesystem]
@@ -176,12 +176,33 @@ toggle-off-foreground = #55
[module/bbswitch]
type = custom/script
-exec = grep -o '\w\+$' /proc/acpi/bbswitch
-exec-if = test -f /proc/acpi/bbswitch
+exec = ~/.config/polybar/bbswitch
interval = 5
-prefix =
format-foreground = ${theme.redF}
+[module/todo]
+type = custom/script
+exec = ~/.config/polybar/todo
+interval = 30
+format-prefix =
+format-foreground = ${theme.yellowF}
+
+[module/mail]
+type = custom/script
+exec = cat ~/.cache/mutt/status
+interval = 1
+format-prefix =
+format-foreground = ${theme.magentaF}
+; format-background = ${theme.magentaB}
+
+[module/linuxmismatch]
+type = custom/script
+exec = echo
+exec-if = ~/.config/polybar/linuxmismatch
+interval = 30
+format-foreground = ${theme.yellowF}
+; format-background = ${theme.yellowF}
+
[module/xbacklight]
type = internal/xbacklight
output = ${env:display:LVDS1}
@@ -201,7 +222,7 @@ card = intel_backlight
[module/cpu]
type = internal/cpu
-interval = 0.5
+interval = 1
format =
format-foreground = ${theme.redF}
ramp-coreload-0 = ▁
@@ -215,10 +236,17 @@ ramp-coreload-7 = █
[module/memory]
type = internal/memory
-interval = 2
+interval = 1
format-foreground = ${theme.greenF}
label = %gb_free%
+[module/keystore]
+type = custom/script
+exec = ~/.config/polybar/keystore
+; exec-if = pgrep openvpn
+interval = 5
+format-foreground = ${theme.greenF}
+
[module/vpncheck]
type = custom/script
exec = echo
@@ -229,7 +257,7 @@ format-foreground = ${theme.blueF}
[module/eth]
type = internal/network
interface = ${env:ethI:eth0}
-interval = 1
+interval = 5
format-connected =
label-connected = %local_ip%
@@ -244,7 +272,7 @@ label-connected = ↑%upspeed% ↓%downspeed%
[module/wlan]
type = internal/network
interface = ${env:wlanI:wlan0}
-interval = 1
+interval = 5
format-connected =
label-connected = %local_ip% %essid%
@@ -281,6 +309,11 @@ format-foreground = ${theme.cyanF}
label = %date% %time%
+[module/shortdate]
+inherit = module/date
+date = " %d/%m"
+
+
[module/volume]
type = internal/volume
@@ -307,11 +340,12 @@ label-full =
format-charging =
format-charging-foreground = ${theme.yellowF}
+format-charging-prefix =
label-charging = %percentage%% (%time%)
format-discharging =
format-discharging-foreground = ${self.format-charging-foreground}
-format-discharging-background = ${theme.redB}
+; format-discharging-background = ${theme.redB}
label-discharging = %percentage%% (%time%)
format-full-prefix = " "
diff --git a/config/polybar/keystore b/config/polybar/keystore
new file mode 100755
index 0000000..7899ec9
--- /dev/null
+++ b/config/polybar/keystore
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+output="$(ssh-add -l)"
+if [ $? -ne 0 ]; then
+ echo
+else
+ echo -n " "
+ echo "$output" | wc -l
+fi
diff --git a/config/polybar/linuxmismatch b/config/polybar/linuxmismatch
new file mode 100755
index 0000000..a36e5fe
--- /dev/null
+++ b/config/polybar/linuxmismatch
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+if ! which pacman &> /dev/null
+then
+ exit 1
+fi
+
+packageVersion=$(pacman -Q linux | cut -d' ' -f2)
+runningVersion=$(uname -r)
+
+if echo "$runningVersion" | grep "^$packageVersion" &> /dev/null
+then
+ exit 1
+else
+ exit 0
+fi
+
diff --git a/config/polybar/todo b/config/polybar/todo
new file mode 100755
index 0000000..e739acd
--- /dev/null
+++ b/config/polybar/todo
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+CALDIR="$HOME/.vdirsyncer/currentCalendars"
+
+function status() {
+ ls "$CALDIR" | while read account
+ do
+ displayname="$(cat "$CALDIR/$account/displayname")"
+ color="$(cat "$CALDIR/$account/color")"
+ nb="$(todo list "$displayname" | grep -v "^$" | wc -l)"
+
+ if [ $nb -gt 0 ]
+ then
+ echo -n " %{F$color}$nb%{F-}"
+ fi
+ done
+ # Newline to tell polybar to update in any case
+ echo
+}
+
+status
diff --git a/config/rofi/.gitignore b/config/rofi/.gitignore
new file mode 100644
index 0000000..c1955d4
--- /dev/null
+++ b/config/rofi/.gitignore
@@ -0,0 +1,3 @@
+theme.config
+theme.rasi
+
diff --git a/config/rofi/config b/config/rofi/config
new file mode 100644
index 0000000..6bc4950
--- /dev/null
+++ b/config/rofi/config
@@ -0,0 +1,8 @@
+#include "theme.config"
+rofi.theme: theme
+rofi.cycle: true
+rofi.case-sensitive: false
+rofi.scroll-method: 0
+rofi.show-match: true
+rofi.lazy-grab: false
+rofi.matching: fuzzy
diff --git a/config/systemd/user/default.target.wants/dunst.service b/config/systemd/user/default.target.wants/dunst.service
new file mode 120000
index 0000000..b24d3b1
--- /dev/null
+++ b/config/systemd/user/default.target.wants/dunst.service
@@ -0,0 +1 @@
+/usr/lib/systemd/user/dunst.service
\ No newline at end of file
diff --git a/config/systemd/user/default.target.wants/mbsync.timer b/config/systemd/user/default.target.wants/mbsync.timer
new file mode 120000
index 0000000..ebd66d7
--- /dev/null
+++ b/config/systemd/user/default.target.wants/mbsync.timer
@@ -0,0 +1 @@
+/home/geoffrey/.local/share/systemd/user/mbsync.timer
\ No newline at end of file
diff --git a/config/systemd/user/default.target.wants/mpd.service b/config/systemd/user/default.target.wants/mpd.service
new file mode 120000
index 0000000..0524f66
--- /dev/null
+++ b/config/systemd/user/default.target.wants/mpd.service
@@ -0,0 +1 @@
+/usr/lib/systemd/user/mpd.service
\ No newline at end of file
diff --git a/config/systemd/user/default.target.wants/offlineimap.service b/config/systemd/user/default.target.wants/offlineimap.service
new file mode 120000
index 0000000..aacc641
--- /dev/null
+++ b/config/systemd/user/default.target.wants/offlineimap.service
@@ -0,0 +1 @@
+/usr/lib/systemd/user/offlineimap.service
\ No newline at end of file
diff --git a/config/systemd/user/default.target.wants/syncthing.service b/config/systemd/user/default.target.wants/syncthing.service
new file mode 120000
index 0000000..d55cc27
--- /dev/null
+++ b/config/systemd/user/default.target.wants/syncthing.service
@@ -0,0 +1 @@
+/usr/lib/systemd/user/syncthing.service
\ No newline at end of file
diff --git a/config/systemd/user/timers.target.wants/vdirsyncer.timer b/config/systemd/user/timers.target.wants/vdirsyncer.timer
new file mode 120000
index 0000000..cc84adf
--- /dev/null
+++ b/config/systemd/user/timers.target.wants/vdirsyncer.timer
@@ -0,0 +1 @@
+/usr/lib/systemd/user/vdirsyncer.timer
\ No newline at end of file
diff --git a/config/todoman/todoman.conf b/config/todoman/todoman.conf
new file mode 100644
index 0000000..74a4616
--- /dev/null
+++ b/config/todoman/todoman.conf
@@ -0,0 +1,4 @@
+[main]
+path = ~/.vdirsyncer/currentCalendars/*
+default_list = Personnel
+humanize = True
diff --git a/config/vdirsyncer/.dfrecur b/config/vdirsyncer/.dfrecur
new file mode 100644
index 0000000..e69de29
diff --git a/config/vdirsyncer/config b/config/vdirsyncer/config
new file mode 100644
index 0000000..7ef5e40
--- /dev/null
+++ b/config/vdirsyncer/config
@@ -0,0 +1,70 @@
+# An example configuration for vdirsyncer.
+#
+# Move it to ~/.vdirsyncer/config or ~/.config/vdirsyncer/config and edit it.
+# Run `vdirsyncer --help` for CLI usage.
+#
+# Optional parameters are commented out.
+# This file doesn't document all available parameters, see
+# http://vdirsyncer.pimutils.org/ for the rest of them.
+
+[general]
+# A folder where vdirsyncer can store some metadata about each pair.
+status_path = "~/.vdirsyncer/status/"
+
+# # CARDDAV
+[pair geoffrey_contacts]
+# A `[pair ]` block defines two storages `a` and `b` that should be
+# synchronized. The definition of these storages follows in `[storage ]`
+# blocks. This is similar to accounts in OfflineIMAP.
+a = "geoffrey_contacts_local"
+b = "geoffrey_contacts_remote"
+
+# Synchronize all collections that can be found.
+# You need to run `vdirsyncer discover` if new calendars/addressbooks are added
+# on the server.
+
+collections = ["from a", "from b"]
+
+# Synchronize the "display name" property into a local file (~/.contacts/displayname).
+metadata = ["displayname"]
+
+# To resolve a conflict the following values are possible:
+# `null` - abort when collisions occur (default)
+# `"a wins"` - assume a's items to be more up-to-date
+# `"b wins"` - assume b's items to be more up-to-date
+#conflict_resolution = null
+
+[storage geoffrey_contacts_local]
+# A storage references actual data on a remote server or on the local disk.
+# Similar to repositories in OfflineIMAP.
+type = "filesystem"
+path = "~/.vdirsyncer/contacts/"
+fileext = ".vcf"
+
+[storage geoffrey_contacts_remote]
+type = "carddav"
+url = "https://dav.frogeye.fr/caldav.php/"
+username = "geoffrey"
+password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]
+
+# CALDAV
+[pair geoffrey_calendar]
+a = "geoffrey_calendar_local"
+b = "geoffrey_calendar_remote"
+collections = ["from a", "from b"]
+
+# Calendars also have a color property
+metadata = ["displayname", "color"]
+# conflict_resolution = "a wins"
+# conflict_resolution = "b wins"
+
+[storage geoffrey_calendar_local]
+type = "filesystem"
+path = "~/.vdirsyncer/calendars/"
+fileext = ".ics"
+
+[storage geoffrey_calendar_remote]
+type = "caldav"
+url = "https://dav.frogeye.fr/caldav.php/"
+username = "geoffrey"
+password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]
diff --git a/gdbinit b/gdbinit
new file mode 100644
index 0000000..9569b3d
--- /dev/null
+++ b/gdbinit
@@ -0,0 +1,3 @@
+define hook-quit
+ set confirm off
+end
diff --git a/gitconfig b/gitconfig
index 17d9812..6860844 100644
--- a/gitconfig
+++ b/gitconfig
@@ -8,3 +8,8 @@
default = matching
[alias]
git = !exec git
+[filter "lfs"]
+ clean = git-lfs clean -- %f
+ smudge = git-lfs smudge -- %f
+ process = git-lfs filter-process
+ required = true
diff --git a/profile b/profile
index 4de32f2..614b95f 100644
--- a/profile
+++ b/profile
@@ -9,4 +9,4 @@
[ -f "$HOME/.config/linuxColors.sh" ] && . "$HOME/.config/linuxColors.sh"
# Bashrc
-[ -f ~/.bashrc ] && . ~/.bashrc
+[ -f ~/.bashrc ] && source ~/.bashrc
diff --git a/scripts/arch-kexec b/scripts/arch-kexec
index b65004e..d5bdc48 100755
--- a/scripts/arch-kexec
+++ b/scripts/arch-kexec
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-sudo kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdlin
+sudo kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdline
sudo systemctl kexec
diff --git a/scripts/changeColors b/scripts/changeColors
new file mode 100755
index 0000000..8ad9fb0
--- /dev/null
+++ b/scripts/changeColors
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+# Fetchs colors from https://github.com/chriskempson/base16 templates
+# and apply them into my configuration
+
+scheme="$1"
+if [ -z "$scheme" ]
+then
+ echo Please specify a scheme
+ exit 1
+fi
+
+# TODO Verify if scheme is known
+
+# Shell (allows to use all colors in Vim while still having nice colors in the rest of the terminal)
+curl "https://raw.githubusercontent.com/chriskempson/base16-shell/master/scripts/base16-${scheme}.sh" > ~/.local/bin/colorSchemeApply
+chmod +x ~/.local/bin/colorSchemeApply
+
+# Xressources (I'm not sure if this is really needed with shell overriding these but i3 load those resources)
+curl "https://raw.githubusercontent.com/chriskempson/base16-xresources/master/xresources/base16-${scheme}-256.Xresources" > ~/.Xresources.d/theme
+
+# Vim
+echo -e "let base16colorspace=256\nset termguicolors\ncolorscheme base16-${scheme}" > ~/.vim/colorscheme.vim
+
+# FZF
+curl "https://raw.githubusercontent.com/nicodebo/base16-fzf/master/bash/base16-${scheme}.config" > ~/.local/bin/colorSchemeApplyFzf
+chmod +x ~/.local/bin/colorSchemeApplyFzf
+
+# qutebrowser
+curl "https://raw.githubusercontent.com/theova/base16-qutebrowser/4a17eea8a39f722c2cee95fb44d4a87f5eb2518f/themes/base16-${scheme}.config.py" > ~/.config/qutebrowser/theme.py
+
+# rofi
+curl "https://raw.githubusercontent.com/0xdec/base16-rofi/master/themes/base16-${scheme}.rasi" > ~/.config/rofi/theme.rasi
+curl "https://raw.githubusercontent.com/0xdec/base16-rofi/master/themes/base16-${scheme}.config" > ~/.config/rofi/theme.config
+
+# TODO dunst (template online, but not to my liking)
+# TODO bar (might change bar in the future, so...)
+# TODO qutebrowser (need to fiddle with the config thing)
+# TODO highlight (there IS a template but the colors look different from vim and mostly the same from when there's no config)
+
+# Reload a bunch of things to make changes immediate
+source ~/.bashrc
+xrdb -load ~/.Xresources
+i3-msg reload
diff --git a/scripts/compressPictureMovies b/scripts/compressPictureMovies
new file mode 100755
index 0000000..da74b42
--- /dev/null
+++ b/scripts/compressPictureMovies
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+import subprocess
+import sys
+import logging
+import coloredlogs
+import progressbar
+import time
+import hashlib
+import tempfile
+import json
+import statistics
+import datetime
+
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
+log = logging.getLogger()
+
+# Constants
+PICTURES_FOLDER = os.path.join(os.path.expanduser("~"), "Images")
+ORIGINAL_FOLDER = os.path.join(os.path.expanduser("~"), ".ImagesOriginaux")
+MOVIE_EXTENSIONS = ["mov", "avi", "mp4", "3gp", "webm", "mkv"]
+OUTPUT_EXTENSION = "webm"
+OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0"]
+# OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libaom-av1", "-crf", "30", "-strict", "experimental", "-c:a", "libopus"]
+DURATION_MAX_DEV = 1
+
+
+def videoMetadata(filename):
+ assert os.path.isfile(filename)
+ cmd = ["ffmpeg", "-i", filename, "-f", "ffmetadata", "-"]
+ p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
+ p.check_returncode()
+ metadataRaw = p.stdout
+ data = dict()
+ for metadataLine in metadataRaw.split(b'\n'):
+ # Skip empty lines
+ if not len(metadataLine):
+ continue
+ # Skip comments
+ if metadataLine.startswith(b';'):
+ continue
+ # Parse key-value
+ metadataLineSplit = metadataLine.split(b'=')
+ if len(metadataLineSplit) != 2:
+ log.warning("Unparsed metadata line: `{}`".format(metadataLine))
+ continue
+ key, val = metadataLineSplit
+ key = key.decode().lower()
+ val = val.decode()
+ data[key] = val
+ return data
+
+def videoInfos(filename):
+ assert os.path.isfile(filename)
+ cmd = ["ffprobe", filename, "-print_format", "json", "-show_streams"]
+ p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
+ p.check_returncode()
+ infosRaw = p.stdout
+ infos = json.loads(infosRaw)
+ return infos
+
+from pprint import pprint
+def streamDuration(stream):
+ if "duration" in stream:
+ return float(stream["duration"])
+ elif "sample_rate" in stream and "nb_frames" in stream:
+ return int(stream["nb_frames"]) / int(stream["sample_rate"])
+ elif "tags" in stream and "DURATION" in stream["tags"]:
+ durRaw = stream["tags"]["DURATION"]
+ durSplit = durRaw.split(":")
+ assert len(durSplit) == 3
+ durSplitFloat = [float(a) for a in durSplit]
+ hours, minutes, seconds = durSplitFloat
+ return (hours * 60 + minutes) * 60 + seconds
+ else:
+ raise KeyError("Can't find duration information in stream")
+
+def videoDuration(filename):
+ # TODO Doesn't work with VP8 / webm
+ infos = videoInfos(filename)
+ durations = [streamDuration(stream) for stream in infos["streams"]]
+ dev = statistics.stdev(durations)
+ assert dev <= DURATION_MAX_DEV, "Too much deviation ({} s)".format(dev)
+ return sum(durations)/len(durations)
+
+
+todos = set()
+totalSize = 0
+totalDuration = 0
+
+# Walk folders
+log.info("Listing files in {}".format(PICTURES_FOLDER))
+allVideos = list()
+for root, dirs, files in os.walk(PICTURES_FOLDER):
+ # If folder is in ORIGINAL_FOLDER, skip it
+ if root.startswith(ORIGINAL_FOLDER):
+ continue
+ # Iterate over files
+ for inputName in files:
+ # If the file is not a video, skip it
+ inputNameBase, inputExt = os.path.splitext(inputName)
+ inputExt = inputExt[1:].lower()
+ if inputExt not in MOVIE_EXTENSIONS:
+ continue
+
+ allVideos.append((root, inputName))
+
+log.info("Analyzing videos")
+for root, inputName in progressbar.progressbar(allVideos):
+ inputNameBase, inputExt = os.path.splitext(inputName)
+ inputExt = inputExt[1:].lower()
+
+ # Generates all needed filepaths
+ ## Found file
+ inputFull = os.path.join(root, inputName)
+ inputRel = os.path.relpath(inputFull, PICTURES_FOLDER)
+ ## Original file
+ originalFull = os.path.join(ORIGINAL_FOLDER, inputRel)
+ originalRel = inputRel
+ assert not os.path.isfile(originalFull), originalFile + " exists"
+
+ ## Compressed file
+ outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION)
+
+ # If the extension is the same of the output one
+ if inputExt == OUTPUT_EXTENSION:
+ # Read the metadata of the video
+ meta = videoMetadata(inputFull)
+
+ # If it has the field with the original file
+ if 'original' in meta:
+ # Skip file
+ continue
+ else:
+ assert not os.path.isfile(outputFull), outputFull + " exists"
+
+
+ size = os.stat(inputFull).st_size
+ try:
+ duration = videoDuration(inputFull)
+ except Exception as e:
+ log.warning("Can't determine duration of {}, skipping".format(inputFull))
+ log.debug(e, exc_info=True)
+ continue
+
+ todo = (inputFull, originalFull, outputFull, size, duration)
+
+ totalDuration += duration
+ totalSize += size
+ todos.add(todo)
+
+log.info("Converting {} videos ({})".format(len(todos), datetime.timedelta(seconds=totalDuration)))
+
+# From https://stackoverflow.com/a/3431838
+def sha256(fname):
+ hash_sha256 = hashlib.sha256()
+ with open(fname, "rb") as f:
+ for chunk in iter(lambda: f.read(131072), b""):
+ hash_sha256.update(chunk)
+ return hash_sha256.hexdigest()
+
+# Progress bar things
+totalDataSize = progressbar.widgets.DataSize()
+totalDataSize.variable = 'max_value'
+barWidgets = [progressbar.widgets.DataSize(), ' of ', totalDataSize, ' ', progressbar.widgets.Bar(), ' ', progressbar.widgets.FileTransferSpeed(), ' ', progressbar.widgets.AdaptiveETA()]
+bar = progressbar.DataTransferBar(max_value=totalSize, widgets=barWidgets)
+bar.start()
+processedSize = 0
+
+
+for inputFull, originalFull, outputFull, size, duration in todos:
+ tmpfile = tempfile.mkstemp(prefix="compressPictureMovies", suffix="."+OUTPUT_EXTENSION)[1]
+ try:
+ # Calculate the sum of the original file
+ checksum = sha256(inputFull)
+
+ # Initiate a conversion in a temporary file
+ originalRel = os.path.relpath(originalFull, ORIGINAL_FOLDER)
+ originalContent = "{} {}".format(originalRel, checksum)
+ metadataCmd = ["-metadata", 'original="{}"'.format(originalContent)]
+ cmd = ["ffmpeg", "-hide_banner", "-y", "-i", inputFull] + OUTPUT_FFMPEG_PARAMETERS + metadataCmd + [tmpfile]
+ p = subprocess.run(cmd)
+ p.check_returncode()
+
+ # Verify the durartion of the new file
+ newDuration = videoDuration(tmpfile)
+ dev = statistics.stdev((duration, newDuration))
+ assert dev < DURATION_MAX_DEV, "Too much deviation in duration"
+
+ # Move the original to the corresponding original folder
+ originalDir = os.path.dirname(originalFull)
+ os.makedirs(originalDir, exist_ok=True)
+ shutil.move(inputFull, originalFull)
+
+ # Move the converted file in place of the original
+ shutil.move(tmpfile, outputFull)
+ except Exception as e:
+ log.error("Couldn't process file {}".format(inputFull))
+ log.error(e, exc_info=True)
+ try:
+ os.unlink(tmpfile)
+ except Exception:
+ pass
+ # Progress bar things
+ processedSize += size
+ bar.update(processedSize)
+bar.finish()
+
+
+# TODO Iterate over the already compressed videos to assert the originals are
+# in their correct place, else move them
diff --git a/scripts/install-prefs b/scripts/install-prefs
index f7e5434..ac288ed 100755
--- a/scripts/install-prefs
+++ b/scripts/install-prefs
@@ -167,10 +167,13 @@ function altInst {
done
}
+function systemdUserUnit {
+ systemctl enable "$1"
+ systemctl start "$1"
+}
# Common CLI
-.Xresources.d/configure
# Utils
inst coreutils man openssl-tool grep sed sh tar
@@ -180,14 +183,15 @@ if [ $TERMUX == 1 ]; then
inst tsu
fi
fi
-inst moreutils screen ncdu lsof htop proxytunnel pv curl wget sshfs netcat mosh bash-completion rsync pwgen
+inst moreutils screen ncdu lsof htop proxytunnel pv curl wget netcat mosh bash-completion rsync pwgen fzf highlight
+# TODO Test those who are on Debian machines and those who aren't
if [ $ARCH == 1 ]; then
- inst bash-completion
+ inst bash-completion tldr
altInst gopass
else
inst pass
- wget -qO ~/.bin/ https://raw.githubusercontent.com/pepa65/tldr-bash-client/master/tldr
- chmod +x ~/.bin/tldr
+ wget -qO ~/.local/bin/ https://raw.githubusercontent.com/pepa65/tldr-bash-client/master/tldr
+ chmod +x ~/.local/bin/tldr
fi
tldr -u
if [[ $ARCH == 1 && $ADMIN == 1 ]]; then
@@ -222,33 +226,14 @@ if [ $DEBIAN == 1 ]; then
else
inst ctags
fi
-git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim
-vim +PluginInstall +qall
-
-# YouCompleteMe (vim plugin)
-if [ $ARCH == 1 ] && [ $ADMIN == 1 ]; then
- if [ $EXTRA == 1 ]; then
- altInst vim-youcompleteme-git
- else
- altInst vim-youcompleteme-core-git
- fi
-else
- if [ $DEBIAN == 1 || $TERMUX == 1 ]; then
- inst python-dev python3-dev cmake
- fi
- YCM_ARGS=""
- if [ $TERMUX != 1 ]; then
- YCM_ARGS="$YCM_ARGS --clang-completer --tern-completer"
- fi
-
- python $HOME/.vim/bundle/YouCompleteMe/install.py $YCM_ARGS
-fi
+vim +PlugUpgrade +PlugUpdate +PlugInstall +qall
# Common GUI
-
if [ $GUI == 1 ]; then
+ .Xresources.d/configure
+
# Desktop manager
- inst i3 i3lock dunst unclutter xautolock feh numlockx scrot xterm xclip
+ inst i3 i3lock dunst unclutter xautolock feh numlockx scrot rxvt-unicode xclip
curl "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" > ~/.local/share/fonts/fontawesome-webfont.ttf
if [ $ARCH == 1 ]; then
inst xorg-xinit
@@ -259,6 +244,9 @@ if [ $GUI == 1 ]; then
inst debhelper cmake libxcb-icccm4-dev libxcb-image0-dev libxcb-randr0-dev libx11-dev libxcb1-dev libxcb-util-dev libx11-xcb-dev linux-libc-dev libboost-dev x11proto-core-dev libxcb-ewmh-dev libxft-dev libasound2-dev libiw-dev libmpdclient-dev xcb-proto python-xcbgen libxcb-xkb-dev i3-wm libcairo2-dev libxcb-xrm-dev
# TODO Figure which one are really needed
#inst libasound2 libc6 libgcc1 libiw30 libmpdclient2 libstdc++6 libx11-6 libx11-xcb1 libxcb-ewmh2 libxcb-icccm4 libxcb-randr0 libxcb-xkb1 libxcb1 libxft2
+ # ↓ really needed
+ inst libcairo2-dev libxcb-xkb-dev libxcb-randr0-dev xcb-proto libxcb-image0-dev libxcb-icccm4-dev libxcb-ewmh-dev libxcb-util0-dev python-xcbgen
+
TMP=$(mktemp -d)
git clone --branch 3.0.5 --recursive https://github.com/jaagr/polybar $TMP
mkdir $TMP/build
@@ -266,7 +254,7 @@ if [ $GUI == 1 ]; then
cmake ..
make -j`nproc`
strip bin/polybar
- mv bin/polybar ~/.bin/
+ mv bin/polybar ~/.local/bin/
rm -rf $TMP
fi
fi
@@ -299,10 +287,10 @@ if [ $GUI == 1 ]; then
if [ $ARCH == 1 ]; then
altInst sct
elif [ $TERMUX != 1 ]; then
- if [ ! -f $HOME/.bin/sct ]; then
+ if [ ! -f $HOME/.local/bin/sct ]; then
TMP=$(mktemp /tmp/XXXXXXXXXX.c)
wget https://gist.githubusercontent.com/ajnirp/208c03d3aa7f02c743d2/raw/55bf3eed25739173d8be57b5179ed5542cf40ed6/sct.c -O $TMP
- cc $TMP --std=c99 -lX11 -lXrandr -o $HOME/.bin/sct
+ cc $TMP --std=c99 -lX11 -lXrandr -o $HOME/.local/bin/sct
rm $TMP
fi
fi
@@ -315,20 +303,28 @@ if [ $GUI == 1 ]; then
fi
fi
+
if [ $EXTRA == 1 ]; then
# Extra dev
inst cmake clang llvm npm
+ inst python-rope
# Extra CLI
- inst ffmpeg youtube-dl optipng syncthing ccache
-
+ inst ffmpeg youtube-dl optipng syncthing ccache mutt
+ systemdUserUnit syncthing.service
if [ $ARCH == 1 ]; then
inst jq
altInst pdftk translate-shell git-lfs js-beautify insect visidata-git
+
+ # Orga
+ # TODO For others
+ inst vdirsyncer khard
+ altInst khal todoman offlineimap
+ systemdUserUnit vdirsyncer.timer
else
# translate-shell
- curl -L git.io/trans > ~/.bin/trans
- chmod +x ~/.bin/trans
+ curl -L git.io/trans > ~/.local/bin/trans
+ chmod +x ~/.local/bin/trans
fi
# Extra GUI
@@ -337,7 +333,7 @@ if [ $EXTRA == 1 ]; then
if [ $ARCH == 1 ]; then
inst simplescreenrecorder
- altInst pacmixer xcursor-menda-git menda-themes-git menda-maia-icon-theme vimpc-git mpc
+ altInst pacmixer xcursor-menda-git menda-themes-git menda-maia-icon-theme vimpc-git mpc ashuffle-git
fi
fi
diff --git a/scripts/install-termux b/scripts/install-termux
index 51e3437..f066717 100755
--- a/scripts/install-termux
+++ b/scripts/install-termux
@@ -31,6 +31,8 @@ apt upgrade
# (needed for install-prefs)
apt install coreutils
apt install grep
+# Used by some of my termux scripts
+apt install jq
# Config
touch ~/.hushlogin
diff --git a/scripts/machines b/scripts/machines
index e9b8a0d..a99aca5 100755
--- a/scripts/machines
+++ b/scripts/machines
@@ -52,7 +52,7 @@ function _machines-api {
wget $MACHINES_API/$route --content-on-error --quiet --output-document=- "$@"
result=$?
if [ $result != 0 ]; then
- echo "[ERROR] wget returned $result..."
+ echo "[ERROR] wget returned $result..." 1>&2;
exit 2
fi
}
@@ -77,7 +77,7 @@ function _machines-pubFromCrt {
function _machines-verifyCertificate {
return
if openssl verify $MACHINES_CONFIG/machines.crt | grep -v 'error 18' | grep 'error' --quiet; then
- echo "[ERROR] Invalid certificate"
+ echo "[ERROR] Invalid certificate" 1>&2;
exit 1
fi
}
@@ -92,7 +92,7 @@ function _machines-ensurePub {
if [ $? == 1 ]; then
mv $CERT_FILE $MACHINES_CONFIG/machines.crt &> /dev/null
else
- echo "[ERROR] Certificate rejected."
+ echo "[ERROR] Certificate rejected." 1>&2;
exit 1
fi
fi
@@ -105,7 +105,7 @@ function _machines-ensurePub {
function _machines-ensureAdmin {
if [ ! -f $MACHINES_CONFIG/machines.key ]; then
- echo "[ERROR] You need have to have the private key to do that"
+ echo "[ERROR] You need have to have the private key to do that" 1>&2;
exit 1
fi
}
@@ -127,6 +127,10 @@ function _machines-getAkey { # network
SIGN_FILE=$(mktemp)
_machines-api akey/$1 > $KEY_FILE
_machines-api akey/$1?signature > $SIGN_FILE
+ md5sum $KEY_FILE &1>&2;
+ md5sum $SIGN_FILE &1>&2;
+ md5sum $MACHINES_CONFIG/machines.pub &1>&2;
+
openssl dgst -sha256 -verify $MACHINES_CONFIG/machines.pub -signature $SIGN_FILE $KEY_FILE &> /dev/null
if [ $? == 0 ]; then
cat $KEY_FILE
@@ -147,7 +151,7 @@ function _machines-updateAkey {
return 0
else
cat $MYKEY_FILE
- echo "[ERROR] Authorized keys are not properly signed"
+ echo "[ERROR] Authorized keys are not properly signed" 1>&2;
rm $MYKEY_FILE
exit 1
fi
@@ -368,12 +372,13 @@ function machines_update-all {
machines_machine_list | while read machine; do
echo "Updating $machine..."
ssh $machine 'machines update' &
+ ssh $machine 'cd .dotfiles && git pull' &
done
}
function machines_regen-keys {
if [[ -e $MACHINES_CONFIG/machines.key || -e $MACHINES_CONFIG/machines.pub || -e $MACHINES_CONFIG/machines.crt ]]; then
- echo "[ERROR] Please delete the pem files manually to prove you know what you're doing."
+ echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." 1>&2;
exit 1
else
openssl genrsa -out $MACHINES_CONFIG/machines.key 4096
@@ -389,7 +394,7 @@ function machines_regen-keys {
function machines_setup {
if [ -e $MACHINES_CONFIG/this.name ]; then
- echo "[ERROR] This machine is already set up"
+ echo "[ERROR] This machine is already set up" 1>&2;
exit 1
fi
diff --git a/scripts/md2html b/scripts/md2html
index 0e79ee1..ca33436 100755
--- a/scripts/md2html
+++ b/scripts/md2html
@@ -113,7 +113,8 @@ if (latex) {
// Conversion
htmlString = marked(markdownString, {
- renderer: renderer
+ renderer: renderer,
+ breaks: false
});
// fullHtmlString = htmlString;
fullHtmlString = template.replace('%BODY%', () => { return htmlString });
diff --git a/scripts/mel b/scripts/mel
new file mode 100755
index 0000000..037e345
--- /dev/null
+++ b/scripts/mel
@@ -0,0 +1,628 @@
+#!/usr/bin/env python3
+
+"""
+Meh mail client
+A dumb Python scripts that leverages notmuch, mbsync, and msmtp
+to become a fully-functional extremly-opinonated mail client.
+"""
+
+# TODO Features
+ # TODO Implement initial command set
+ # TODO Lockfiles for write operations on mail files (mbsync, tags→maildir operations)
+ # TODO OPTI Lockfile per account and process everything in parallel (if implemented, this
+ # should be optional since while it may speed up the mail fetching process, its multi-threading
+ # nature would cause a lot of cache flushes and be not very efficient on battery)
+ # TODO Handle true character width
+ # TODO IMAP IDLE watches?
+ # TODO GPG
+# TODO (only then) Refactor
+ # TODO OOP-based
+ # TODO Merge file with melConf
+
+# DEBUG Small perf profiler
+import time
+perf_dict = dict()
+perf_last = time.perf_counter()
+def perfstep(name):
+ t = time.perf_counter()
+ global perf_last
+ global perf_dict
+ diff = t - perf_last
+ if name not in perf_dict:
+ perf_dict[name] = 0
+ perf_dict[name] += diff
+ perf_last = time.perf_counter()
+
+
+import notmuch
+import logging
+import coloredlogs
+import colorama
+import datetime
+import os
+import progressbar
+import argparse
+import configparser
+import base64
+import shutil
+import argparse
+import xdg.BaseDirectory
+import sys
+import subprocess
+import html
+import re
+import email.parser
+
+perfstep("import")
+
+ACCOUNTS = dict()
+ALIASES = set()
+db = None
+config = None
+
+def notmuch_new():
+ close_database()
+ log.info("Indexing mails")
+ notmuchConfigPath = os.path.expanduser("~/.notmuchrc") # TODO Better
+ cmd = ["notmuch", "--config", notmuchConfigPath, "new"]
+ log.debug(" ".join(cmd))
+ subprocess.run(cmd)
+
+def list_folders():
+ storagePath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
+ folders = list()
+ for account in ACCOUNTS.keys():
+ storagePathAccount = os.path.join(storagePath, account)
+ for root, dirs, files in os.walk(storagePathAccount):
+ if "cur" not in dirs or "new" not in dirs or "tmp" not in dirs:
+ continue
+ assert root.startswith(storagePath)
+ path = root[len(storagePath):]
+ pathSplit = path.split('/')
+ if pathSplit[0] == '':
+ pathSplit = pathSplit[1:]
+ folders.append(tuple(pathSplit))
+ return folders
+
+def open_database(write=False):
+ global db
+ mode = notmuch.Database.MODE.READ_WRITE if write else notmuch.Database.MODE.READ_ONLY
+ if db:
+ if db.mode == mode:
+ return
+ else:
+ log.info("Current database not in required mode, closing")
+ close_database()
+ log.info("Opening database in mode {}".format(mode))
+ dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
+ db = notmuch.Database(mode=mode, path=dbPath)
+
+def close_database():
+ global db
+ if db:
+ log.info("Closing database")
+ db.close()
+ db = None
+
+def generate_aliases():
+ for name in config.sections():
+ if not name.islower():
+ continue
+ section = config[name]
+ ALIASES.add(section["from"])
+ if "alternatives" in section:
+ for alt in section["alternatives"].split(";"):
+ ALIASES.add(alt)
+ ACCOUNTS[name] = section
+
+
+def get_location(msg):
+ path = msg.get_filename()
+ path = os.path.dirname(path)
+ base = db.get_path()
+ assert path.startswith(base)
+ path = path[len(base):]
+ pathSplit = path.split('/')
+ mailbox = pathSplit[1]
+ assert mailbox in ACCOUNTS
+ state = pathSplit[-1]
+ folder = tuple(pathSplit[2:-1])
+ assert state in {'cur', 'tmp', 'new'}
+ return (mailbox, folder, state)
+
+MAILBOX_COLORS = dict()
+
+def get_mailbox_color(mailbox):
+ if mailbox not in MAILBOX_COLORS:
+ colorStr = config[mailbox]["color"]
+ colorStr = colorStr[1:] if colorStr[0] == '#' else colorStr
+ R = int(colorStr[0:2], 16)
+ G = int(colorStr[2:4], 16)
+ B = int(colorStr[4:6], 16)
+ MAILBOX_COLORS[mailbox] = '\x1b[38;2;{};{};{}m'.format(R, G, B)
+ return MAILBOX_COLORS[mailbox]
+
+def format_date(date):
+ now = datetime.datetime.now()
+ midnight = datetime.datetime(year=now.year, month=now.month, day=now.day)
+ if date > midnight:
+ return date.strftime('%H:%M:%S')
+ else:
+ return date.strftime('%d/%m/%y')
+
+WIDTH_FIXED = 31
+WIDTH_RATIO_DEST_SUBJECT = 0.3
+ISATTY = sys.stdout.isatty()
+destWidth = None
+subjectWidth = None
+def compute_line_format():
+ if ISATTY:
+ columns, rows = shutil.get_terminal_size((80, 20))
+ remain = columns - WIDTH_FIXED - 1
+ global destWidth, subjectWidth
+ destWidth = int(remain * WIDTH_RATIO_DEST_SUBJECT)
+ subjectWidth = remain - destWidth
+ else:
+ destWidth = None
+ subjectWidth = None
+
+def clip_text(size, text):
+ if size is None:
+ return text
+ l = len(text)
+ if l == size:
+ return text
+ elif l > size:
+ return text[:size-1] + '…'
+ elif l < size:
+ return text + " " * (size - l)
+
+
+def print_msg(msg):
+ if not destWidth:
+ compute_line_format()
+
+ sep = " " if ISATTY else "\t"
+ line = ""
+ tags = set(msg.get_tags())
+ mailbox, folder, state = get_location(msg)
+ if ISATTY:
+ line += get_mailbox_color(mailbox)
+
+ # UID
+ uid = None
+ for tag in tags:
+ if tag.startswith('tuid'):
+ uid = tag[4:]
+ assert isUID(uid), uid
+ line += uid
+
+ # Date
+ line += sep
+ date = datetime.datetime.fromtimestamp(msg.get_date())
+ line += format_date(date)
+
+ # Icons
+ line += sep
+ def tags2col1(tag1, tag2, both, first, second, none):
+ nonlocal line
+ if tag1 in tags:
+ if tag2 in tags:
+ line += both
+ else:
+ line += first
+ else:
+ if tag2 in tags:
+ line += second
+ else:
+ line += none
+
+ tags2col1('spam', 'draft', '?', 'S', 'D', ' ')
+ tags2col1('attachment', 'encrypted', 'E', 'A', 'E', ' ')
+ tags2col1('unread', 'flagged', '!', 'U', 'F', ' ')
+ tags2col1('sent', 'replied', '?', '↑', '↪', ' ')
+
+ if 'sent' in tags:
+ dest = msg.get_header("to")
+ else:
+ dest = msg.get_header("from")
+ line += sep
+ line += clip_text(destWidth, dest)
+
+ # Subject
+ line += sep
+ subject = msg.get_header("subject")
+ line += clip_text(subjectWidth, subject)
+
+ if ISATTY:
+ line += colorama.Style.RESET_ALL
+ print(line)
+
+
+def retag_msg(msg):
+ mailbox, folder, state = get_location(msg)
+
+ # Search-friendly folder name
+ slugFolderList = list()
+ for f, fold in [(f, folder[f]) for f in range(len(folder))]:
+ if f == 0 and len(folder) > 1 and fold == "INBOX":
+ continue
+ slugFolderList.append(fold.upper())
+ slugFolder = tuple(slugFolderList)
+
+ tags = set(msg.get_tags())
+ def tag_if(tag, condition):
+ if condition and tag not in tags:
+ msg.add_tag(tag)
+ elif not condition and tag in tags:
+ msg.remove_tag(tag)
+ expeditor = extract_email(msg.get_header('from'))
+
+ tag_if('inbox', slugFolder[0] == 'INBOX')
+ tag_if('spam', slugFolder[0] == 'JUNK' or slugFolder[0] == 'SPAM')
+ tag_if('deleted', slugFolder[0] == 'TRASH')
+ tag_if('draft', slugFolder[0] == 'DRAFTS')
+ tag_if('sent', expeditor in ALIASES)
+ tag_if('unprocessed', False)
+
+ # UID
+ uid = msg.get_header("X-TUID")
+ assert isUID(uid)
+ uidtag = 'tuid{}'.format(uid)
+ # Remove eventual others UID
+ for tag in tags:
+ if tag.startswith('tuid') and tag != uidtag:
+ msg.remove_tag(tag)
+ msg.add_tag(uidtag)
+
+
+
+def extract_email(field):
+ try:
+ sta = field.index('<')
+ sto = field.index('>')
+ return field[sta+1:sto]
+ except ValueError:
+ return field
+
+def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb=True, **kwargs):
+ open_database(write=write)
+
+ log.info("Querying {}".format(queryStr))
+ query = notmuch.Query(db, queryStr)
+ query.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
+
+ elements = query.search_messages()
+ nbMsgs = query.count_messages()
+
+ iterator = progressbar.progressbar(elements, max_value=nbMsgs) if showProgress else elements
+
+ log.info("Executing {}".format(action))
+ for msg in iterator:
+ if write:
+ msg.freeze()
+
+ action(msg, *args, **kwargs)
+
+ if write:
+ msg.thaw()
+ msg.tags_to_maildir_flags()
+
+ if closeDb:
+ close_database()
+
+ return nbMsgs
+
+# def update_polybar_status():
+def update_polybar_status(*args, **kwargs):
+ log.info("Updating polybar status")
+ accountsList = sorted(ACCOUNTS.keys())
+ print(accountsList)
+ open_database()
+ statusArr = []
+ for account in accountsList:
+ queryStr = 'folder:/{}/ and tag:unread'.format(account)
+ query = notmuch.Query(db, queryStr)
+ nbMsgs = query.count_messages()
+ if nbMsgs < 1:
+ continue
+ color = config[account]['color']
+ statusAccStr = '%{F' + color + '}' + str(nbMsgs) + '%{F-}'
+ statusArr.append(statusAccStr)
+ close_database()
+ statusStr = ('_' + ' '.join(statusArr)) if len(statusArr) else '\n'
+ statusPath = os.path.expanduser("~/.cache/mutt/status") # TODO Better
+ with open(statusPath, 'w') as f:
+ f.write(statusStr)
+ # statusPath = os.path.expanduser("~/.cache/mel/polybarstatus") # TODO Better
+
+def notify_msg(msg):
+ log.info("Sending notification for {}".format(msg))
+ subject = msg.get_header("subject")
+ expd = msg.get_header("from")
+ account, _, _ = get_location(msg)
+
+ summary = '{} ({})'.format(html.escape(expd), account)
+ body = html.escape(subject)
+ cmd = ["notify-send", "-u", "low", "-i", "mail-message-new", summary, body]
+ print(' '.join(cmd))
+ subprocess.run(cmd)
+
+
+def notify_all(*args, **kwargs):
+ open_database()
+ nbMsgs = applyMsgs('tag:unread and tag:unprocessed', notify_msg)
+ if nbMsgs > 0:
+ log.info("Playing notification sound ({} new message(s))".format(nbMsgs))
+ cmd = ["play", "-n", "synth", "sine", "E4", "sine", "A5", "remix", "1-2", "fade", "0.5", "1.2", "0.5", "2"]
+ subprocess.run(cmd)
+ close_database()
+
+def isUID(uid):
+ return isinstance(uid, str) and len(uid) == 12 and re.match('^[a-zA-Z0-9+/]{12}$', uid)
+
+# From https://stackoverflow.com/a/312464
+def chunks(l, n):
+ """Yield successive n-sized chunks from l."""
+ for i in range(0, len(l), n):
+ yield l[i:i + n]
+
+def apply_msgs_input(argmessages, action, write=False):
+ if not len(argmessages):
+ fromStdin = not sys.stdin.isatty()
+ else:
+ fromStdin = len(argmessages) == 1 and argmessages == '-'
+
+ messages = list()
+ if fromStdin:
+ for line in sys.stdin:
+ uid = line[:12]
+ if not isUID(uid):
+ log.error("Not an UID: {}".format(uid))
+ continue
+ messages.append(uid)
+ else:
+ for uids in argmessages:
+ if len(uids) > 12:
+ log.warn("Might have forgotten some spaces between the UIDs. Don't worry, I'll split them for you")
+ for uid in chunks(uids, 12):
+ if not isUID(uid):
+ log.error("Not an UID: {}".format(uid))
+ continue
+ messages.append(uid)
+
+ for message in messages:
+ queryStr = 'tag:tuid{}'.format(message)
+ nbMsgs = applyMsgs(queryStr, action, write=write, closeDb=False)
+ if nbMsgs < 1:
+ log.error("Couldn't execute function for message {}".format(message))
+ close_database()
+
+def format_header_value(val):
+ return val.replace('\n', '').replace('\t', '').strip()
+
+# From https://stackoverflow.com/a/1094933
+def sizeof_fmt(num, suffix='B'):
+ for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
+ if abs(num) < 1024.0:
+ return "%3.1f %s%s" % (num, unit, suffix)
+ num /= 1024.0
+ return "%.1f %s%s" % (num, 'Yi', suffix)
+
+PART_MULTI_FORMAT = colorama.Fore.BLUE + '{nb} {indent}+ {typ}' + colorama.Style.RESET_ALL
+PART_LEAF_FORMAT = colorama.Fore.BLUE + '{nb} {indent}→ {desc} ({typ}; {size})' + colorama.Style.RESET_ALL
+def show_parts_tree(part, lvl=0, nb=1):
+ indent = lvl * '\t'
+ typ = part.get_content_type()
+ if part.is_multipart():
+ print(PART_MULTI_FORMAT.format(nb=nb, indent=indent, typ=typ))
+ payl = part.get_payload()
+ size = 1
+ for obj in payl:
+ size += show_parts_tree(obj, lvl=lvl+1, nb=nb+size)
+ return size
+ else:
+ size = len(part.get_payload(decode=True))
+ desc = part.get('Content-Description', '')
+ print(PART_LEAF_FORMAT.format(nb=nb, indent=indent, typ=typ, desc=desc, size=sizeof_fmt(size)))
+ return 1
+
+INTERESTING_HEADERS = ["Date", "From", "Subject", "To", "Cc", "Message-Id"]
+HEADER_FORMAT = colorama.Fore.BLUE + colorama.Style.BRIGHT + '{}:' + colorama.Style.NORMAL + ' {}' + colorama.Style.RESET_ALL
+def read_msg(msg):
+ # Parse
+ filename = msg.get_filename()
+ parser = email.parser.BytesParser()
+ with open(filename, 'rb') as f:
+ mail = parser.parse(f)
+
+ # Debug
+ global a
+ a = mail
+
+ # Defects
+ if len(mail.defects):
+ log.warn("Defects found in the mail:")
+ for defect in mail.defects:
+ log.warn(mail.defects)
+
+
+ # Headers
+ for key in INTERESTING_HEADERS:
+ val = mail.get(key)
+ if val:
+ val = format_header_value(val)
+ print(HEADER_FORMAT.format(key, val))
+ # TODO Show all headers
+ # TODO BONUS Highlight failed verifications
+
+ show_parts_tree(mail)
+ print()
+
+ # Show text/plain
+ for part in mail.walk():
+ if part.get_content_type() == "text/plain":
+ payl = part.get_payload(decode=True)
+ print(payl.decode())
+
+
+perfstep("definitions")
+
+if __name__ == "__main__":
+ # Main arguments
+ parser = argparse.ArgumentParser(description="Meh mail client")
+ selectedVerbosityLevels = ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
+ parser.add_argument('-v', '--verbosity', choices=selectedVerbosityLevels, default='WARNING', help="Verbosity of log messages")
+ # parser.add_argument('-n', '--dry-run', action='store_true', help="Don't do anything") # DEBUG
+ defaultConfigFile = os.path.join(xdg.BaseDirectory.xdg_config_home, 'mel', 'accounts.conf')
+ parser.add_argument('-c', '--config', default=defaultConfigFile, help="Accounts config file")
+
+ subparsers = parser.add_subparsers(help="Action to execute")
+
+ ## List messages
+
+ def func_default(args):
+ applyMsgs('tag:inbox', print_msg)
+ parser.set_defaults(func=func_default)
+
+ # inbox (default)
+ def func_inbox(args):
+ queryStr = 'tag:unread' if args.only_unread else 'tag:inbox'
+ applyMsgs(queryStr, print_msg)
+
+ parserInbox = subparsers.add_parser("inbox", help="Show unread, unsorted and flagged messages")
+ parserInbox.add_argument('-u', '--only-unread', action='store_true', help="Show unread messages only")
+ # TODO Make this more relevant
+ parserInbox.set_defaults(func=func_inbox)
+
+
+ # list folder [--recurse]
+ ## List actions
+
+
+ # flag msg...
+ def func_flag(args):
+ def flag_msg(msg):
+ msg.add_tag('flagged')
+ apply_msgs_input(args.message, flag_msg, write=True)
+ parserFlag = subparsers.add_parser("flag", help="Mark messages as flagged")
+ parserFlag.add_argument('message', nargs='*', help="Messages")
+ parserFlag.set_defaults(func=func_flag)
+
+
+ # unflag msg...
+ def func_unflag(args):
+ def unflag_msg(msg):
+ msg.remove_tag('flagged')
+ apply_msgs_input(args.message, unflag_msg, write=True)
+ parserUnflag = subparsers.add_parser("unflag", help="Mark messages as not-flagged")
+ parserUnflag.add_argument('message', nargs='*', help="Messages")
+ parserUnflag.set_defaults(func=func_unflag)
+
+
+ # delete msg...
+ # spam msg...
+ # move dest msg...
+ ## Read message
+
+
+ # read msg [--html] [--plain] [--browser]
+ def func_read(args):
+ apply_msgs_input(args.message, read_msg)
+ parserRead = subparsers.add_parser("read", help="Read message")
+ parserRead.add_argument('message', nargs=1, help="Messages")
+ parserRead.set_defaults(func=func_read)
+
+
+ # attach msg [id] [--save] (list if no id, xdg-open else)
+ ## Redaction
+ # new account
+ # reply msg [--all]
+ ## Folder management
+ # tree [folder]
+ # mkdir folder
+ # rmdir folder (prevent if folder isn't empty (mail/subfolder))
+ # (yeah that should do)
+ ## Meta
+ # setup (interactive thing maybe)
+
+
+ # fetch (mbsync, notmuch new, retag, notify; called by greater gods)
+ def func_fetch(args):
+ # Fetch mails
+ log.info("Fetching mails")
+ mbsyncConfigPath = os.path.expanduser("~/.mbsyncrc") # TODO Better
+ cmd = ["mbsync", "--config", mbsyncConfigPath, "--all"]
+ subprocess.run(cmd)
+
+ # Index new mails
+ notmuch_new()
+
+ # Notify
+ notify_all()
+ update_polybar_status()
+
+ # Tag new mails
+ applyMsgs('tag:unprocessed', retag_msg, showProgress=True, write=True)
+
+ parserFetch = subparsers.add_parser("fetch", help="Fetch mail, tag them, and run notifications")
+ parserFetch.set_defaults(func=func_fetch)
+
+
+ ## Debug
+ # debug (various)
+ def func_expose(args):
+ # And leave the door open
+ def expose_msg(a):
+ global msg
+ msg = a
+ applyMsgs('tag:tuidyviU45m6flff', expose_msg, closeDb=False)
+ def func_debug(args):
+ from pprint import pprint
+ pprint(list_folders())
+ parserDebug = subparsers.add_parser("debug", help="Who know what this holds...")
+ parserDebug.set_defaults(verbosity='DEBUG')
+ parserDebug.set_defaults(func=func_debug)
+
+ # retag (all or unprocessed)
+ def func_retag(args):
+ applyMsgs('*', retag_msg, showProgress=True, write=True)
+ parserRetag = subparsers.add_parser("retag", help="Retag all mails (when you changed configuration)")
+ parserRetag.set_defaults(func=func_retag)
+
+ # all
+ def func_all(args):
+ applyMsgs('*', print_msg)
+
+ parserAll = subparsers.add_parser("all", help="Show ALL messages")
+ parserAll.set_defaults(func=func_all)
+
+ # Init
+ args = parser.parse_args()
+ perfstep("parse_args")
+
+ colorama.init()
+ coloredlogs.install(level=args.verbosity, fmt='%(levelname)s %(message)s')
+ log = logging.getLogger()
+
+ log.info("Loading config {}".format(args.config))
+ if not os.path.isfile(args.config):
+ log.fatal("Config file not found: {}".format(args.config))
+ sys.exit(1)
+ # TODO Create it, maybe?
+ config = configparser.ConfigParser()
+ config.read(args.config)
+
+ generate_aliases()
+ perfstep("config")
+
+ if args.func:
+ log.info("Executing function {}".format(args.func))
+ args.func(args)
+
+ perfstep("exec")
+
+ # DEBUG
+ sys.exit(0)
+ for kv in sorted(perf_dict.items(), key=lambda p: p[1]):
+ log.debug("{1:.6f} {0}".format(*kv))
diff --git a/scripts/melConf b/scripts/melConf
new file mode 100755
index 0000000..c46e30e
--- /dev/null
+++ b/scripts/melConf
@@ -0,0 +1,227 @@
+#!/usr/bin/env python3
+
+"""
+Meh mail client conf generator for other things
+"""
+
+import configparser
+import os
+import sys
+
+# TODO Find config file from XDG
+# TODO Signature file
+# TODO Write ~/.mail/[mailbox]/color file if required by sth?
+# TODO Fix IMAPS with mbsync
+
+configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel', 'accounts.conf')
+
+config = configparser.ConfigParser()
+config.read(configPath)
+
+storageFull = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
+config["GENERAL"]["storage"] = storageFull
+
+SERVER_DEFAULTS = {
+ "imap": {"port": 143, "starttls": True},
+ "smtp": {"port": 587, "starttls": True},
+ }
+SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"}
+
+# Reading sections
+accounts = dict()
+mails = set()
+
+for name in config.sections():
+ if not name.islower():
+ continue
+ section = config[name]
+
+ data = dict()
+ for server in SERVER_DEFAULTS.keys():
+ for item in SERVER_ITEMS:
+ key = server + item
+ try:
+ val = section.get(key) or section.get(item) or SERVER_DEFAULTS[server][item]
+ except KeyError:
+ raise KeyError("{}.{}".format(name, key))
+
+ if isinstance(val, str):
+ if val == "True":
+ val = True
+ elif val == "False":
+ val = False
+ elif val.isnumeric():
+ val = int(val)
+ data[key] = val
+
+ for key in section.keys():
+ if key in SERVER_ITEMS:
+ continue
+ data[key] = section[key]
+
+ mails.add(section["from"])
+ if "alternatives" in section:
+ for alt in section["alternatives"].split(";"):
+ mails.add(alt)
+
+ data["account"] = name
+ data["storage"] = os.path.join(config['GENERAL']['storage'], name)
+ data["storageInbox"] = os.path.join(data["storage"], "INBOX")
+ accounts[name] = data
+
+general = dict()
+section = config["GENERAL"]
+for key in section.keys():
+ general[key] = section[key]
+general["main"] = accounts[general["main"]]
+
+
+# OfflineIMAP
+
+OFFLINEIMAP_BEGIN = """[general]
+# List of accounts to be synced, separated by a comma.
+accounts = {}
+maxsyncaccounts = {}
+stocktimeout = 60
+pythonfile = ~/.config/offlineimap.py
+
+[mbnames]
+enabled = yes
+filename = ~/.mutt/mailboxes
+header = "mailboxes "
+peritem = "+%(accountname)s/%(foldername)s"
+sep = " "
+footer = "\\n"
+
+"""
+
+OFFLINEIMAP_ACCOUNT = """[Account {account}]
+localrepository = {account}-local
+remoterepository = {account}-remote
+autorefresh = 0.5
+quick = 10
+utf8foldernames = yes
+postsynchook = ~/.mutt/postsync
+
+[Repository {account}-local]
+type = Maildir
+localfolders = {storage}
+
+[Repository {account}-remote]
+type = IMAP
+{secconf}
+keepalive = 60
+holdconnectionopen = yes
+remotehost = {imaphost}
+remoteport = {imapport}
+remoteuser = {imapuser}
+remotepass = {imappass}
+
+"""
+
+offlineIMAPstr = OFFLINEIMAP_BEGIN.format(','.join(accounts), len(accounts))
+for name, account in accounts.items():
+ if account["imapstarttls"]:
+ secconf = "ssl = no"
+ else:
+ secconf = "sslcacertfile = /etc/ssl/certs/ca-certificates.crt"
+ offlineIMAPstr += OFFLINEIMAP_ACCOUNT.format(**account, secconf=secconf)
+# TODO Write
+
+# mbsync
+MBSYNC_ACCOUNT = """IMAPAccount {account}
+Host {imaphost}
+Port {imapport}
+User {imapuser}
+Pass "{imappassEscaped}"
+{secconf}
+
+IMAPStore {account}-remote
+Account {account}
+
+MaildirStore {account}-local
+Subfolders Verbatim
+Path {storage}/
+Inbox {storageInbox}/
+
+Channel {account}
+Master :{account}-remote:
+Slave :{account}-local:
+Patterns *
+Create Both
+SyncState *
+
+"""
+
+mbsyncStr = ""
+for name, account in accounts.items():
+ if account["imapstarttls"]:
+ secconf = "SSLType STARTTLS"
+ else:
+ secconf = "SSLType IMAPS"
+ if "certificate" in account:
+ secconf += "\nCertificateFile {certificate}".format(**account)
+ imappassEscaped = account["imappass"].replace("\\", "\\\\")
+ mbsyncStr += MBSYNC_ACCOUNT.format(**account, secconf=secconf, imappassEscaped=imappassEscaped)
+msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.mbsyncrc')
+with open(msbsyncFilepath, 'w') as f:
+ f.write(mbsyncStr)
+
+# msmtp
+MSMTP_BEGIN = """defaults
+protocol smtp
+auth on
+tls_trust_file /etc/ssl/certs/ca-certificates.crt
+
+"""
+
+MSMTP_ACCOUNT = """account {account}
+from {from}
+user {smtpuser}
+password {smtppass}
+host {smtphost}
+port {smtpport}
+tls on
+
+"""
+
+msmtpStr = MSMTP_BEGIN
+for name, account in accounts.items():
+ msmtpStr += MSMTP_ACCOUNT.format(**account)
+msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.msmtprc')
+with open(msbsyncFilepath, 'w') as f:
+ f.write(msmtpStr)
+
+
+# notmuch
+NOTMUCH_BEGIN = """[database]
+path={storage}
+
+[user]
+name={main[name]}
+primary_email={main[from]}
+other_email={other_email}
+
+[new]
+tags=unprocessed;unread;
+ignore=
+
+[search]
+exclude_tags=deleted;spam;
+
+[maildir]
+synchronize_flags=true
+
+[crypto]
+gpg_path=gpg
+
+"""
+
+other_email = mails.copy()
+other_email.remove(general["main"]["from"])
+other_email = ";".join(other_email)
+notmuchStr = NOTMUCH_BEGIN.format(**general, other_email=other_email)
+msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.notmuchrc')
+with open(msbsyncFilepath, 'w') as f:
+ f.write(notmuchStr)
+
diff --git a/scripts/musiqueBof b/scripts/musiqueBof
new file mode 100755
index 0000000..b0f9658
--- /dev/null
+++ b/scripts/musiqueBof
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import shutil
+import logging
+import coloredlogs
+
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
+log = logging.getLogger()
+
+MUSICS_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
+BOF_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueBof")
+
+for f in sys.argv[1:]:
+ src = os.path.realpath(f)
+ if not os.path.isfile(src):
+ log.error("{} does not exists".format(src))
+ continue
+
+ srcBase = None
+ if src.startswith(MUSICS_FOLDER):
+ srcBase = MUSICS_FOLDER
+ dstBase = BOF_FOLDER
+ elif src.startswith(BOF_FOLDER):
+ srcBase = BOF_FOLDER
+ dstBase = MUSIC_FOLDER
+ else:
+ log.error("{} not in any music folder".format(src))
+ continue
+
+ common = os.path.relpath(src, srcBase)
+ dst = os.path.join(dstBase, common)
+ dstFolder = os.path.dirname(dst)
+
+ log.info("{} → {}".format(src, dst))
+ os.makedirs(dstFolder, exist_ok=True)
+ shutil.move(src, dst)
+
diff --git a/scripts/optimize b/scripts/optimize
index bbb3543..d127d83 100755
--- a/scripts/optimize
+++ b/scripts/optimize
@@ -146,20 +146,26 @@ do
done <<< "$(find "$dir" -type f -iname "*.png")"
-# SVG (requires svgo)
-while read image
-do
- if [ -z "$image" ]; then continue; fi
- echo Processing $image
+# # SVG (requires scour)
+# while read image
+# do
+# if [ -z "$image" ]; then continue; fi
+# echo Processing $image
+#
+# temp=$(mktemp --suffix .svg)
+# scour --quiet "$image" "$temp" --no-line-breaks
+# echo "→ Optimize done"
+#
+# replaceImg "$temp" "$image"
+#
+# done <<< "$(find "$dir" -type f -iname "*.svg")"
- temp=$(mktemp --suffix .svg)
- cp "$image" "$temp"
- svgo --quiet --config $HOME/.config/optiSvgo.yml "$temp"
- echo "→ Optimize done"
-
- replaceImg "$temp" "$image"
-
-done <<< "$(find "$dir" -type f -iname "*.svg")"
+# NOTE Explicitely disabled since:
+# - I only have ~50 MiB of SVG in TOTAL
+# - Most conversions are not image losseless
+# - Even when they are losseless, they are mostly worthless
+# - I might want to keep editor data and/or ids for some of them
+# So rather use scour explicitely when needed
cleandev
diff --git a/scripts/pdfpages b/scripts/pdfpages
new file mode 100755
index 0000000..e805435
--- /dev/null
+++ b/scripts/pdfpages
@@ -0,0 +1,4 @@
+#!/usr/bin/bash
+
+# From https://stackoverflow.com/a/14736593
+pdftk "$1" dump_data | grep NumberOfPages | awk '{print $2}'
diff --git a/scripts/rep b/scripts/rep
index a7b2d84..789f25f 100755
--- a/scripts/rep
+++ b/scripts/rep
@@ -2,7 +2,7 @@
# Moves a file to another place and put a symbolic link in place
-function proxy_help {
+function rep_help {
echo "Usage: $0 SOURCE DEST"
echo
echo "Arguments:"
@@ -17,9 +17,9 @@ ln -s "$2" "$1"
# MAIN
command="$1"
shift
-if type "proxy_$command" &> /dev/null; then
- "proxy_$command" "$@"
+if type "rep_$command" &> /dev/null; then
+ "rep_$command" "$@"
else
- proxy_help
+ rep_help
fi
diff --git a/scripts/replayGain b/scripts/replayGain
new file mode 100755
index 0000000..c6178ce
--- /dev/null
+++ b/scripts/replayGain
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+
+# Normalisation is done at the default of each program,
+# which is usually -89.0 dB
+
+import os
+import coloredlogs
+import logging
+import r128gain
+import sys
+
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
+log = logging.getLogger()
+
+# TODO Remove debug
+
+# Constants
+FORCE = '-f' in sys.argv
+if FORCE:
+ sys.argv.remove('-f')
+SOURCE_FOLDER = os.path.realpath(sys.argv[1]) if len(sys.argv) >= 2 else os.path.join(os.path.expanduser("~"), "Musique")
+
+def isMusic(f):
+ ext = os.path.splitext(f)[1][1:].lower()
+ return ext in r128gain.AUDIO_EXTENSIONS
+
+# Get album paths
+log.info("Listing albums and tracks")
+albums = set()
+singleFiles = set()
+for root, dirs, files in os.walk(SOURCE_FOLDER):
+
+ relRoot = os.path.relpath(root, SOURCE_FOLDER)
+
+ head, tail = os.path.split(relRoot)
+ # 1 component in the path: save files path as single
+ if not len(head):
+ for f in files:
+ if isMusic(f):
+ fullPath = os.path.join(root, f)
+ singleFiles.add(fullPath)
+ head, tail = os.path.split(head)
+ if len(head):
+ continue
+ # 2 components in the path: save album path
+ albums.add(root)
+
+log.info("Processing single files")
+# r128gain.process(list(singleFiles), album_gain=False, skip_tagged=not FORCE, report=True)
+for album in albums:
+ albumName = os.path.relpath(album, SOURCE_FOLDER)
+ log.info("Processing album {}".format(albumName))
+
+ musicFiles = set()
+ for root, dirs, files in os.walk(album):
+ for f in files:
+ if isMusic(f):
+ fullPath = os.path.join(root, f)
+ musicFiles.add(fullPath)
+
+ # print(musicFiles)
+ if not len(musicFiles):
+ continue
+ r128gain.process(list(musicFiles), album_gain=True, skip_tagged=not FORCE, report=True)
+ print("==============================")
+
+
+
diff --git a/scripts/rms b/scripts/rms
new file mode 100755
index 0000000..885b287
--- /dev/null
+++ b/scripts/rms
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+find . -name "*.sync-conflict-*" -delete
diff --git a/scripts/tagCreatorPhotos b/scripts/tagCreatorPhotos
new file mode 100755
index 0000000..0002db7
--- /dev/null
+++ b/scripts/tagCreatorPhotos
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import piexif
+
+assert len(sys.argv) >= 3, "Usage {} CREATOR FILENAMES...".format(sys.argv[0])
+creator = sys.argv[1]
+filenames = sys.argv[2:]
+
+for filename in filenames:
+ assert os.path.isfile(filename)
+ exifDict = piexif.load(filename)
+ exifDict['0th'][piexif.ImageIFD.Copyright] = creator.encode()
+ exifBytes = piexif.dump(exifDict)
+ piexif.insert(exifBytes, filename)
+
diff --git a/scripts/updateCompressedMusic b/scripts/updateCompressedMusic
new file mode 100755
index 0000000..252fc5e
--- /dev/null
+++ b/scripts/updateCompressedMusic
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+import os
+import subprocess
+import progressbar
+import logging
+import coloredlogs
+
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
+log = logging.getLogger()
+
+# Constants
+SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
+OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed")
+CONVERSIONS = {"flac": "opus"}
+FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"]
+FORGIVEN_FILENAMES = ["cover.jpg"]
+IGNORED_EMPTY_FOLDER = [".stfolder"]
+
+# TODO FEAT Make the directory structure the same as the base one and
+# remove IGNORED_EMPTY_FOLDER variable
+
+# Listing files
+log.info("Listing files")
+sourceFiles = dict()
+for root, dirs, files in os.walk(SOURCE_FOLDER):
+ for f in files:
+ fullPath = os.path.join(root, f)
+ path = os.path.relpath(fullPath, SOURCE_FOLDER)
+ sourceFiles[path] = os.path.getctime(fullPath)
+
+outputFiles = dict()
+for root, dirs, files in os.walk(OUTPUT_FOLDER):
+ for f in files:
+ fullPath = os.path.join(root, f)
+ path = os.path.relpath(fullPath, OUTPUT_FOLDER)
+ outputFiles[path] = os.path.getctime(fullPath)
+
+# Sorting files
+remainingConversions = dict()
+extraFiles = set(outputFiles.keys())
+
+
+def convertPath(path):
+ filename, extension = os.path.splitext(path)
+ extension = extension[1:].lower()
+ # If the extension isn't allowed
+ if extension in FORBIDDEN_EXTENSIONS:
+ basename = os.path.basename(path)
+ # And the filename is not an exception
+ if basename not in FORGIVEN_FILENAMES:
+ # This file shouldn't be copied nor converted
+ return False
+ # If this needs a conversion
+ elif extension in CONVERSIONS:
+ extension = CONVERSIONS[extension]
+ return filename + "." + extension
+ # In all other case, this is a simple copy
+ return path
+
+
+log.info("Determining action over {} files".format(len(sourceFiles)))
+for sourceFile in sourceFiles:
+ outputFile = convertPath(sourceFile)
+ # If the file should not be converted, do nothing
+ if outputFile == False:
+ continue
+ # If the file already has something as an output
+ elif outputFile in outputFiles:
+ extraFiles.remove(outputFile)
+ # If the output file is newer than the source file, do not initiate a
+ # conversion
+ if outputFiles[outputFile] >= sourceFiles[sourceFile]:
+ continue
+ # If the file needs to be converted, do it
+ remainingConversions[sourceFile] = outputFile
+
+log.debug("{} actions will need to be taken".format(len(remainingConversions)))
+log.info("Copying files that do not require a conversion")
+conversions = set()
+for sourceFile in remainingConversions:
+ outputFile = remainingConversions[sourceFile]
+
+ # Creating folder if it doesn't exists
+ fullOutputFile = os.path.join(OUTPUT_FOLDER, outputFile)
+ fullOutputDir = os.path.dirname(fullOutputFile)
+ os.makedirs(fullOutputDir, exist_ok=True)
+
+ # Converting
+ fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile)
+ if sourceFile == outputFile:
+ log.debug('{} → {}'.format(fullSourceFile, fullOutputFile))
+ if os.path.isfile(fullOutputFile):
+ os.remove(fullOutputFile)
+ os.link(fullSourceFile, fullOutputFile)
+ else:
+ conversions.add((fullSourceFile, fullOutputFile))
+
+log.info("Removing extra files")
+for extraFile in extraFiles:
+ fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile)
+ log.debug('× {}'.format(fullExtraFile))
+ os.remove(fullExtraFile)
+
+log.info("Listing files that will be converted")
+for fullSourceFile, fullOutputFile in conversions:
+ log.debug('{} ⇒ {}'.format(fullSourceFile, fullOutputFile))
+
+log.info("Converting files")
+for fullSourceFile, fullOutputFile in progressbar.progressbar(conversions):
+ cmd = ["ffmpeg", "-y", "-i", fullSourceFile, "-c:a", "libopus",
+ "-movflags", "+faststart", "-b:a", "128k", "-vbr", "on",
+ "-compression_level", "10", fullOutputFile]
+ subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+
+# Removing empty dirs
+for root, dirs, files in os.walk(OUTPUT_FOLDER):
+ if not dirs and not files:
+ dirBasename = os.path.basename(root)
+ if dirBasename not in IGNORED_EMPTY_FOLDER:
+ os.rmdir(root)
diff --git a/scripts/updatedate b/scripts/updatedate
new file mode 100755
index 0000000..c2d01c3
--- /dev/null
+++ b/scripts/updatedate
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+echo ssh "$1" sudo date --set="'$(date -R)'"
diff --git a/scripts/vidcmp b/scripts/vidcmp
new file mode 100755
index 0000000..f8e27eb
--- /dev/null
+++ b/scripts/vidcmp
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+# Compresses video using FFMPEG using
+# FFMPEG's reasonable default settings
+
+import os
+import sys
+import subprocess
+
+files = sys.argv[1:]
+
+remove = False
+if '-r' in files:
+ files.remove('-r')
+ remove = True
+
+for f in files:
+ print(os.path.splitext(f))
+
+
diff --git a/terminfo/a/alacritty b/terminfo/a/alacritty
new file mode 100644
index 0000000..d02394c
Binary files /dev/null and b/terminfo/a/alacritty differ
diff --git a/terminfo/a/alacritty-256color b/terminfo/a/alacritty-256color
new file mode 100644
index 0000000..a76418f
Binary files /dev/null and b/terminfo/a/alacritty-256color differ
diff --git a/terminfo/r/rxvt-unicode b/terminfo/r/rxvt-unicode
new file mode 100644
index 0000000..7650d3c
Binary files /dev/null and b/terminfo/r/rxvt-unicode differ
diff --git a/terminfo/r/rxvt-unicode-256color b/terminfo/r/rxvt-unicode-256color
new file mode 100644
index 0000000..3f43d0d
Binary files /dev/null and b/terminfo/r/rxvt-unicode-256color differ
diff --git a/termux/scripts/tsu b/termux/scripts/tsu
new file mode 100755
index 0000000..e4f6ab7
--- /dev/null
+++ b/termux/scripts/tsu
@@ -0,0 +1,6 @@
+#!/data/data/com.termux/files/usr/bin/env bash
+
+# Force the passing of the environment variables on LineageOS where the --preserve-environment
+# option on the su binary doesn't seem to work well
+
+/data/data/com.termux/files/usr/bin/tsu -s "$(env | sed "s/^\([^=]\+\)=\(.*\)/\1='\2'/" | tr '\n' ' ') $(which bash)"
diff --git a/termux/services/syncthing b/termux/services/syncthing
index 70bbaca..dc5cbc8 100755
--- a/termux/services/syncthing
+++ b/termux/services/syncthing
@@ -8,7 +8,7 @@ LOGFILE="$HOME/.local/log/syncthing.log"
start() {
printf "Starting Syncthing: "
- sudo start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE"
+ sudo start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE" -home ~/.config/syncthing
echo "OK"
}
diff --git a/vimpcrc b/vimpcrc
index 97f442c..d3f547c 100644
--- a/vimpcrc
+++ b/vimpcrc
@@ -5,3 +5,4 @@ map ° D:browseA:shuffle:play:playlist
set songformat {%a - %b: %t}|{%f}$E$R $H[$H%l$H]$H
set libraryformat %n \| {%t}|{%f}$E$R $H[$H%l$H]$H
set ignorecase
+set sort library
diff --git a/vimrc b/vimrc
index 6e10446..f6c1eef 100644
--- a/vimrc
+++ b/vimrc
@@ -5,56 +5,69 @@ set nocompatible
filetype off
""" PLUGINS MANAGEMENT """
-" Voir :h vundle
-set rtp+=~/.vim/bundle/Vundle.vim
-call vundle#begin()
-Plugin 'gmarik/Vundle.vim'
+" NOTE 18-06-24 Got rid of Vundle in favor of vim-plug (why: more recent, supports
+" Neovim, simpler). Commented out plugins that seemed useless, feel free to
+" uncomment them again if you need to
-Plugin 'L9'
-Plugin 'rstacruz/sparkup', {'rtp': 'vim/'}
-Plugin 'tomasr/molokai'
-"Bundle 'Shougo/neosnippet'
-"Bundle 'Shougo/neosnippet-snippets'
-Plugin 'tpope/vim-surround'
-Plugin 'tpope/vim-fugitive'
-Plugin 'tpope/vim-repeat'
-Plugin 'tpope/tpope-vim-abolish'
-Plugin 'vim-airline/vim-airline'
-Plugin 'vim-airline/vim-airline-themes'
-Plugin 'airblade/vim-gitgutter'
-Plugin 'ctrlpvim/ctrlp.vim'
-Plugin 'mbbill/undotree'
-Plugin 'xolox/vim-misc'
-Plugin 'xolox/vim-easytags'
-Plugin 'majutsushi/tagbar'
-Plugin 'wellle/targets.vim'
-Plugin 'Chiel92/vim-autoformat'
-" Plugin 'Valloric/YouCompleteMe'
-Plugin 'artur-shaik/vim-javacomplete2'
-Plugin 'tomtom/tcomment_vim'
-Plugin 'Shougo/denite.nvim'
-Plugin 'tomlion/vim-solidity'
-Plugin 'godlygeek/tabular'
+" Auto-install vim-plug
+if empty(glob('~/.vim/autoload/plug.vim'))
+ silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
+ \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
+ autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
+endif
-call vundle#end() " required
-filetype plugin indent on " required
+" Plugin definition
+call plug#begin()
+
+" Plug 'L9'
+" Plug 'rstacruz/sparkup', {'rtp': 'vim/'}
+Plug 'chriskempson/base16-vim'
+Plug 'tpope/vim-surround'
+" Plug 'tpope/vim-fugitive'
+" Plug 'tpope/vim-repeat'
+Plug 'tpope/tpope-vim-abolish'
+Plug 'vim-airline/vim-airline'
+Plug 'vim-airline/vim-airline-themes'
+Plug 'airblade/vim-gitgutter'
+Plug 'mbbill/undotree'
+Plug 'xolox/vim-misc' " Required for 'xolox/vim-easytags'
+Plug 'xolox/vim-easytags'
+Plug 'majutsushi/tagbar'
+Plug 'wellle/targets.vim'
+Plug 'Chiel92/vim-autoformat'
+Plug 'tomtom/tcomment_vim'
+" Plug 'Shougo/denite.nvim'
+" Plug 'tomlion/vim-solidity'
+" Plug 'godlygeek/tabular'
+" Plug 'jrozner/vim-antlr'
+"
+" Plug 'maralla/completor.vim'
+if has('nvim')
+ Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
+else
+ Plug 'Shougo/deoplete.nvim'
+ Plug 'roxma/nvim-yarp'
+ Plug 'roxma/vim-hug-neovim-rpc'
+endif
+Plug 'zchee/deoplete-jedi'
+
+Plug 'python-mode/python-mode', { 'branch': 'develop' }
+Plug 'junegunn/fzf', {'do': './install --bin'}
+Plug 'junegunn/fzf.vim'
+Plug 'ervandew/supertab'
+Plug 'dpelle/vim-LanguageTool'
+Plug 'terryma/vim-smooth-scroll'
+
+call plug#end()
+
+""" COMPLETOR """
+
+let g:deoplete#enable_at_startup = 1
""" UNDOTREE """
-nmap :UndotreeToggle:UndotreeFocus
-
-""" CTRLP """
-
-let g:ctrlp_custom_ignore = {
- \ 'dir': '\v([\/]\.(git|hg|svn)|log|node_modules|bower_components|__pycache__|vendor|output|buildroot|doc)$',
- \ 'file': '\v\.(exe|so|dll|o|pyc)$',
- \ 'link': 'SOME_BAD_SYMBOLIC_LINKS',
- \ }
-
-let g:ctrlp_map = ''
-let g:ctrlp_cmd = 'CtrlPMixed'
-map :CtrlPMRUFiles
+nmap :UndotreeToggle
""" TAGBAR """
@@ -71,27 +84,65 @@ let g:airline#extensions#tabline#enabled = 1
let g:airline_section_a = airline#section#create(['mode'])
let g:airline_section_b = airline#section#create(['branch', 'hunks'])
let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c'])
-let g:airline_theme = 'wombat'
-
-""" YOUCOMPLETEME """
-
-" let g:ycm_global_ycm_extra_conf = '~/.config/ycm_extra_conf.py'
-let g:ycm_global_ycm_extra_conf = '/usr/share/vim/vimfiles/third_party/ycmd/cpp/ycm/.ycm_extra_conf.py'
-let g:ycm_server_python_interpreter = '/usr/bin/python2'
-
-nmap gTi :YcmCompleter GoToInclude
-nmap gTc :YcmCompleter GoToDeclaration
-nmap gTf :YcmCompleter GoToDefinition
-nmap gt :YcmCompleter GoTo
-nmap gT :YcmCompleter GoToImprecise
-nmap gTr :YcmCompleter GoToReference
+let g:airline_theme = 'base16_monokai'
""" AUTOFORMAT """
nmap :Autoformat
-""" JAVACOMPLETE """
+""" PYMODE """
+
+let g:pymode_virtualenv = 1
+let g:pymode_lint_ignore = ["W0401"]
+let g:pymode_lint_cwindow = 0
+
+" TODO Even with magic pymod_motion complains about the option `magic&` being not set :/
+let g:pymode_motion = 0
+set magic
+
+""" FZF """
+
+let g:fzf_layout = { 'down': '~100%' }
+let g:fzf_colors =
+\ { 'fg': ['fg', 'Normal'],
+ \ 'bg': ['bg', 'Normal'],
+ \ 'hl': ['fg', 'Comment'],
+ \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
+ \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
+ \ 'hl+': ['fg', 'Statement'],
+ \ 'info': ['fg', 'PreProc'],
+ \ 'border': ['fg', 'Ignore'],
+ \ 'prompt': ['fg', 'Conditional'],
+ \ 'pointer': ['fg', 'Exception'],
+ \ 'marker': ['fg', 'Keyword'],
+ \ 'spinner': ['fg', 'Label'],
+ \ 'header': ['fg', 'Comment'] }
+
+nmap gF :Files
+nmap gf :GFiles
+nmap gb :Buffers
+nmap gL :Lines
+nmap gl :BLines
+nmap gT :Tags
+nmap gt :BTags
+nmap gm :Marks
+nmap gw :Windows
+nmap gh :History
+nmap gH :History:
+nmap gS :History/
+nmap gs :Snippets
+
+" TODO `gd` → go to tag matching selected word, or show a list with that
+" of tags pre-filtered with that word
+
+""" SUPERTAB """
+
+let g:SuperTabDefaultCompletionType = "" " Go down when completing
+let g:SuperTabContextDefaultCompletionType = ""
+
+""" LanguageTool """
+
+let g:languagetool_jar = "/usr/share/java/languagetool/languagetool-commandline.jar"
-autocmd FileType java setlocal omnifunc=javacomplete#Complete
""" VIM SETTINGS """
@@ -125,11 +176,9 @@ set updatetime=250
set cursorcolumn
-syntax enable
+set splitbelow
-set background=dark
-colorscheme molokai
-let g:molokai_original = 1
+syntax enable
" From http://stackoverflow.com/a/5004785/2766106
set list
@@ -140,10 +189,16 @@ filetype on
filetype plugin on
filetype indent on
+set wildmode=longest,list
+set showcmd
+
" Put plugins and dictionaries in this dir (also on Windows)
let vimDir = '$HOME/.vim'
let &runtimepath.=','.vimDir
+" theme
+source $HOME/.vim/colorscheme.vim
+
" Keep undo history across sessions by storing it in a file
if has('persistent_undo')
let myUndoDir = expand(vimDir . '/undodir')
@@ -159,11 +214,19 @@ endif
cmap w!! w !sudo tee > /dev/null %
imap jk
-imap mù
-map o
+vmap
+
+nmap o
nmap :bp
nmap :bn
-nmap kkkkkkkkkkkkkkkkkkkkk
-nmap jjjjjjjjjjjjjjjjjjjjj
+if has('nvim')
+ " nmap 20k
+ " nmap 20j
+ noremap :call smooth_scroll#up(20, 5, 1)
+ noremap :call smooth_scroll#down(20, 5, 1)
+else
+ nmap kkkkkkkkkkkkkkkkkkkkk
+ nmap jjjjjjjjjjjjjjjjjjjjj
+endif