1
0
Fork 0

Nonatomic

This commit is contained in:
Malte Brandy 2018-05-26 22:48:33 +02:00
parent 074a7f21ab
commit d54d749a86
No known key found for this signature in database
GPG key ID: 226A2D41EF5378C9
34 changed files with 3046 additions and 380 deletions

View file

@ -6,6 +6,25 @@ if [[ -z "$TMUX" ]] {
exec tmux;
}
}
# Prompt
function default_host () {
H="${HOST}%{%}:"
REPLY=${H/apollo%{%}:/}
REPLY=${REPLY/.olymp.space/}
echo $REPLY
}
function default_user () {
U=${USER}@
REPLY=${U/maralorn@/}
echo $REPLY
}
setopt prompt_subst
autoload -U colors && colors # Enable colors in prompt
PROMPT='%{%F{red}%}$(default_user)$(default_host)%{%F{blue}%}%~%{%}> %{%f%}'
precmd() {
local s=$? c=( $(fc -L -D -l -1) )
if [[ $launched && "${c[2]}" != "0:00" ]] {
@ -22,10 +41,14 @@ alias m=man
alias t="tmux attach"
alias tn="tmux new-session"
alias w="develop-here"
alias ls=exa
alias ls='exa -lh --git'
export BROWSER=qutebrowser
export EDITOR=nvim
export MANPAGER="most -s"
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs
zstyle ':chpwd:*' recent-dirs-max 25
if [[ $PWD == "$HOME" ]] {
cd "$(head -n1 ~/.chpwd-recent-dirs | sed s/\\$// | sed s/\'//g)"
}
bindkey '^R' history-incremental-pattern-search-backward

View file

@ -1,7 +1,21 @@
{ pkgs, ... }:
{ pkgs, config, ... }:
let
rust-scripts = with pkgs; callPackage ../packages/rust-scripts {};
unstable-pkgs = import <unstable> {};
eventd = unstable-pkgs.callPackage ../packages/eventd {};
st = import graphical/st;
in {
nixpkgs.config.packageOverrides = pkgs: {
eventd = eventd;
st = st pkgs config.common.colors;
};
home.file.".tmux.conf".text = ''
set -g default-terminal "st-256color"
set -ga terminal-overrides ",st-256color:Tc"
set -g history-limit 50000
set -g status off
set -g escape-time 1
'';
programs = {
home-manager = {
enable = true;
@ -46,7 +60,7 @@ in {
treeView = true;
};
ssh = {
controlMaster = "autoask";
controlMaster = "yes";
enable = true;
matchBlocks = let
matheGwProxy = "ssh -q gw nc -q0 %h %p";
@ -55,16 +69,16 @@ in {
in [
{ host = "charon"; hostname = "charon.olymp.space"; }
{ host = "*.olymp.space"; user = "maralorn"; }
{ host = "ag-forward"; hostname = agHost; proxyCommand = matheGwProxy; }
{ host = "ag"; hostname = agHost; }
{ host = "kiva-forward"; hostname = kivaHost; proxyCommand = matheGwProxy; }
{ host = "kiva"; hostname = kivaHost; }
{ host = "gw"; hostname = "gwres4.mathematik.tu-darmstadt.de"; }
{ host = "*.mathematik.tu-darmstadt.de"; user = "brandy"; }
{ host = "ag-forward"; hostname = agHost; proxyCommand = matheGwProxy;user="brandy";}
{ host = "ag"; hostname = agHost;user="brandy";}
{ host = "kiva-forward"; hostname = kivaHost; proxyCommand = matheGwProxy;user="brandy";}
{ host = "kiva"; hostname = kivaHost;user="brandy";}
{ host = "gw"; hostname = "gwres4.mathematik.tu-darmstadt.de";user="brandy";}
{ host = "shells"; hostname = "shells.darmstadt.ccc.de"; }
{ host = "vorstand"; hostname = "vorstand.darmstadt.ccc.de"; }
{ host = "*.darmstadt.ccc.de"; user = "maralorn"; }
{ host = "whisky"; hostname = "whisky.w17.io"; user = "chaos"; }
{ host = "door.w17.io"; identityFile = "~/.ssh/door_rsa";}
];
};
zsh = {
@ -76,9 +90,20 @@ in {
size = 100000;
};
initExtra = builtins.readFile ./configs/zshrc;
oh-my-zsh = {
enable = true;
plugins = [ "colored-man-pages" "git-prompt" ];
};
};
};
home.sessionVariables = {
BROWSER="${pkgs.firefox}/bin/firefox";
EDITOR="${pkgs.neovim}/bin/nvim";
TERMINAL=config.common.terminal;
};
systemd.user.startServices = true;
imports = [
@ -101,6 +126,7 @@ in {
tig
exa
fzf
ag
pythonPackages.qrcode
ranger

View file

@ -19,7 +19,7 @@ in {
"leisure"
"config"
];
terminal = "urxvt";
terminal = "${pkgs.st}/bin/st";
colors = {
"foreground" = "#dddbff";
"background" = "#05004a";
@ -44,6 +44,7 @@ in {
home = {
packages = with pkgs; [
tasktree
st
];
keyboard = {
layout = "de";
@ -73,7 +74,6 @@ in {
enable = true;
components = [
"secrets"
"ssh"
];
};
random-background = {
@ -83,6 +83,9 @@ in {
};
gpg-agent = {
enable = true;
enableSshSupport = true;
defaultCacheTtl = 900;
defaultCacheTtlSsh = 900;
};
redshift = {
enable = true;

View file

@ -1,7 +1,5 @@
{ pkgs, lib, config, ... }:
let
eventd-pkgs = import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/fe61c3b84e8e81a8ec2bf6b3ed2a0e8652cea190.tar.gz) {};
eventd = eventd-pkgs.callPackage ../../packages/eventd {};
colors = config.common.colors;
in {
home = {
@ -23,8 +21,8 @@ in {
Service = {
Type="notify";
Sockets="eventd-control.socket eventd.socket";
ExecStart="${eventd}/bin/eventd --listen systemd";
ExecReload="${eventd}/bin/eventdctl reload";
ExecStart="${pkgs.eventd}/bin/eventd --listen systemd";
ExecReload="${pkgs.eventd}/bin/eventdctl reload";
};
};
};
@ -58,21 +56,6 @@ in {
Spacing = 2;
Limit = 10;
};
"Queue mode" = {
Anchor = "top";
Margin = 300;
Limit = 1;
};
"Queue feedback" = {
Anchor = "top";
Margin = 450;
Limit = 1;
};
"Queue workspace" = {
Anchor = "bottom-left";
Margin = 10;
Limit = 1;
};
"Queue command" = {
Anchor = "bottom-right";
Margin = 10;
@ -81,20 +64,15 @@ in {
};
"Queue critical" = {
Anchor = "top";
Margin = 10;
Margin = 450;
Spacing = 2;
Limit = 10;
};
"Queue state" = {
Anchor = "top-left";
Margin = 10;
Spacing = 2;
Limit = 10;
};
"Queue music" = {
"Queue tasks" = {
Anchor = "bottom";
Margin = 10;
Limit = 1;
Margin = 0;
Spacing = 1;
Limit = 20;
};
Notification = {
Text = "\${message}";
@ -113,17 +91,11 @@ in {
};
};
"eventd/notification.event".text = lib.generators.toINI {} {
"Event mode *" = {
Actions = "mode";
};
"Event notification *" = {
Actions = "notification";
};
"Event feedback *" = {
Actions = "feedback";
};
"Event workspace *" = {
Actions = "workspace";
"Event notification kassandra" = {
Actions = "kassandra";
};
"Event command success" = {
Actions = "command-success";
@ -134,25 +106,6 @@ in {
"Event critical *" = {
Actions = "critical";
};
"Event state *" = {
Actions = "state";
};
};
"eventd/mode.action".text = lib.generators.toINI {} {
Action = {
Name = "mode";
};
Notification = {
};
NotificationBubble = {
Timeout = 0;
Queue = "mode";
Padding = 40;
MinWidth = 10;
};
NotificationText = {
Font = "Linux Libertine 40";
};
};
"eventd/command-success.action".text = lib.generators.toINI {} {
Action = {
@ -163,6 +116,7 @@ in {
};
NotificationBubble = {
Colour = colors.black;
Queue = "command";
};
};
"eventd/command-failure.action".text = lib.generators.toINI {} {
@ -173,30 +127,31 @@ in {
Text="<b>\${command}</b>\\nfailed after \${time} @ \${host}";
};
NotificationBubble = {
Queue = "critical";
Colour = colors.red;
};
};
"eventd/workspace.action".text = lib.generators.toINI {} {
"eventd/critical.action".text = lib.generators.toINI {} {
Action = {
Name = "workspace";
Name = "critical";
};
Notification = {
Text = "<b>\${title}</b>\${message/^/\\n}";
};
NotificationBubble = {
Queue = "workspace";
MinWidth = 10;
Queue = "critical";
Colour = colors.red;
};
};
"eventd/feedback.action".text = lib.generators.toINI {} {
"eventd/kassandra.action".text = lib.generators.toINI {} {
Action = {
Name = "feedback";
Name = "kassandra";
};
Notification = {
Text = "<b>\${title}</b>\${message/^/\\n}";
};
NotificationBubble = {
Timeout = 500;
Queue = "feedback";
Colour = colors.red;
Queue = "critical";
};
};
"eventd/notification.action".text = lib.generators.toINI {} {

View file

@ -4,6 +4,18 @@ let
workspaces = config.common.workspaces;
terminal = config.common.terminal;
exec = "exec --no-startup-id";
taskstatus = pkgs.writeShellScriptBin "taskstatus" ''
while true;
do
echo \
$(date "+%Y-%m-%d %a %H:%M") "|" \
$(cat ~/.kassandra_state | tail -n3 | sed "s/$/ | /") \
Inbox: $(task +PENDING -BLOCKED -TAGGED count) "|" \
Active Task: $(task rc.verbose=nothing active || echo "No task active") "|" \
Tags: $(task +PENDING -BLOCKED -project -optional -later rc.verbose=nothing tags | sed "s/\(.\)$/\1 |/" )
sleep 10s;
done
'';
addMods = oldbindings: builtins.foldl' (newbindings: key:
newbindings // {
"Mod4+${key}" = oldbindings.${key};
@ -12,7 +24,7 @@ let
{}
(builtins.attrNames oldbindings);
in {
imports = [
imports = [
./eventd.nix
./rofi
./urxvt.nix
@ -56,7 +68,18 @@ in {
text = colors.foreground;
};
};
bars = [ {
bars = [
{
statusCommand = "${taskstatus}/bin/taskstatus";
position = "top";
mode = "dock";
workspaceButtons = false;
colors = {
separator = colors.white;
background = colors.background;
};
}
{
mode = "hide";
colors = {
separator = colors.white;
@ -87,28 +110,32 @@ in {
titlebar = false;
border = 1;
};
gaps = {
inner = 0;
outer = 0;
smartBorders = "off";
smartGaps = false;
};
keybindings = addMods ({
# gaps = {
# inner = 0;
# outer = 0;
# smartBorders = "off";
# smartGaps = false;
# };
keybindings = {
"XF86AudioMute" = "exec pactl set-sink-mute '@DEFAULT_SINK@' toggle";
"XF86AudioLowerVolume" = "exec pactl set-sink-volume '@DEFAULT_SINK@' -5%";
"XF86AudioRaiseVolume" = "exec pactl set-sink-volume '@DEFAULT_SINK@' +5%";
"XF86AudioMicMute" = "exec pactl set-source-mute '@DEFAULT_SOURCE@' toggle";
"XF86MonBrightnessUp" = "exec xbacklight +5";
"XF86MonBrightnessDown" = "exec xbacklight -5";
"XF86Display" = "exec arandr";
"Ctrl+Escape" = "${exec} loginctl lock-session;";
} //
addMods ({
"Left" = "focus left";
"Down" = "focus down";
"Up" = "focus up";
"Right" = "focus right";
"Tab" = "${exec} rofi -show window";
"w" = "${exec} skippy-xd";
"Tab" = "${exec} skippy-xd";
"Prior" = "focus parent";
"Next" = "focus child";
"Ctrl+Escape" = "${exec} loginctl lock-session;";
"Return" = "${exec} ${terminal}";
"p" = "${exec} rofi-pass";
"r" = "${exec} rofi -show combi";
"o" = "${exec} rofi -show web";
"n" = "${exec} rofi -show ssh";
"a" = "${exec} tasklauncher";
"shift+Left" = "move left";
"shift+Down" = "move down";
"shift+Up" = "move up";
@ -118,8 +145,11 @@ in {
"t" = "layout tabbed";
"s" = "layout toggle split";
"q" = "kill";
"m" = "move workspace to output up";
"n" = "move workspace to output right";
"shift+space" = "floating toggle";
"shift+q" = "exec i3-nagbar -t warning -m 'do you want to exit i3?' -b 'yes' 'i3-msg exit'";
"space" = "exec ~/config/nixos/packages/rust-scripts/target/release/hotkeys";
} // builtins.foldl' (bindings: name: let
number = toString ((builtins.length (builtins.attrNames bindings)) / 2);
in

View file

@ -3,25 +3,25 @@ let
workspaces = config.common.workspaces;
terminal = config.common.terminal;
colors = config.common.colors;
rofiScriptWeb = pkgs.writeShellScriptBin "rofi-script-web" ''
if [[ -z $@ ]]; then
sed 's/^[0-9]*\(-r\)\? \?//;s/^\([^[:space:]]*\).*$/\1/' $HOME/.local/share/qutebrowser/history | tac
else
${pkgs.qutebrowser}/bin/qutebrowser "$1" > /dev/null &
fi
'';
rofiScriptI3 = pkgs.writeShellScriptBin "rofi-script-i3" ''
if [ -z $@ ]; then
(i3-msg -t get_workspaces | tr ',' '\n' | grep "name" | sed 's/"name":"\(.*\)"/\1/g';
echo "${builtins.concatStringsSep "\n" (builtins.foldl' (labels: name: let
number = toString (builtins.length labels);
in
labels ++ [ "${number}:${name}" ]
) [] workspaces)}") | sort -u
else
i3-msg workspace "$@" >/dev/null
fi
'';
#rofiScriptWeb = pkgs.writeShellScriptBin "rofi-script-web" ''
#if [[ -z $@ ]]; then
#sed 's/^[0-9]*\(-r\)\? \?//;s/^\([^[:space:]]*\).*$/\1/' $HOME/.local/share/qutebrowser/history | tac
#else
#${pkgs.qutebrowser}/bin/qutebrowser "$1" > /dev/null &
#fi
#'';
#rofiScriptI3 = pkgs.writeShellScriptBin "rofi-script-i3" ''
#if [ -z $@ ]; then
#(i3-msg -t get_workspaces | tr ',' '\n' | grep "name" | sed 's/"name":"\(.*\)"/\1/g';
#echo "${builtins.concatStringsSep "\n" (builtins.foldl' (labels: name: let
#number = toString (builtins.length labels);
#in
#labels ++ [ "${number}:${name}" ]
#) [] workspaces)}") | sort -u
#else
#i3-msg workspace "$@" >/dev/null
#fi
#'';
rofiTask = pkgs.writeScriptBin "tasklauncher" (builtins.readFile ./tasklauncher.py);
# recollPython = pkgs.python2.withPackages (ps: [
# pkgs.recoll
@ -31,8 +31,8 @@ in {
home = {
packages = with pkgs; [
rofi
rofiScriptWeb
rofiScriptI3
#rofiScriptWeb
#rofiScriptI3
rofiTask
# rofiFind
rofi-pass
@ -43,22 +43,22 @@ in {
rofi = {
enable = true;
extraConfig = ''
rofi.modi: combi,window,drun,run,ssh,keys,web:rofi-script-web,i3:rofi-script-i3
rofi.sidebar-mode: true
rofi.modi: combi,window,drun,run,ssh,keys
rofi.combi-modi: window,drun,run
'';
borderWidth = 0;
separator = "none";
fullscreen = false;
width = 1920;
terminal = terminal;
location = "center";
yoffset = 19;
location = "top";
scrollbar = false;
padding = 200;
padding = 10;
cycle = false;
lines = 30;
colors = {
window = {
background = "argb:a0${builtins.substring 1 6 colors.background}";
background = "argb:c0${builtins.substring 1 6 colors.background}";
border = colors.blue;
separator = colors.blue;
};

View file

@ -0,0 +1,453 @@
/* See LICENSE file for copyright and license details. */
/*
* appearance
*
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
*/
static char font[] = "Roboto Mono:pixelsize=12:antialias=true:autohint=true";
static int borderpx = 2;
#define histsize 20000
/*
* What program is execed by st depends of these precedence rules:
* 1: program passed with -e
* 2: utmp option
* 3: SHELL environment variable
* 4: value of shell in /etc/passwd
* 5: value of shell in config.h
*/
static char shell[] = "/bin/sh";
static char *utmp = NULL;
static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400";
/* identification sequence returned in DA and DECID */
static char vtiden[] = "\033[?6c";
/* Kerning / character bounding-box multipliers */
static float cwscale = 1.0;
static float chscale = 1.0;
/*
* word delimiter string
*
* More advanced example: " `'\"()[]{}"
*/
static char worddelimiters[] = " ";
/* selection timeouts (in milliseconds) */
static unsigned int doubleclicktimeout = 300;
static unsigned int tripleclicktimeout = 600;
/* alt screens */
static int allowaltscreen = 1;
/* frames per second st should at maximum draw to the screen */
static unsigned int xfps = 120;
static unsigned int actionfps = 30;
/*
* blinking timeout (set to 0 to disable blinking) for the terminal blinking
* attribute.
*/
static unsigned int blinktimeout = 800;
/*
* thickness of underline and bar cursors
*/
static unsigned int cursorthickness = 2;
/*
* bell volume. It must be a value between -100 and 100. Use 0 for disabling
* it
*/
static int bellvolume = 0;
/* default TERM value */
static char termname[] = "st-256color";
/*
* spaces per tab
*
* When you are changing this value, don't forget to adapt the »it« value in
* the st.info and appropriately install the st.info in the environment where
* you use this st version.
*
* it#$tabspaces,
*
* Secondly make sure your kernel is not expanding tabs. When running `stty
* -a` »tab0« should appear. You can tell the terminal to not expand tabs by
* running following command:
*
* stty tabs
*/
static unsigned int tabspaces = 3;
static const int alpha = 0xdd;
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
"@black@",
"@red@",
"@green@",
"@yellow@",
"@blue@",
"@magenta@",
"@cyan@",
"@white@",
/* 8 bright colors */
"@brightBlack@",
"@brightRed@",
"@brightGreen@",
"@brightYellow@",
"@brightBlue@",
"@brightMagenta@",
"@brightCyan@",
"@brightWhite@",
[255] = 0,
/* more colors can be added after 255 to use with DefaultXX */
"@foreground@",
"@background@",
"@background@"
};
/*
* Default colors (colorname index)
* foreground, background, cursor, reverse cursor
*/
static unsigned int defaultfg = 256;
static unsigned int defaultbg = 258;
static unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
/*
* Default shape of cursor
* 2: Block ("")
* 4: Underline ("_")
* 6: Bar ("|")
* 7: Snowman ("")
*/
static unsigned int cursorshape = 4;
/*
* Default colour and shape of the mouse cursor
*/
static unsigned int mouseshape = XC_xterm;
static unsigned int mousefg = 7;
static unsigned int mousebg = 0;
/*
* Colors used, when the specific fg == defaultfg. So in reverse mode this
* will reverse too. Another logic would only make the simple feature too
* complex.
*/
static unsigned int defaultitalic = 11;
static unsigned int defaultunderline = 7;
/*
* Internal mouse shortcuts.
* Beware that overloading Button1 will disable the selection.
*/
static MouseShortcut mshortcuts[] = {
/* button mask string */
{ Button4, XK_ANY_MOD, "\031" },
{ Button5, XK_ANY_MOD, "\005" },
};
/* Internal keyboard shortcuts. */
#define MODKEY Mod1Mask
static char* openurlcmd[] = { "@openUrlCmd@", NULL };
static Shortcut shortcuts[] = {
/* mask keysym function argument */
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
{ ControlMask, XK_Print, toggleprinter, {.i = 0} },
{ ShiftMask, XK_Print, printscreen, {.i = 0} },
{ XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
{ MODKEY|ShiftMask, XK_Prior, xzoom, {.f = +1} },
{ MODKEY|ShiftMask, XK_Next, xzoom, {.f = -1} },
{ MODKEY|ShiftMask, XK_Home, xzoomreset, {.f = 0} },
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
{ MODKEY|ShiftMask, XK_Insert, clippaste, {.i = 0} },
{ MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} },
{ MODKEY|ShiftMask, XK_V, clippaste, {.i = 0} },
{ MODKEY, XK_Num_Lock, numlock, {.i = 0} },
// { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
// { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
{ MODKEY|ShiftMask, XK_U, externalpipe, {.v = openurlcmd } },
};
/*
* Special keys (change & recompile st.info accordingly)
*
* Mask value:
* * Use XK_ANY_MOD to match the key no matter modifiers state
* * Use XK_NO_MOD to match the key alone (no modifiers)
* appkey value:
* * 0: no value
* * > 0: keypad application mode enabled
* * = 2: term.numlock = 1
* * < 0: keypad application mode disabled
* appcursor value:
* * 0: no value
* * > 0: cursor application mode enabled
* * < 0: cursor application mode disabled
* crlf value
* * 0: no value
* * > 0: crlf mode is enabled
* * < 0: crlf mode is disabled
*
* Be careful with the order of the definitions because st searches in
* this table sequentially, so any XK_ANY_MOD must be in the last
* position for a key.
*/
/*
* If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
* to be mapped below, add them to this array.
*/
static KeySym mappedkeys[] = { -1 };
/*
* State bits to ignore when matching key or button events. By default,
* numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
*/
static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
/*
* Override mouse-select while mask is active (when MODE_MOUSE is set).
* Note that if you want to use ShiftMask with selmasks, set this to an other
* modifier, set to 0 to not use it.
*/
static uint forceselmod = ShiftMask;
/*
* This is the huge key array which defines all compatibility to the Linux
* world. Please decide about changes wisely.
*/
static Key key[] = {
/* keysym mask string appkey appcursor crlf */
{ XK_KP_Home, ShiftMask, "\033[2J", 0, -1, 0},
{ XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1, 0},
{ XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1, 0},
{ XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1, 0},
{ XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0, 0},
{ XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1, 0},
{ XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1, 0},
{ XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0, 0},
{ XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1, 0},
{ XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1, 0},
{ XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0, 0},
{ XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1, 0},
{ XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1, 0},
{ XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0, 0},
{ XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1, 0},
{ XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1, 0},
{ XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0, 0},
{ XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0, 0},
{ XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0, 0},
{ XK_KP_End, ControlMask, "\033[J", -1, 0, 0},
{ XK_KP_End, ControlMask, "\033[1;5F", +1, 0, 0},
{ XK_KP_End, ShiftMask, "\033[K", -1, 0, 0},
{ XK_KP_End, ShiftMask, "\033[1;2F", +1, 0, 0},
{ XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0, 0},
{ XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0, 0},
{ XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0, 0},
{ XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0, 0},
{ XK_KP_Insert, ShiftMask, "\033[4l", -1, 0, 0},
{ XK_KP_Insert, ControlMask, "\033[L", -1, 0, 0},
{ XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0, 0},
{ XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0, 0},
{ XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0, 0},
{ XK_KP_Delete, ControlMask, "\033[M", -1, 0, 0},
{ XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0, 0},
{ XK_KP_Delete, ShiftMask, "\033[2K", -1, 0, 0},
{ XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0, 0},
{ XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0, 0},
{ XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0, 0},
{ XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0, 0},
{ XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0, 0},
{ XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0, 0},
{ XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0, -1},
{ XK_KP_Enter, XK_ANY_MOD, "\r\n", -1, 0, +1},
{ XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0, 0},
{ XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0, 0},
{ XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0, 0},
{ XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0, 0},
{ XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0, 0},
{ XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0, 0},
{ XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0, 0},
{ XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0, 0},
{ XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0, 0},
{ XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0, 0},
{ XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0, 0},
{ XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0, 0},
{ XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0, 0},
{ XK_Up, ShiftMask, "\033[1;2A", 0, 0, 0},
{ XK_Up, ControlMask, "\033[1;5A", 0, 0, 0},
{ XK_Up, Mod1Mask, "\033[1;3A", 0, 0, 0},
{ XK_Up, XK_ANY_MOD, "\033[A", 0, -1, 0},
{ XK_Up, XK_ANY_MOD, "\033OA", 0, +1, 0},
{ XK_Down, ShiftMask, "\033[1;2B", 0, 0, 0},
{ XK_Down, ControlMask, "\033[1;5B", 0, 0, 0},
{ XK_Down, Mod1Mask, "\033[1;3B", 0, 0, 0},
{ XK_Down, XK_ANY_MOD, "\033[B", 0, -1, 0},
{ XK_Down, XK_ANY_MOD, "\033OB", 0, +1, 0},
{ XK_Left, ShiftMask, "\033[1;2D", 0, 0, 0},
{ XK_Left, ControlMask, "\033[1;5D", 0, 0, 0},
{ XK_Left, Mod1Mask, "\033[1;3D", 0, 0, 0},
{ XK_Left, XK_ANY_MOD, "\033[D", 0, -1, 0},
{ XK_Left, XK_ANY_MOD, "\033OD", 0, +1, 0},
{ XK_Right, ShiftMask, "\033[1;2C", 0, 0, 0},
{ XK_Right, ControlMask, "\033[1;5C", 0, 0, 0},
{ XK_Right, Mod1Mask, "\033[1;3C", 0, 0, 0},
{ XK_Right, XK_ANY_MOD, "\033[C", 0, -1, 0},
{ XK_Right, XK_ANY_MOD, "\033OC", 0, +1, 0},
{ XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0, 0},
{ XK_Return, Mod1Mask, "\033\r", 0, 0, -1},
{ XK_Return, Mod1Mask, "\033\r\n", 0, 0, +1},
{ XK_Return, XK_ANY_MOD, "\r", 0, 0, -1},
{ XK_Return, XK_ANY_MOD, "\r\n", 0, 0, +1},
{ XK_Insert, ShiftMask, "\033[4l", -1, 0, 0},
{ XK_Insert, ShiftMask, "\033[2;2~", +1, 0, 0},
{ XK_Insert, ControlMask, "\033[L", -1, 0, 0},
{ XK_Insert, ControlMask, "\033[2;5~", +1, 0, 0},
{ XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0, 0},
{ XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0, 0},
{ XK_Delete, ControlMask, "\033[M", -1, 0, 0},
{ XK_Delete, ControlMask, "\033[3;5~", +1, 0, 0},
{ XK_Delete, ShiftMask, "\033[2K", -1, 0, 0},
{ XK_Delete, ShiftMask, "\033[3;2~", +1, 0, 0},
{ XK_Delete, XK_ANY_MOD, "\033[P", -1, 0, 0},
{ XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0, 0},
{ XK_BackSpace, XK_NO_MOD, "\177", 0, 0, 0},
{ XK_BackSpace, Mod1Mask, "\033\177", 0, 0, 0},
{ XK_Home, ShiftMask, "\033[2J", 0, -1, 0},
{ XK_Home, ShiftMask, "\033[1;2H", 0, +1, 0},
{ XK_Home, XK_ANY_MOD, "\033[H", 0, -1, 0},
{ XK_Home, XK_ANY_MOD, "\033[1~", 0, +1, 0},
{ XK_End, ControlMask, "\033[J", -1, 0, 0},
{ XK_End, ControlMask, "\033[1;5F", +1, 0, 0},
{ XK_End, ShiftMask, "\033[K", -1, 0, 0},
{ XK_End, ShiftMask, "\033[1;2F", +1, 0, 0},
{ XK_End, XK_ANY_MOD, "\033[4~", 0, 0, 0},
{ XK_Prior, ControlMask, "\033[5;5~", 0, 0, 0},
{ XK_Prior, ShiftMask, "\033[5;2~", 0, 0, 0},
{ XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0, 0},
{ XK_Next, ControlMask, "\033[6;5~", 0, 0, 0},
{ XK_Next, ShiftMask, "\033[6;2~", 0, 0, 0},
{ XK_Next, XK_ANY_MOD, "\033[6~", 0, 0, 0},
{ XK_F1, XK_NO_MOD, "\033OP" , 0, 0, 0},
{ XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0, 0},
{ XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0, 0},
{ XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0, 0},
{ XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0, 0},
{ XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0, 0},
{ XK_F2, XK_NO_MOD, "\033OQ" , 0, 0, 0},
{ XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0, 0},
{ XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0, 0},
{ XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0, 0},
{ XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0, 0},
{ XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0, 0},
{ XK_F3, XK_NO_MOD, "\033OR" , 0, 0, 0},
{ XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0, 0},
{ XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0, 0},
{ XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0, 0},
{ XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0, 0},
{ XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0, 0},
{ XK_F4, XK_NO_MOD, "\033OS" , 0, 0, 0},
{ XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0, 0},
{ XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0, 0},
{ XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0, 0},
{ XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0, 0},
{ XK_F5, XK_NO_MOD, "\033[15~", 0, 0, 0},
{ XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0, 0},
{ XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0, 0},
{ XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0, 0},
{ XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0, 0},
{ XK_F6, XK_NO_MOD, "\033[17~", 0, 0, 0},
{ XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0, 0},
{ XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0, 0},
{ XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0, 0},
{ XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0, 0},
{ XK_F7, XK_NO_MOD, "\033[18~", 0, 0, 0},
{ XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0, 0},
{ XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0, 0},
{ XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0, 0},
{ XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0, 0},
{ XK_F8, XK_NO_MOD, "\033[19~", 0, 0, 0},
{ XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0, 0},
{ XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0, 0},
{ XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0, 0},
{ XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0, 0},
{ XK_F9, XK_NO_MOD, "\033[20~", 0, 0, 0},
{ XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0, 0},
{ XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0, 0},
{ XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0, 0},
{ XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0, 0},
{ XK_F10, XK_NO_MOD, "\033[21~", 0, 0, 0},
{ XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0, 0},
{ XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0, 0},
{ XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0, 0},
{ XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0, 0},
{ XK_F11, XK_NO_MOD, "\033[23~", 0, 0, 0},
{ XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0, 0},
{ XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0, 0},
{ XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0, 0},
{ XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0, 0},
{ XK_F12, XK_NO_MOD, "\033[24~", 0, 0, 0},
{ XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0, 0},
{ XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0, 0},
{ XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0, 0},
{ XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0, 0},
{ XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0, 0},
{ XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0, 0},
{ XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0, 0},
{ XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0, 0},
{ XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0, 0},
{ XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0, 0},
{ XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0, 0},
{ XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0, 0},
{ XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0, 0},
{ XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0, 0},
{ XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0, 0},
{ XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0, 0},
{ XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0, 0},
{ XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0, 0},
{ XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0, 0},
{ XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0, 0},
{ XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0, 0},
{ XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0, 0},
{ XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0, 0},
{ XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0, 0},
{ XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0, 0},
{ XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0, 0},
{ XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0, 0},
};
/*
* Selection types' masks.
* Use the same masks as usual.
* Button1Mask is always unset, to make masks match between ButtonPress.
* ButtonRelease and MotionNotify.
* If no match is found, regular selection is used.
*/
static uint selmasks[] = {
[SEL_RECTANGULAR] = Mod1Mask,
};
/*
* Printable characters in ASCII, used to estimate the advance width
* of single wide characters.
*/
static char ascii_printable[] =
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~";

View file

@ -0,0 +1,18 @@
pkgs: colors:
let
openUrlCmd = pkgs.writeScript "openUrlCmd" ''
${pkgs.xurls}/bin/xurls | ${pkgs.rofi}/bin/rofi -dmenu | xargs -r ${pkgs.firefox}/bin/firefox
'';
in
pkgs.st.overrideDerivation (old: {
patches = old.patches ++ [
# scrollback patches from https://st.suckless.org/patches/scrollback/
# https://st.suckless.org/patches/scrollback/st-scrollback-0.7.diff
./st-alpha-0.7.diff
./st-externalpipe-0.7.diff
];
enableParallelBuilds = true;
prePatch = ''
cp ${(pkgs.substituteAll { src = ./config.h; inherit openUrlCmd; inherit (colors) black brightBlack red brightRed green brightGreen yellow brightYellow blue brightBlue magenta brightMagenta cyan brightCyan white brightWhite background foreground;})} config.h
'';
})

View file

@ -0,0 +1,188 @@
diff --git a/config.def.h b/config.def.h
index b41747f..e22ebd2 100644
--- a/config.def.h
+++ b/config.def.h
@@ -82,6 +82,9 @@ static char termname[] = "st-256color";
*/
static unsigned int tabspaces = 8;
+/* bg opacity */
+static const int alpha = 0xdd;
+
/* Terminal colors (16 first used in escape sequence) */
static const char *colorname[] = {
/* 8 normal colors */
@@ -109,6 +112,7 @@ static const char *colorname[] = {
/* more colors can be added after 255 to use with DefaultXX */
"#cccccc",
"#555555",
+ "black",
};
@@ -117,7 +121,7 @@ static const char *colorname[] = {
* foreground, background, cursor, reverse cursor
*/
static unsigned int defaultfg = 7;
-static unsigned int defaultbg = 0;
+static unsigned int defaultbg = 257;
static unsigned int defaultcs = 256;
static unsigned int defaultrcs = 257;
diff --git a/config.mk b/config.mk
index c84c5ee..19a03bb 100644
--- a/config.mk
+++ b/config.mk
@@ -14,7 +14,7 @@ X11LIB = /usr/X11R6/lib
INCS = -I. -I/usr/include -I${X11INC} \
`pkg-config --cflags fontconfig` \
`pkg-config --cflags freetype2`
-LIBS = -L/usr/lib -lc -L${X11LIB} -lm -lrt -lX11 -lutil -lXft \
+LIBS = -L/usr/lib -lc -L${X11LIB} -lm -lrt -lX11 -lutil -lXext -lXft -lXrender\
`pkg-config --libs fontconfig` \
`pkg-config --libs freetype2`
diff --git a/st.c b/st.c
index 2594c65..f9ba75b 100644
--- a/st.c
+++ b/st.c
@@ -61,6 +61,7 @@ char *argv0;
#define XK_ANY_MOD UINT_MAX
#define XK_NO_MOD 0
#define XK_SWITCH_MOD (1<<13)
+#define OPAQUE 0Xff
/* macros */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -81,6 +82,8 @@ char *argv0;
(t1.tv_nsec-t2.tv_nsec)/1E6)
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+#define USE_ARGB (alpha != OPAQUE && opt_embed == NULL)
+
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
#define IS_TRUECOL(x) (1 << 24 & (x))
#define TRUERED(x) (((x) & 0xff0000) >> 8)
@@ -268,6 +271,7 @@ typedef struct {
int w, h; /* window width and height */
int ch; /* char height */
int cw; /* char width */
+ int depth; /* bit depth */
char state; /* focus, redraw, visible */
int cursor; /* cursor style */
} XWindow;
@@ -3137,7 +3141,7 @@ xresize(int col, int row)
XFreePixmap(xw.dpy, xw.buf);
xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
- DefaultDepth(xw.dpy, xw.scr));
+ xw.depth);
XftDrawChange(xw.draw, xw.buf);
xclear(0, 0, xw.w, xw.h);
}
@@ -3191,6 +3195,14 @@ xloadcols(void)
else
die("Could not allocate color %d\n", i);
}
+
+ /* set alpha value of bg color */
+ if (USE_ARGB) {
+ dc.col[defaultbg].color.alpha = (0xffff * alpha) / OPAQUE; //0xcccc;
+ dc.col[defaultbg].pixel &= 0x00111111;
+ dc.col[defaultbg].pixel |= alpha << 24; // 0xcc000000;
+ }
+
loaded = 1;
}
@@ -3212,6 +3224,16 @@ xsetcolorname(int x, const char *name)
return 0;
}
+void
+xtermclear(int col1, int row1, int col2, int row2) {
+ XftDrawRect(xw.draw,
+ &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg],
+ borderpx + col1 * xw.cw,
+ borderpx + row1 * xw.ch,
+ (col2-col1+1) * xw.cw,
+ (row2-row1+1) * xw.ch);
+}
+
/*
* Absolute coordinates.
*/
@@ -3443,7 +3465,38 @@ xinit(void)
if (!(xw.dpy = XOpenDisplay(NULL)))
die("Can't open display\n");
xw.scr = XDefaultScreen(xw.dpy);
- xw.vis = XDefaultVisual(xw.dpy, xw.scr);
+ xw.depth = (USE_ARGB) ? 32: XDefaultDepth(xw.dpy, xw.scr);
+ if (! USE_ARGB)
+ xw.vis = XDefaultVisual(xw.dpy, xw.scr);
+ else {
+ XVisualInfo *vis;
+ XRenderPictFormat *fmt;
+ int nvi;
+ int i;
+
+ XVisualInfo tpl = {
+ .screen = xw.scr,
+ .depth = 32,
+ .class = TrueColor
+ };
+
+ vis = XGetVisualInfo(xw.dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &tpl, &nvi);
+ xw.vis = NULL;
+ for(i = 0; i < nvi; i ++) {
+ fmt = XRenderFindVisualFormat(xw.dpy, vis[i].visual);
+ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
+ xw.vis = vis[i].visual;
+ break;
+ }
+ }
+
+ XFree(vis);
+
+ if (! xw.vis) {
+ fprintf(stderr, "Couldn't find ARGB visual.\n");
+ exit(1);
+ }
+ }
/* font */
if (!FcInit())
@@ -3453,7 +3506,10 @@ xinit(void)
xloadfonts(usedfont, 0);
/* colors */
- xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
+ if (! USE_ARGB)
+ xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
+ else
+ xw.cmap = XCreateColormap(xw.dpy, XRootWindow(xw.dpy, xw.scr), xw.vis, None);
xloadcols();
/* adjust fixed window geometry */
@@ -3476,16 +3532,17 @@ xinit(void)
if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
parent = XRootWindow(xw.dpy, xw.scr);
xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
- xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
+ xw.w, xw.h, 0, xw.depth, InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs);
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False;
- dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
+ xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h, xw.depth);
+ dc.gc = XCreateGC(xw.dpy,
+ (USE_ARGB)? xw.buf: parent,
+ GCGraphicsExposures,
&gcvalues);
- xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
- DefaultDepth(xw.dpy, xw.scr));
XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h);

View file

@ -0,0 +1,90 @@
diff --git a/st.c b/st.c
index 2594c65..ecd9bdc 100644
--- a/st.c
+++ b/st.c
@@ -329,6 +329,7 @@ static void clipcopy(const Arg *);
static void clippaste(const Arg *);
static void numlock(const Arg *);
static void selpaste(const Arg *);
+static void externalpipe(const Arg *);
static void xzoom(const Arg *);
static void xzoomabs(const Arg *);
static void xzoomreset(const Arg *);
@@ -337,6 +338,9 @@ static void printscreen(const Arg *) ;
static void toggleprinter(const Arg *);
static void sendbreak(const Arg *);
+/* variables used in config.h */
+static char winid[64];
+
/* Config.h for applying patches and the configuration. */
#include "config.h"
@@ -2922,6 +2926,59 @@ eschandle(uchar ascii)
}
void
+externalpipe(const Arg *arg)
+{
+ int to[2];
+ char buf[UTF_SIZ];
+ void (*oldsigpipe)(int);
+ Glyph *bp, *end;
+ int lastpos, n, newline;
+
+ if (pipe(to) == -1)
+ return;
+
+ switch (fork()) {
+ case -1:
+ close(to[0]);
+ close(to[1]);
+ return;
+ case 0:
+ dup2(to[0], STDIN_FILENO);
+ close(to[0]);
+ close(to[1]);
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ fprintf(stderr, "st: execvp %s ", ((char **)arg->v)[0]);
+ perror("failed");
+ exit(0);
+ }
+
+ close(to[0]);
+ /* ignore sigpipe for now, in case child exits early */
+ oldsigpipe = signal(SIGPIPE, SIG_IGN);
+ newline = 0;
+ for (n = 0; n < term.row; n++) {
+ bp = term.line[n];
+ lastpos = MIN(tlinelen(n) + 1, term.col) - 1;
+ if (lastpos < 0)
+ break;
+ end = &bp[lastpos + 1];
+ for (; bp < end; ++bp)
+ if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
+ break;
+ if ((newline = term.line[n][lastpos].mode & ATTR_WRAP))
+ continue;
+ if (xwrite(to[1], "\n", 1) < 0)
+ break;
+ newline = 0;
+ }
+ if (newline)
+ (void)xwrite(to[1], "\n", 1);
+ close(to[1]);
+ /* restore */
+ signal(SIGPIPE, oldsigpipe);
+}
+
+void
tputc(Rune u)
{
char c[UTF_SIZ];
@@ -3479,6 +3536,7 @@ xinit(void)
xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
| CWEventMask | CWColormap, &xw.attrs);
+ snprintf(winid, LEN(winid), "%lu", (unsigned long)xw.win);
memset(&gcvalues, 0, sizeof(gcvalues));
gcvalues.graphics_exposures = False;

View file

@ -60,6 +60,9 @@
Notification = {
Text = template;
};
NotificationBubble = {
Queue = "tasks";
};
};
};
in {

34
homes/apollo/battery.nix Normal file
View file

@ -0,0 +1,34 @@
{pkgs, ... }:
let
battery-watch = pkgs.writeShellScriptBin "battery-watch" ''
critical_level=20 #percent
while true
do
if [ "$(${pkgs.acpi}/bin/acpi -a | grep -o off)" == "off" ]; then
battery_level=`${pkgs.acpi}/bin/acpi -b | sed 's/.*[dg], //g;s/\%,.*//g'`
if [ $battery_level -le $critical_level ]; then
${pkgs.eventd}/bin/eventc critical battery -d "title='Battery level is low!'" -d "message='Only $battery_level% of the charge remains.'"
else
${pkgs.eventd}/bin/eventc notification battery -d "title='Battery is discharging!'" -d "message='Only $battery_level% of the charge remains.'"
sleep 18m
fi
fi
sleep 2m
done
'';
in {
systemd.user = {
services.battery = {
Unit = {
Description = "Watch battery state and warn user";
};
Service = {
ExecStart="/bin/sh ${battery-watch}/bin/battery-watch";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -1,4 +1,19 @@
{ pkgs, ... }:
let
jali = with pkgs.python36Packages; buildPythonApplication rec {
name = "${pname}-${version}";
pname = "jali";
doCheck = false;
version = "1d1c5d0a";
src = pkgs.fetchgit {
url = "https://git.darmstadt.ccc.de/jali/jali.git";
rev = version;
sha256 = "1nzzangp7yr2gq66qz7wk2cqqwjlhrfaqmc85qigjv4vpfmlphl0";
};
propagatedBuildInputs = with pkgs; [ jinja2 pendulum GitPython aqbanking ];
};
in
{
imports = [
@ -6,6 +21,8 @@
../../home-common/my-systems.nix
../../home-common/graphical
../../home-common/latex.nix
./battery.nix
./sleep-nag.nix
];
programs = {
@ -19,6 +36,9 @@
};
};
};
home.sessionVariables = {
MOZ_USE_XINPUT2 = "1";
};
services = {
udiskie = {
@ -32,14 +52,31 @@
] ++ (with pkgs; [
# web
chromium
signal-desktop
tdesktop
acpi
dino
rustracer
arandr
qutebrowser
mumble
xorg.xev
xorg.xbacklight
meld
icedtea8_web
hledger
haskellPackages.hledger-ui
ledger
jali
# tools & office
feh
gimp
imagemagick
libreoffice-fresh

View file

@ -0,0 +1,26 @@
{pkgs, ... }:
let
sleep-nag = pkgs.writeShellScriptBin "sleep-nag" ''
while true
do
if [[ `date +%H` -ge 23 ]] || [[ `date +%H` -lt 6 ]]; then
${pkgs.eventd}/bin/eventc notification kassandra -d "title='Es ist $(date +%H:%M) Uhr: Zeit ins Bett zu gehen!'" -d "message='Du kannst das hier auch morgen tun!'"
fi
sleep 10m
done
'';
in {
systemd.user = {
services.sleep-nag = {
Unit = {
Description = "Sleep nag";
};
Service = {
ExecStart="/bin/sh ${sleep-nag}/bin/sleep-nag";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -0,0 +1,83 @@
require ["vnd.dovecot.duplicate", "fileinto", "regex", "imap4flags", "mailbox"];
if header :regex "X-spamd-result" "st-ludwig-darmstadt.de" {
keep;
}
elsif anyof (
header :regex "X-spam" "^yes$",
header :contains "From" "paypal@mail.paypal.de",
header :contains "To" "lac@maralorn.de",
header :contains "To" "malte@maralorn.de",
header :contains "To" "maltemail@maralorn.de",
header :contains "To" "mail@maralorn.de",
header :contains "From" "promotion5@amazon.de")
{
setflag "\\Seen";
fileinto "Spam";
}
elsif anyof (
duplicate
) {
setflag "\\Seen";
fileinto "Trash";
}
elsif anyof (
address :domain :is "From" [
"maralorn.de",
"facebookmail.com"
],
address :domain :is "From" [
"tickets.darmstadt.ccc.de"
],
header :contains "List-Id" [
"hackspace.lists.darmstadt.ccc.de",
"members.lists.darmstadt.ccc.de",
"darmstadt.lists.darmstadt.ccc.de"
]
)
{
setflag "\\Seen";
}
elsif anyof (
header :is "From" "vib@vereinsknowhow.de",
address :domain :is "From" [
"dpg-physik.de",
"physikstudenten.de"
],
header :contains "List-Id" [
"intern.lists.ccc.de",
"erfa.lists.ccc.de",
"ctf-announce.lists.cased.de",
"ctf-team.lists.cased.de",
"darmstadt-freifunk.net"
],
header :contains "Subject" "Moodle TU Darmstadt: Zusammenfassung des Forums"
)
{
setflag "\\Seen";
fileinto :create "Move.readlater";
}
elsif header :contains ["To", "From", "CC"] [
"noc@karlshof.de",
"karlshof@whm.stwda.de",
"noc-karlshof@maralorn.de"
]
{
setflag "\\Seen";
fileinto :create "Archiv.karlshof.noc";
}
elsif header :contains "List-Id" "chor.lists.tu-darmstadt.de" {
setflag "\\Seen";
fileinto :create "Archiv.tuchor";
}
elsif header :contains "From" "kdwachlin@web.de" {
setflag "\\Seen";
fileinto "Archiv.unsortiert";
}
elsif header :contains "Subject" "[VED-Wiki]" {
setflag "\\Seen";
fileinto :create "Archiv.ved.wiki";
} elsif header :regex "X-spamd-result" "[2-5]\.[0-9]\{2\} / 15\.00" {
setflag "\\Seen";
}

View file

@ -1,9 +1,16 @@
{
imports = [
../../snippets/everywhere.nix
../../snippets/my-systems.nix
../../home-common/default.nix
../../home-common/my-systems.nix
./morgenreport.nix
./sort-mail.nix
./habitask.nix
];
home.forceCopies.paths = [ ".dovecot.sieve" ];
home.file = {
sieve-rules = {
target = ".dovecot.sieve";
text = builtins.readFile ./dovecot.sieve;
};
};
}

View file

@ -1,17 +1,20 @@
{ pkgs, ... }:
{
let
morgenreport-script = pkgs.writeShellScriptBin "morgenreport" ''
cd $HOME/data/aktuell/media/ebooks/morgenreport/
DATE=`date +%Y-%m-%d`
PATH=$PATH:/run/wrappers/bin/
PATH=$PATH:${pkgs.imagemagickBig}/bin
PATH=$PATH:${pkgs.qt5.qtbase}/bin
PATH=$PATH:${pkgs.qt5.qtsvg}/bin
${pkgs.calibre}/bin/ebook-convert $HOME/data/aktuell/it/code/calibre-recipes/morgenreport.recipe morgenreport-$DATE.mobi --output-profile=kindle_pw3
echo "File created, sending to kindle now "
echo 'Siehe Anhang' | ${pkgs.mutt}/bin/mutt -s "Morgenreport $DATE" -a morgenreport-$DATE.mobi -- maralorn@kindle.com
'';
in {
home.packages = [ morgenreport-script];
systemd.user = {
services.morgenreport =
let
morgenreport-script = pkgs.writeShellScriptBin "morgenreport" ''
cd $HOME/data/aktuell/media/ebooks/morgenreport/
DATE=`date +%Y-%m-%d`
${pkgs.calibre}/bin/ebook-convert $HOME/data/aktuell/it/code/calibre-recipes/morgenreport.recipe morgenreport-$DATE.mobi --output-profile=kindle_pw3
echo "File created, sending to kindle now "
PATH=$PATH:/run/wrappers/bin/
echo 'Siehe Anhang' | ${pkgs.mutt}/bin/mutt -s "Morgenreport $DATE" -a morgenreport-$DATE.mobi -- maralorn@kindle.com
'';
in {
services.morgenreport = {
Unit = {
Description = "Send morgenreport to kindle";
};

View file

@ -21,7 +21,6 @@
automatic = true;
options = "--delete-older-than 5d";
};
package = pkgs.nixUnstable;
optimise.automatic = true;
};
system.autoUpgrade.enable = true;
@ -32,6 +31,10 @@
mutableUsers = false;
};
security.sudo.extraConfig = "
Defaults timestamp_type=global, timestamp_timeout=15
";
networking.firewall.allowPing = true;
services = {
@ -44,7 +47,6 @@
gnumake
python3
python
pandoc
mkpasswd
rxvt_unicode.terminfo
htop

View file

@ -7,8 +7,8 @@
hostName = "charon.olymp.space";
interfaces.ens3 = {
ip4 = [ { address = "45.32.154.139"; prefixLength = 22; } ];
ip6 = [ { address = "2001:19f0:6c01:b0d::1"; prefixLength = 64; } ];
ipv4.addresses = [{ address = "45.32.154.139"; prefixLength = 22; }];
ipv6.addresses = [{ address = "2001:19f0:6c01:b0d::1"; prefixLength = 64; }];
};
defaultGateway = "45.32.152.1";
nameservers = [ "108.61.10.10" "2001:19f0:300:1704::6" ];

View file

@ -31,7 +31,7 @@
socketActivation = false;
rspamd = {
extraConfig = ''
extended_spam_headers = yes;
extended_spam_headers = true;
'';
enable = true;
};
@ -45,6 +45,8 @@
sslServerKey = "/var/lib/acme/charon.olymp.space/key.pem";
extraConfig =
''
postmaster_address=postmaster@charon.olymp.space
ssl = required
service auth {
unix_listener /var/lib/postfix/queue/private/auth {

View file

@ -53,6 +53,7 @@
server_name = "maralorn.de";
database_type = "psycopg2";
max_upload_size = "30M";
create_local_database = false;
database_args = {
user = "matrix-synapse";
database = "matrix-synapse";

40
modules/mpd.nix Normal file
View file

@ -0,0 +1,40 @@
{ lib, config, pkgs, ... }:
with lib;
{
options = {
mpd = {
mkOption {
default = false;
type = types.bool;
};
};
};
systemd.user = {
services.morgenreport =
let
morgenreport-script = pkgs.writeShellScriptBin "morgenreport" ''
cd $HOME/data/aktuell/media/ebooks/morgenreport/
DATE=`date +%Y-%m-%d`
${pkgs.calibre}/bin/ebook-convert $HOME/data/aktuell/it/code/calibre-recipes/morgenreport.recipe morgenreport-$DATE.mobi --output-profile=kindle_pw3
echo "File created, sending to kindle now "
PATH=$PATH:/run/wrappers/bin/
echo 'Siehe Anhang' | ${pkgs.mutt}/bin/mutt -s "Morgenreport $DATE" -a morgenreport-$DATE.mobi -- maralorn@kindle.com
'';
in {
Unit = {
Description = "Send morgenreport to kindle";
};
Service = {
Type = "oneshot";
ExecStart="/bin/sh ${morgenreport-script}/bin/morgenreport";
};
};
timers.morgenreport = {
Timer = {
OnCalendar = "20:00";
};
};
};
}

View file

@ -1,7 +1,7 @@
{ fetchFromGitHub, stdenv, ninja, meson, pkgconfig, glib, cairo, gdk_pixbuf, glib_networking, pango, libudev, xorg, libxslt, docbook_xml_xslt, git, libuuid, dbus, libsoup, docbook_xml_dtd_45, docbook5_xsl, gettext, autoconf, libtool, utillinux, libxkbcommon }:
stdenv.mkDerivation rec {
name = "eventd";
version = "279d3c3";
version = "v0.24.1";
src = fetchFromGitHub {
owner = "sardemff7";
repo = "eventd";
@ -9,9 +9,6 @@ stdenv.mkDerivation rec {
sha256 = "162gr3agmjn6d0wdj3lixv8qfvgfm9qg3wphbvwywdp4qcwvnjz8";
fetchSubmodules = true;
};
patchPhase = ''
sed s/0.44.1/0.43.0/ -i meson.build
'';
buildInputs = [
ninja
meson

View file

@ -1,12 +1,12 @@
[[package]]
name = "backtrace"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -15,7 +15,7 @@ name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -26,17 +26,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.1"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.10"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -44,9 +44,9 @@ name = "chrono"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -71,7 +71,6 @@ dependencies = [
[[package]]
name = "dialog"
version = "0.1.0"
source = "git+https://git.darmstadt.ccc.de/maralorn/dialog-rs.git#f33f0c9ac4f4407899da55507147a8a808e2f521"
dependencies = [
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustyline 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -93,7 +92,7 @@ name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -101,7 +100,7 @@ name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -110,11 +109,30 @@ name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "iso8601"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kairos"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iso8601 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -124,17 +142,35 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "linked-hash-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -147,21 +183,29 @@ dependencies = [
]
[[package]]
name = "num-integer"
version = "0.1.36"
name = "nom"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.3.6"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -177,7 +221,7 @@ name = "quote"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -199,13 +243,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "rust-scripts"
version = "0.1.0"
dependencies = [
"dialog 0.1.0 (git+https://git.darmstadt.ccc.de/maralorn/dialog-rs.git)",
"task-hookrs 0.5.0 (git+https://github.com/maralorn/task-hookrs.git?branch=preview)",
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dialog 0.1.0",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kairos 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"task-hookrs 0.5.0",
"uuid 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -223,37 +275,38 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.42"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.42"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.13.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_yaml"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -268,10 +321,10 @@ dependencies = [
[[package]]
name = "syn"
version = "0.13.1"
version = "0.13.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -287,15 +340,14 @@ dependencies = [
[[package]]
name = "task-hookrs"
version = "0.5.0"
source = "git+https://github.com/maralorn/task-hookrs.git?branch=preview#24a3ad5519a9bb26fcf0eeee368f8899211e47e1"
dependencies = [
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -309,7 +361,7 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.39"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
@ -337,9 +389,9 @@ name = "uuid"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -371,46 +423,58 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "yaml-rust"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e"
"checksum backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea58cd16fd6c9d120b5bcb01d63883ae4cc7ba2aed35c1841b862a3c7ef6639"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba"
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6"
"checksum derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c998e6ab02a828dd9735c18f154e14100e674ed08cb4e1938f0e4177543f439"
"checksum derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "735e24ee9e5fa8e16b86da5007856e97d592e11867e45d76e0c0d0a164a0b757"
"checksum dialog 0.1.0 (git+https://git.darmstadt.ccc.de/maralorn/dialog-rs.git)" = "<none>"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum encode_unicode 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "28d65f1f5841ef7c6792861294b72beda34c664deb8be27970f36c306b7da1ce"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum iso8601 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83e03bd24a535789afdce1c8274f2f5dc1a265c39e937884426ecd5322285d1e"
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
"checksum kairos 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4eb20c83592f59719373e14ef037aea9b09e11bc8b02e2f0ca2e9c8eeac6ba6b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79"
"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe"
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
"checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118"
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
"checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45"
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb"
"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
"checksum rustyline 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00b06ac9c8e8e3e83b33d175d39a9f7b6c2c930c82990593719c8e48788ae2d9"
"checksum serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "a73973861352c932ed1365ce22b32467ce260ac4c8db11cf750ce56334ff2dcf"
"checksum serde_derive 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b392c5a0cebb98121454531c50e60e2ffe0fbeb1a44da277da2d681d08d7dc0b"
"checksum serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d30c4596450fd7bbda79ef15559683f9a79ac0193ea819db90000d7e1cae794"
"checksum serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8c6c4e049dc657a99e394bd85c22acbf97356feeec6dbf44150f2dcf79fb3118"
"checksum serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "97f6a6c3caba0cf8f883b53331791036404ce3c1bd895961cf8bb2f8cecfd84b"
"checksum serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "f51b0ef935cf8a41a77bce553da1f8751a739b7ad82dd73669475a22e6ecedb0"
"checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1"
"checksum serde_yaml 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "107bb818146aaf922e7bbcf6a940f1db2f0dcf381779b451e400331b2c6f86db"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
"checksum syn 0.13.10 (registry+https://github.com/rust-lang/crates.io-index)" = "77961dcdac942fa8bc033c16f3a790b311c8a27d00811b878ebd8cf9b7ba39d5"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum task-hookrs 0.5.0 (git+https://github.com/maralorn/task-hookrs.git?branch=preview)" = "<none>"
"checksum termios 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70226acdf12d182df757d9fb07c0257a1558ec48c8059f607d6b38145ce4e2fa"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
@ -420,3 +484,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"

View file

@ -4,5 +4,14 @@ version = "0.1.0"
authors = ["Malte Brandy <malte.brandy@maralorn.de>"]
[dependencies]
task-hookrs = { git = "https://github.com/maralorn/task-hookrs.git", branch = "preview"}
dialog = { git = "https://git.darmstadt.ccc.de/maralorn/dialog-rs.git" }
#task-hookrs = { git = "https://github.com/maralorn/task-hookrs.git", branch = "preview"}
task-hookrs = { path = "/home/maralorn/code/task-hookrs"}
dialog = { path = "/home/maralorn/code/dialog-rs" }
lazy_static = "1.0.0"
uuid = "0.6"
kairos = "0.1.0"
chrono = "0.4.2"
error-chain = "0.11.0"
serde_derive = "1.0"
serde = "1.0"
serde_yaml = "0.7"

View file

@ -1,74 +1,162 @@
extern crate rust_scripts;
use rust_scripts::hotkeys::*;
extern crate task_hookrs;
#[macro_use]
extern crate error_chain;
fn main() {
let state;
let location;
use std::rc::Rc;
use rust_scripts::hotkeys::{run, term, holdterm, menu, main_loop, Next};
use rust_scripts::kassandra::{kassandra, change_state, new_tasks};
use rust_scripts::error::Result;
let volume_up = (
c("Volume up"),
Fork(cmd(
vec!["pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"],
)),
quick_main!(|| -> Result<()> {
let w17menu = {
let summer = holdterm("Summer", "ssh summer@door.w17.io");
let lock = holdterm("Lock", "ssh close@door.w17.io");
let unlock = holdterm("Unlock", "ssh open@door.w17.io");
let mpd_whisky = term("MPD Whisky", "ncmpcpp -h whisky");
let hub = run("Hub", "firefox --new-window https://hub.w17.io");
let kitchen = run("Kitchen", "firefox --new-window http://kitchen.w17.io");
menu("w17", vec![lock, unlock, summer, hub, mpd_whisky, kitchen])
};
let powermenu = {
let inhibit = term(
"Inhibit Suspend",
"systemd-inhibit --what handle-lid-switch watch echo 'Lid switch inhibited'",
);
let logout = run("Logout", "i3-msg exit");
let shutdown = run("Shutdown", "systemctl poweroff");
let suspend = run("Suspend", "systemctl suspend");
let reboot = run("Reboot", "systemctl reboot");
let lock = run("Lock", "loginctl lock-session");
menu(
"Power",
vec![shutdown, suspend, reboot, lock, inhibit, logout],
)
};
let soundmenu = {
let mpd = term("MPD", "ncmpcpp");
let grammofy = run("Grammofy", "firefox --new-window https://grammofy.com");
let pavucontrol = run("Lautstärke", "pavucontrol");
menu("Sound", vec![mpd, pavucontrol, grammofy])
};
let apps = {
menu(
"Apps",
vec![
run("Launch", "rofi -show combi"),
run("Private Browser", "firefox --private-window"),
run("Browser", "firefox --new-window"),
run("Deluge", "deluge"),
run("Filemanager", "nautilus"),
menu(
"Messaging",
vec![
run("Mails", "evolution"),
run("Riot", "firefox --new-window https://riot.im/app"),
run("WhatsApp", "firefox --new-window https://web.whatsapp.com"),
run("Telegram", "telegram-desktop"),
run("Signal", "signal-desktop"),
run("Jabber", "dino"),
run(
"Regiotelko",
"mumble mumble://maralorn@mumble.c3pb.de/CCC/Regiotelko"
),
]
),
menu(
"Accounting",
vec![
holdterm("Jali", "jali -l ."),
run(
"Beschlüsse",
"firefox --new-window https://git.darmstadt.ccc.de/vorstand/beschluesse/raw/master/beschl%C3%BCsse"
),
term(
"Private Buchhaltung",
"hledger -f data/aktuell/lebenshaltung/buchhaltung/buchhaltung.ledger ui"
),
term(
"CDA Buchhaltung",
"hledger -f data/aktuell/ccc/cda/vorstand/buchhaltung/buchhaltung.ledger ui"
),
]
),
],
)
};
let maintenance = {
let keymenu = menu(
"Keymap",
vec![
run("neo", "setxkbmap de neo"),
run("qwertz", "setxkbmap de"),
],
);
let monitor = term("Monitor", "htop");
let wifi = term("WLAN", "nmtui");
let update_home = holdterm("Update Home", "home-manager switch");
let update_sys = holdterm("Update Sys", "sudo nixos-rebuild switch");
let gc = holdterm("Collect Garbage", "nix-collect-garbage -d");
let optimise = holdterm("Optimise", "nix optimise-store");
menu(
"Maintenance",
vec![
wifi,
update_home,
update_sys,
gc,
optimise,
monitor,
keymenu,
run("Bildschirme", "arandr"),
],
)
};
let ssh = menu(
"ssh",
vec![
("kiva", "brandy@kiva-forward"),
("ag", "brandy@ag-forward"),
("whisky", "whisky"),
("vorstand", "vorstand"),
("shells", "shells"),
("charon", "charon"),
].into_iter()
.map(|(name, login)| term(name, &format!("ssh {}", login)))
.collect(),
);
let volume_down = (
c("Volume down"),
Fork(cmd(
vec!["pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"],
)),
);
let ncmpcpp = (c("Music"), Exec(cmd(vec!["urxvt", "-e", "ncmpcpp"])));
let wizard = (c("Magic"), Script);
let startmenu = Menu((c("Hauptmenü"), vec![wizard]))
// arandr?
// keymap
// shutdown options
// lautstärken micro, lautsprecher, regler zeigen
// zeige Buchhaltung
// mumble
// hub.w17.io
// select wifi
// sshuttle
// tinc
// routes
// debug network
// grammofy
// browser + pm
// IM dienste
// evolution
// door options, music options, hub options
// lock inhibit
// suspend inhibit
// ssh kiva, ag, whisky, vorstand, shells, charon
let w17menu
main_loop(startmenu);
// Brightness -> i3
// Basic Sound -> i3
}
fn wizard() -> Next {
// states: work, research, normal, idle
// enter new tasks
// always: add task/subtask
// what about running tasks?
// check unread E-Mail + ak?
// update
// system
// home
// inbox
//
// treesort
// mails
// sort inbox
// sort inboxkiva
// sort inboxak
// sort to sort
// go trough todo
// go trough toread
// go trough readlater
// pick tasks
// optional, later
// await
// do accounting own, cda
// dirty gits
// task generator
// task completion helper
}
let startmenu = menu(
"Hauptmenü",
vec![
(
"New Task".into(),
Next::Do(Rc::new(|| new_tasks().map(|_| Next::Exit)))
),
(
"Kassandra".into(),
Next::Do(Rc::new(|| kassandra().map(|_| Next::Exit)))
),
(
"Change State".into(),
Next::Do(Rc::new(|| change_state().map(|_| Next::Exit)))
),
ssh,
apps,
run("Tasks", "tasklauncher"),
run("Tasktree", "tasktree"),
term("Files", "ranger"),
soundmenu,
w17menu,
powermenu,
maintenance,
],
).1;
main_loop(startmenu)
});

View file

@ -0,0 +1,7 @@
extern crate rust_scripts;
#[macro_use]
extern crate error_chain;
use rust_scripts::kassandra::kassandra;
quick_main!(kassandra);

View file

@ -0,0 +1,323 @@
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
//! This module contains the generate function and the GeneratedTask trait.
//! You can routinely generate GeneratedTasks which represent some external state,
//! like e-mails you need to read, tickets from an issue tracker, or updates you have to do on a
//! system. If you pass them to the generate function, it will update your taskwarrior accordingly.
//! Create new tasks, complete old ones and update once which have new information.
//! Fields which you don't set in a GeneratedTask can be set in taskwarrior and won't be
//! overwritten on calling generate again.
use task_hookrs::task::Task;
use task_hookrs::status::TaskStatus as TS;
use task_hookrs::uda::UDAValue as U;
use generate::OrphanBehavior as O;
use task_hookrs::cache::TaskCache;
use task_hookrs::error::{Result, ResultExt};
use std::collections::HashMap;
use uuid::Uuid;
/// This trait contains additional methods for the Task struct, for representing programatically
/// generated tasks
pub trait GeneratedTask {
/// The name of this generator type. e.g. "mail"
fn gen_name(&self) -> Option<&String>;
/// The name of this generator type. e.g. "mail", mutable
fn gen_name_mut(&mut self) -> Option<&mut String>;
/// Set the name of this generator type. e.g. "mail"
fn set_gen_name<T>(&mut self, new: Option<T>)
where
T: Into<String>;
/// The id of the generator type, which should be unique for all generator Tasks with the same
/// name
fn gen_id(&self) -> Option<&String>;
/// The generator id, mutable
fn gen_id_mut(&mut self) -> Option<&mut String>;
/// Set the generator id
fn set_gen_id<T>(&mut self, new: Option<T>)
where
T: Into<String>;
/// What should happen with the task, when the generator is missing.
/// This is relevant, when the generate method is called with one or more tasks with a
/// generator name set, all existing tasks with that generator name will be dealt with
/// according to this field.
fn gen_orphan(&self) -> OrphanBehavior;
/// Set the OrphanBehavior
fn set_gen_orphan<T>(&mut self, new: OrphanBehavior);
}
lazy_static! {
static ref DELETE: U = U::Str("delete".into());
static ref COMPLETE: U = U::Str("complete".into());
static ref EMPTY: U = U::Str(String::default());
}
static GEN_NAME: &str = "gen_name";
static GEN_ID: &str = "gen_id";
static GEN_ORPHAN: &str = "gen_orphan";
/// The behavior if a task with missing generator is encountered.
pub enum OrphanBehavior {
/// Complete generated tasks, with missing generator
CompleteOrphan,
/// Delete generated tasks, with missing generator
DeleteOrphan,
/// Don't do anything with generated tasks, with missing generator
KeepOrphan,
}
impl GeneratedTask for Task {
fn gen_name(&self) -> Option<&String> {
self.uda().get(GEN_NAME).and_then(
|x| if let &U::Str(ref x) = x {
Some(x)
} else {
None
},
)
}
fn gen_name_mut(&mut self) -> Option<&mut String> {
self.uda_mut().get_mut(GEN_NAME).and_then(|x| {
if let &mut U::Str(ref mut x) = x {
Some(x)
} else {
None
}
})
}
fn set_gen_name<T>(&mut self, new: Option<T>)
where
T: Into<String>,
{
if let Some(new) = new {
self.uda_mut().insert(GEN_NAME.into(), U::Str(new.into()));
} else {
self.uda_mut().remove(GEN_NAME);
}
}
fn gen_id(&self) -> Option<&String> {
self.uda().get(GEN_ID).and_then(
|x| if let &U::Str(ref x) = x {
Some(x)
} else {
None
},
)
}
fn gen_id_mut(&mut self) -> Option<&mut String> {
self.uda_mut().get_mut(GEN_ID).and_then(|x| {
if let &mut U::Str(ref mut x) = x {
Some(x)
} else {
None
}
})
}
fn set_gen_id<T>(&mut self, new: Option<T>)
where
T: Into<String>,
{
if let Some(new) = new {
self.uda_mut().insert(GEN_ID.into(), U::Str(new.into()));
} else {
self.uda_mut().remove(GEN_ID);
}
}
fn gen_orphan(&self) -> OrphanBehavior {
let u = self.uda().get(GEN_ORPHAN).unwrap_or(&*EMPTY);
if *u == *COMPLETE {
O::CompleteOrphan
} else if *u == *DELETE {
O::DeleteOrphan
} else {
O::KeepOrphan
}
}
fn set_gen_orphan<T>(&mut self, new: OrphanBehavior) {
match new {
O::DeleteOrphan => self.uda_mut().insert(GEN_ORPHAN.into(), DELETE.clone()),
O::CompleteOrphan => self.uda_mut().insert(GEN_ORPHAN.into(), COMPLETE.clone()),
O::KeepOrphan => self.uda_mut().remove(GEN_ORPHAN),
};
}
}
fn check_ignores(cache: &TaskCache) -> Result<()> {
if cache.ignore().len() > 0 {
return Err("Don't use generate with a TaskCache with ignores".into());
};
Ok(())
}
fn process_new(cache: &mut TaskCache, new: Vec<Task>) {
for task in new {
cache.set(task);
}
}
fn r<'a>(c: &'a TaskCache, u: &Uuid) -> Result<&'a Task> {
c.get(u).chain_err(|| "Cache miss for changed task")
}
fn w<'a>(c: &'a mut TaskCache, u: &Uuid) -> Result<&'a mut Task> {
c.get_mut(u).chain_err(|| "Cache miss for changed task")
}
fn process_matches(cache: &mut TaskCache, matches: Vec<(Uuid, Task)>) -> Result<()> {
let c = cache;
for (u, new) in matches {
if r(c,&u)?.status() != new.status() {
*w(c,&u)?.status_mut() = new.status().clone();
}
if r(c,&u)?.description() != new.description() {
*w(c,&u)?.description_mut() = new.description().clone();
}
if let Some(ann) = new.annotations() {
if r(c,&u)?.annotations().map(|x| x != ann) == Some(true) {
w(c,&u)?.annotations_mut().map(|o| *o = ann.clone());
}
}
if let Some(dep) = new.depends() {
if r(c,&u)?.depends().map(|x| x != dep) == Some(true) {
w(c,&u)?.depends_mut().map(|o| *o = dep.clone());
}
}
if let Some(due) = new.due() {
if r(c,&u)?.due().map(|x| x != due) == Some(true) {
w(c,&u)?.due_mut().map(|o| *o = due.clone());
}
}
if let Some(end) = new.end() {
if r(c,&u)?.end().map(|x| x != end) == Some(true) {
w(c,&u)?.end_mut().map(|o| *o = end.clone());
}
}
if let Some(ann) = new.annotations() {
if r(c,&u)?.annotations().map(|x| x != ann) == Some(true) {
w(c,&u)?.annotations_mut().map(|o| *o = ann.clone());
}
}
if let Some(recur) = new.recur() {
if r(c,&u)?.recur().map(|x| x != recur) == Some(true) {
w(c,&u)?.recur_mut().map(|o| *o = recur.clone());
}
}
if let Some(scheduled) = new.scheduled() {
if r(c,&u)?.scheduled().map(|x| x != scheduled) == Some(true) {
w(c,&u)?.scheduled_mut().map(|o| *o = scheduled.clone());
}
}
if let Some(start) = new.start() {
if r(c,&u)?.start().map(|x| x != start) == Some(true) {
w(c,&u)?.start_mut().map(|o| *o = start.clone());
}
}
if let Some(tags) = new.tags() {
if r(c,&u)?.tags().map(|x| x != tags) == Some(true) {
w(c,&u)?.tags_mut().map(|o| *o = tags.clone());
}
}
if let Some(until) = new.until() {
if r(c,&u)?.until().map(|x| x != until) == Some(true) {
w(c,&u)?.until_mut().map(|o| *o = until.clone());
}
}
if let Some(wait) = new.wait() {
if r(c,&u)?.wait().map(|x| x != wait) == Some(true) {
w(c,&u)?.wait_mut().map(|o| *o = wait.clone());
}
}
if !(r(c,&u)?.uda() >= new.uda()) {
w(c,&u)?.uda_mut().append(&mut new.uda().clone());
}
}
Ok(())
}
fn process_orphans(
cache: &mut TaskCache,
orphans: HashMap<String, HashMap<String, Uuid>>,
) -> Result<()> {
for uuid in orphans.values().flat_map(HashMap::values) {
match cache.get(uuid).chain_err(|| "Cache miss for orphan")?.gen_orphan() {
O::CompleteOrphan => if *cache.get(uuid).chain_err(|| "Cache miss for orphan")?.status() != TS::Completed {
*cache.get_mut(uuid).chain_err(|| "Cache miss for orphan")?.status_mut() = TS::Completed;
}
O::DeleteOrphan => if *cache.get(uuid).chain_err(|| "Cache miss for orphan")?.status() != TS::Deleted {
*cache.get_mut(uuid).chain_err(|| "Cache miss for orphan")?.status_mut() = TS::Deleted;
}
_ => ()
}
}
Ok(())
}
/// Take a TaskCache (which should not ignore any task states) and make sure, that all given
/// generator tasks are represented in taskwarrior.
pub trait TaskGenerator {
fn get_by_gen(&self, generator: &Task) -> Option<&Task>;
fn get_by_gen_mut(&mut self, generator: &Task) -> Option<&mut Task>;
fn generate<T>(&mut self, generators: T) -> Result<()>
where
T: IntoIterator,
T::Item: Into<Task>;
}
fn gen_match(a: &Task, b: &Task) -> bool {
a.gen_name() == b.gen_name() && a.gen_id() == b.gen_id()
}
impl TaskGenerator for TaskCache {
fn get_by_gen(&self, generator: &Task) -> Option<&Task> {
self.filter(|t| gen_match(t, generator)).into_iter().next()
}
fn get_by_gen_mut(&mut self, generator: &Task) -> Option<&mut Task> {
self.filter_mut(|t| gen_match(t, generator)).into_iter().next()
}
fn generate<T>(&mut self, generators: T) -> Result<()>
where
T: IntoIterator,
T::Item: Into<Task>,
{
check_ignores(self)?;
let mut orphans = HashMap::<String, HashMap<String, Uuid>>::default();
let mut create = Vec::<Task>::default();
let mut changes = Vec::<(Uuid, Task)>::default();
for g in generators {
let new = g.into();
if let Some(old) = {
let name = new.gen_name().chain_err(|| "gen_name missing")?;
let id = new.gen_id().chain_err(|| "gen_id missing")?;
orphans
.entry(name.clone())
.or_insert_with(|| {
self
.filter(|t| t.gen_name() == Some(name))
.iter()
.filter_map(|t| t.gen_id().map(|id| (id.clone(), t.uuid().clone())))
.collect()
})
.remove(id)
}
{
changes.push((old, new));
} else {
create.push(new);
}
}
process_new(self, create);
process_matches(self, changes)?;
process_orphans(self, orphans)
}
}

View file

@ -0,0 +1,97 @@
use std::rc::Rc;
use dialog::DialogProvider;
use dialog::errors::{Error, ErrorKind as EK};
use dialog::rofi::RofiDialogProvider;
use std::process::Command as StdCommand;
use std::os::unix::process::CommandExt;
use error::{Result, ResultExt};
use hotkeys::Next::*;
pub fn run<T: Into<String>>(name: T, command: &str) -> Item {
let command = command.to_owned();
(
name.into(),
Do(Rc::new(move || {
str2cmd(&command).exec();
Ok(Exit)
})),
)
}
pub fn term<T: Into<String>>(name: T, command: &str) -> Item {
run(name, &format!("urxvt -e {}", command))
}
pub fn holdterm<T: Into<String>>(name: T, command: &str) -> Item {
run(name, &format!("urxvt -hold -e {}", command))
}
pub fn menu<T: Into<String>>(name: T, options: Vec<Item>) -> Item {
let name = name.into();
(name.clone(), Menu((name.clone(), options)))
}
#[derive(Clone)]
pub enum Next {
Menu(Dialog),
Back,
Exit,
Stay,
Do(Rc<Fn() -> Result<Next>>),
}
type Dialog = (String, Vec<Item>);
type Item = (String, Next);
type Command = Vec<String>;
fn show_menu<T: DialogProvider>(dialog_provider: &mut T, menu: Dialog) -> Result<Next> {
let (msg, mut options) = menu;
options.insert(0, (".Back".into(), Back));
match dialog_provider.select_option(msg, options) {
Ok(next) => Ok(next),
Err(Error(EK::InvalidUserInput, _)) => Ok(Stay),
err => err.chain_err(|| "User Input Error"),
}
}
pub fn str2cmd(cmd: &str) -> StdCommand {
mk_cmd(
cmd.split_whitespace()
.map(str::to_owned)
.collect::<Vec<_>>(),
)
}
pub fn mk_cmd(cmd: Command) -> StdCommand {
let mut cmd_iter = cmd.iter();
let mut prg = StdCommand::new(cmd_iter.next().expect("Called program without name"));
for arg in cmd_iter {
prg.arg(arg);
}
prg
}
pub fn main_loop(startmenu: Next) -> Result<()> {
let mut dialog_provider = RofiDialogProvider;
let mut history = vec![];
let mut next = startmenu;
loop {
next = match next {
Exit => break,
Back => {
history.pop().chain_err(|| "Empty history")?;
Stay
}
Do(script) => script()?,
Menu(menu) => {
history.push(menu.clone());
show_menu(&mut dialog_provider, menu)?
}
Stay => Menu(history.pop().chain_err(|| "Empty history")?.clone()),
};
}
Ok(())
}

View file

@ -0,0 +1,779 @@
use std::fs::File;
use std::env::home_dir;
use std::iter::once;
use serde_yaml::{from_reader, to_writer, to_string};
use uuid::Uuid;
use task_hookrs::cache::TaskCache;
use task_hookrs::task::{Task, TaskBuilder};
use dialog::rofi::RofiDialogProvider;
use dialog::DialogProvider;
use dialog::errors::ErrorKind as DEK;
use update::update_tasks;
use generate::GeneratedTask;
use error::{Result, ResultExt, ErrorKind as EK, Error};
use hotkeys::str2cmd;
use tasktree::{TreeCache, TaskNode};
fn print_task_short(task: &Task) -> String {
let mut info = vec![task.description().clone()];
if let Some(tags) = task.tags() {
info.push(format!("+{}", tags.join(",+")));
}
if let Some(project) = task.project() {
info.push(format!("({})", project));
}
info.join(" ")
}
fn print_task(task: &Task) -> String {
let mut info = vec![task.description().clone()];
info.push(format!("status: {}", task.status()));
if let Some(project) = task.project() {
info.push(format!("project: {}", project));
}
if let Some(tags) = task.tags() {
info.push(format!("tags: {}", tags.join(", ")));
}
info.join("\n")
}
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
enum Mode {
#[serde(rename = "work")]
Work,
#[serde(rename = "research")]
Research,
#[serde(rename = "orga")]
Orga,
#[serde(rename = "idle")]
Idle,
}
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
enum Connectivity {
#[serde(rename = "online")]
Online,
#[serde(rename = "offline")]
Offline,
#[serde(rename = "limited")]
Limited,
}
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
enum Location {
#[serde(rename = "home")]
Home,
#[serde(rename = "w17")]
W17,
#[serde(rename = "city")]
City,
#[serde(rename = "uni")]
Uni,
#[serde(rename = "anywhere")]
Anywhere,
}
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
struct State {
mode: Mode,
location: Location,
connectivity: Connectivity,
}
fn task_blocked(cache: &TaskCache, task: &Task) -> bool {
task.depends()
.map(|dependencies| {
for dependency in dependencies {
if cache.get(dependency).map(|t| !t.obsolete()).unwrap_or(
false,
)
{
return true;
}
}
false
})
.unwrap_or(false)
}
fn task_needs_sorting(cache: &TaskCache, uuid: &Uuid) -> Result<bool> {
Ok(!cache.is_project(&uuid) && cache.get_parent(&uuid)? == None)
}
fn task_inbox(cache: &TaskCache, task: &Task) -> bool {
task.pending() && !task.tagged() && !task_blocked(cache, task)
}
fn save_state(state: &State) -> Result<()> {
let state_path = &{
let mut s = home_dir().chain_err(|| "No Home")?;
s.push(".kassandra_state");
s
};
let state_file = File::create(&state_path)?;
to_writer(state_file, &state)?;
Ok(())
}
fn get_state() -> Result<State> {
let state_path = &{
let mut s = home_dir().chain_err(|| "No Home")?;
s.push(".kassandra_state");
s
};
Ok(if let Ok(state_file) = File::open(&state_path) {
from_reader(state_file)?
} else {
let state = State {
mode: Mode::Orga,
connectivity: Connectivity::Online,
location: Location::Anywhere,
};
save_state(&state)?;
state
})
}
fn enter_new_task<T: DialogProvider, S: Into<String>>(dialog: &mut T, msg: S) -> Result<Task> {
Ok(TaskBuilder::default()
.description(dialog.input_line(msg, Vec::<String>::default())?)
.build()?)
}
struct Kassandra {
state: State,
dialog: RofiDialogProvider,
cache: TaskCache,
}
impl Kassandra {
fn new() -> Result<Self> {
Ok(Kassandra {
state: get_state()?,
dialog: RofiDialogProvider {},
cache: TaskCache::new(vec![]),
})
}
fn run(&mut self) -> Result<()> {
self.cache.load()?;
update_tasks(&mut self.cache)?;
self.cache.write()?;
self.handle_active_tasks()?;
self.clear_inbox()?;
self.assure_all_sorted()?;
// check unread E-Mail + ak?
// update
// system
// home
// mails
// sort inbox
// sort inboxkiva
// sort inboxak
// sort to sort
// go trough todo
// go trough toread
// go trough readlater
// pick tasks
// optional, later
// await
// dirty gits
// task generator
// task completion helper
self.update_accounting()?;
self.select_next_task()?;
Ok(())
}
fn get_sorted_uuids<T: Fn(&Task) -> bool>(&self, filter: T) -> Vec<Uuid> {
self.get_sorted_tasks(filter)
.into_iter()
.map(|x| x.uuid().clone())
.collect()
}
fn get_sorted_tasks<T: Fn(&Task) -> bool>(&self, filter: T) -> Vec<&Task> {
let mut tasks = self.cache.filter(filter);
tasks.sort_unstable_by_key(|t| t.entry().date());
tasks
}
fn get_notes(&mut self) -> Result<()> {
loop {
match enter_new_task(
&mut self.dialog,
"Do you have anything to note?
For example, what you are doing right now or still have to do today?",
) {
Ok(task) => {
self.cache.set(task);
self.cache.write()?;
}
Err(Error(EK::DialogError(DEK::InputCanceled), _)) => break Ok(()),
err => {
err?;
}
}
}
}
fn confirm_state(&mut self) -> Result<()> {
loop {
match self.dialog.select_option(
format!(
"State: {}
Do you want to change the state? (Esc to cancel)",
to_string(&self.state)?
),
vec![
("working: I am at work", "work"),
("researching: I am researching", "research"),
("orga: I am in normal mode", "orga"),
("home: I am at home", "home"),
("uni: I am at the Mathebau", "uni"),
("hackspace: I am in the hackspace", "w17"),
("city: I am in the city", "city"),
("anywhere: I'm nowhere special", "anywhere"),
(
"idle: I am not interested in doing anything",
"idle"
),
],
)? {
"work" => self.state.mode = Mode::Work,
"research" => self.state.mode = Mode::Research,
"orga" => self.state.mode = Mode::Orga,
"home" => self.state.location = Location::Home,
"w17" => self.state.location = Location::W17,
"city" => self.state.location = Location::City,
"idle" => self.state.mode = Mode::Idle,
"uni" => self.state.location = Location::Uni,
"anywhere" => self.state.location = Location::Anywhere,
_ => (),
};
save_state(&self.state)?;
}
}
fn handle_active_tasks(&mut self) -> Result<()> {
while let Some(uuid) = self.get_sorted_uuids(|t| t.start().is_some() && t.pending())
.into_iter()
.next()
{
match self.dialog.select_option(
format!(
"You are currently working on {}
What's the progress?",
print_task(
self.cache.get(&uuid).chain_err(|| "uuid miss")?,
)
),
vec![
("Continue: I'll get back to it", "continue"),
("Done: I've done that!", "done"),
("Later: I'll do that later", "later"),
("Edit: I have changes to this task", "edit"),
],
)? {
"done" => {
{
let task = self.cache.get_mut(&uuid).chain_err(|| "missing uuid")?;
task.tw_stop();
task.tw_done();
}
self.cache.write()?;
}
"later" => {
self.cache
.get_mut(&uuid)
.chain_err(|| "missing uuid")?
.tw_stop();
self.cache.write()?;
}
"edit" => {
self.cache
.get_mut(&uuid)
.chain_err(|| "missing uuid")?
.tw_stop();
self.cache.write()?;
self.edit_task(&uuid)?;
}
_ => bail!("Continuing with task"),
};
}
Ok(())
}
pub fn assure_all_sorted(&mut self) -> Result<()> {
if self.cache
.filter(|t| {
t.gen_name() == Some(&"Sortiere Tasktree".into()) && t.pending()
})
.len() > 0
{
while let Some(uuid) = self.get_sorted_uuids(|t| {
!t.obsolete() && task_needs_sorting(&self.cache, t.uuid()).unwrap_or(false)
}).into_iter()
.next()
{
self.sort(&uuid)?;
}
for task in self.cache.filter_mut(|t| {
t.gen_name() == Some(&"Sortiere Tasktree".into()) && t.pending()
})
{
task.tw_done()
}
self.cache.refresh_tree();
self.cache.write()?;
}
Ok(())
}
pub fn sort(&mut self, uuid: &Uuid) -> Result<()> {
let task_name = print_task(self.cache.get(uuid).chain_err(|| "mising uuid")?);
let partof = self.select_entry_point(
format!("Select Project for Task\n{}", task_name),
uuid,
)?;
self.cache
.get_mut(uuid)
.chain_err(|| "assure_sorted: missing uuid")?
.set_partof(partof);
update_tasks(&mut self.cache)?;
self.cache.write()?;
Ok(())
}
pub fn select_entry_point<T: Into<String>>(
&mut self,
msg: T,
uuid: &Uuid,
) -> Result<Option<Uuid>> {
let msg = msg.into();
let format_msg = |cache: &TaskCache, uuid, text| -> Result<String> {
Ok(format!(
"{}\n{}: {}",
&msg,
text,
if let Some(uuid) = uuid {
cache.get_project_path(&uuid)?
} else {
String::default()
}
))
};
let mut parent = None;
loop {
match {
let mut options = self.cache.filter(|t| {
t.pending() && t.partof().map(|partof| partof == parent).unwrap_or(false) &&
(parent.is_some() || self.cache.is_project(t.uuid()))
});
options.sort_unstable_by_key(|t| t.entry().date());
match self.dialog.select_option(
format_msg(&self.cache, parent, "currently at")?,
vec![
("select this level".into(), Err(0)),
("insert new node".into(), Err(1)),
("go one level up".into(), Err(2)),
("edit task".into(), Err(4)),
].into_iter()
.chain(options.into_iter().map(|t| (print_task_short(t), Ok(t)))),
)? {
Ok(task) => {
parent = Some(task.uuid().clone());
3
}
Err(n) => n,
}
} {
0 => return Ok(parent),
1 => {
let mut task = enter_new_task(
&mut self.dialog,
format_msg(&self.cache, parent, "inserting new node at")?,
)?;
task.set_partof(parent);
parent = Some(task.uuid().clone());
self.cache.set(task);
self.cache.write()?;
}
2 => {
parent = if let Some(parent) = parent {
if let Some(parent) = self.cache.get(&parent) {
parent.partof()?
} else {
None
}
} else {
None
}
}
3 => (),
4 => {
self.edit_task(uuid)?;
parent = None;
}
_ => bail!("Hö?"),
}
}
}
pub fn edit_task(&mut self, uuid: &Uuid) -> Result<()> {
loop {
let task_name = print_task(
self.cache.get(uuid).chain_err(|| "edit_task: missing uuid")?,
);
match self.dialog.select_option(
format!(
"Handling Task: {}\nDo you want to do this now? Can it be done in under 2 minutes?",
task_name
),
vec![
("Do it: I'll get to it", "do"),
("Done: I already did this", "done"),
("Delete: This does not need to be done anymore","delete"),
("Edit description: I want to change the description", "edit"),
("Move: Change position in tasktree", "move"),
("Split: Give this tasks subtasks", "split"),
("Depend: Set dependency", "depend"),
("Postpone: Set wait time", "postpone"),
("Tag: Add a tag", "tag"),
("Clear tags", "clear_tags"),
("Manual: I have to change something by hand", "manual"),
("Quit", "quit"),
],
)? {
"do" => {
self.cache.get_mut(uuid).chain_err(|| "BUG!")?.tw_start();
self.cache.write()?;
bail!("Work on Task now!");
}
"done" => {
self.cache.get_mut(uuid).chain_err(|| "BUG!")?.tw_done();
self.cache.write()?;
break;
}
"delete" => {
self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.tw_delete();
self.cache.write()?;
break;
}
"edit" => {
*self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.description_mut() = self.dialog.input_line(
format!(
"Enter new description for {}",
task_name
),
vec![
self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.description()
.clone(),
],
)?;
}
"manual" => {
str2cmd("tasklauncher").output()?;
self.cache.refresh()?;
}
"move" => {
self.sort(uuid)?;
}
"quit" => {break;}
"split" => {self.make_project(uuid)?;}
"depend" => {
let query = format!(
"Selecting dependency for task\n{}",
print_task(self.cache.get(uuid).chain_err(|| "missing uuid")?)
);
if let Some(dependency) = self.select_entry_point(query, uuid)? {
let task = self.cache.get_mut(uuid).chain_err(|| "missing uuid")?;
if task.depends().is_some() {
task.depends_mut().unwrap().push(dependency);
} else {
task.set_depends(Some(Some(dependency)));
}
}
self.cache.write()?;
}
"tag" => {
let tag = self.dialog.input_line(
format!("Enter tag for {}", task_name),
vec![
"alber",
"await",
"city",
"home",
"pc",
"research",
"streicher",
"uni",
"work",
"claire",
"burkhard",
"cornelia",
"nathalie",
],
)?;
self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.add_tag(tag);
self.cache.write()?;
}
"clear_tags" => {
self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.set_tags(None as Option<Vec<String>>);
self.cache.write()?;
}
"postpone" => {
str2cmd(&format!(
"task {} mod wait:{}",
uuid,
self.dialog.input_line(
format!(
"How long do you want to postpone {}",
task_name
),
vec!["tomorrow"],
)?
)).output()?;
self.cache.refresh()?;
}
_ => {}
}
}
Ok(())
}
pub fn clear_inbox(&mut self) -> Result<()> {
if self.cache
.filter(|t| {
t.gen_name() == Some(&"Leere Inbox".into()) && t.pending()
})
.len() > 0
{
while let Some(uuid) = self.get_sorted_uuids(|t| task_inbox(&self.cache, t))
.into_iter()
.next()
{
self.handle_task(&uuid)?;
}
for task in self.cache.filter_mut(|t| {
t.gen_name() == Some(&"Leere Inbox".into()) && t.pending()
})
{
task.tw_done()
}
self.cache.write()?;
}
Ok(())
}
pub fn make_project(&mut self, uuid: &Uuid) -> Result<bool> {
let task_name = print_task(self.cache.get(uuid).chain_err(|| "missing uuid")?);
let mut new_tasks = vec![];
loop {
match enter_new_task(
&mut self.dialog,
format!("Adding sub tasks of {}\n\n(Esc to cancel)", task_name),
) {
Ok(mut task) => {
task.set_partof(Some(uuid.clone()));
new_tasks.push(task.uuid().clone());
self.cache.set(task);
self.cache.write()?;
}
Err(Error(EK::DialogError(DEK::InputCanceled), _)) => break,
err => {
err?;
}
}
}
if new_tasks.len() > 0 {
for uuid in new_tasks {
self.handle_task(&uuid)?;
}
update_tasks(&mut self.cache)?;
self.cache.write()?;
return Ok(true);
} else {
return Ok(false);
}
}
pub fn handle_task(&mut self, uuid: &Uuid) -> Result<()> {
let task_name = print_task(self.cache.get(uuid).chain_err(
|| "handle_task: missing uuid",
)?);
match self.dialog.select_option(
format!(
"Handling Task: {}\nCan this be done in under 2 minutes?",
task_name
),
vec![
("Yes: I'll get to it", Some(true)),
(
"No: Do you know how short 2 minutes are?",
Some(false)
),
(
"Edit: I'll change that task on my own",
None
),
],
)? {
Some(true) => {
self.cache.get_mut(uuid).chain_err(|| "BUG!")?.tw_start();
self.cache.write()?;
bail!("Work on Task now!");
}
Some(false) => (),
None => {
self.edit_task(uuid)?;
return Ok(());
}
}
if task_needs_sorting(&self.cache, uuid)? {
self.sort(uuid)?;
}
if !self.make_project(uuid)? {
if let Some(tag) = self.dialog.select_option(
format!(
"Handling Task: {}\nWhen will you do this?",
print_task(
self.cache.get(uuid).chain_err(|| "missing uuid")?,
)
),
vec![
("Optional: I might never", Some("optional")),
("Later: This has to wait", Some("later")),
(
"Await: Somebody else has to do this",
Some("await")
),
("PC: When I'm at my computer", Some("pc")),
("Work: While working", Some("work")),
(
"Research: While doing research",
Some("research")
),
("Home: When I'm at home", Some("home")),
("Edit: This task instead", None),
],
)?
{
self.cache
.get_mut(uuid)
.chain_err(|| "missing uuid")?
.add_tag(tag);
self.cache.write()?;
} else {
self.edit_task(uuid)?;
}
}
Ok(())
}
pub fn update_accounting(&mut self) -> Result<()> {
if self.cache
.filter(|t| {
t.gen_name() == Some(&"Aktualisiere Buchhaltung".into()) && t.pending()
})
.len() > 0
{
str2cmd("urxvt -e sh -c")
.arg("jali -l. && task gen_id:'Aktualisiere Buchhaltung' done")
.output()?;
}
self.cache.write()?;
Ok(())
}
pub fn select_next_task(&mut self) -> Result<()> {
while self.cache
.filter(|t| t.start().is_some() && t.pending())
.len() == 0 &&
self.cache
.filter(|t| {
t.pending() && self.is_relevant(t) && !task_blocked(&self.cache, t)
})
.len() > 0
{
if let Some(uuid) = {
let next_tasks = self.get_sorted_tasks(|t| {
t.pending() && self.is_relevant(t) && !task_blocked(&self.cache, t)
}).into_iter()
.map(|t| (print_task_short(t), Some(t.uuid().clone())))
.collect::<Vec<_>>();
self.dialog.select_option(
"What are you going to do now?",
once(("Manual: Edit my tasks".into(), None))
.chain(next_tasks.into_iter()),
)?
}
{
self.edit_task(&uuid)?;
} else {
str2cmd("tasklauncher").output()?;
self.cache.refresh()?;
return self.select_next_task();
}
}
Ok(())
}
fn is_relevant(&self, task: &Task) -> bool {
let can_do_this_here = |location| match location {
Location::Home => task.has_tag("home"),
Location::City => task.has_tag("city"),
Location::Uni => task.has_tag("uni"),
_ => false,
};
match self.state.mode {
Mode::Research => task.has_tag("research"),
Mode::Work => {
task.has_tag("work") || task.has_tag("pc") || can_do_this_here(self.state.location)
}
Mode::Orga => task.has_tag("pc") || can_do_this_here(self.state.location),
Mode::Idle => false,
}
}
}
pub fn change_state() -> Result<()> {
Kassandra::new()?.confirm_state()
}
pub fn new_tasks() -> Result<()> {
Kassandra::new()?.get_notes()
}
pub fn kassandra() -> Result<()> {
Kassandra::new()?.run()
}

View file

@ -1,108 +1,36 @@
extern crate dialog;
extern crate uuid;
extern crate task_hookrs;
#[macro_use]
extern crate lazy_static;
extern crate kairos;
extern crate chrono;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate serde_yaml;
pub mod hotkeys;
pub mod generate;
pub mod refresh;
pub mod update;
pub mod kassandra;
pub mod tasktree;
pub mod hotkeys {
use std::rc::Rc;
use dialog::DialogProvider;
use dialog::errors::Error;
use dialog::errors::ErrorKind::*;
use dialog::rofi::RofiDialogProvider;
use std::process::Command as StdCommand;
use std::os::unix::process::CommandExt;
pub use hotkeys::Next::*;
#[derive(Clone)]
pub enum Next {
Menu(Dialog),
TmpMenu(Dialog),
Run(Command),
Fork(Command),
Exec(Command),
Back,
Exit,
Stay,
Script(Rc<Fn(State) -> Next>),
}
type Dialog = (String, Vec<Item>);
type Item = (String, Next);
type Command = Vec<String>;
type State = String;
pub fn c<T>(string: T) -> String
where
T: Into<String>,
{
string.into()
}
pub fn cmd<S, K>(argv: S) -> Command
where
S: IntoIterator<Item = K>,
K: Into<String>,
{
argv.into_iter().map(|x| x.into()).collect()
}
fn show_menu<T: DialogProvider>(dialog_provider: &mut T, menu: Dialog) -> Next {
let (msg, options) = menu;
match dialog_provider.select_option(msg, options) {
Ok(next) => next,
Err(Error(InputCanceled, _)) => Exit,
Err(Error(InvalidUserInput, _)) => Stay,
Err(err) => {
println!("DialogError: {}", err);
Stay
}
pub mod error {
use task_hookrs::error as terror;
use dialog::errors as derror;
use serde_yaml;
error_chain! {
links {
TaskError(terror::Error, terror::ErrorKind);
DialogError(derror::Error, derror::ErrorKind);
}
}
fn build_command(cmd: Command) -> StdCommand {
let mut cmd_iter = cmd.iter();
let mut prg = StdCommand::new(cmd_iter.next().expect("Called program without name"));
for arg in cmd_iter {
prg.arg(arg);
}
prg
}
pub fn main_loop(startmenu: Next) {
let mut dialog_provider = RofiDialogProvider;
let mut history = vec![];
let mut next = startmenu;
loop {
next = match next {
Exit => break,
Back => {
history.pop().expect("Empty history");
Stay
}
Script(script) => script(c("work")),
TmpMenu(menu) => show_menu(&mut dialog_provider, menu),
Menu(menu) => {
history.push(menu.clone());
show_menu(&mut dialog_provider, menu)
}
Fork(cmd) => {
build_command(cmd).spawn().is_ok();
Stay
}
Run(cmd) => {
build_command(cmd).output().is_ok();
Stay
}
Exec(cmd) => {
build_command(cmd).exec();
break;
}
Stay => Menu(history.pop().expect("Empty history").clone()),
};
foreign_links {
Io(::std::io::Error);
Yaml(serde_yaml::Error);
PathError(::std::env::JoinPathsError);
}
}
}

View file

@ -0,0 +1,74 @@
use chrono::offset::{Local, TimeZone};
use kairos::timetype::TimeType as TT;
use kairos::iter::Iter;
use kairos::error::Result as KairosResult;
use task_hookrs::status::TaskStatus as TS;
use task_hookrs::task::Task;
use task_hookrs::cache::TaskCache;
use task_hookrs::error::{Result, ResultExt};
use task_hookrs::date::Date;
use generate::TaskGenerator;
use tasktree::TaskNode;
pub enum Timer {
DeadTime(TT),
Repetition(Iter),
}
pub trait TaskRefresher {
fn reactivate<T>(&mut self, tasks: T, recurrence: Timer) -> Result<()>
where
T: IntoIterator<Item = Task>;
}
impl TaskRefresher for TaskCache {
fn reactivate<T>(&mut self, tasks: T, recurrence: Timer) -> Result<()>
where
T: IntoIterator<Item = Task>,
{
let now = TT::Moment(Local::now().naive_local());
let recent = match recurrence {
Timer::DeadTime(time) => {
(now - time).calculate().chain_err(
|| "Failed to calculate recent from deadtime",
)?
}
Timer::Repetition(iter) => {
iter.filter_map(KairosResult::ok)
.take_while(|t| *t <= now)
.last()
.ok_or("Repetition starts in the future")?
.clone()
}
};
let mut uuids = vec![];
let changes = tasks
.into_iter()
.filter(|task| if let Some(old) = self.get_by_gen(&task) {
if old.obsolete() &&
TT::Moment(
Local
.from_utc_datetime(
&**old.end().expect("Ended tasks have to have an end date"),
)
.naive_local(),
) < recent
{
uuids.push(old.uuid().clone());
}
false
} else {
true
})
.collect::<Vec<_>>();
for uuid in uuids {
let old = self.get_mut(&uuid).expect("BUG!");
*old.status_mut() = TS::Pending;
old.set_end(None as Option<Date>);
}
self.generate(changes)
}
}

View file

@ -0,0 +1,169 @@
use uuid::Uuid;
use task_hookrs::task::Task;
use task_hookrs::status::TaskStatus as TS;
use task_hookrs::cache::TaskCache;
use task_hookrs::uda::UDAValue;
use error::{Result, ResultExt};
pub trait TaskNode {
fn partof(&self) -> Result<Option<Uuid>>;
fn set_partof(&mut self, Option<Uuid>);
fn add_tag<T: Into<String>>(&mut self, T);
fn remove_tag<T: Into<String>>(&mut self, T);
fn has_tag<T: Into<String>>(&self, T) -> bool;
fn tagged(&self) -> bool;
fn pending(&self) -> bool;
fn obsolete(&self) -> bool;
}
impl TaskNode for Task {
fn partof(&self) -> Result<Option<Uuid>> {
Ok(if let Some(uuid) = self.uda().get("partof".into()) {
if let &UDAValue::Str(ref uuid) = uuid {
Some(Uuid::parse_str(uuid).chain_err(|| "No uuid in partof uda")?)
} else {
Err("No String in partof uda")?
}
} else {
None
})
}
fn set_partof(&mut self, partof: Option<Uuid>) {
if let Some(uuid) = partof {
self.uda_mut().insert(
"partof".into(),
UDAValue::Str(uuid.to_string()),
);
} else {
self.uda_mut().remove("partof".into());
}
}
fn add_tag<T: Into<String>>(&mut self, tag: T) {
let tag = tag.into();
if !self.has_tag(tag.clone()) {
if self.tags().is_some() {
self.tags_mut().unwrap().push(tag);
} else {
self.set_tags(Some(Some(tag)));
}
}
}
fn remove_tag<T: Into<String>>(&mut self, tag: T) {
if let Some(tags) = self.tags_mut() {
let tag = tag.into();
tags.retain(|t| *t != tag);
}
}
fn has_tag<T: Into<String>>(&self, tag: T) -> bool {
self.tags()
.map(|tags| tags.contains(&tag.into()))
.unwrap_or(false)
}
fn tagged(&self) -> bool {
self.tags().map(|t| t.len() > 0).unwrap_or(false)
}
fn pending(&self) -> bool {
*self.status() == TS::Pending
}
fn obsolete(&self) -> bool {
*self.status() == TS::Completed || *self.status() == TS::Deleted
}
}
pub trait TreeCache {
fn get_parent(&self, uuid: &Uuid) -> Result<Option<&Task>>;
fn get_parent_mut(&mut self, uuid: &Uuid) -> Result<Option<&mut Task>>;
fn get_children(&self, uuid: &Uuid) -> Vec<&Task>;
fn get_children_mut(&mut self, uuid: &Uuid) -> Vec<&mut Task>;
fn get_project_path(&self, uuid: &Uuid) -> Result<String>;
fn is_project(&self, uuid: &Uuid) -> bool;
fn refresh_tree(&mut self);
}
fn get_project_name(cache: &TaskCache, task: &Task) -> Result<Option<String>> {
Ok(if let Some(parent_uuid) = task.partof()? {
Some(cache.get_project_path(&parent_uuid)?)
} else {
None
})
}
impl TreeCache for TaskCache {
fn get_parent(&self, uuid: &Uuid) -> Result<Option<&Task>> {
let task = self.get(uuid).chain_err(|| "task uuid not found")?;
Ok(if let Some(uuid) = task.partof()? {
Some(self.get(&uuid).chain_err(|| "parent uuid not found")?)
} else {
None
})
}
fn get_parent_mut(&mut self, uuid: &Uuid) -> Result<Option<&mut Task>> {
let parent_option = self.get(uuid).chain_err(|| "task uuid not found")?.partof()?;
Ok(if let Some(uuid) = parent_option {
Some(self.get_mut(&uuid).chain_err(|| "parent uuid not found")?)
} else {
None
})
}
fn get_children(&self, uuid: &Uuid) -> Vec<&Task> {
self.filter(|t| t.partof().unwrap_or(None) == Some(*uuid))
}
fn get_children_mut(&mut self, uuid: &Uuid) -> Vec<&mut Task> {
self.filter_mut(|t| t.partof().unwrap_or(None) == Some(*uuid))
}
fn get_project_path(&self, uuid: &Uuid) -> Result<String> {
let task = self.get(uuid).chain_err(|| "Uuid not found in Cache")?;
let end = task.description().to_lowercase().replace(
char::is_whitespace,
"",
);
Ok(if let Some(parent) = task.partof()? {
format!("{}.{}", self.get_project_path(&parent)?, end)
} else {
end
})
}
fn is_project(&self, uuid: &Uuid) -> bool {
self.get_children(uuid)
.iter()
.filter(|t| !t.obsolete())
.count() > 0
}
fn refresh_tree(&mut self) {
let task_uuids = self.filter(|t| {
!t.obsolete() &&
(get_project_name(self, t)
.map(|path| path.as_ref() != t.project())
.unwrap_or(false) ||
(self.is_project(t.uuid()) != t.has_tag("project")))
}).iter()
.map(|t| t.uuid().clone())
.collect::<Vec<_>>();
for task_uuid in task_uuids {
let new_project_name = get_project_name(self, self.get(&task_uuid).expect("Bug"))
.expect("Bug");
let is_project = self.is_project(&task_uuid);
let task = self.get_mut(&task_uuid).expect("Bug");
task.set_project(new_project_name);
if is_project {
task.add_tag("project");
} else {
task.remove_tag("project");
}
}
}
}

View file

@ -0,0 +1,106 @@
use task_hookrs::error::Result;
use task_hookrs::cache::TaskCache;
use task_hookrs::task::{Task, TaskBuilder};
use generate::GeneratedTask;
use refresh::{TaskRefresher, Timer};
use tasktree::TreeCache;
use chrono::NaiveDate;
use kairos::timetype::TimeType as TT;
use kairos::iter::extensions::{Weekly, Monthly, Daily};
fn simple_task(name: &str) -> Task {
let mut task = TaskBuilder::default().description(name).build().expect(
"TaskBuilding failed inspite of set description",
);
task.set_gen_name(Some(name));
task.set_gen_id(Some(name));
task
}
fn simple_tasks<'a, T>(names: T) -> Vec<Task>
where
T: IntoIterator<Item = &'a str>,
{
names.into_iter().map(simple_task).collect()
}
pub fn update_tasks(cache: &mut TaskCache) -> Result<()> {
let daily = || {
Timer::Repetition(
TT::moment(NaiveDate::from_ymd(2018, 5, 8).and_hms(20, 0, 0))
.daily(1)
.unwrap(),
)
};
let weekly = || {
Timer::Repetition(
TT::moment(NaiveDate::from_ymd(2018, 5, 8).and_hms(20, 0, 0))
.weekly(1)
.unwrap(),
)
};
let monthly = || {
Timer::Repetition(
TT::moment(NaiveDate::from_ymd(2018, 5, 3).and_hms(20, 0, 0))
.monthly(1)
.unwrap(),
)
};
cache.reactivate(
simple_tasks(vec![
"Staubsaugen",
"Putze Waschbecken",
"Wäsche sortieren und entscheiden, welche Waschgänge notwendig sind",
]),
Timer::DeadTime(TT::weeks(2)),
)?;
cache.reactivate(
simple_tasks(
vec!["Reinige Toilette", "Zehennägel schneiden"],
),
Timer::DeadTime(TT::weeks(4)),
)?;
cache.reactivate(
simple_tasks(vec!["Friseurtermin machen"]),
Timer::DeadTime(TT::weeks(6)),
)?;
cache.reactivate(
simple_tasks(vec![
"Aktualisiere Buchhaltung",
"Leere Inbox",
"Sortiere Tasktree",
"Sortiere Inbox",
"Sortiere Inbox Auslandskoordination",
"Sortiere Inbox Kiva",
"Update nixos apollo",
"Update home apollo",
"Update home fb4",
"Update home hera",
"Update home charon",
"Klavier üben",
]),
daily(),
)?;
cache.reactivate(
simple_tasks(vec!["Verbuche Kontoauszüge"]),
monthly(),
)?;
cache.reactivate(
simple_tasks(vec![
"Kontrolliere Spam",
"Korrigiere Portemonnaiezählstand",
"Sortiere Archiv",
"Sortiere Archiv Kiva",
"Sortiere Archiv Auslandskoordination",
"Kontrolliere +later",
"Kontrolliere +optional",
"Kontrolliere +await",
"Block leeren und wegsortieren",
"Leere Kiva Fächer",
"Inbox zu Hause wegsortieren",
]),
weekly(),
)?;
cache.refresh_tree();
Ok(())
}