This commit is contained in:
Geoffrey Frogeye 2018-08-19 11:34:39 +02:00
commit f94154c8f5
73 changed files with 2216 additions and 823 deletions

View file

@ -1,3 +1,4 @@
#include ".Xresources.d/xft"
#include ".Xresources.d/theme"
#include ".Xresources.d/xterm"
#include ".Xresources.d/urxvt"

1
Xresources.d/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
theme

View file

@ -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

55
Xresources.d/urxvt Normal file
View file

@ -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

79
bashrc
View file

@ -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 {

2
bin/.gitignore vendored
View file

@ -1,2 +0,0 @@
*
!.gitignore

View file

@ -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;
};
};

View file

@ -1,300 +1,63 @@
[global]
font = Cantarell 10
# Allow a small subset of html markup:
# <b>bold</b>
# <i>italic</i>
# <s>strikethrough</s>
# <u>underline</u>
#
# For a complete reference see
# <http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>.
# 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 = "<b>%s %p</b>\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

7
config/i3/ashuffle Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
while true
do
ashuffle
sleep 1
done

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

55
config/i3/batteryNotify Executable file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 "$@"

View file

@ -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

26
config/i3/multimediaKey Executable file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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[@]}"

34
config/khal/config Normal file
View file

@ -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

43
config/khard/khard.conf Normal file
View file

@ -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

3
config/nvim/init.vim Normal file
View file

@ -0,0 +1,3 @@
set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath = &runtimepath
source ~/.vimrc

10
config/offlineimap.py Normal file
View file

@ -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)

18
config/polybar/bbswitch Executable file
View file

@ -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

View file

@ -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 = <ramp-coreload>
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>
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 = <ramp-signal> <label-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 = <ramp-capacity> <label-charging>
format-charging-foreground = ${theme.yellowF}
format-charging-prefix = 
label-charging = %percentage%% (%time%)
format-discharging = <ramp-capacity> <label-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 = " "

9
config/polybar/keystore Executable file
View file

@ -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

17
config/polybar/linuxmismatch Executable file
View file

@ -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

21
config/polybar/todo Executable file
View file

@ -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

3
config/rofi/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
theme.config
theme.rasi

8
config/rofi/config Normal file
View file

@ -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

View file

@ -0,0 +1 @@
/usr/lib/systemd/user/dunst.service

View file

@ -0,0 +1 @@
/home/geoffrey/.local/share/systemd/user/mbsync.timer

View file

@ -0,0 +1 @@
/usr/lib/systemd/user/mpd.service

View file

@ -0,0 +1 @@
/usr/lib/systemd/user/offlineimap.service

View file

@ -0,0 +1 @@
/usr/lib/systemd/user/syncthing.service

View file

@ -0,0 +1 @@
/usr/lib/systemd/user/vdirsyncer.timer

View file

@ -0,0 +1,4 @@
[main]
path = ~/.vdirsyncer/currentCalendars/*
default_list = Personnel
humanize = True

View file

70
config/vdirsyncer/config Normal file
View file

@ -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 <name>]` block defines two storages `a` and `b` that should be
# synchronized. The definition of these storages follows in `[storage <name>]`
# 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"]

3
gdbinit Normal file
View file

@ -0,0 +1,3 @@
define hook-quit
set confirm off
end

View file

@ -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

View file

@ -9,4 +9,4 @@
[ -f "$HOME/.config/linuxColors.sh" ] && . "$HOME/.config/linuxColors.sh"
# Bashrc
[ -f ~/.bashrc ] && . ~/.bashrc
[ -f ~/.bashrc ] && source ~/.bashrc

View file

@ -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

44
scripts/changeColors Executable file
View file

@ -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

213
scripts/compressPictureMovies Executable file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -113,7 +113,8 @@ if (latex) {
// Conversion
htmlString = marked(markdownString, {
renderer: renderer
renderer: renderer,
breaks: false
});
// fullHtmlString = htmlString;
fullHtmlString = template.replace('%BODY%', () => { return htmlString });

628
scripts/mel Executable file
View file

@ -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 = '{} (<i>{}</i>)'.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', '<no 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))

227
scripts/melConf Executable file
View file

@ -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)

39
scripts/musiqueBof Executable file
View file

@ -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)

View file

@ -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

4
scripts/pdfpages Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/bash
# From https://stackoverflow.com/a/14736593
pdftk "$1" dump_data | grep NumberOfPages | awk '{print $2}'

View file

@ -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

68
scripts/replayGain Executable file
View file

@ -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("==============================")

2
scripts/rms Executable file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
find . -name "*.sync-conflict-*" -delete

17
scripts/tagCreatorPhotos Executable file
View file

@ -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)

121
scripts/updateCompressedMusic Executable file
View file

@ -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)

3
scripts/updatedate Executable file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
echo ssh "$1" sudo date --set="'$(date -R)'"

20
scripts/vidcmp Executable file
View file

@ -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))

BIN
terminfo/a/alacritty Normal file

Binary file not shown.

Binary file not shown.

BIN
terminfo/r/rxvt-unicode Normal file

Binary file not shown.

Binary file not shown.

6
termux/scripts/tsu Executable file
View file

@ -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)"

View file

@ -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"
}

View file

@ -5,3 +5,4 @@ map ° D:browse<C-M>A:shuffle<C-M>:play<C-M>:playlist<C-M>
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

199
vimrc
View file

@ -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 <F7> :UndotreeToggle<CR>:UndotreeFocus<CR>
""" 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 = '<c-p>'
let g:ctrlp_cmd = 'CtrlPMixed'
map <a-p> :CtrlPMRUFiles<CR>
nmap <F7> :UndotreeToggle<CR>
""" 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<CR>
nmap gTc :YcmCompleter GoToDeclaration<CR>
nmap gTf :YcmCompleter GoToDefinition<CR>
nmap gt :YcmCompleter GoTo<CR>
nmap gT :YcmCompleter GoToImprecise<CR>
nmap gTr :YcmCompleter GoToReference<CR>
let g:airline_theme = 'base16_monokai'
""" AUTOFORMAT """
nmap <F3> :Autoformat<CR>
""" 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<CR>
nmap gf :GFiles<CR>
nmap gb :Buffers<CR>
nmap gL :Lines<CR>
nmap gl :BLines<CR>
nmap gT :Tags<CR>
nmap gt :BTags<CR>
nmap gm :Marks<CR>
nmap gw :Windows<CR>
nmap gh :History<CR>
nmap gH :History:<CR>
nmap gS :History/<CR>
nmap gs :Snippets<CR>
" 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 = "<c-n>" " Go down when completing
let g:SuperTabContextDefaultCompletionType = "<c-n>"
""" 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 <Esc>
imap <Esc>
map <Enter> o<Esc>
vmap <Enter> <Esc>
nmap <Enter> o<Esc>
nmap <C-H> :bp<CR>
nmap <C-L> :bn<CR>
nmap <C-K> kkkkkkkkkkkkkkkkkkkkk
nmap <C-J> jjjjjjjjjjjjjjjjjjjjj
if has('nvim')
" nmap <C-K> 20k
" nmap <C-J> 20j
noremap <silent> <C-K> :call smooth_scroll#up(20, 5, 1)<CR>
noremap <silent> <C-J> :call smooth_scroll#down(20, 5, 1)<CR>
else
nmap <C-K> kkkkkkkkkkkkkkkkkkkkk
nmap <C-J> jjjjjjjjjjjjjjjjjjjjj
endif