Compare commits

..

No commits in common. "main" and "crash2" have entirely different histories.
main ... crash2

367 changed files with 6373 additions and 8931 deletions

3
.gitignore vendored
View file

@ -1,2 +1 @@
result
*.qcow2
__pycache__

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "config/automatrop/roles/mnussbaum.base16-builder-ansible"]
path = config/automatrop/roles/mnussbaum.base16-builder-ansible
url = https://github.com/GeoffreyFrogeye/base16-builder-ansible.git
[submodule "config/automatrop/plugins/modules/aur"]
path = config/automatrop/plugins/modules/aur
url = https://github.com/kewlfft/ansible-aur.git

View file

@ -1,36 +0,0 @@
# Geoffrey Frogeye's dotfiles
This repo holds most of my systems configuration.
It is built on top of the Nix ecosystem
## Directory structure
- `os`, `hm`, `nod`: Re-usable configuration for [NixOS](https://nixos.org/), [home-manager](https://github.com/nix-community/home-manager/#home-manager-using-nix), [Nix-on-Droid](https://github.com/nix-community/nix-on-droid#nix-on-droid) respectively
- `<module-name>`: Module. Used to separate configuration in separate logical units.
- `default.nix`: Entrypoint for that module. Contains Nix configuration for the module.
- `<other file>`: Extra files: scripts to be installed, or ... whatever.
- `default.nix`: Entrypoint for that system. Import all the modules.
- `<other file>`: Files non-conforming to the structure above because I'm hurrying to have everything working before cleaning.
- `dk`: Re-usable configuration for [disko](https://github.com/nix-community/disko#disko---declarative-disk-partitioning)
- `<name>.nix`: Partitionning scheme configuration. Don't assume a specific context, it will be imported as Disko and NixOS config.
- `options.nix`: Definition of options exposed to all systems (even though if not relevant for all, it's for simplicity sake).
- `<profile-name>` (e.g. `curacao`): Configurations for my different devices
- `options.nix`: Common options configuration. Don't assume a specific context (for reasons unclear, I'm sure I can do something with that).
- `hardware.nix`: NixOS configuration for that specific device.
- `dk.nix`: Partitionning configuration. Import a top-level `dk` scheme, adding disks ids and other configuration required.
- `os.nix`, `hm.nix`, `nod.nix`: Entrypoint for the device/system configuration. Imports the above files (last two only for NixOS) and contains configuration specific to this combination.
- `<profile-name>_<media>` (e.g. `pindakaas_sd`): Alternate configuration for a device. Used to test a configuration without altering the "main" one.
- `options.nix`: Same as above. Can be a symlink.
- `hardware.nix`: Same as above. Should be a symlink.
- `dk.nix`: Same as above. Should not be a symlink.
- `os.nix`, `hm.nix`, `nod.nix`: Same as above. Should not be a symlink.
## Scripts
They all have a `-h` flag.
## Extensions
There's some things I'm not putting in this public repo: work-related things, and other sensitive things that probably shouldn't be public.
Those are stored in extensions, i.e. repos with a similar structure to this one, and ultimately importing things from it.
This is why you might see options not being seemingly used.

2
bash_logout Normal file
View file

@ -0,0 +1,2 @@
clear
reset

7
bash_profile Normal file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
source ~/.config/shell/shenv
source ~/.config/shell/commonenv
source ~/.config/shell/shrc
source ~/.config/shell/commonrc
source ~/.config/shell/bashrc

5
bashrc Normal file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
source ~/.config/shell/shrc
source ~/.config/shell/commonrc
source ~/.config/shell/bashrc

View file

@ -1,73 +0,0 @@
#!/usr/bin/env nix-shell
#! nix-shell -i bash
#! nix-shell -p nix
set -euo pipefail
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
# Parse arguments
function help {
echo "Usage: $0 [-h|-e|-b] [flake-uri#]name"
echo "Build a NixOS configuration on the local machine."
echo
echo "Arguments:"
echo " profile: OS/disk profile to use"
echo
echo "Options:"
echo " -h: Display this help message."
echo " -v: Build a virtual machine."
echo " -b: Build a virtual machine with boot loader."
}
arg=build
while getopts "hvb" OPTION
do
case "$OPTION" in
h)
help
exit 0
;;
v)
arg=build-vm
;;
b)
arg=build-vm-with-bootloader
;;
?)
help
exit 2
;;
esac
done
shift "$((OPTIND -1))"
if [ "$#" -ne 1 ]
then
help
exit 2
fi
if [[ "$1" == *"#"* ]]
then
flake_uri="$(echo "$1" | cut -d'#' -f1)"
flake_uri=$( cd -- "$flake_uri" &> /dev/null && pwd )
name="$(echo "$1" | cut -d'#' -f2)"
else
flake_uri="$SCRIPT_DIR"
name="$1"
fi
if [ ! -f "$flake_uri/flake.nix" ]
then
echo "Flake not found."
fi
flake="${flake_uri}#${name}"
set -x
nix --extra-experimental-features "nix-command flakes" run "${SCRIPT_DIR}#nixos-rebuild" -- "$arg" --flake "$flake"
echo 
# TODO Use update-local-flakes?

View file

@ -1,68 +0,0 @@
{ pkgs, lib, config, ... }:
let
passwordFile = "/tmp/dotfiles_${config.networking.hostName}_password";
in
{
disko.devices = {
disk = {
"${config.networking.hostName}" = {
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
# Needs enough to store multiple kernel generations
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"defaults"
];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "${config.networking.hostName}";
passwordFile = passwordFile;
settings = {
# Not having SSDs die fast is more important than crypto
# nerds that could potentially discover which filesystem I
# use from TRIM patterns
allowDiscards = true;
};
content = {
type = "btrfs";
extraArgs = [ "-f" ];
subvolumes = {
"/nixos" = {
mountpoint = "/";
mountOptions = [ "compress=zstd" "noatime" ];
};
"/home" = {
mountpoint = "/home";
mountOptions = [ "compress=zstd" "relatime" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "noatime" ];
};
# Maybe later
# "/swap" = {
# mountpoint = "/.swapvol";
# swap.swapfile.size = "20M";
# };
};
};
};
};
};
};
};
};
};
}

View file

@ -1,22 +0,0 @@
{ pkgs, lib, config, ... }:
let
generator = pkgs.writers.writePython3 "frogarized"
{
libraries = [ pkgs.python3Packages.colorspacious ];
}
(builtins.readFile ./frogarized.py);
frogarized_json = polarity: pkgs.runCommand "frogarized-${polarity}.json" { } "${generator} --polarity ${polarity} --output json > $out";
frogarized_nix = polarity: builtins.fromJSON (builtins.readFile (frogarized_json polarity));
in
{
config = {
stylix = {
base16Scheme = frogarized_nix config.stylix.polarity;
# On purpose also enable without a DE because stylix complains otherwise
image = builtins.fetchurl {
url = "https://get.wallhere.com/photo/sunlight-abstract-minimalism-green-simple-circle-light-leaf-wave-material-line-wing-computer-wallpaper-font-close-up-macro-photography-124350.png";
sha256 = "sha256:1zfq3f3v34i45mi72pkfqphm8kbhczsg260xjfl6dbydy91d7y93";
};
};
};
}

View file

@ -1,112 +0,0 @@
import argparse
import json
import colorspacious
import numpy as np
# Original values for the Solarized color scheme,
# created by Ethan Schoonover (https://ethanschoonover.com/solarized/)
SOLARIZED_LAB = np.array(
[
[15, -12, -12],
[20, -12, -12],
[45, -7, -7],
[50, -7, -7],
[60, -6, -3],
[65, -5, -2],
[92, -0, 10],
[97, 0, 10],
[50, 65, 45],
[50, 50, 55],
[60, 10, 65],
[60, -20, 65],
[60, -35, -5],
[55, -10, -45],
[50, 15, -45],
[50, 65, -5],
]
)
# I couldn't get a perfect translation of Solarized L*a*b values into sRGB,
# so here is upstream's translation for reference
SOLARIZED_RGB = np.array(
[
[0, 43, 54],
[7, 54, 66],
[88, 110, 117],
[101, 123, 131],
[131, 148, 150],
[147, 161, 161],
[238, 232, 213],
[253, 246, 227],
[220, 50, 47],
[203, 75, 22],
[181, 137, 0],
[133, 153, 0],
[42, 161, 152],
[38, 139, 210],
[108, 113, 196],
[211, 54, 130],
]
)
# Parse arguments
parser = argparse.ArgumentParser(
description="Generate a base16-theme based derived from Solarized"
)
parser.add_argument("--source", choices=["lab", "rgb"], default="lab")
parser.add_argument("--lightness_factor", type=float, default=1.0)
parser.add_argument("--chroma-factor", type=float, default=1.0)
parser.add_argument("--hue_shift", type=float, default=-75.0)
parser.add_argument("--polarity", choices=["dark", "light"], default="dark")
parser.add_argument(
"--output", choices=["json", "truecolor"], default="truecolor"
)
args = parser.parse_args()
# Convert source to JCh color space
if args.source == "lab":
solarized_jch = colorspacious.cspace_convert(
SOLARIZED_LAB, "CIELab", "JCh"
)
elif args.source == "rgb":
solarized_jch = colorspacious.cspace_convert(
SOLARIZED_RGB, "sRGB255", "JCh"
)
# Build frogarized theme
jch_factor = [args.lightness_factor, args.chroma_factor, 1]
jch_shift = [0, 0, args.hue_shift]
frogarzied_jch = np.vstack(
[solarized_jch[:8] * jch_factor + jch_shift, solarized_jch[8:]]
)
# Convert frogarized to RGB
frogarized_srgb = colorspacious.cspace_convert(
frogarzied_jch, "JCh", "sRGB255"
)
frogarized_rgb = np.uint8(np.rint(np.clip(frogarized_srgb, 0, 255)))
if args.polarity == "light":
frogarized_rgb = np.vstack([frogarized_rgb[7::-1], frogarized_rgb[8:]])
# Output
palette = dict()
for i in range(16):
rgb = frogarized_rgb[i]
r, g, b = rgb
hex = f"#{r:02x}{g:02x}{b:02x}"
palette[f"base{i:02X}"] = hex
if args.output == "truecolor":
print(f"\033[48;2;{r};{g};{b}m{hex}\033[0m") # ]]
# treesitter is silly and will consider brackets in strings
# as indentation, hence the comment above
if args.output == "json":
scheme = palette.copy()
scheme.update(
{
"slug": f"frogarized-{args.polarity}",
"scheme": f"Frogarized {args.polarity.title()}",
"author": "Geoffrey Frogeye (with work from Ethan Schoonover)",
}
)
print(json.dumps(scheme, indent=4))

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

@ -0,0 +1 @@
theme

8
config/Xresources/configure vendored Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
mkdir -p $HOME/.local/share/fonts
echo $(for i in "" "%20Bold" "%20Oblique" "%20Bold%20Oblique"; do
cd $HOME/.local/share/fonts
wget -c http://raw.githubusercontent.com/powerline/fonts/master/DejaVuSansMono/DejaVu%20Sans%20Mono$i%20for%20Powerline.ttf
done)
wget -c "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" -O $HOME/.local/share/fonts/fontawesome-webfont.ttf

4
config/Xresources/main Normal file
View file

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

61
config/Xresources/urxvt Normal file
View file

@ -0,0 +1,61 @@
! 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:Twemoji:size=12:antialias=true,xft:symbola:size=12:minspace=False
! 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
! Open URL in browser (extension: matcher)
! URxvt.url-launcher: o
! URxvt.matcher.button: 1
! URxvt.matcher.rend.0: Uline Bold fg5

9
config/Xresources/xft Normal file
View file

@ -0,0 +1,9 @@
Xft.dpi: 96
Xft.antialias: true
Xft.hinting: true
Xft.rgba: rgb
Xft.autohint: false
Xft.hintstyle: hintslight
Xft.lcdfilter: lcddefault
Xcursor.theme: Menda-Cursor
Xcursor.size: 0

15
config/Xresources/xterm Normal file
View file

@ -0,0 +1,15 @@
xterm*faceName : DejaVu Sans Mono for Powerline:size=12:antialias=true
xterm*dynamicColors: true
xterm*utf8: 2
xterm*eightBitInput: true
xterm*saveLines: 512
xterm*scrollKey: true
xterm*scrollTtyOutput: false
xterm*scrollBar: false
xterm*rightScrollBar: false
xterm*jumpScroll: true
xterm*multiScroll: true
xterm*toolBar: false
XTerm.vt100.translations: #override \n\
Ctrl Shift <Key>C: copy-selection(CLIPBOARD) \n\
Ctrl Shift <Key>V: insert-selection(CLIPBOARD)

2
config/alacritty/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
theme.yml
alacritty.yml

View file

@ -0,0 +1,599 @@
# vi:syntax=yaml
# Configuration for Alacritty, the GPU enhanced terminal emulator.
# Any items in the `env` entry below will be added as
# environment variables. Some entries may override variables
# set by alacritty itself.
env:
# TERM variable
#
# This value is used to set the `$TERM` environment variable for
# each instance of Alacritty. If it is not present, alacritty will
# check the local terminfo database and use `alacritty` if it is
# available, otherwise `xterm-256color` is used.
TERM: xterm-256color
window:
# Window dimensions (changes require restart)
#
# Specified in number of columns/lines, not pixels.
# If both are `0`, this setting is ignored.
#dimensions:
# columns: 0
# lines: 0
# Window position (changes require restart)
#
# Specified in number of pixels.
# If the position is not set, the window manager will handle the placement.
#position:
# x: 0
# y: 0
# Window padding (changes require restart)
#
# Blank space added around the window in pixels. This padding is scaled
# by DPI and the specified value is always added at both opposing sides.
#padding:
# x: 0
# y: 0
# Spread additional padding evenly around the terminal content.
dynamic_padding: false
# Window decorations
#
# Values for `decorations`:
# - full: Borders and title bar
# - none: Neither borders nor title bar
#
# Values for `decorations` (macOS only):
# - transparent: Title bar, transparent background and title bar buttons
# - buttonless: Title bar, transparent background, but no title bar buttons
#decorations: full
# Startup Mode (changes require restart)
#
# Values for `startup_mode`:
# - Windowed
# - Maximized
# - Fullscreen
#
# Values for `startup_mode` (macOS only):
# - SimpleFullscreen
#startup_mode: Windowed
# Window title
#title: Alacritty
# Allow terminal applications to change Alacritty's window title.
dynamic_title: true
# Window class (Linux/BSD only):
#class:
# Application instance name
#instance: Alacritty
# General application class
#general: Alacritty
# GTK theme variant (Linux/BSD only)
#
# Override the variant of the GTK theme. Commonly supported values are `dark` and `light`.
# Set this to `None` to use the default theme variant.
#gtk_theme_variant: None
#scrolling:
# Maximum number of lines in the scrollback buffer.
# Specifying '0' will disable scrolling.
#history: 10000
# Scrolling distance multiplier.
#multiplier: 3
# Font configuration
font:
# Normal (roman) font face
#normal:
# Font family
#
# Default:
# - (macOS) Menlo
# - (Linux/BSD) monospace
# - (Windows) Consolas
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Regular
# Bold font face
#bold:
# Font family
#
# If the bold family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Bold
# Italic font face
#italic:
# Font family
#
# If the italic family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Italic
# Bold italic font face
#bold_italic:
# Font family
#
# If the bold italic family is not specified, it will fall back to the
# value specified for the normal font.
#family: monospace
# The `style` can be specified to pick a specific face.
#style: Bold Italic
# Point size
size: 12.0
# Offset is the extra space around each character. `offset.y` can be thought of
# as modifying the line spacing, and `offset.x` as modifying the letter spacing.
#offset:
# x: 0
# y: 0
# Glyph offset determines the locations of the glyphs within their cells with
# the default being at the bottom. Increasing `x` moves the glyph to the right,
# increasing `y` moves the glyph upward.
#glyph_offset:
# x: 0
# y: 0
# Thin stroke font rendering (macOS only)
#
# Thin strokes are suitable for retina displays, but for non-retina screens
# it is recommended to set `use_thin_strokes` to `false`.
#use_thin_strokes: true
{{ base16_schemes['schemes'][base16_scheme]['alacritty']['colors']['base16-' + base16_scheme + '.yml'] }}
# Bell
#
# The bell is rung every time the BEL control character is received.
bell:
# Visual Bell Animation
#
# Animation effect for flashing the screen when the visual bell is rung.
#
# Values for `animation`:
# - Ease
# - EaseOut
# - EaseOutSine
# - EaseOutQuad
# - EaseOutCubic
# - EaseOutQuart
# - EaseOutQuint
# - EaseOutExpo
# - EaseOutCirc
# - Linear
animation: EaseOutExpo
# Duration of the visual bell flash. A `duration` of `0` will disable the
# visual bell animation.
duration: 100
# Visual bell animation color.
color: '#000000'
# Bell Command
#
# This program is executed whenever the bell is rung.
#
# When set to `command: None`, no command will be executed.
#
# Example:
# command:
# program: notify-send
# args: ["Hello, World!"]
#
command:
program: play
args: ['-n', 'synth', 'sine', 'C5', 'sine', 'E4', 'remix', '1-2', 'fade', '0.1', '0.2', '0.1']
# program: notify-send
# args: ["Hello, World!"]
# Background opacity
#
# Window opacity as a floating point number from `0.0` to `1.0`.
# The value `0.0` is completely transparent and `1.0` is opaque.
#background_opacity: 1.0
#selection:
#semantic_escape_chars: ",│`|:\"' ()[]{}<>\t"
# When set to `true`, selected text will be copied to the primary clipboard.
#save_to_clipboard: false
cursor:
# Cursor style
#
# Values for `style`:
# - ▇ Block
# - _ Underline
# - | Beam
#style: Block
# Vi mode cursor style
#
# If the vi mode cursor style is `None` or not specified, it will fall back to
# the style of the active value of the normal cursor.
#
# See `cursor.style` for available options.
vi_mode_style: Underline
# If this is `true`, the cursor will be rendered as a hollow box when the
# window is not focused.
#unfocused_hollow: true
# Thickness of the cursor relative to the cell width as floating point number
# from `0.0` to `1.0`.
#thickness: 0.15
# Live config reload (changes require restart)
#live_config_reload: true
# Shell
#
# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`.
# Entries in `shell.args` are passed unmodified as arguments to the shell.
#
# Default:
# - (macOS) /bin/bash --login
# - (Linux/BSD) user login shell
# - (Windows) powershell
#shell:
# program: /bin/bash
# args:
# - --login
# Startup directory
#
# Directory the shell is started in. If this is unset, or `None`, the working
# directory of the parent process will be used.
#working_directory: None
# WinPTY backend (Windows only)
#
# Alacritty defaults to using the newer ConPTY backend if it is available,
# since it resolves a lot of bugs and is quite a bit faster. If it is not
# available, the WinPTY backend will be used instead.
#
# Setting this option to `true` makes Alacritty use the legacy WinPTY backend,
# even if the ConPTY backend is available.
#winpty_backend: false
# Send ESC (\x1b) before characters when alt is pressed.
#alt_send_esc: true
mouse:
# Click settings
#
# The `double_click` and `triple_click` settings control the time
# alacritty should wait for accepting multiple clicks as one double
# or triple click.
#double_click: { threshold: 300 }
#triple_click: { threshold: 300 }
# If this is `true`, the cursor is temporarily hidden when typing.
#hide_when_typing: false
#url:
# URL launcher
#
# This program is executed when clicking on a text which is recognized as a URL.
# The URL is always added to the command as the last parameter.
#
# When set to `launcher: None`, URL launching will be disabled completely.
#
# Default:
# - (macOS) open
# - (Linux/BSD) xdg-open
# - (Windows) explorer
#launcher:
# program: xdg-open
# args: []
# URL modifiers
#
# These are the modifiers that need to be held down for opening URLs when clicking
# on them. The available modifiers are documented in the key binding section.
modifiers: Control
# Mouse bindings
#
# Mouse bindings are specified as a list of objects, much like the key
# bindings further below.
#
# To trigger mouse bindings when an application running within Alacritty captures the mouse, the
# `Shift` modifier is automatically added as a requirement.
#
# Each mouse binding will specify a:
#
# - `mouse`:
#
# - Middle
# - Left
# - Right
# - Numeric identifier such as `5`
#
# - `action` (see key bindings)
#
# And optionally:
#
# - `mods` (see key bindings)
#mouse_bindings:
# - { mouse: Middle, action: PasteSelection }
# Key bindings
#
# Key bindings are specified as a list of objects. For example, this is the
# default paste binding:
#
# `- { key: V, mods: Control|Shift, action: Paste }`
#
# Each key binding will specify a:
#
# - `key`: Identifier of the key pressed
#
# - A-Z
# - F1-F24
# - Key0-Key9
#
# A full list with available key codes can be found here:
# https://docs.rs/glutin/*/glutin/event/enum.VirtualKeyCode.html#variants
#
# Instead of using the name of the keys, the `key` field also supports using
# the scancode of the desired key. Scancodes have to be specified as a
# decimal number. This command will allow you to display the hex scancodes
# for certain keys:
#
# `showkey --scancodes`.
#
# Then exactly one of:
#
# - `chars`: Send a byte sequence to the running application
#
# The `chars` field writes the specified string to the terminal. This makes
# it possible to pass escape sequences. To find escape codes for bindings
# like `PageUp` (`"\x1b[5~"`), you can run the command `showkey -a` outside
# of tmux. Note that applications use terminfo to map escape sequences back
# to keys. It is therefore required to update the terminfo when changing an
# escape sequence.
#
# - `action`: Execute a predefined action
#
# - ToggleViMode
# - SearchForward
# - SearchBackward
# - Copy
# - Paste
# - PasteSelection
# - IncreaseFontSize
# - DecreaseFontSize
# - ResetFontSize
# - ScrollPageUp
# - ScrollPageDown
# - ScrollHalfPageUp
# - ScrollHalfPageDown
# - ScrollLineUp
# - ScrollLineDown
# - ScrollToTop
# - ScrollToBottom
# - ClearHistory
# - Hide
# - Minimize
# - Quit
# - ToggleFullscreen
# - SpawnNewInstance
# - ClearLogNotice
# - ClearSelection
# - ReceiveChar
# - None
#
# (`mode: Vi` only):
# - Open
# - Up
# - Down
# - Left
# - Right
# - First
# - Last
# - FirstOccupied
# - High
# - Middle
# - Low
# - SemanticLeft
# - SemanticRight
# - SemanticLeftEnd
# - SemanticRightEnd
# - WordRight
# - WordLeft
# - WordRightEnd
# - WordLeftEnd
# - Bracket
# - ToggleNormalSelection
# - ToggleLineSelection
# - ToggleBlockSelection
# - ToggleSemanticSelection
# - SearchNext
# - SearchPrevious
# - SearchStart
# - SearchEnd
#
# (macOS only):
# - ToggleSimpleFullscreen: Enters fullscreen without occupying another space
#
# (Linux/BSD only):
# - CopySelection: Copies into selection buffer
#
# - `command`: Fork and execute a specified command plus arguments
#
# The `command` field must be a map containing a `program` string and an
# `args` array of command line parameter strings. For example:
# `{ program: "alacritty", args: ["-e", "vttest"] }`
#
# And optionally:
#
# - `mods`: Key modifiers to filter binding actions
#
# - Command
# - Control
# - Option
# - Super
# - Shift
# - Alt
#
# Multiple `mods` can be combined using `|` like this:
# `mods: Control|Shift`.
# Whitespace and capitalization are relevant and must match the example.
#
# - `mode`: Indicate a binding for only specific terminal reported modes
#
# This is mainly used to send applications the correct escape sequences
# when in different modes.
#
# - AppCursor
# - AppKeypad
# - Alt
#
# A `~` operator can be used before a mode to apply the binding whenever
# the mode is *not* active, e.g. `~Alt`.
#
# Bindings are always filled by default, but will be replaced when a new
# binding with the same triggers is defined. To unset a default binding, it can
# be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for
# a no-op if you do not wish to receive input characters for that binding.
#
# If the same trigger is assigned to multiple actions, all of them are executed
# in the order they were defined in.
key_bindings:
#- { key: Paste, action: Paste }
#- { key: Copy, action: Copy }
#- { key: L, mods: Control, action: ClearLogNotice }
#- { key: L, mods: Control, mode: ~Vi, chars: "\x0c" }
#- { key: PageUp, mods: Shift, mode: ~Alt, action: ScrollPageUp, }
#- { key: PageDown, mods: Shift, mode: ~Alt, action: ScrollPageDown }
#- { key: Home, mods: Shift, mode: ~Alt, action: ScrollToTop, }
#- { key: End, mods: Shift, mode: ~Alt, action: ScrollToBottom }
# Vi Mode
#- { key: Space, mods: Shift|Control, mode: Vi, action: ScrollToBottom }
- { key: Space, mods: Alt|Control, action: ToggleViMode }
#- { key: Escape, mode: Vi, action: ClearSelection }
#- { key: I, mode: Vi, action: ScrollToBottom }
#- { key: I, mode: Vi, action: ToggleViMode }
#- { key: Y, mods: Control, mode: Vi, action: ScrollLineUp }
#- { key: E, mods: Control, mode: Vi, action: ScrollLineDown }
#- { key: G, mode: Vi, action: ScrollToTop }
#- { key: G, mods: Shift, mode: Vi, action: ScrollToBottom }
#- { key: B, mods: Control, mode: Vi, action: ScrollPageUp }
#- { key: F, mods: Control, mode: Vi, action: ScrollPageDown }
#- { key: U, mods: Control, mode: Vi, action: ScrollHalfPageUp }
#- { key: D, mods: Control, mode: Vi, action: ScrollHalfPageDown }
- { key: K, mods: Control, mode: Vi, action: ScrollHalfPageUp }
- { key: J, mods: Control, mode: Vi, action: ScrollHalfPageDown }
#- { key: Y, mode: Vi, action: Copy }
#- { key: Y, mode: Vi, action: ClearSelection }
#- { key: Copy, mode: Vi, action: ClearSelection }
#- { key: V, mode: Vi, action: ToggleNormalSelection }
#- { key: V, mods: Shift, mode: Vi, action: ToggleLineSelection }
#- { key: V, mods: Control, mode: Vi, action: ToggleBlockSelection }
#- { key: V, mods: Alt, mode: Vi, action: ToggleSemanticSelection }
#- { key: Return, mode: Vi, action: Open }
#- { key: K, mode: Vi, action: Up }
#- { key: J, mode: Vi, action: Down }
#- { key: H, mode: Vi, action: Left }
#- { key: L, mode: Vi, action: Right }
#- { key: Up, mode: Vi, action: Up }
#- { key: Down, mode: Vi, action: Down }
#- { key: Left, mode: Vi, action: Left }
#- { key: Right, mode: Vi, action: Right }
#- { key: Key0, mode: Vi, action: First }
#- { key: Key4, mods: Shift, mode: Vi, action: Last }
#- { key: Key6, mods: Shift, mode: Vi, action: FirstOccupied }
#- { key: H, mods: Shift, mode: Vi, action: High }
#- { key: M, mods: Shift, mode: Vi, action: Middle }
#- { key: L, mods: Shift, mode: Vi, action: Low }
#- { key: B, mode: Vi, action: SemanticLeft }
#- { key: W, mode: Vi, action: SemanticRight }
#- { key: E, mode: Vi, action: SemanticRightEnd }
#- { key: B, mods: Shift, mode: Vi, action: WordLeft }
#- { key: W, mods: Shift, mode: Vi, action: WordRight }
#- { key: E, mods: Shift, mode: Vi, action: WordRightEnd }
#- { key: Key5, mods: Shift, mode: Vi, action: Bracket }
#- { key: Slash, mode: Vi, action: SearchForward }
#- { key: Slash, mods: Shift, mode: Vi, action: SearchBackward }
#- { key: N, mode: Vi, action: SearchNext }
#- { key: N, mods: Shift, mode: Vi, action: SearchPrevious }
# (Windows, Linux, and BSD only)
- { key: V, mods: Control|Alt, action: Paste }
- { key: C, mods: Control|Alt, action: Copy }
- { key: F, mods: Control|Alt, action: SearchForward }
- { key: B, mods: Control|Alt, action: SearchBackward }
- { key: C, mods: Control|Alt, mode: Vi, action: ClearSelection }
#- { key: Insert, mods: Shift, action: PasteSelection }
#- { key: Key0, mods: Control, action: ResetFontSize }
#- { key: Key0, mods: Control, action: ResetFontSize }
#- { key: Equals, mods: Control, action: IncreaseFontSize }
#- { key: Add, mods: Control, action: IncreaseFontSize }
#- { key: Subtract, mods: Control, action: DecreaseFontSize }
#- { key: Minus, mods: Control, action: DecreaseFontSize }
# (Windows only)
#- { key: Return, mods: Alt, action: ToggleFullscreen }
# (macOS only)
#- { key: K, mods: Command, mode: ~Vi, chars: "\x0c" }
#- { key: Key0, mods: Command, action: ResetFontSize }
#- { key: Equals, mods: Command, action: IncreaseFontSize }
#- { key: Add, mods: Command, action: IncreaseFontSize }
#- { key: Minus, mods: Command, action: DecreaseFontSize }
#- { key: K, mods: Command, action: ClearHistory }
#- { key: V, mods: Command, action: Paste }
#- { key: C, mods: Command, action: Copy }
#- { key: C, mods: Command, mode: Vi, action: ClearSelection }
#- { key: H, mods: Command, action: Hide }
#- { key: M, mods: Command, action: Minimize }
#- { key: Q, mods: Command, action: Quit }
#- { key: W, mods: Command, action: Quit }
#- { key: N, mods: Command, action: SpawnNewInstance }
#- { key: F, mods: Command|Control, action: ToggleFullscreen }
#- { key: F, mods: Command, action: SearchForward }
#- { key: B, mods: Command, action: SearchBackward }
#debug:
# Display the time it takes to redraw each frame.
#render_timer: false
# Keep the log file after quitting Alacritty.
#persistent_logging: false
# Log level
#
# Values for `log_level`:
# - None
# - Error
# - Warn
# - Info
# - Debug
# - Trace
#log_level: Warn
# Print all received window events.
#print_events: false

View file

@ -6,4 +6,3 @@ library=plugins/modules
[ssh_connection]
pipelining = True # does not work with requiretty in /etc/sudoers
ssh_args=-o ForwardAgent=yes # no need for installing/configuring/unlocking SSH/GPG keys on the host to be able to git clone extensions

3
config/automatrop/hosts Normal file
View file

@ -0,0 +1,3 @@
curacao.geoffrey.frogeye.fr
triffle.geoffrey.frogeye.fr
bufobufo.frogeye.nl

View file

@ -0,0 +1,10 @@
- name: Default
hosts: all
roles:
- role: software
tags: software
- role: mnussbaum.base16-builder-ansible # Required for color
tags: color
- role: color
tags: color

@ -0,0 +1 @@
Subproject commit 592c6d9841674211f904cf9ea2a951fca3fd5a80

View file

@ -0,0 +1,14 @@
- name: xrdb-reload
command: "xrdb -I{{ ansible_env.HOME }} {{ ansible_env.HOME }}/.config/Xresources/main"
- name: i3-reload
command: i3-msg reload
- name: shell-reload
command: "{{ ansible_env.HOME }}/.local/bin/colorSchemeApply"
- name: fzf-reload
shell: "source {{ ansible_env.HOME }}/.local/bin/colorSchemeApplyFzf"
- name: qutebrowser-reload
shell: "! pgrep qutebrowser || qutebrowser :config-source"

View file

@ -0,0 +1,117 @@
- name: Ensure directories for theme are present
file:
state: directory
path: "{{ ansible_env.HOME }}/{{ item }}"
with_items:
- ".config/Xresources"
- ".config/rofi"
- ".local/bin"
- ".config/qutebrowser"
- ".config/tridactyl/themes"
# Build a single color scheme and template and assign it to a variable
- base16_builder:
scheme: "{{ base16_scheme }}"
template: # This requires https://github.com/mnussbaum/base16-builder-ansible/pull/6
- i3
- xresources
- rofi
- termux
- alacritty
- shell
- fzf
- vim
- qutebrowser
- tridactyl
- dunst
register: base16_schemes
- name: Configure Alacritty
template:
src: "{{ ansible_env.HOME }}/.config/alacritty/alacritty.yml.j2"
dest: "{{ ansible_env.HOME }}/.config/alacritty/alacritty.yml"
mode: "u+rw,g=r,o=r"
# Alacritty has live config reload, so no command to execute
# However, it doesn't work with yaml includes, hence the template
- name: Set base16 theme for Xresources
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['xresources']['xresources']['base16-' + base16_scheme + '-256.Xresources'] }}"
dest: "{{ ansible_env.HOME }}/.config/Xresources/theme"
mode: "u+rw,g=r,o=r"
notify:
- xrdb-reload
- name: Download base16 theme for qutebrowser
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['qutebrowser']['themes/minimal']['base16-' + base16_scheme + '.config.py'] }}"
dest: "{{ ansible_env.HOME }}/.config/qutebrowser/theme.py"
mode: "u+rw,g=r,o=r"
notify:
- qutebrowser-reload
- name: Download base16 theme for Tridactyl
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['tridactyl']['base16-' + base16_scheme + '.config.py'] }}"
# url: "https://raw.githubusercontent.com/bezmi/base16-tridactyl/master/base16-{{ base16_scheme }}.css"
dest: "{{ ansible_env.HOME }}/.config/tridactyl/themes/theme.css"
mode: "u+rw,g=r,o=r"
when: False # Not currently used
- name: Configure i3
template:
src: "{{ ansible_env.HOME }}/.config/i3/config.j2"
dest: "{{ ansible_env.HOME }}/.config/i3/config"
mode: "u+rw,g=r,o=r"
notify:
- i3-reload
- name: Set base16 theme for rofi
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['rofi']['themes']['base16-' + base16_scheme + '.' + item] }}"
dest: "{{ ansible_env.HOME }}/.config/rofi/theme.{{ item }}"
mode: "u+rw,g=r,o=r"
with_items:
- rasi
- config
- name: Configure Dunst
template:
src: "{{ ansible_env.HOME }}/.config/dunst/dunstrc.j2"
dest: "{{ ansible_env.HOME }}/.config/dunst/dunstrc"
mode: "u+rw,g=r,o=r"
- name: Download base16 theme for fzf
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['fzf']['bash']['base16-' + base16_scheme + '.config'] }}"
dest: "{{ ansible_env.HOME }}/.local/bin/colorSchemeApplyFzf"
mode: "u+rw,g=r,o=r"
notify:
- fzf-reload
- name: Download base16 theme for Termux
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['termux']['colors']['base16-' + base16_scheme + '.properties'] }}"
dest: "{{ ansible_env.HOME }}/.termux/colors.properties"
mode: "u+rw,g=r,o=r"
# TODO Only on Termux
- name: Download base16 theme for shell
copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['shell']['script']['base16-' + base16_scheme + '.sh'] }}"
dest: "{{ ansible_env.HOME }}/.local/bin/colorSchemeApply"
mode: "u+rwx,g=rx,o=rx"
notify:
- shell-reload
when: False # Not currently used
- name: Set used base16 theme for vim
copy:
path: "{{ ansible_env.HOME }}/.config/vim/colorscheme.vim"
mode: "u+rw,g=r,o=r"
content: "colorscheme base16-{{ base16_scheme }}"
when: False # Not currently used
# TODO bar (might change bar in the future, so...)
# TODO highlight (there IS a template but the colors look different from vim and mostly the same from when there's no config)
# TODO https://github.com/makuto/auto-base16-theme ? :P

View file

@ -0,0 +1 @@
base16_scheme: solarized-dark

@ -0,0 +1 @@
Subproject commit a977bb298fb9e1c057e0b2587dbd4d047e13f5bd

View file

@ -0,0 +1,55 @@
- name: Install base shell packages (Pacman)
pacman:
name:
- base
- coreutils
- bash
- grep
- sed
- tar
- openssl # Used for machines script
when: arch
- name: Install base shell packages (Apt)
apt:
name:
- coreutils
- bash
- grep
- sed
- tar
- openssl
when: apt
- name: Install extended shell packages (Pacman)
pacman:
name:
- moreutils
- tmux
- bash-completion
- fzf
- highlight
- powerline-go-bin
when: arch
- name: Install extended shell packages (Debian)
pacman:
name:
- moreutils
- tmux
- bash-completion
- fzf
- highlight
- powerline-go
when: debian
- name: Install extended shell packages (Termux)
pacman:
name:
- moreutils
- tmux
- bash-completion
- fzf
# - highlight
# - powerline-go
when: termux

View file

@ -0,0 +1,96 @@
- name: Set variables
set_fact:
arch: "{{ ansible_distribution == 'Archlinux' }}"
termux: "{{ ansible_distribution == 'OtherLinux' and ansible_python.executable == '/data/data/com.termux/files/usr/bin/python' }}"
debian: "{{ ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' }}"
- name: Set secondary variables
set_fact:
pacman: "{{ arch }}"
apt: "{{ termux or debian }}"
sudo: "{{ not termux }}"
# TODO Install python if not done
- name: Install python-apt dependency for Termux
block:
# TODO Check if the correct version
- name: Check for DistUtilsExtra (Termux)
command: python -c 'import DistUtilsExtra'
changed_when: False
rescue:
- name: Create temporarty folder for DistUtilsExtra (Termux)
tempfile:
state: directory
suffix: python-distutils-extra
# path: /data/data/com.termux/files/usr/tmp/
register: pde_tempdir
- name: Download DistUtilsExtra (Termux)
get_url:
url: "https://launchpad.net/python-distutils-extra/trunk/{{ version }}/+download/python-distutils-extra-{{ version }}.tar.gz"
dest: "{{ pde_tempdir.path }}/python-distutils-extra.tar.gz"
- name: Extract DistUtilsExtra (Termux)
unarchive:
src: "{{ pde_tempdir.path }}/python-distutils-extra.tar.gz"
remote_src: yes
dest: "{{ pde_tempdir.path }}"
- name: Install DistUtilsExtra (Termux)
command:
cmd: python3 setup.py install
chdir: "{{ pde_tempdir.path }}/python-distutils-extra-{{ version }}"
when: termux
vars:
version: 2.39
- name: Install python-apt (Termux)
pip:
name: python-apt
when: termux
# Collecting python-apt
# Using cached python-apt-0.7.8.tar.bz2 (49 kB)
# ERROR: Command errored out with exit status 1:
# command: /data/data/com.termux/files/usr/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/data/data/com.termux/files/usr/tmp/pip-install-dsga__i7/python-apt/setup.py'"'"'; __file__='"'"'/data/data/com.termux/files/usr/tmp/pip-install-dsga__i7/python-apt/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /data/data/com.termux/files/usr/tmp/pip-pip-egg-info-ptpprl0m
# cwd: /data/data/com.termux/files/usr/tmp/pip-install-dsga__i7/python-apt/
# Complete output (5 lines):
# Traceback (most recent call last):
# File "<string>", line 1, in <module>
# File "/data/data/com.termux/files/usr/tmp/pip-install-dsga__i7/python-apt/setup.py", line 11, in <module>
# string.split(parse_makefile("python/makefile")["APT_PKG_SRC"]))
# AttributeError: module 'string' has no attribute 'split'
# ----------------------------------------
# ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
# WARNING: You are using pip version 20.2.3; however, version 20.3.3 is available.
# You should consider upgrading via the '/data/data/com.termux/files/usr/bin/python3 -m pip install --upgrade pip' command.
# Install alternative package managers
- name: Install AUR package manager
aur:
name: yay-bin
when: arch
- name: Install python pip (Pacman)
pacman:
name: python-pip
when: arch
become: "{{ sudo }}"
become_user: root
- name: Install python pip (Debian)
apt:
name: python3-pip
when: debian
become: "{{ sudo }}"
become_user: root
# Termux: pip is included with Python
- name: Install base packages
include_tasks:
file: base.yml
apply:
become: "{{ sudo }}"
become_user: root

View file

@ -24,9 +24,3 @@ fi
feh --no-fehbg --bg-fill "$filepath"
# Make i3 distribute the workspaces on all screens
monitors_json="$(xrandr --listmonitors | tail -n+2 | awk '{ print $4 }' | sed 's|.\+|"\0"|' | tr '\n' ',')"
automatrop -e '{"x11_screens":['"$monitors_json"']}' --tags i3
# TODO Make sure it goes from left to right
# Either with the "main" display or using the geometry data

1
config/ccache.conf Normal file
View file

@ -0,0 +1 @@
ccache_dir = $HOME/.cache/ccache

2
config/dunst/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
dunstrc
theme

65
config/dunst/dunstrc.j2 Normal file
View file

@ -0,0 +1,65 @@
[global]
alignment = left
always_run_script = true
browser = /usr/bin/qutebrowser
class = Dunst
dmenu = /usr/bin/dmenu -p dunst:
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
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]
close_all = ctrl+mod4+n
close = mod4+n
context = mod1+mod4+n
history = shift+mod4+n
[urgency_low]
background = "#272822"
foreground = "#F8F8F2"
frame_color = "#A6E22E"
timeout = 10
[urgency_normal]
background = "#272822"
foreground = "#F8F8F2"
frame_color = "#F4BF75"
timeout = 10
[urgency_critical]
background = "#272822"
foreground = "#F8F8F2"
frame_color = "#F92672"
timeout = 0
{{ base16_schemes['schemes'][base16_scheme]['dunst']['themes']['base16-' + base16_scheme + '.dunstrc'] }}
# TODO Not used. Not sure how it's supposed to be used :D

3
config/flake8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
# Compatibility with Black
max-line-length = 88

3
config/gdbinit Normal file
View file

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

1
config/git/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
gitk

16
config/git/config Normal file
View file

@ -0,0 +1,16 @@
[user]
name = Geoffrey “Frogeye” Preud'homme
email = geoffrey@frogeye.fr
signingkey = 0x8312C8CAC1BAC289
[core]
editor = nvim
excludesfile = ~/.config/git/gitignore
[push]
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

5
config/git/gitignore Normal file
View file

@ -0,0 +1,5 @@
*.swp
*.swo
*.ycm_extra_conf.py
tags
.mypy_cache

1
config/gtk-3.0/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bookmarks

View file

@ -0,0 +1,16 @@
[Settings]
gtk-theme-name=Greenbird
gtk-icon-theme-name=Faenza-Green
gtk-font-name=Sans 10
gtk-cursor-theme-size=0
gtk-toolbar-style=GTK_TOOLBAR_BOTH
gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
gtk-button-images=1
gtk-menu-images=1
gtk-enable-event-sounds=1
gtk-enable-input-feedback-sounds=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
gtk-xft-rgba=rgb
gtk-cursor-theme-name=Menda-Cursor

2
config/i3/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
config
theme

7
config/i3/ashuffle Executable file
View file

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

10
config/i3/autorandrdefaultmenu Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
shopt -s nullglob globstar
profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Default profile" "$@")
[[ -n $profile ]] || exit
autorandr --default "$profile"

10
config/i3/autorandrloadmenu Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
shopt -s nullglob globstar
profile=$(echo -e "common\nclone-largest\nhorizontal\nvertical\n$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Load profile" "$@")
[[ -n $profile ]] || exit
autorandr --load "$profile"

10
config/i3/autorandrremovemenu Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
shopt -s nullglob globstar
profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Remove profile" "$@")
[[ -n $profile ]] || exit
autorandr --remove "$profile"

10
config/i3/autorandrsavemenu Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
shopt -s nullglob globstar
profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Save profile" "$@")
[[ -n $profile ]] || exit
autorandr --save "$profile"

16
config/i3/aw_start Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
# TODO Make a good service out of this
cd /opt/activitywatch # Put your ActivityWatch install folder here
killall aw-server
killall aw-watcher-afk
killall aw-watcher-window
./aw-server/aw-server &
./aw-watcher-afk/aw-watcher-afk &
./aw-watcher-window/aw-watcher-window & # you can add --exclude-title here to exclude window title tracking for this session only
notify-send "ActivityWatch started" # Optional, sends a notification when ActivityWatch is started

View file

@ -1,3 +1,5 @@
#!/usr/bin/env bash
BATT="/sys/class/power_supply/BAT0"
LOW=10
CRIT=3
@ -23,10 +25,10 @@ function computeState() {
if [ "$acpiStatus" == "Discharging" ]
then
if [ "$acpiCapacity" -le $CRIT ]
if [ $acpiCapacity -le $CRIT ]
then
setState "CRIT" -u critical -i battery-caution "Battery level is critical" "$acpiCapacity %"
elif [ "$acpiCapacity" -le $LOW ]
elif [ $acpiCapacity -le $LOW ]
then
setState "LOW" -u critical -i battery-low "Battery level is low" "$acpiCapacity %"
else

406
config/i3/config.j2 Normal file
View file

@ -0,0 +1,406 @@
# vi:syntax=conf
# i3 config file (v4)
# Please see http://i3wm.org/docs/userguide.html for a complete reference!
# Set mod key (Mod1=<Alt>, Mod4=<Super>)
set $mod Mod4
# set default desktop layout (default is tiling)
# workspace_layout tabbed <stacking|tabbed>
# Configure border style <normal|1pixel|pixel xx|none|pixel>
new_window pixel 2
new_float normal
# Hide borders
hide_edge_borders both
# change borders
#bindsym $mod+u border none
#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:DejaVu Sans 8
font pango:Sans 8
# Use Mouse+$mod to drag floating windows
floating_modifier $mod
# kill focused window
bindsym $mod+z kill
bindsym button2 kill
bindsym $mod+c exec --no-startup-id rofi-pass --last-used
bindsym $mod+i exec --no-startup-id rofimoji
bindsym $mod+plus exec --no-startup-id rofi -modi ssh -show ssh
bindsym $mod+ù 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 rofi -modi run -show run
bindsym $mod+Shift+d exec --no-startup-id rofi -modi drun -show drun
# Start Applications
# bindsym $mod+Return exec urxvtc
bindsym $mod+Return exec alacritty
bindsym $mod+Shift+Return exec urxvt
bindsym $mod+p exec thunar
bindsym $mod+m exec qutebrowser --override-restore --backend=webengine
# bindsym $mod+m exec firefox
# Volume control
bindsym XF86AudioRaiseVolume exec pactl set-sink-mute @DEFAULT_SINK@ false; exec pactl set-sink-volume @DEFAULT_SINK@ +5%
bindsym XF86AudioLowerVolume exec pactl set-sink-mute @DEFAULT_SINK@ false; exec pactl set-sink-volume @DEFAULT_SINK@ -5%
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 XF86AudioPrev exec mpc prev
bindsym XF86AudioPlay exec mpc toggle
bindsym XF86AudioNext exec mpc next
bindsym $mod+F10 exec ~/.config/scripts/showKeyboardLayout
bindsym $mod+F11 exec xterm -e 'pacmixer'
bindsym $mod+F12 exec xterm -e 'pacmixer'
#Brightness control
bindsym XF86MonBrightnessDown exec xbacklight -dec 5 -time 0
bindsym XF86MonBrightnessUp exec xbacklight -inc 5 -time 0
# Screenshots
bindsym Print exec scrot -ue 'mv $f ~/Screenshots/ && optipng ~/Screenshots/$f'
bindsym $mod+Print exec scrot -e 'mv $f ~/Screenshots/ && optipng ~/Screenshots/$f'
bindsym Ctrl+Print exec sleep 1 && scrot -se 'mv $f ~/Screenshots/ && optipng ~/Screenshots/$f'
focus_follows_mouse no
mouse_warping output
# change focus
bindsym $mod+h focus left; exec ~/.config/i3/focus_windows
bindsym $mod+j focus down; exec ~/.config/i3/focus_windows
bindsym $mod+k focus up; exec ~/.config/i3/focus_windows
bindsym $mod+l focus right; exec ~/.config/i3/focus_windows
## alternatively, you can use the cursor keys:
#bindsym $mod+Left focus left
#bindsym $mod+Down focus down
#bindsym $mod+Up focus up
#bindsym $mod+Right focus right
# move focused window
bindsym $mod+Shift+h move left; exec ~/.config/i3/focus_windows
bindsym $mod+Shift+j move down; exec ~/.config/i3/focus_windows
bindsym $mod+Shift+k move up; exec ~/.config/i3/focus_windows
bindsym $mod+Shift+l move right; exec ~/.config/i3/focus_windows
## alternatively, you can use the cursor keys:
#bindsym $mod+Shift+Left move left
#bindsym $mod+Shift+Down move down
#bindsym $mod+Shift+Up move up
#bindsym $mod+Shift+Right move right
# workspace back and forth (with/without active container)
workspace_auto_back_and_forth no
bindsym $mod+b workspace back_and_forth; exec ~/.config/i3/focus_windows
bindsym $mod+Shift+b move container to workspace back_and_forth; workspace back_and_forth; exec ~/.config/i3/focus_windows
# split in horizontal orientation
bindsym $mod+g split h; exec ~/.config/i3/focus_windows
# split in vertical orientation
bindsym $mod+v split v; exec ~/.config/i3/focus_windows
# toggle fullscreen mode for the focused container
bindsym $mod+f fullscreen toggle; exec ~/.config/i3/focus_windows
# change container layout (stacked, tabbed, toggle split)
bindsym $mod+s layout stacking; exec ~/.config/i3/focus_windows
bindsym $mod+w layout tabbed; exec ~/.config/i3/focus_windows
bindsym $mod+e layout toggle split; exec ~/.config/i3/focus_windows
# toggle tiling / floating
bindsym $mod+Shift+space floating toggle; exec ~/.config/i3/focus_windows
# change focus between tiling / floating windows
bindsym $mod+space focus mode_toggle; exec ~/.config/i3/focus_windows
# focus the parent container
bindsym $mod+a focus parent; exec ~/.config/i3/focus_windows
# focus the child container
bindsym $mod+q focus child; exec ~/.config/i3/focus_windows
# Workspace names
set $WS1 1
set $WS2 2
set $WS3 3
set $WS4 4
set $WS5 5
set $WS6 6
set $WS7 7
set $WS8 8
set $WS9 9
set $WS10 10
# Workspace output
workspace "$WS1" output eDP-1-1
workspace "$WS2" output HDMI-0
workspace "$WS3" output eDP-1-1
workspace "$WS4" output HDMI-0
workspace "$WS5" output eDP-1-1
workspace "$WS6" output HDMI-0
workspace "$WS7" output eDP-1-1
workspace "$WS8" output HDMI-0
workspace "$WS9" output eDP-1-1
workspace "$WS10" output HDMI-0
# switch to workspace
bindsym $mod+1 workspace $WS1; exec ~/.config/i3/focus_windows
bindsym $mod+2 workspace $WS2; exec ~/.config/i3/focus_windows
bindsym $mod+3 workspace $WS3; exec ~/.config/i3/focus_windows
bindsym $mod+4 workspace $WS4; exec ~/.config/i3/focus_windows
bindsym $mod+5 workspace $WS5; exec ~/.config/i3/focus_windows
bindsym $mod+6 workspace $WS6; exec ~/.config/i3/focus_windows
bindsym $mod+7 workspace $WS7; exec ~/.config/i3/focus_windows
bindsym $mod+8 workspace $WS8; exec ~/.config/i3/focus_windows
bindsym $mod+9 workspace $WS9; exec ~/.config/i3/focus_windows
bindsym $mod+0 workspace $WS10; exec ~/.config/i3/focus_windows
#navigate workspaces next / previous
bindsym $mod+Ctrl+h workspace prev_on_output; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+l workspace next_on_output; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+j workspace prev; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+k workspace next; exec ~/.config/i3/focus_windows
##navigate workspaces next / previous (arrow keys)
#bindsym $mod+Ctrl+Left workspace prev_on_output
#bindsym $mod+Ctrl+Right workspace next_on_output
#bindsym $mod+Ctrl+Down workspace prev
#bindsym $mod+Ctrl+Up workspace next
# Move to workspace next / previous with focused container
bindsym $mod+Ctrl+Shift+h move container to workspace prev_on_output; workspace prev_on_output; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+Shift+l move container to workspace next_on_output; workspace next_on_output; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+Shift+j move container to workspace prev; workspace prev; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+Shift+k move container to workspace next; workspace next; exec ~/.config/i3/focus_windows
## Move to workspace next / previous with focused container (arrow keys)
#bindsym $mod+Ctrl+Shift+Left move container to workspace prev_on_output; workspace prev_on_output
#bindsym $mod+Ctrl+Shift+Right move container to workspace next_on_output; workspace next_on_output
#bindsym $mod+Ctrl+Shift+Down move container to workspace prev; workspace prev
#bindsym $mod+Ctrl+Shift+Up move container to workspace next; workspace next
# move focused container to workspace
bindsym $mod+ctrl+1 move container to workspace $ws1; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+2 move container to workspace $ws2; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+3 move container to workspace $ws3; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+4 move container to workspace $ws4; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+5 move container to workspace $ws5; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+6 move container to workspace $ws6; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+7 move container to workspace $ws7; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+8 move container to workspace $ws8; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+9 move container to workspace $ws9; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+0 move container to workspace $ws10; exec ~/.config/i3/focus_windows
# move to workspace with focused container
bindsym $mod+shift+1 move container to workspace $ws1; workspace $ws1; exec ~/.config/i3/focus_windows
bindsym $mod+shift+2 move container to workspace $ws2; workspace $ws2; exec ~/.config/i3/focus_windows
bindsym $mod+shift+3 move container to workspace $ws3; workspace $ws3; exec ~/.config/i3/focus_windows
bindsym $mod+shift+4 move container to workspace $ws4; workspace $ws4; exec ~/.config/i3/focus_windows
bindsym $mod+shift+5 move container to workspace $ws5; workspace $ws5; exec ~/.config/i3/focus_windows
bindsym $mod+shift+6 move container to workspace $ws6; workspace $ws6; exec ~/.config/i3/focus_windows
bindsym $mod+shift+7 move container to workspace $ws7; workspace $ws7; exec ~/.config/i3/focus_windows
bindsym $mod+shift+8 move container to workspace $ws8; workspace $ws8; exec ~/.config/i3/focus_windows
bindsym $mod+shift+9 move container to workspace $ws9; workspace $ws9; exec ~/.config/i3/focus_windows
bindsym $mod+shift+0 move container to workspace $ws10; workspace $ws10; exec ~/.config/i3/focus_windows
## move workspaces to screen
#bindsym $mod+ctrl+shift+r move workspace to output right
#bindsym $mod+ctrl+shift+l move workspace to output left
#bindsym $mod+Ctrl+Shift+u move workspace to output above
#bindsym $mod+Ctrl+Shift+d move workspace to output below
# move workspaces to screen (arrow keys)
bindsym $mod+ctrl+shift+Right move workspace to output right; exec ~/.config/i3/focus_windows
bindsym $mod+ctrl+shift+Left move workspace to output left; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+Shift+Up move workspace to output above; exec ~/.config/i3/focus_windows
bindsym $mod+Ctrl+Shift+Down move workspace to output below; exec ~/.config/i3/focus_windows
# Default layout = tabs, since I mostly exclusively use them
workspace_layout tabbed
# Open specific applications in floating mode
for_window [title="pacmixer"] floating enable border pixel 2
for_window [class="Firefox"] layout tabbed # Doesn't seem to work anymore
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
# focus urgent window
#bindsym $mod+x [urgent=latest] focus
# reload the configuration file
bindsym $mod+Shift+c reload
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
bindsym $mod+Shift+r restart
# exit i3 (logs you out of your X session)
bindsym $mod+Shift+e exit
# Set shut down, restart and locking features
set $mode_kblock Keyboard lock
mode "$mode_kblock" {
bindsym $mod+Shift+Escape mode "$mode_kblock"
}
bindsym $mod+Shift+Escape mode "$mode_kblock"
# Set shut down, restart and locking features
set $locker $HOME/.config/i3/lock
set $mode_system [L] Vérouillage [E] Déconnexion [S] Veille [H] Hibernation [R] Redémarrage [P] Extinction
mode "$mode_system" {
bindsym l exec --no-startup-id $locker, mode "default"
bindsym e exit, mode "default"
bindsym s exec --no-startup-id $locker & systemctl suspend, mode "default"
bindsym h exec --no-startup-id $locker & systemctl hibernate, mode "default"
bindsym r exec --no-startup-id systemctl reboot, mode "default"
bindsym p exec --no-startup-id systemctl poweroff -i, mode "default"
# back to normal: Enter or Escape
bindsym Return mode "default"
bindsym Escape mode "default"
}
bindsym $mod+Escape mode "$mode_system"
# resize window (you can also use the mouse for that)
mode "Resize" {
# These bindings trigger as soon as you enter the resize mode
# Pressing left will shrink the windows width.
# Pressing right will grow the windows width.
# Pressing up will shrink the windows height.
# Pressing down will grow the windows height.
bindsym h resize shrink width 10 px or 10 ppt; exec ~/.config/i3/focus_windows
bindsym j resize grow height 10 px or 10 ppt; exec ~/.config/i3/focus_windows
bindsym k resize shrink height 10 px or 10 ppt; exec ~/.config/i3/focus_windows
bindsym l resize grow width 10 px or 10 ppt; exec ~/.config/i3/focus_windows
## same bindings, but for the arrow keys
#bindsym Left resize shrink width 10 px or 10 ppt
#bindsym Down resize grow height 10 px or 10 ppt
#bindsym Up resize shrink height 10 px or 10 ppt
#bindsym Right resize grow width 10 px or 10 ppt
# back to normal: Enter or Escape
bindsym Return mode "default"
bindsym Escape mode "default"
}
bindsym $mod+r mode "Resize"
set $mode_pres_main "Presentation (main display)"
mode $mode_pres_main {
bindsym b workspace $WS3, workspace $WS4, mode $mode_pres_sec
# back to normal: Enter or Escape
bindsym q mode "default"
# bindsym Escape mode "default"
bindsym Return mode "default"
}
set $mode_pres_sec "Presentation (secondary display)"
mode $mode_pres_sec {
bindsym b workspace $WS2, workspace $WS1, mode $mode_pres_main
# back to normal: Enter or Escape
bindsym q mode "default"
# bindsym Escape mode "default"
bindsym Return mode "default"
}
bindsym $mod+Shift+p mode $mode_pres_main
set $mode_screen Screen setup [A] Auto [L] Load [S] Save [R] Remove [D] Default
bindsym $mod+t mode "$mode_screen"
mode "$mode_screen" {
bindsym a exec autorandr --change --force, mode "default"
bindsym l exec ~/.config/i3/autorandrloadmenu, mode "default"
bindsym s exec ~/.config/i3/autorandrsavemenu, mode "default"
bindsym r exec ~/.config/i3/autorandrremovemenu, mode "default"
bindsym d exec ~/.config/i3/autorandrdefaultmenu, mode "default"
# back to normal: Enter or Escape
bindsym Return mode "default"
bindsym Escape mode "default"
}
# Screen temperature ("redness") setting
bindsym $mod+y mode "$mode_temp"
set $mode_temp Temperature [R] Red [D] Dust storm [C] Campfire [O] Normal [A] All nighter [B] Blue
mode "$mode_temp" {
bindsym r exec sct 1000
bindsym d exec sct 2000
bindsym c exec sct 4500
bindsym o exec sct
bindsym a exec sct 8000
bindsym b exec sct 10000
# back to normal: Enter or Escape
bindsym Return mode "default"
bindsym Escape mode "default"
}
# Inactivity settings
exec --no-startup-id xautolock -time 10 -locker 'xset dpms force standby' -killtime 1 -killer '$locker'
bindsym $mod+F1 exec --no-startup-id sh -c "sleep .25 && xset dpms force off"
bindsym $mod+F4 exec --no-startup-id xautolock -disable
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 urxvtd -q -f # urxvt daemon
exec --no-startup-id numlockx on # Activate Num lock
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 mpd # Music Player Daemon (handled by systemd)
# exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill
exec --no-startup-id autorandr --change --force # Screen configuration and everything that depends on it
exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification
# exec --no-startup-id ~/.config/i3/aw_start # Activity tracker
{{ base16_schemes['schemes'][base16_scheme]['i3']['colors']['base16-' + base16_scheme + '.config'] }}
set $ignore #ff00ff
# Basic color configuration using the Base16 variables for windows and borders.
# Property Name Border BG Text Indicator Child Border
client.focused $base0B $base0B $base00 $base00 $base0B
client.focused_inactive $base02 $base02 $base05 $base02 $base02
client.unfocused $base05 $base04 $base00 $base04 $base00
client.urgent $base0F $base08 $base00 $base08 $base0F
client.placeholder $ignore $base00 $base05 $ignore $base00
client.background $base07
# I set the color of the active tab as the the background color
# of the terminal so they merge together. This is the opposite
# of what I used before: unfocused color was the terminal
# background color. Either I get used to it, or I should revert.
# bar {
# i3bar_command ~/.config/lemonbar/bar.py
# }

2
config/i3/dmenu_cmd Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
dmenu -fn 'DejaVu Sans Mono-10' -nb '#48483e' -nf '#f1ebeb' -sb '#8fc029' -sf '#272822' -i -l 8 -f "$@"

15
config/i3/dmenu_run Executable file
View file

@ -0,0 +1,15 @@
#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
cache=$cachedir/dmenu_run
else
cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
(
IFS=:
if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache" | $HOME/.config/i3/dmenu_cmd -p 'Run' "$@"
else
$HOME/.config/i3/dmenu_cmd -p 'Run' "$@" < "$cache"
fi
) | ${SHELL:-"/bin/sh"} &

12
config/i3/focus_windows Executable file
View file

@ -0,0 +1,12 @@
#!/bin/bash
XDT=/usr/bin/xdotool
WINDOW=`$XDT getwindowfocus`
# this brings in variables WIDTH and HEIGHT
eval `xdotool getwindowgeometry --shell $WINDOW`
TX=`expr $WIDTH / 2`
TY=`expr $HEIGHT / 2`
$XDT mousemove -window $WINDOW $TX $TY

23
config/i3/lock Executable file
View file

@ -0,0 +1,23 @@
#!/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
if [ -d ~/.cache/lockpatterns ]
then
pattern=$(find ~/.cache/lockpatterns/ | sort -R | head -1)
else
pattern=$HOME/.config/i3/lock.png
fi
revert() {
xset dpms 0 0 0
}
trap revert SIGHUP SIGINT SIGTERM
xset dpms 5 5 5
i3lock --nofork --color 648901 --image=$pattern --tiling --ignore-empty-password
revert
fi

BIN
config/i3/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

1
config/i3/lock.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" height="50" width="50"><path fill="#82a401" d="M0 50h50V0H0z"/><path d="M0 0l50 50H25L0 25zm50 0v25L25 0z" fill="#466c01"/></svg>

After

Width:  |  Height:  |  Size: 189 B

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

BIN
config/i3/pitch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

66
config/i3/screentime Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""
Logs what window is in focus
"""
import csv
import datetime
import os
import typing
import i3ipc
class ScreenTime:
FIELDS = ["date", "type", "class", "role", "instance", "title"]
def write(self, line: typing.Dict) -> None:
now = datetime.datetime.now()
line["date"] = now.timestamp()
print("WROTE", line)
with open(self.csv_path, 'a') as typedfd:
writer = csv.DictWriter(typedfd, fieldnames=self.FIELDS)
writer.writerow(line)
def on_window_event(self, _: i3ipc.connection.Connection,
e: i3ipc.events.WindowEvent) -> None:
focused = self.i3.get_tree().find_focused()
self.write({
"type": "window_" + e.change,
"class": focused.window_class,
"role": focused.window_role,
"title": focused.window_title,
"instance": focused.window_instance,
})
def on_mode_event(self, _: i3ipc.connection.Connection,
e: i3ipc.events.ModeEvent) -> None:
self.write({
"type": "mode",
"title": e.change
})
def __init__(self) -> None:
self.i3 = i3ipc.Connection()
self.i3.on(i3ipc.Event.WINDOW, self.on_window_event)
self.i3.on(i3ipc.Event.MODE, self.on_mode_event)
self.csv_path = os.path.join(
os.path.expanduser(
os.getenv('XDG_CACHE_PATH', '~/.cache/')),
'screentime.csv')
if not os.path.isfile(self.csv_path):
with open(self.csv_path, 'w') as typedfd:
writer = csv.DictWriter(typedfd, fieldnames=self.FIELDS)
writer.writeheader()
self.write({"type": "start"})
def main(self) -> None:
self.i3.main()
if __name__ == '__main__':
ST = ScreenTime()
ST.main()

61
config/iftoprc Normal file
View file

@ -0,0 +1,61 @@
# interface: if
# Sets the network interface to if.
dns-resolution: yes
# Controls reverse lookup of IP addresses.
port-resolution: no
# Controls conversion of port numbers to service names.
# filter-code: bpf
# Sets the filter code to bpf.
show-bars: yes
# Controls display of bar graphs.
promiscuous: no
# Puts the interface into promiscuous mode.
port-display: on
# Controls display of port numbers.
link-local: yes
# Determines displaying of link-local IPv6 addresses.
hide-source: no
# Hides source host names.
hide-destination: no
# Hides destination host names.
use-bytes: yes
# Use bytes for bandwidth display, rather than bits.
sort: 10s
# Sets which column is used to sort the display.
line-display: two-line
# Controls the appearance of each item in the display.
show-totals: yes
# Shows cumulative total for each item.
log-scale: yes
# Use a logarithmic scale for bar graphs.
# max-bandwidth: bw
# Fixes the maximum for the bar graph scale to bw, e.g. "10M".
# Note that the value has to always be in bits, regardless if the
# option to display in bytes has been chosen.
# net-filter: net/mask
# Defines an IP network boundary for determining packet direc
# tion.
# net-filter6: net6/mask6
# Defines an IPv6 network boundary for determining packet direc
# tion.
# screen-filter: regexp
# Sets a regular expression to filter screen output.

View file

@ -25,8 +25,6 @@ set visible-stats off
$if mode=vi
# these are for vi-command mode
set keymap vi-command
"k": history-search-backward
"j": history-search-forward
"\e[A": history-search-backward
"\e[B": history-search-forward
Control-l: clear-screen

View file

@ -1,11 +1,11 @@
[calendars]
[[calendars]]
path = ~/.cache/vdirsyncer/calendars/*
path = ~/.vdirsyncer/calendars/*
type = discover
# [[birthdays]]
# type=birthdays
# path = ~/.cache/vdirsyncer/contacts/contacts/
# path = ~/.vdirsyncer/contacts/contacts/
# color = light magenta
[locale]

View file

@ -3,7 +3,7 @@
[addressbooks]
[[contacts]]
path = ~/.cache/vdirsyncer/contacts/contacts/
path = ~/.vdirsyncer/contacts/contacts/
[general]
debug = no

View file

@ -1,51 +1,42 @@
#!/usr/bin/env python3
from frobar.providers import *
from providers import *
# TODO If multiple screen, expand the sections and share them
# TODO Graceful exit
def run():
if __name__ == "__main__":
Bar.init()
Updater.init()
WORKSPACE_THEME = 8
FOCUS_THEME = 2
URGENT_THEME = 0
CUSTOM_SUFFIXES = "▲■"
WORKSPACE_THEME = 0
FOCUS_THEME = 3
URGENT_THEME = 1
CUSTOM_SUFFIXES = '▲■'
customNames = dict()
for i in range(len(CUSTOM_SUFFIXES)):
short = str(i + 1)
full = short + " " + CUSTOM_SUFFIXES[i]
short = str(i+1)
full = short + ' ' + CUSTOM_SUFFIXES[i]
customNames[short] = full
Bar.addSectionAll(
I3WorkspacesProvider(
theme=WORKSPACE_THEME,
themeFocus=FOCUS_THEME,
themeUrgent=URGENT_THEME,
themeMode=URGENT_THEME,
customNames=customNames,
),
BarGroupType.LEFT,
)
Bar.addSectionAll(I3WorkspacesProvider(theme=WORKSPACE_THEME, themeFocus=FOCUS_THEME, themeUrgent=URGENT_THEME, themeMode=URGENT_THEME, customNames=customNames), BarGroupType.LEFT)
# TODO Middle
Bar.addSectionAll(MpdProvider(theme=9), BarGroupType.LEFT)
Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT)
# Bar.addSectionAll(I3WindowTitleProvider(), BarGroupType.LEFT)
# TODO Computer modes
SYSTEM_THEME = 3
DANGER_THEME = 1
CRITICAL_THEME = 0
SYSTEM_THEME = 2
DANGER_THEME = FOCUS_THEME
CRITICAL_THEME = URGENT_THEME
Bar.addSectionAll(CpuProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(RamProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(TemperatureProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(BatteryProvider(), BarGroupType.RIGHT)
# Peripherals
PERIPHERAL_THEME = 6
NETWORK_THEME = 5
PERIPHERAL_THEME = 5
NETWORK_THEME = 4
# TODO Disk space provider
# TODO Screen (connected, autorandr configuration, bbswitch) provider
Bar.addSectionAll(PulseaudioProvider(theme=PERIPHERAL_THEME), BarGroupType.RIGHT)
@ -53,12 +44,13 @@ def run():
Bar.addSectionAll(NetworkProvider(theme=NETWORK_THEME), BarGroupType.RIGHT)
# Personal
PERSONAL_THEME = 7
# Bar.addSectionAll(KeystoreProvider(theme=PERSONAL_THEME), BarGroupType.RIGHT)
PERSONAL_THEME = 0
Bar.addSectionAll(KeystoreProvider(theme=PERSONAL_THEME), BarGroupType.RIGHT)
# Bar.addSectionAll(NotmuchUnreadProvider(dir='~/.mail/', theme=PERSONAL_THEME), BarGroupType.RIGHT)
Bar.addSectionAll(TaskWarriorProvider(theme=PERSONAL_THEME), BarGroupType.RIGHT)
# Bar.addSectionAll(TodoProvider(dir='~/.vdirsyncer/currentCalendars/', theme=PERSONAL_THEME), BarGroupType.RIGHT)
TIME_THEME = 4
TIME_THEME = 6
Bar.addSectionAll(TimeProvider(theme=TIME_THEME), BarGroupType.RIGHT)
# Bar.run()

View file

@ -1,19 +1,17 @@
#!/usr/bin/env python3
import enum
import logging
import threading
import time
import i3ipc
import os
import signal
import subprocess
import threading
import time
import logging
import coloredlogs
import i3ipc
import updaters
from frobar.notbusy import notBusy
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger()
@ -56,7 +54,7 @@ class Bar:
"""
# Constants
FONTS = ["DejaVuSansM Nerd Font"]
FONTS = ["DejaVu Sans Mono for Powerline", "Font Awesome"]
FONTSIZE = 10
@staticmethod
@ -64,13 +62,12 @@ class Bar:
Bar.running = True
Section.init()
cmd = ["lemonbar", "-b", "-a", "64"]
cmd = ['lemonbar', '-b', '-a', '64']
for font in Bar.FONTS:
cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)]
Bar.process = subprocess.Popen(
cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
Bar.stdoutThread = BarStdoutThread()
Bar.process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
Bar.stdoutThread = BarStdoutThread()
Bar.stdoutThread.start()
# Debug
@ -95,7 +92,7 @@ class Bar:
print(88)
try:
i3.on("ipc_shutdown", doStop)
i3.on('ipc_shutdown', doStop)
i3.main()
except BaseException:
print(93)
@ -117,7 +114,7 @@ class Bar:
if function in Bar.actionsF2H.keys():
return Bar.actionsF2H[function]
handle = "{:x}".format(Bar.nextHandle).encode()
handle = '{:x}'.format(Bar.nextHandle).encode()
Bar.nextHandle += 1
Bar.actionsF2H[function] = handle
@ -170,6 +167,7 @@ class Bar:
@staticmethod
def updateAll():
if Bar.running:
Bar.string = ""
for bar in Bar.everyone:
@ -179,7 +177,7 @@ class Bar:
Bar.string += BarGroup.color(*Section.EMPTY)
# print(Bar.string)
Bar.process.stdin.write(bytes(Bar.string + "\n", "utf-8"))
Bar.process.stdin.write(bytes(Bar.string + '\n', 'utf-8'))
Bar.process.stdin.flush()
@ -198,7 +196,7 @@ class BarGroup:
self.parent = parent
self.sections = list()
self.string = ""
self.string = ''
self.parts = []
#: One of the sections that had their theme or visibility changed
@ -222,11 +220,11 @@ class BarGroup:
@staticmethod
def fgColor(color):
return "%{F" + (color or "-") + "}"
return "%{F" + (color or '-') + "}"
@staticmethod
def bgColor(color):
return "%{B" + (color or "-") + "}"
return "%{B" + (color or '-') + "}"
@staticmethod
def color(fg, bg):
@ -245,9 +243,8 @@ class BarGroup:
oSec = secs[s + 1] if s < lenS - 1 else None
else:
oSec = secs[s - 1] if s > 0 else None
oTheme = (
Section.THEMES[oSec.theme] if oSec is not None else Section.EMPTY
)
oTheme = Section.THEMES[oSec.theme] \
if oSec is not None else Section.EMPTY
if self.groupType == BarGroupType.LEFT:
if s == 0:
@ -266,6 +263,7 @@ class BarGroup:
parts.append(BarGroup.color(*theme))
parts.append(sec)
# TODO OPTI Concatenate successive strings
self.parts = parts
@ -296,7 +294,7 @@ class SectionThread(threading.Thread):
def run(self):
while Section.somethingChanged.wait():
notBusy.wait()
updaters.notBusy.wait()
Section.updateAll()
animTime = self.ANIMATION_START
frameTime = time.perf_counter()
@ -312,27 +310,16 @@ class SectionThread(threading.Thread):
class Section:
# TODO Update all of that to base16
COLORS = [
"#092c0e",
"#143718",
"#5a7058",
"#677d64",
"#89947f",
"#99a08d",
"#fae2e3",
"#fff0f1",
"#e0332e",
"#cf4b15",
"#bb8801",
"#8d9800",
"#1fa198",
"#008dd1",
"#5c73c4",
"#d43982",
]
FGCOLOR = "#fff0f1"
BGCOLOR = "#092c0e"
# COLORS = ['#272822', '#383830', '#49483e', '#75715e', '#a59f85', '#f8f8f2',
# '#f5f4f1', '#f9f8f5', '#f92672', '#fd971f', '#f4bf75', '#a6e22e',
# '#a1efe4', '#66d9ef', '#ae81ff', '#cc6633']
COLORS = ['#181818', '#AB4642', '#A1B56C', '#F7CA88', '#7CAFC2', '#BA8BAF',
'#86C1B9', '#D8D8D8', '#585858', '#AB4642', '#A1B56C', '#F7CA88',
'#7CAFC2', '#BA8BAF', '#86C1B9', '#F8F8F8']
FGCOLOR = '#F8F8F2'
BGCOLOR = '#272822'
THEMES = list()
EMPTY = (FGCOLOR, BGCOLOR)
@ -350,8 +337,6 @@ class Section:
def init():
for t in range(8, 16):
Section.THEMES.append((Section.COLORS[0], Section.COLORS[t]))
Section.THEMES.append((Section.COLORS[0], Section.COLORS[3]))
Section.THEMES.append((Section.COLORS[0], Section.COLORS[6]))
Section.updateThread.start()
@ -362,18 +347,17 @@ class Section:
if theme is None:
theme = Section.lastChosenTheme
Section.lastChosenTheme = (Section.lastChosenTheme + 1) % len(
Section.THEMES
)
Section.lastChosenTheme = (Section.lastChosenTheme + 1) \
% len(Section.THEMES)
self.theme = theme
#: Displayed text
self.curText = ""
self.curText = ''
#: Displayed text size
self.curSize = 0
#: Destination text
self.dstText = Text(" ", Text(), " ")
self.dstText = Text(' ', Text(), ' ')
#: Destination size
self.dstSize = 0
@ -383,16 +367,13 @@ class Section:
self.icon = self.ICON
self.persistent = self.PERSISTENT
def __str__(self):
try:
return "<{}><{}>{:01d}{}{:02d}/{:02d}".format(
self.curText,
self.dstText,
self.theme,
"+" if self.visible else "-",
self.curSize,
self.dstSize,
)
return "<{}><{}>{:01d}{}{:02d}/{:02d}" \
.format(self.curText, self.dstText,
self.theme, "+" if self.visible else "-",
self.curSize, self.dstSize)
except:
return super().__str__()
@ -418,15 +399,9 @@ class Section:
elif isinstance(text, Text) and not len(text.elements):
text = None
self.dstText[0] = (
None
if (text is None and not self.persistent)
else ((" " + self.icon + " ") if self.icon else " ")
)
self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ')
self.dstText[1] = text
self.dstText[2] = (
" " if self.dstText[1] is not None and len(self.dstText[1]) else None
)
self.dstText[2] = ' ' if self.dstText[1] is not None and len(self.dstText[1]) else None
self.dstSize = len(self.dstText)
self.dstText.setSection(self)
@ -506,7 +481,7 @@ class Section:
elif p < 0:
return ramp[0]
else:
return ramp[round(p * (len(ramp) - 1))]
return ramp[round(p * (len(ramp)-1))]
class StatefulSection(Section):
@ -517,11 +492,10 @@ class StatefulSection(Section):
def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs)
self.state = self.DEFAULT_STATE
if hasattr(self, "onChangeState"):
if hasattr(self, 'onChangeState'):
self.onChangeState(self.state)
self.setDecorators(
clickLeft=self.incrementState, clickRight=self.decrementState
)
self.setDecorators(clickLeft=self.incrementState,
clickRight=self.decrementState)
def incrementState(self):
newState = min(self.state + 1, self.NUMBER_STATES - 1)
@ -535,17 +509,16 @@ class StatefulSection(Section):
assert isinstance(state, int)
assert state < self.NUMBER_STATES
self.state = state
if hasattr(self, "onChangeState"):
if hasattr(self, 'onChangeState'):
self.onChangeState(state)
self.refreshData()
class ColorCountsSection(StatefulSection):
# TODO FEAT Blend colors when not expanded
# TODO FEAT Blend colors with importance of count
# TODO FEAT Allow icons instead of counts
NUMBER_STATES = 3
COLORABLE_ICON = "?"
COLORABLE_ICON = '?'
def __init__(self, theme=None):
StatefulSection.__init__(self, theme=theme)
@ -565,12 +538,12 @@ class ColorCountsSection(StatefulSection):
# Icon + Total
elif self.state == 1 and len(counts) > 1:
total = sum([count for count, color in counts])
return Text(self.COLORABLE_ICON, " ", total)
return Text(self.COLORABLE_ICON, ' ', total)
# Icon + Counts
else:
text = Text(self.COLORABLE_ICON)
for count, color in counts:
text.append(" ", Text(count, fg=color))
text.append(' ', Text(count, fg=color))
return text
@ -613,12 +586,12 @@ class Text:
if self.prefix is not None and self.suffix is not None:
return
self.prefix = ""
self.suffix = ""
self.prefix = ''
self.suffix = ''
def nest(prefix, suffix):
self.prefix = self.prefix + "%{" + prefix + "}"
self.suffix = "%{" + suffix + "}" + self.suffix
self.prefix = self.prefix + '%{' + prefix + '}'
self.suffix = '%{' + suffix + '}' + self.suffix
def getColor(val):
# TODO Allow themes
@ -627,27 +600,27 @@ class Text:
def button(number, function):
handle = Bar.getFunctionHandle(function)
nest("A" + number + ":" + handle.decode() + ":", "A" + number)
nest('A' + number + ':' + handle.decode() + ':', 'A' + number)
for key, val in self.decorators.items():
if val is None:
continue
if key == "fg":
if key == 'fg':
reset = self.section.THEMES[self.section.theme][0]
nest("F" + getColor(val), "F" + reset)
elif key == "bg":
nest('F' + getColor(val), 'F' + reset)
elif key == 'bg':
reset = self.section.THEMES[self.section.theme][1]
nest("B" + getColor(val), "B" + reset)
nest('B' + getColor(val), 'B' + reset)
elif key == "clickLeft":
button("1", val)
button('1', val)
elif key == "clickMiddle":
button("2", val)
button('2', val)
elif key == "clickRight":
button("3", val)
button('3', val)
elif key == "scrollUp":
button("4", val)
button('4', val)
elif key == "scrollDown":
button("5", val)
button('5', val)
else:
log.warn("Unkown decorator: {}".format(key))
@ -679,7 +652,7 @@ class Text:
curString += self.suffix
if pad and remSize > 0:
curString += " " * remSize
curString += ' ' * remSize
curSize += remSize
if size is not None:
@ -715,6 +688,7 @@ class Text:
curSize += len(str(element))
return curSize
def __getitem__(self, index):
return self.elements[index]

13
config/lemonbar/launch.sh Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
ex="$DIR/bar.py"
# Terminate already running bar instances
ps x | grep "python3 $ex" | grep -v grep | awk '{print $1}' | while read p; do kill $p; done
killall -q lemonbar
$ex

161
config/lemonbar/oldbar.py Executable file
View file

@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Debugging script
"""
import i3ipc
import os
import psutil
# import alsaaudio
from time import time
import subprocess
i3 = i3ipc.Connection()
lemonbar = subprocess.Popen(['lemonbar', '-b'], stdin=subprocess.PIPE)
# Utils
def upChart(p):
block = ' ▁▂▃▄▅▆▇█'
return block[round(p * (len(block)-1))]
def humanSizeOf(num, suffix='B'): # TODO Credit
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
if abs(num) < 1024.0:
return "%3.0f%2s%s" % (num, unit, suffix)
num /= 1024.0
return "%.0f%2s%s" % (num, 'Yi', suffix)
# Values
mode = ''
container = i3.get_tree().find_focused()
workspaces = i3.get_workspaces()
outputs = i3.get_outputs()
username = os.environ['USER']
hostname = os.environ['HOSTNAME']
if '-' in hostname:
hostname = hostname.split('-')[-1]
oldNetIO = dict()
oldTime = time()
def update():
activeOutputs = sorted(sorted(list(filter(lambda o: o.active, outputs)), key=lambda o: o.rect.y), key=lambda o: o.rect.x)
z = ''
for aOutput in range(len(activeOutputs)):
output = activeOutputs[aOutput]
# Mode || Workspaces
t = []
if (mode != ''):
t.append(mode)
else:
t.append(' '.join([(w.name.upper() if w.focused else w.name) for w in workspaces if w.output == output.name]))
# Windows Title
#if container:
# t.append(container.name)
# CPU
t.append('C' + ''.join([upChart(p/100) for p in psutil.cpu_percent(percpu=True)]))
# Memory
t.append('M' + str(round(psutil.virtual_memory().percent)) + '% ' +
'S' + str(round(psutil.swap_memory().percent)) + '%')
# Disks
d = []
for disk in psutil.disk_partitions():
e = ''
if disk.device.startswith('/dev/sd'):
e += 'S' + disk.device[-2:].upper()
elif disk.device.startswith('/dev/mmcblk'):
e += 'M' + disk.device[-3] + disk.device[-1]
else:
e += '?'
e += ' '
e += str(round(psutil.disk_usage(disk.mountpoint).percent)) + '%'
d.append(e)
t.append(' '.join(d))
# Network
netStats = psutil.net_if_stats()
netIO = psutil.net_io_counters(pernic=True)
net = []
for iface in filter(lambda i: i != 'lo' and netStats[i].isup, netStats.keys()):
s = ''
if iface.startswith('eth'):
s += 'E'
elif iface.startswith('wlan'):
s += 'W'
else:
s += '?'
s += ' '
now = time()
global oldNetIO, oldTime
sent = ((oldNetIO[iface].bytes_sent if iface in oldNetIO else 0) - (netIO[iface].bytes_sent if iface in netIO else 0)) / (oldTime - now)
recv = ((oldNetIO[iface].bytes_recv if iface in oldNetIO else 0) - (netIO[iface].bytes_recv if iface in netIO else 0)) / (oldTime - now)
s += '' + humanSizeOf(abs(recv), 'B/s') + '' + humanSizeOf(abs(sent), 'B/s')
oldNetIO = netIO
oldTime = now
net.append(s)
t.append(' '.join(net))
# Battery
if os.path.isdir('/sys/class/power_supply/BAT0'):
with open('/sys/class/power_supply/BAT0/charge_now') as f:
charge_now = int(f.read())
with open('/sys/class/power_supply/BAT0/charge_full_design') as f:
charge_full = int(f.read())
t.append('B' + str(round(100*charge_now/charge_full)) + '%')
# Volume
# t.append('V ' + str(alsaaudio.Mixer('Master').getvolume()[0]) + '%')
t.append(username + '@' + hostname)
# print(' - '.join(t))
# t = [output.name]
z += ' - '.join(t) + '%{S' + str(aOutput + 1) + '}'
#lemonbar.stdin.write(bytes(' - '.join(t), 'utf-8'))
#lemonbar.stdin.write(bytes('%{S' + str(aOutput + 1) + '}', 'utf-8'))
lemonbar.stdin.write(bytes(z+'\n', 'utf-8'))
lemonbar.stdin.flush()
# Event listeners
def on_mode(i3, e):
global mode
if (e.change == 'default'):
mode = ''
else :
mode = e.change
update()
i3.on("mode", on_mode)
#def on_window_focus(i3, e):
# global container
# container = e.container
# update()
#
#i3.on("window::focus", on_window_focus)
def on_workspace_focus(i3, e):
global workspaces
workspaces = i3.get_workspaces()
update()
i3.on("workspace::focus", on_workspace_focus)
# Starting
update()
i3.main()

View file

@ -13,40 +13,25 @@ import i3ipc
import difflib
# Constants
FONT = "DejaVuSansMono Nerd Font Mono"
FONT = "DejaVu Sans Mono for Powerline"
# TODO Update to be in sync with base16
thm = [
"#002b36",
"#dc322f",
"#859900",
"#b58900",
"#268bd2",
"#6c71c4",
"#2aa198",
"#93a1a1",
"#657b83",
"#dc322f",
"#859900",
"#b58900",
"#268bd2",
"#6c71c4",
"#2aa198",
"#fdf6e3",
]
fg = "#93a1a1"
bg = "#002b36"
thm = ['#002b36', '#dc322f', '#859900', '#b58900', '#268bd2', '#6c71c4',
'#2aa198', '#93a1a1', '#657b83', '#dc322f', '#859900', '#b58900',
'#268bd2', '#6c71c4', '#2aa198', '#fdf6e3']
fg = '#93a1a1'
bg = '#002b36'
THEMES = {
"CENTER": (fg, bg),
"DEFAULT": (thm[0], thm[8]),
"1": (thm[0], thm[9]),
"2": (thm[0], thm[10]),
"3": (thm[0], thm[11]),
"4": (thm[0], thm[12]),
"5": (thm[0], thm[13]),
"6": (thm[0], thm[14]),
"7": (thm[0], thm[15]),
'CENTER': (fg, bg),
'DEFAULT': (thm[0], thm[8]),
'1': (thm[0], thm[9]),
'2': (thm[0], thm[10]),
'3': (thm[0], thm[11]),
'4': (thm[0], thm[12]),
'5': (thm[0], thm[13]),
'6': (thm[0], thm[14]),
'7': (thm[0], thm[15]),
}
# Utils
@ -64,7 +49,7 @@ def fitText(text, size):
diff = size - t
return text + " " * diff
else:
return ""
return ''
def fgColor(theme):
@ -78,20 +63,20 @@ def bgColor(theme):
class Section:
def __init__(self, theme="DEFAULT"):
self.text = ""
def __init__(self, theme='DEFAULT'):
self.text = ''
self.size = 0
self.toSize = 0
self.theme = theme
self.visible = False
self.name = ""
self.name = ''
def update(self, text):
if text == "":
if text == '':
self.toSize = 0
else:
if len(text) < len(self.text):
self.text = text + self.text[len(text) :]
self.text = text + self.text[len(text):]
else:
self.text = text
self.toSize = len(text) + 3
@ -108,39 +93,39 @@ class Section:
self.visible = self.size
return self.toSize == self.size
def draw(self, left=True, nextTheme="DEFAULT"):
s = ""
def draw(self, left=True, nextTheme='DEFAULT'):
s = ''
if self.visible:
if not left:
if self.theme == nextTheme:
s += ""
s += ''
else:
s += "%{F" + bgColor(self.theme) + "}"
s += "%{B" + bgColor(nextTheme) + "}"
s += ""
s += "%{F" + fgColor(self.theme) + "}"
s += "%{B" + bgColor(self.theme) + "}"
s += " " if self.size > 1 else ""
s += '%{F' + bgColor(self.theme) + '}'
s += '%{B' + bgColor(nextTheme) + '}'
s += ''
s += '%{F' + fgColor(self.theme) + '}'
s += '%{B' + bgColor(self.theme) + '}'
s += ' ' if self.size > 1 else ''
s += fitText(self.text, self.size - 3)
s += " " if self.size > 2 else ""
s += ' ' if self.size > 2 else ''
if left:
if self.theme == nextTheme:
s += ""
s += ''
else:
s += "%{F" + bgColor(self.theme) + "}"
s += "%{B" + bgColor(nextTheme) + "}"
s += ""
s += '%{F' + bgColor(self.theme) + '}'
s += '%{B' + bgColor(nextTheme) + '}'
s += ''
return s
# Section definition
sTime = Section("3")
sTime = Section('3')
hostname = os.environ["HOSTNAME"].split(".")[0]
sHost = Section("2")
hostname = os.environ['HOSTNAME'].split('.')[0]
sHost = Section('2')
sHost.update(
os.environ["USER"] + "@" + hostname.split("-")[-1] if "-" in hostname else hostname
)
os.environ['USER'] + '@' + hostname.split('-')[-1]
if '-' in hostname else hostname)
# Groups definition
@ -148,7 +133,7 @@ gLeft = []
gRight = [sTime, sHost]
# Bar handling
bar = subprocess.Popen(["lemonbar", "-f", FONT, "-b"], stdin=subprocess.PIPE)
bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE)
def updateBar():
@ -156,45 +141,35 @@ def updateBar():
global gLeft, gRight
global outputs
text = ""
text = ''
for oi in range(len(outputs)):
output = outputs[oi]
gLeftFiltered = list(
filter(
lambda s: s.visible and (not s.output or s.output == output.name), gLeft
)
)
tLeft = ""
lambda s: s.visible and (
not s.output or s.output == output.name),
gLeft))
tLeft = ''
l = len(gLeftFiltered)
for gi in range(l):
g = gLeftFiltered[gi]
# Next visible section for transition
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else "CENTER"
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER'
tLeft = tLeft + g.draw(True, nextTheme)
tRight = ""
tRight = ''
for gi in range(len(gRight)):
g = gRight[gi]
nextTheme = "CENTER"
for gn in gRight[gi + 1 :]:
nextTheme = 'CENTER'
for gn in gRight[gi + 1:]:
if gn.visible:
nextTheme = gn.theme
break
tRight = g.draw(False, nextTheme) + tRight
text += (
"%{l}"
+ tLeft
+ "%{r}"
+ tRight
+ "%{B"
+ bgColor("CENTER")
+ "}"
+ "%{S"
+ str(oi + 1)
+ "}"
)
text += '%{l}' + tLeft + '%{r}' + tRight + \
'%{B' + bgColor('CENTER') + '}' + '%{S' + str(oi + 1) + '}'
bar.stdin.write(bytes(text + "\n", "utf-8"))
bar.stdin.write(bytes(text + '\n', 'utf-8'))
bar.stdin.flush()
@ -207,10 +182,12 @@ def on_output():
global outputs
outputs = sorted(
sorted(
list(filter(lambda o: o.active, i3.get_outputs())), key=lambda o: o.rect.y
),
key=lambda o: o.rect.x,
)
list(
filter(
lambda o: o.active,
i3.get_outputs())),
key=lambda o: o.rect.y),
key=lambda o: o.rect.x)
on_output()
@ -232,33 +209,34 @@ def on_workspace_focus():
if workspace.visible:
section.update(workspace.name)
else:
section.update(workspace.name.split(" ")[0])
section.update(workspace.name.split(' ')[0])
if workspace.focused:
section.theme = "4"
section.theme = '4'
elif workspace.urgent:
section.theme = "1"
section.theme = '1'
else:
section.theme = "6"
section.theme = '6'
else:
section.update("")
section.theme = "6"
section.update('')
section.theme = '6'
for tag, i, j, k, l in difflib.SequenceMatcher(None, sNames, wNames).get_opcodes():
if tag == "equal": # If the workspaces didn't changed
for tag, i, j, k, l in difflib.SequenceMatcher(
None, sNames, wNames).get_opcodes():
if tag == 'equal': # If the workspaces didn't changed
for a in range(j - i):
workspace = workspaces[k + a]
section = gLeft[i + a]
actuate(section, workspace)
newGLeft.append(section)
if tag in ("delete", "replace"): # If the workspaces were removed
if tag in ('delete', 'replace'): # If the workspaces were removed
for section in gLeft[i:j]:
if section.visible:
actuate(section, None)
newGLeft.append(section)
else:
del section
if tag in ("insert", "replace"): # If the workspaces were removed
if tag in ('insert', 'replace'): # If the workspaces were removed
for workspace in workspaces[k:l]:
section = Section()
actuate(section, workspace)
@ -277,14 +255,12 @@ def i3events(i3childPipe):
# Proxy functions
def on_workspace_focus(i3, e):
global i3childPipe
i3childPipe.send("on_workspace_focus")
i3childPipe.send('on_workspace_focus')
i3.on("workspace::focus", on_workspace_focus)
def on_output(i3, e):
global i3childPipe
i3childPipe.send("on_output")
i3childPipe.send('on_output')
i3.on("output", on_output)
i3.main()
@ -298,7 +274,7 @@ i3process.start()
def updateValues():
# Time
now = datetime.datetime.now()
sTime.update(now.strftime("%x %X"))
sTime.update(now.strftime('%x %X'))
def updateAnimation():
@ -312,9 +288,9 @@ while True:
now = time.time()
if i3parentPipe.poll():
msg = i3parentPipe.recv()
if msg == "on_workspace_focus":
if msg == 'on_workspace_focus':
on_workspace_focus()
elif msg == "on_output":
elif msg == 'on_output':
on_output()
# TODO Restart lemonbar
else:

View file

@ -1,34 +1,33 @@
#!/usr/bin/env python3
import datetime
import ipaddress
import json
import logging
import random
import socket
import subprocess
import coloredlogs
import mpd
import notmuch
import psutil
from updaters import *
from display import *
import pulsectl
import psutil
import subprocess
import socket
import ipaddress
import logging
import coloredlogs
import json
import notmuch
import mpd
import random
import taskw
import math
from frobar.display import *
from frobar.updaters import *
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger()
# TODO Generator class (for I3WorkspacesProvider, NetworkProvider and later
# PulseaudioProvider and MpdProvider)
def humanSize(num):
"""
Returns a string of width 3+3
"""
for unit in ("B ", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"):
for unit in ('B ','KiB','MiB','GiB','TiB','PiB','EiB','ZiB'):
if abs(num) < 1000:
if num >= 10:
return "{:3d}{}".format(int(num), unit)
@ -37,14 +36,16 @@ def humanSize(num):
num /= 1024.0
return "{:d}YiB".format(num)
def randomColor(seed=0):
random.seed(seed)
return "#{:02x}{:02x}{:02x}".format(*[random.randint(0, 255) for _ in range(3)])
return '#{:02x}{:02x}{:02x}'.format(*[random.randint(0, 255) for _ in range(3)])
class TimeProvider(StatefulSection, PeriodicUpdater):
FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"]
FORMATS = ["%H:%M",
"%m-%d %H:%M:%S",
"%a %y-%m-%d %H:%M:%S"]
NUMBER_STATES = len(FORMATS)
DEFAULT_STATE = 1
@ -55,18 +56,18 @@ class TimeProvider(StatefulSection, PeriodicUpdater):
def __init__(self, theme=None):
PeriodicUpdater.__init__(self)
StatefulSection.__init__(self, theme)
self.changeInterval(1) # TODO OPTI When state < 1
self.changeInterval(1) # TODO OPTI When state < 1
class AlertLevel(enum.Enum):
NORMAL = 0
WARNING = 1
DANGER = 2
class AlertingSection(StatefulSection):
# TODO EASE Correct settings for themes
THEMES = {AlertLevel.NORMAL: 3, AlertLevel.WARNING: 1, AlertLevel.DANGER: 0}
THEMES = {AlertLevel.NORMAL: 2,
AlertLevel.WARNING: 3,
AlertLevel.DANGER: 1}
PERSISTENT = True
def getLevel(self, quantity):
@ -92,16 +93,16 @@ class AlertingSection(StatefulSection):
class CpuProvider(AlertingSection, PeriodicUpdater):
NUMBER_STATES = 3
ICON = ""
ICON = ''
def fetcher(self):
percent = psutil.cpu_percent(percpu=False)
self.updateLevel(percent / 100)
self.updateLevel(percent/100)
if self.state >= 2:
percents = psutil.cpu_percent(percpu=True)
return "".join([Section.ramp(p / 100) for p in percents])
return ''.join([Section.ramp(p/100) for p in percents])
elif self.state >= 1:
return Section.ramp(percent / 100)
return Section.ramp(percent/100)
def __init__(self, theme=None):
AlertingSection.__init__(self, theme)
@ -113,13 +114,12 @@ class RamProvider(AlertingSection, PeriodicUpdater):
"""
Shows free RAM
"""
NUMBER_STATES = 4
ICON = ""
ICON = ''
def fetcher(self):
mem = psutil.virtual_memory()
freePerc = mem.percent / 100
freePerc = mem.percent/100
self.updateLevel(freePerc)
if self.state < 1:
@ -131,7 +131,7 @@ class RamProvider(AlertingSection, PeriodicUpdater):
text.append(freeStr)
if self.state >= 3:
totalStr = humanSize(mem.total)
text.append("/", totalStr)
text.append('/', totalStr)
return text
@ -147,18 +147,18 @@ class TemperatureProvider(AlertingSection, PeriodicUpdater):
def fetcher(self):
allTemp = psutil.sensors_temperatures()
if "coretemp" not in allTemp:
if 'coretemp' not in allTemp:
# TODO Opti Remove interval
return ""
temp = allTemp["coretemp"][0]
return ''
temp = allTemp['coretemp'][0]
self.warningThresold = temp.high
self.dangerThresold = temp.critical
self.updateLevel(temp.current)
self.icon = Section.ramp(temp.current / temp.high, self.RAMP)
self.icon = Section.ramp(temp.current/temp.high, self.RAMP)
if self.state >= 1:
return "{:.0f}°C".format(temp.current)
return '{:.0f}°C'.format(temp.current)
def __init__(self, theme=None):
AlertingSection.__init__(self, theme)
@ -177,16 +177,15 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
self.icon = None
return None
self.icon = ("" if bat.power_plugged else "") + Section.ramp(
bat.percent / 100, self.RAMP
)
self.icon = ("" if bat.power_plugged else "") + \
Section.ramp(bat.percent/100, self.RAMP)
self.updateLevel(1 - bat.percent / 100)
self.updateLevel(1-bat.percent/100)
if self.state < 1:
return
t = Text("{:.0f}%".format(bat.percent))
t = Text('{:.0f}%'.format(bat.percent))
if self.state < 2:
return t
@ -202,6 +201,7 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
self.changeInterval(5)
class PulseaudioProvider(StatefulSection, ThreadedUpdater):
NUMBER_STATES = 3
DEFAULT_STATE = 1
@ -209,27 +209,28 @@ class PulseaudioProvider(StatefulSection, ThreadedUpdater):
def __init__(self, theme=None):
ThreadedUpdater.__init__(self)
StatefulSection.__init__(self, theme)
self.pulseEvents = pulsectl.Pulse("event-handler")
self.pulseEvents = pulsectl.Pulse('event-handler')
self.pulseEvents.event_mask_set(pulsectl.PulseEventMaskEnum.sink)
self.pulseEvents.event_callback_set(self.handleEvent)
self.start()
self.refreshData()
def fetcher(self):
sinks = []
with pulsectl.Pulse("list-sinks") as pulse:
with pulsectl.Pulse('list-sinks') as pulse:
for sink in pulse.sink_list():
if sink.port_active.name == "analog-output-headphones":
icon = ""
elif sink.port_active.name == "analog-output-speaker":
icon = "" if sink.mute else ""
elif sink.port_active.name == "headset-output":
icon = ""
icon = ''
else:
icon = "?"
vol = pulse.volume_get_all_chans(sink)
fg = (sink.mute and "#333333") or (vol > 1 and "#FF0000") or None
fg = (sink.mute and '#333333') or (vol > 1 and '#FF0000') or None
t = Text(icon, fg=fg)
sinks.append(t)
@ -245,7 +246,7 @@ class PulseaudioProvider(StatefulSection, ThreadedUpdater):
vol -= 1
t.append(ramp)
else:
t.append(" {:2.0f}%".format(vol * 100))
t.append(" {:2.0f}%".format(vol*100))
return Text(*sinks)
@ -257,32 +258,33 @@ class PulseaudioProvider(StatefulSection, ThreadedUpdater):
class NetworkProviderSection(StatefulSection, Updater):
NUMBER_STATES = 5
DEFAULT_STATE = 1
def actType(self):
self.ssid = None
if self.iface.startswith("eth") or self.iface.startswith("enp"):
if "u" in self.iface:
self.icon = ""
if self.iface.startswith('eth') or self.iface.startswith('enp'):
if 'u' in self.iface:
self.icon = ''
else:
self.icon = ""
elif self.iface.startswith("wlan") or self.iface.startswith("wl"):
self.icon = ""
self.icon = ''
elif self.iface.startswith('wlan') or self.iface.startswith('wl'):
self.icon = ''
if self.showSsid:
cmd = ["iwgetid", self.iface, "--raw"]
p = subprocess.run(cmd, stdout=subprocess.PIPE)
self.ssid = p.stdout.strip().decode()
elif self.iface.startswith("tun") or self.iface.startswith("tap"):
self.icon = ""
elif self.iface.startswith("docker"):
self.icon = ""
elif self.iface.startswith("veth"):
self.icon = ""
elif self.iface.startswith("vboxnet"):
self.icon = ""
elif self.iface.startswith('tun') or self.iface.startswith('tap'):
self.icon = ''
elif self.iface.startswith('docker'):
self.icon = ''
elif self.iface.startswith('veth'):
self.icon = ''
elif self.iface.startswith('vboxnet'):
self.icon = ''
else:
self.icon = "?"
self.icon = '?'
def getAddresses(self):
ipv4 = None
@ -297,11 +299,9 @@ class NetworkProviderSection(StatefulSection, Updater):
def fetcher(self):
self.icon = None
self.persistent = False
if (
self.iface not in self.parent.stats
or not self.parent.stats[self.iface].isup
or self.iface.startswith("lo")
):
if self.iface not in self.parent.stats or \
not self.parent.stats[self.iface].isup or \
self.iface.startswith('lo'):
return None
# Get addresses
@ -318,36 +318,30 @@ class NetworkProviderSection(StatefulSection, Updater):
if self.showAddress:
if ipv4:
netStrFull = "{}/{}".format(ipv4.address, ipv4.netmask)
netStrFull = '{}/{}'.format(ipv4.address, ipv4.netmask)
addr = ipaddress.IPv4Network(netStrFull, strict=False)
addrStr = "{}/{}".format(ipv4.address, addr.prefixlen)
addrStr = '{}/{}'.format(ipv4.address, addr.prefixlen)
text.append(addrStr)
# TODO IPV6
# if ipv6:
# text += ' ' + ipv6.address
if self.showSpeed:
recvDiff = (
self.parent.IO[self.iface].bytes_recv
recvDiff = self.parent.IO[self.iface].bytes_recv \
- self.parent.prevIO[self.iface].bytes_recv
)
sentDiff = (
self.parent.IO[self.iface].bytes_sent
sentDiff = self.parent.IO[self.iface].bytes_sent \
- self.parent.prevIO[self.iface].bytes_sent
)
recvDiff /= self.parent.dt
sentDiff /= self.parent.dt
text.append("{}{}".format(humanSize(recvDiff), humanSize(sentDiff)))
text.append('{}{}'.format(humanSize(recvDiff),
humanSize(sentDiff)))
if self.showTransfer:
text.append(
"{}{}".format(
humanSize(self.parent.IO[self.iface].bytes_recv),
humanSize(self.parent.IO[self.iface].bytes_sent),
)
)
text.append('{}{}'.format(
humanSize(self.parent.IO[self.iface].bytes_recv),
humanSize(self.parent.IO[self.iface].bytes_sent)))
return " ".join(text)
return ' '.join(text)
def onChangeState(self, state):
self.showSsid = state >= 1
@ -409,33 +403,32 @@ class NetworkProvider(Section, PeriodicUpdater):
self.fetchData()
self.changeInterval(5)
class RfkillProvider(Section, PeriodicUpdater):
# TODO FEAT rfkill doesn't seem to indicate that the hardware switch is
# toggled
PATH = "/sys/class/rfkill"
PATH = '/sys/class/rfkill'
def fetcher(self):
t = Text()
for device in os.listdir(self.PATH):
with open(os.path.join(self.PATH, device, "soft"), "rb") as f:
softBlocked = f.read().strip() != b"0"
with open(os.path.join(self.PATH, device, "hard"), "rb") as f:
hardBlocked = f.read().strip() != b"0"
with open(os.path.join(self.PATH, device, 'soft'), 'rb') as f:
softBlocked = f.read().strip() != b'0'
with open(os.path.join(self.PATH, device, 'hard'), 'rb') as f:
hardBlocked = f.read().strip() != b'0'
if not hardBlocked and not softBlocked:
continue
with open(os.path.join(self.PATH, device, "type"), "rb") as f:
with open(os.path.join(self.PATH, device, 'type'), 'rb') as f:
typ = f.read().strip()
fg = (hardBlocked and "#CCCCCC") or (softBlocked and "#FF0000")
if typ == b"wlan":
icon = ""
elif typ == b"bluetooth":
icon = ""
fg = (hardBlocked and '#CCCCCC') or (softBlocked and '#FF0000')
if typ == b'wlan':
icon = ''
elif typ == b'bluetooth':
icon = ''
else:
icon = "?"
icon = '?'
t.append(Text(icon, fg=fg))
return t
@ -445,7 +438,6 @@ class RfkillProvider(Section, PeriodicUpdater):
Section.__init__(self, theme)
self.changeInterval(5)
class SshAgentProvider(PeriodicUpdater):
def fetcher(self):
cmd = ["ssh-add", "-l"]
@ -453,18 +445,17 @@ class SshAgentProvider(PeriodicUpdater):
if proc.returncode != 0:
return None
text = Text()
for line in proc.stdout.split(b"\n"):
for line in proc.stdout.split(b'\n'):
if not len(line):
continue
fingerprint = line.split()[1]
text.append(Text("", fg=randomColor(seed=fingerprint)))
text.append(Text('', fg=randomColor(seed=fingerprint)))
return text
def __init__(self):
PeriodicUpdater.__init__(self)
self.changeInterval(5)
class GpgAgentProvider(PeriodicUpdater):
def fetcher(self):
cmd = ["gpg-connect-agent", "keyinfo --list", "/bye"]
@ -473,41 +464,39 @@ class GpgAgentProvider(PeriodicUpdater):
if proc.returncode != 0:
return None
text = Text()
for line in proc.stdout.split(b"\n"):
if not len(line) or line == b"OK":
for line in proc.stdout.split(b'\n'):
if not len(line) or line == b'OK':
continue
spli = line.split()
if spli[6] != b"1":
if spli[6] != b'1':
continue
keygrip = spli[2]
text.append(Text("", fg=randomColor(seed=keygrip)))
text.append(Text('', fg=randomColor(seed=keygrip)))
return text
def __init__(self):
PeriodicUpdater.__init__(self)
self.changeInterval(5)
class KeystoreProvider(Section, MergedUpdater):
# TODO OPTI+FEAT Use ColorCountsSection and not MergedUpdater, this is useless
ICON = ""
ICON = ''
def __init__(self, theme=None):
MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider())
Section.__init__(self, theme)
class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
COLORABLE_ICON = ""
COLORABLE_ICON = ''
def subfetcher(self):
db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY, path=self.dir)
counts = []
for account in self.accounts:
queryStr = "folder:/{}/ and tag:unread".format(account)
queryStr = 'folder:/{}/ and tag:unread'.format(account)
query = notmuch.Query(db, queryStr)
nbMsgs = query.count_messages()
if account == "frogeye":
if account == 'frogeye':
global q
q = query
if nbMsgs < 1:
@ -516,7 +505,7 @@ class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
# db.close()
return counts
def __init__(self, dir="~/.mail/", theme=None):
def __init__(self, dir='~/.mail/', theme=None):
PeriodicUpdater.__init__(self)
ColorCountsSection.__init__(self, theme)
@ -524,24 +513,56 @@ class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
assert os.path.isdir(self.dir)
# Fetching account list
self.accounts = sorted(
[a for a in os.listdir(self.dir) if not a.startswith(".")]
)
self.accounts = sorted([a for a in os.listdir(self.dir)
if not a.startswith('.')])
# Fetching colors
self.colors = dict()
for account in self.accounts:
filename = os.path.join(self.dir, account, "color")
with open(filename, "r") as f:
filename = os.path.join(self.dir, account, 'color')
with open(filename, 'r') as f:
color = f.read().strip()
self.colors[account] = color
self.addPath(os.path.join(self.dir, ".notmuch", "xapian"))
self.addPath(os.path.join(self.dir, '.notmuch', 'xapian'))
class TaskWarriorProvider(StatefulSection, InotifyUpdater):
ICON = ''
NUMBER_STATES = 2
DEFAULT_STATE = 1
def __init__(self, theme=None):
InotifyUpdater.__init__(self)
StatefulSection.__init__(self, theme=theme)
self.taskw = taskw.TaskWarrior()
self.addPath(os.path.expanduser(self.taskw.config['data']['location']))
def fetcher(self):
maxi = -math.inf
total = 0
for task in self.taskw.load_tasks('pending')['pending']:
urgency = task['urgency']
if urgency > maxi:
maxi = urgency
if urgency > 0:
total += urgency
t = Text()
t.append(f"{maxi:.1f}")
if self.showTotal:
t.append(f" | {total:.1f}")
return t
def onChangeState(self, state):
self.showTotal = state >= 1
class TodoProvider(ColorCountsSection, InotifyUpdater):
# TODO OPT/UX Maybe we could get more data from the todoman python module
# TODO OPT Specific callback for specific directory
COLORABLE_ICON = ""
COLORABLE_ICON = ''
def updateCalendarList(self):
calendars = sorted(os.listdir(self.dir))
@ -551,13 +572,13 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
self.addPath(os.path.join(self.dir, calendar), refresh=False)
# Fetching name
path = os.path.join(self.dir, calendar, "displayname")
with open(path, "r") as f:
path = os.path.join(self.dir, calendar, 'displayname')
with open(path, 'r') as f:
self.names[calendar] = f.read().strip()
# Fetching color
path = os.path.join(self.dir, calendar, "color")
with open(path, "r") as f:
path = os.path.join(self.dir, calendar, 'color')
with open(path, 'r') as f:
self.colors[calendar] = f.read().strip()
self.calendars = calendars
@ -592,8 +613,8 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
if self.state < 2:
c = self.countUndone(None)
if c > 0:
counts.append((c, "#00000"))
counts.append((0, "#FFFFF"))
counts.append((c, '#00000'))
counts.append((0, '#FFFFF'))
return counts
# Optimisation ends here
@ -604,7 +625,6 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
counts.append((c, self.colors[calendar]))
return counts
class I3WindowTitleProvider(Section, I3Updater):
# TODO FEAT To make this available from start, we need to find the
# `focused=True` element following the `focus` array
@ -617,7 +637,6 @@ class I3WindowTitleProvider(Section, I3Updater):
Section.__init__(self, theme=theme)
self.on("window", self.on_window)
class I3WorkspacesProviderSection(Section):
def selectTheme(self):
if self.urgent:
@ -641,12 +660,12 @@ class I3WorkspacesProviderSection(Section):
def setName(self, name):
self.shortName = name
self.fullName = (
self.parent.customNames[name] if name in self.parent.customNames else name
)
self.fullName = self.parent.customNames[name] \
if name in self.parent.customNames else name
def switchTo(self):
self.parent.i3.command("workspace {}".format(self.shortName))
self.parent.i3.command('workspace {}'.format(self.shortName))
def __init__(self, name, parent):
Section.__init__(self)
@ -667,6 +686,7 @@ class I3WorkspacesProviderSection(Section):
self.updateText(None)
class I3WorkspacesProvider(Section, I3Updater):
# TODO FEAT Multi-screen
@ -727,7 +747,7 @@ class I3WorkspacesProvider(Section, I3Updater):
self.sections[e.current.num].show()
def on_mode(self, i3, e):
if e.change == "default":
if e.change == 'default':
self.modeSection.updateText(None)
for section in self.sections.values():
section.tempShow()
@ -736,9 +756,7 @@ class I3WorkspacesProvider(Section, I3Updater):
for section in self.sections.values():
section.tempEmpty()
def __init__(
self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2, customNames=dict()
):
def __init__(self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2, customNames=dict()):
I3Updater.__init__(self)
Section.__init__(self)
self.themeNormal = theme
@ -762,14 +780,13 @@ class I3WorkspacesProvider(Section, I3Updater):
parent.addSection(self.modeSection)
self.initialPopulation(parent)
class MpdProvider(Section, ThreadedUpdater):
# TODO FEAT More informations and controls
MAX_LENGTH = 50
def connect(self):
self.mpd.connect("localhost", 6600)
self.mpd.connect('localhost', 6600)
def __init__(self, theme=None):
ThreadedUpdater.__init__(self)
@ -801,16 +818,18 @@ class MpdProvider(Section, ThreadedUpdater):
infosStr = " - ".join(infos)
if len(infosStr) > MpdProvider.MAX_LENGTH:
infosStr = infosStr[: MpdProvider.MAX_LENGTH - 1] + ""
infosStr = infosStr[:MpdProvider.MAX_LENGTH-1] + ''
return "{}".format(infosStr)
def loop(self):
try:
self.mpd.idle("player")
self.mpd.idle('player')
self.refreshData()
except mpd.base.ConnectionError as e:
log.warn(e, exc_info=True)
self.connect()
except BaseException as e:
log.error(e, exc_info=True)

View file

@ -0,0 +1,14 @@
coloredlogs==10.0
enum-compat==0.0.2
humanfriendly==4.16.1
i3ipc==1.6.0
ifaddr==0.1.4
ipaddress==1.0.22
psutil==5.4.7
pulsectl==18.8.0
pyinotify==0.9.6
python-mpd2==1.0.0
python-uinput==0.11.2
taskw==1.2.0
yoke==0.1.1
zeroconf==0.21.3

View file

@ -1,25 +1,22 @@
#!/usr/bin/env python3
import functools
import logging
import math
import os
import functools
import threading
import pyinotify
import os
import time
import logging
import coloredlogs
import i3ipc
import pyinotify
from display import Text
from frobar.display import Text
from frobar.notbusy import notBusy
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger()
# TODO Sync bar update with PeriodicUpdater updates
notBusy = threading.Event()
class Updater:
@staticmethod
@ -55,9 +52,8 @@ class PeriodicUpdaterThread(threading.Thread):
counter = 0
while True:
notBusy.set()
if PeriodicUpdater.intervalsChanged.wait(
timeout=PeriodicUpdater.intervalStep
):
if PeriodicUpdater.intervalsChanged \
.wait(timeout=PeriodicUpdater.intervalStep):
# ↑ sleeps here
notBusy.clear()
PeriodicUpdater.intervalsChanged.clear()
@ -131,6 +127,7 @@ class PeriodicUpdater(Updater):
class InotifyUpdaterEventHandler(pyinotify.ProcessEvent):
def process_default(self, event):
# DEBUG
# from pprint import pprint
@ -158,9 +155,8 @@ class InotifyUpdater(Updater):
@staticmethod
def init():
notifier = pyinotify.ThreadedNotifier(
InotifyUpdater.wm, InotifyUpdaterEventHandler()
)
notifier = pyinotify.ThreadedNotifier(InotifyUpdater.wm,
InotifyUpdaterEventHandler())
notifier.start()
# TODO Mask for folders
@ -178,7 +174,8 @@ class InotifyUpdater(Updater):
self.dirpath = os.path.dirname(path)
self.filename = os.path.basename(path)
else:
raise FileNotFoundError("No such file or directory: '{}'".format(path))
raise FileNotFoundError("No such file or directory: '{}'"
.format(path))
# Register watch action
if self.dirpath not in InotifyUpdater.paths:
@ -243,6 +240,7 @@ class I3Updater(ThreadedUpdater):
class MergedUpdater(Updater):
# TODO OPTI Do not update until end of periodic batch
def fetcher(self):
text = Text()
@ -268,4 +266,4 @@ class MergedUpdater(Updater):
updater.updateText = newUpdateText.__get__(updater, Updater)
self.updaters.append(updater)
self.texts[updater] = ""
self.texts[updater] = ''

1
config/llpp.conf Normal file
View file

@ -0,0 +1 @@
savepath-command='echo %s'

0
config/mpd/.dfrecur Normal file
View file

413
config/mpd/mpd.conf Normal file
View file

@ -0,0 +1,413 @@
# An example configuration file for MPD.
# Read the user manual for documentation: http://www.musicpd.org/doc/user/
# Files and directories #######################################################
#
# This setting controls the top directory which MPD will search to discover the
# available audio files and add them to the daemon's online database. This
# setting defaults to the XDG directory, otherwise the music directory will be
# be disabled and audio files will only be accepted over ipc socket (using
# file:// protocol) or streaming files over an accepted protocol.
#
music_directory "~/Musiques"
#
# This setting sets the MPD internal playlist directory. The purpose of this
# directory is storage for playlists created by MPD. The server will use
# playlist files not created by the server but only if they are in the MPD
# format. This setting defaults to playlist saving being disabled.
#
playlist_directory "~/.config/mpd/playlists"
#
# This setting sets the location of the MPD database. This file is used to
# load the database at server start up and store the database while the
# server is not up. This setting defaults to disabled which will allow
# MPD to accept files over ipc socket (using file:// protocol) or streaming
# files over an accepted protocol.
#
db_file "~/.cache/mpd/database"
#
# These settings are the locations for the daemon log files for the daemon.
# These logs are great for troubleshooting, depending on your log_level
# settings.
#
# The special value "syslog" makes MPD use the local syslog daemon. This
# setting defaults to logging to syslog, otherwise logging is disabled.
#
log_file "~/.cache/mpd/log"
#
# This setting sets the location of the file which stores the process ID
# for use of mpd --kill and some init scripts. This setting is disabled by
# default and the pid file will not be stored.
#
pid_file "~/.cache/mpd/pid"
#
# This setting sets the location of the file which contains information about
# most variables to get MPD back into the same general shape it was in before
# it was brought down. This setting is disabled by default and the server
# state will be reset on server start up.
#
state_file "~/.cache/mpd/state"
#
# The location of the sticker database. This is a database which
# manages dynamic information attached to songs.
#
sticker_file "~/.cache/mpd/sticker.sql"
#
###############################################################################
# General music daemon options ################################################
#
# This setting specifies the user that MPD will run as. MPD should never run as
# root and you may use this setting to make MPD change its user ID after
# initialization. This setting is disabled by default and MPD is run as the
# current user.
#
#user "nobody"
#
# This setting specifies the group that MPD will run as. If not specified
# primary group of user specified with "user" setting will be used (if set).
# This is useful if MPD needs to be a member of group such as "audio" to
# have permission to use sound card.
#
#group "nogroup"
#
# This setting sets the address for the daemon to listen on. Careful attention
# should be paid if this is assigned to anything other then the default, any.
# This setting can deny access to control of the daemon.
#
# For network
#bind_to_address "any"
#
# And for Unix Socket
#bind_to_address "~/.mpd/socket"
#
# This setting is the TCP port that is desired for the daemon to get assigned
# to.
#
#port "6600"
#
# This setting controls the type of information which is logged. Available
# setting arguments are "default", "secure" or "verbose". The "verbose" setting
# argument is recommended for troubleshooting, though can quickly stretch
# available resources on limited hardware storage.
#
log_level "default"
#
# If you have a problem with your MP3s ending abruptly it is recommended that
# you set this argument to "no" to attempt to fix the problem. If this solves
# the problem, it is highly recommended to fix the MP3 files with vbrfix
# (available from <http://www.willwap.co.uk/Programs/vbrfix.php>), at which
# point gapless MP3 playback can be enabled.
#
#gapless_mp3_playback "yes"
#
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
# of starting playback after startup.
#
restore_paused "yes"
#
# This setting enables MPD to create playlists in a format usable by other
# music players.
#
#save_absolute_paths_in_playlists "no"
#
# This setting defines a list of tag types that will be extracted during the
# audio file discovery process. The complete list of possible values can be
# found in the user manual.
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in
# music_directory are changed.
#
auto_update "yes"
#
# Limit the depth of the directories being watched, 0 means only watch
# the music directory itself. There is no limit by default.
#
#auto_update_depth "3"
#
###############################################################################
# Symbolic link behavior ######################################################
#
# If this setting is set to "yes", MPD will discover audio files by following
# symbolic links outside of the configured music_directory.
#
#follow_outside_symlinks "yes"
#
# If this setting is set to "yes", MPD will discover audio files by following
# symbolic links inside of the configured music_directory.
#
#follow_inside_symlinks "yes"
#
###############################################################################
# Zeroconf / Avahi Service Discovery ##########################################
#
# If this setting is set to "yes", service information will be published with
# Zeroconf / Avahi.
#
#zeroconf_enabled "yes"
#
# The argument to this setting will be the Zeroconf / Avahi unique name for
# this MPD server on the network.
#
#zeroconf_name "Music Player"
#
###############################################################################
# Permissions #################################################################
#
# If this setting is set, MPD will require password authorization. The password
# can setting can be specified multiple times for different password profiles.
#
#password "password@read,add,control,admin"
#
# This setting specifies the permissions a user has who has not yet logged in.
#
#default_permissions "read,add,control,admin"
#
###############################################################################
# Database #######################################################################
#
#database {
# plugin "proxy"
# host "other.mpd.host"
# port "6600"
#}
# Input #######################################################################
#
input {
plugin "curl"
# proxy "proxy.isp.com:8080"
# proxy_user "user"
# proxy_password "password"
}
#
###############################################################################
# Audio Output ################################################################
#
# MPD supports various audio output types, as well as playing through multiple
# audio outputs at the same time, through multiple audio_output settings
# blocks. Setting this block is optional, though the server will only attempt
# autodetection for one sound card.
#
# An example of an ALSA output:
#
#audio_output {
# type "alsa"
# name "My ALSA Device"
## device "hw:0,0" # optional
## mixer_type "hardware" # optional
## mixer_device "default" # optional
## mixer_control "PCM" # optional
## mixer_index "0" # optional
#}
#
# An example of an OSS output:
#
#audio_output {
# type "oss"
# name "My OSS Device"
## device "/dev/dsp" # optional
## mixer_type "hardware" # optional
## mixer_device "/dev/mixer" # optional
## mixer_control "PCM" # optional
#}
#
# An example of a shout output (for streaming to Icecast):
#
#audio_output {
# type "shout"
# encoding "ogg" # optional
# name "My Shout Stream"
# host "localhost"
# port "8000"
# mount "/mpd.ogg"
# password "hackme"
# quality "5.0"
# bitrate "128"
# format "44100:16:1"
## protocol "icecast2" # optional
## user "source" # optional
## description "My Stream Description" # optional
## url "http://example.com" # optional
## genre "jazz" # optional
## public "no" # optional
## timeout "2" # optional
## mixer_type "software" # optional
#}
#
# An example of a recorder output:
#
#audio_output {
# type "recorder"
# name "My recorder"
# encoder "vorbis" # optional, vorbis or lame
# path "/var/lib/mpd/recorder/mpd.ogg"
## quality "5.0" # do not define if bitrate is defined
# bitrate "128" # do not define if quality is defined
# format "44100:16:1"
#}
#
# An example of a httpd output (built-in HTTP streaming server):
#
audio_output {
type "httpd"
name "geoffrey-music-httpd"
encoder "vorbis" # optional, vorbis or lame
port "8600"
bind_to_address "0.0.0.0" # optional, IPv4 or IPv6
# quality "5.0" # do not define if bitrate is defined
bitrate "128" # do not define if quality is defined
format "44100:16:1"
max_clients "0" # optional 0=no limit
}
#
# An example of a pulseaudio output (streaming to a remote pulseaudio server)
#
audio_output {
type "pulse"
name "geoffrey-music-pulse"
## server "remote_server" # optional
## sink "remote_server_sink" # optional
}
#
# An example of a winmm output (Windows multimedia API).
#
#audio_output {
# type "winmm"
# name "My WinMM output"
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
# or
## device "0" # optional
## mixer_type "hardware" # optional
#}
#
# An example of an openal output.
#
#audio_output {
# type "openal"
# name "My OpenAL output"
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
#}
#
## Example "pipe" output:
#
#audio_output {
# type "pipe"
# name "my pipe"
# command "aplay -f cd 2>/dev/null"
## Or if you're want to use AudioCompress
# command "AudioCompress -m | aplay -f cd 2>/dev/null"
## Or to send raw PCM stream through PCM:
# command "nc example.org 8765"
# format "44100:16:2"
#}
#
## An example of a null output (for no audio output):
#
#audio_output {
# type "null"
# name "My Null Output"
# mixer_type "none" # optional
#}
#
# If MPD has been compiled with libsamplerate support, this setting specifies
# the sample rate converter to use. Possible values can be found in the
# mpd.conf man page or the libsamplerate documentation. By default, this is
# setting is disabled.
#
#samplerate_converter "Fastest Sinc Interpolator"
#
###############################################################################
# Normalization automatic volume adjustments ##################################
#
# This setting specifies the type of ReplayGain to use. This setting can have
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
# chooses between "track" and "album" depending on the current state of
# random playback. If random playback is enabled then "track" mode is used.
# See <http://www.replaygain.org> for more details about ReplayGain.
# This setting is off by default.
#
replaygain "auto"
#
# This setting sets the pre-amp used for files that have ReplayGain tags. By
# default this setting is disabled.
#
#replaygain_preamp "0"
#
# This setting sets the pre-amp used for files that do NOT have ReplayGain tags.
# By default this setting is disabled.
#
#replaygain_missing_preamp "0"
#
# This setting enables or disables ReplayGain limiting.
# MPD calculates actual amplification based on the ReplayGain tags
# and replaygain_preamp / replaygain_missing_preamp setting.
# If replaygain_limit is enabled MPD will never amplify audio signal
# above its original level. If replaygain_limit is disabled such amplification
# might occur. By default this setting is enabled.
#
#replaygain_limit "yes"
#
# This setting enables on-the-fly normalization volume adjustment. This will
# result in the volume of all playing audio to be adjusted so the output has
# equal "loudness". This setting is disabled by default.
#
#volume_normalization "no"
#
###############################################################################
# Character Encoding ##########################################################
#
# If file or directory names do not display correctly for your locale then you
# may need to modify this setting.
#
#filesystem_charset "UTF-8"
#
# This setting controls the encoding that ID3v1 tags should be converted from.
#
#id3v1_encoding "ISO-8859-1"
#
###############################################################################
# SIDPlay decoder #############################################################
#
# songlength_database:
# Location of your songlengths file, as distributed with the HVSC.
# The sidplay plugin checks this for matching MD5 fingerprints.
# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq
#
# default_songlength:
# This is the default playing time in seconds for songs not in the
# songlength database, or in case you're not using a database.
# A value of 0 means play indefinitely.
#
# filter:
# Turns the SID filter emulation on or off.
#
#decoder {
# plugin "sidplay"
# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt"
# default_songlength "120"
# filter "true"
#}
#
###############################################################################

0
config/mpv/.dfrecur Normal file
View file

1
config/mpv/mpv.conf Normal file
View file

@ -0,0 +1 @@
no-audio-display

7
config/mypy/config Normal file
View file

@ -0,0 +1,7 @@
[mypy]
cache_dir = ~/.cache/mypy
ignore_missing_imports = True
disallow_untyped_defs = True
disallow_untyped_calls = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True

1
config/nvim/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.netrwhist

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

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

View file

@ -51,8 +51,9 @@ padding-right = 2
module-margin-left = 1
module-margin-right = 1
font-0 = "DejaVu Sans:size=10;0"
font-1 = "DejaVuSansMono Nerd Font Mono:pixelsize=10;0"
font-0 = "Font Awesome:size=10;0"
font-1 = "DejaVu Sans:size=10;0"
font-2 = "DejaVu Sans Mono for Powerline:pixelsize=10;0"
modules-left = i3

View file

@ -21,7 +21,7 @@ do
else
bar="secondary"
fi
polybar -q $bar -c ~/.config/polybar/config.ini &
polybar $bar &
done
echo "Bars launched..."

0
config/pulse/.dfrecur Normal file
View file

1
config/pulse/client.conf Normal file
View file

@ -0,0 +1 @@
cookie-file = .config/pulse/pulse-cookie

3
config/pycodestyle Normal file
View file

@ -0,0 +1,3 @@
[pycodestyle]
# Compatibility with Black
max-line-length = 88

View file

@ -1,11 +1,10 @@
#!/usr/bin/env python3
import rlcompleter
import sys
import os
# From https://github.com/python/cpython/blob/v3.7.0b5/Lib/site.py#L436
# Changing the history file
def register_readline() -> None:
def register_readline():
import atexit
try:
import readline

View file

View file

@ -0,0 +1,78 @@
import os
# Public static configuration for qutebrowser
# Note that private stuff (permissions, per-site rules)
# are in autoconfig in gdotfiles
# Prompt the user for the download location. If set to false,
# `downloads.location.directory` will be used.
# Type: Bool
c.downloads.location.prompt = False
# When to show the tab bar.
# Type: String
# Valid values:
# - always: Always show the tab bar.
# - never: Always hide the tab bar.
# - multiple: Hide the tab bar if only one tab is open.
# - switching: Show the tab bar when switching tabs.
c.tabs.show = "never"
# Open a new window for every tab.
# Type: Bool
c.tabs.tabs_are_windows = True
# Open base URL of the searchengine if a searchengine shortcut is
# invoked without parameters.
# Type: Bool
c.url.open_base_url = True
# Search engines which can be used via the address bar. Maps a search
# engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}`
# placeholder. The placeholder will be replaced by the search term, use
# `{{` and `}}` for literal `{`/`}` signs. The search engine named
# `DEFAULT` is used when `url.auto_search` is turned on and something
# else than a URL was entered to be opened. Other search engines can be
# used by prepending the search engine name to the search term, e.g.
# `:open google qutebrowser`.
# Type: Dict
c.url.searchengines = {
"DEFAULT": "https://www.ecosia.org/search?q={}",
"aw": "http://www.amp-what.com/unicode/search/{}",
"ddg": "https://duckduckgo.com/?q={}&ia=web",
"duckduckgo": "https://duckduckgo.com/?q={}&ia=web",
"ecosia": "https://www.ecosia.org/search?q={}",
"github": "https://github.com/search?q={}",
"google": "https://www.google.fr/search?q={}",
"npm": "https://www.npmjs.com/search?q={}",
"q": "https://www.qwant.com/?t=web&q={}",
"qwant": "https://www.qwant.com/?t=web&q={}",
"wolfram": "https://www.wolframalpha.com/input/?i={}",
"youtube": "https://www.youtube.com/results?search_query={}",
}
# Only allow first party cookies
config.set("content.cookies.accept", "no-3rdparty", "chrome://*/*")
# Page(s) to open at the start.
# Type: List of FuzzyUrl, or FuzzyUrl
c.url.start_pages = "https://geoffrey.frogeye.fr/blank.html"
# Bindings for normal mode
config.bind("H", "tab-prev")
config.bind("J", "back")
config.bind("K", "forward")
config.bind("L", "tab-next")
config.unbind("T")
config.bind("af", "spawn --userscript freshrss")
config.bind("as", "spawn --userscript shaarli")
config.bind("u", "undo --window")
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, "theme.py")
if os.path.exists(filename):
with open(filename) as file:
exec(file.read())
# Uncomment this to still load settings configured via autoconfig.yml
config.load_autoconfig()

Some files were not shown because too many files have changed in this diff Show more