diff --git a/home-common/configs/zshrc b/home-common/configs/zshrc index d07f3664..d57d71fe 100644 --- a/home-common/configs/zshrc +++ b/home-common/configs/zshrc @@ -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 diff --git a/home-common/default.nix b/home-common/default.nix index ab865fa0..04d8b916 100644 --- a/home-common/default.nix +++ b/home-common/default.nix @@ -1,7 +1,21 @@ -{ pkgs, ... }: +{ pkgs, config, ... }: let rust-scripts = with pkgs; callPackage ../packages/rust-scripts {}; + unstable-pkgs = import {}; + 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 diff --git a/home-common/graphical/default.nix b/home-common/graphical/default.nix index 70ec1db7..55f33fcb 100644 --- a/home-common/graphical/default.nix +++ b/home-common/graphical/default.nix @@ -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; diff --git a/home-common/graphical/eventd.nix b/home-common/graphical/eventd.nix index e44951f7..918a5c8e 100644 --- a/home-common/graphical/eventd.nix +++ b/home-common/graphical/eventd.nix @@ -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="\${command}\\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 = "\${title}\${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 = "\${title}\${message/^/\\n}"; }; NotificationBubble = { - Timeout = 500; - Queue = "feedback"; - Colour = colors.red; + Queue = "critical"; }; }; "eventd/notification.action".text = lib.generators.toINI {} { diff --git a/home-common/graphical/i3.nix b/home-common/graphical/i3.nix index 2467dca1..a50a8301 100644 --- a/home-common/graphical/i3.nix +++ b/home-common/graphical/i3.nix @@ -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 diff --git a/home-common/graphical/rofi/default.nix b/home-common/graphical/rofi/default.nix index 1ef4be9c..7e1d63fb 100644 --- a/home-common/graphical/rofi/default.nix +++ b/home-common/graphical/rofi/default.nix @@ -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; }; diff --git a/home-common/graphical/st/config.h b/home-common/graphical/st/config.h new file mode 100644 index 00000000..a7f33f20 --- /dev/null +++ b/home-common/graphical/st/config.h @@ -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{|}~"; diff --git a/home-common/graphical/st/default.nix b/home-common/graphical/st/default.nix new file mode 100644 index 00000000..13232ddb --- /dev/null +++ b/home-common/graphical/st/default.nix @@ -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 + ''; + }) diff --git a/home-common/graphical/st/st-alpha-0.7.diff b/home-common/graphical/st/st-alpha-0.7.diff new file mode 100644 index 00000000..75fdc89f --- /dev/null +++ b/home-common/graphical/st/st-alpha-0.7.diff @@ -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); + diff --git a/home-common/graphical/st/st-externalpipe-0.7.diff b/home-common/graphical/st/st-externalpipe-0.7.diff new file mode 100644 index 00000000..4c0cce9d --- /dev/null +++ b/home-common/graphical/st/st-externalpipe-0.7.diff @@ -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; diff --git a/home-common/taskwarrior.nix b/home-common/taskwarrior.nix index d96f7e3b..04c66e20 100644 --- a/home-common/taskwarrior.nix +++ b/home-common/taskwarrior.nix @@ -60,6 +60,9 @@ Notification = { Text = template; }; + NotificationBubble = { + Queue = "tasks"; + }; }; }; in { diff --git a/homes/apollo/battery.nix b/homes/apollo/battery.nix new file mode 100644 index 00000000..443bf78f --- /dev/null +++ b/homes/apollo/battery.nix @@ -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" ]; + }; + }; + }; +} diff --git a/homes/apollo/home.nix b/homes/apollo/home.nix index ed212d75..5b5d92f9 100644 --- a/homes/apollo/home.nix +++ b/homes/apollo/home.nix @@ -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 diff --git a/homes/apollo/sleep-nag.nix b/homes/apollo/sleep-nag.nix new file mode 100644 index 00000000..2e7b8295 --- /dev/null +++ b/homes/apollo/sleep-nag.nix @@ -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" ]; + }; + }; + }; +} diff --git a/homes/charon/dovecot.sieve b/homes/charon/dovecot.sieve new file mode 100644 index 00000000..072a674a --- /dev/null +++ b/homes/charon/dovecot.sieve @@ -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"; +} diff --git a/homes/charon/home.nix b/homes/charon/home.nix index 68322f25..6772aed0 100644 --- a/homes/charon/home.nix +++ b/homes/charon/home.nix @@ -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; + }; + }; } diff --git a/homes/charon/morgenreport.nix b/homes/charon/morgenreport.nix index f29a1af7..dcbbfdf1 100644 --- a/homes/charon/morgenreport.nix +++ b/homes/charon/morgenreport.nix @@ -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"; }; diff --git a/host-common/common.nix b/host-common/common.nix index bc504449..f26882f2 100644 --- a/host-common/common.nix +++ b/host-common/common.nix @@ -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 diff --git a/hosts/charon/configuration.nix b/hosts/charon/configuration.nix index fa9bc743..e8d45ee4 100644 --- a/hosts/charon/configuration.nix +++ b/hosts/charon/configuration.nix @@ -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" ]; diff --git a/hosts/charon/mail.nix b/hosts/charon/mail.nix index 216171eb..51cb9f4b 100644 --- a/hosts/charon/mail.nix +++ b/hosts/charon/mail.nix @@ -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 { diff --git a/hosts/charon/matrix.nix b/hosts/charon/matrix.nix index 81b238b6..6462f533 100644 --- a/hosts/charon/matrix.nix +++ b/hosts/charon/matrix.nix @@ -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"; diff --git a/modules/mpd.nix b/modules/mpd.nix new file mode 100644 index 00000000..cabf1d50 --- /dev/null +++ b/modules/mpd.nix @@ -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"; + }; + }; + }; +} diff --git a/packages/eventd/default.nix b/packages/eventd/default.nix index 0a16adcb..bac0b24f 100644 --- a/packages/eventd/default.nix +++ b/packages/eventd/default.nix @@ -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 diff --git a/packages/rust-scripts/Cargo.lock b/packages/rust-scripts/Cargo.lock index 4606dab3..f5c47b52 100644 --- a/packages/rust-scripts/Cargo.lock +++ b/packages/rust-scripts/Cargo.lock @@ -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)" = "" "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)" = "" "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" diff --git a/packages/rust-scripts/Cargo.toml b/packages/rust-scripts/Cargo.toml index e95e4cab..e1f5912e 100644 --- a/packages/rust-scripts/Cargo.toml +++ b/packages/rust-scripts/Cargo.toml @@ -4,5 +4,14 @@ version = "0.1.0" authors = ["Malte Brandy "] [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" diff --git a/packages/rust-scripts/src/bin/hotkeys.rs b/packages/rust-scripts/src/bin/hotkeys.rs index 39f6e5c5..f2c0c985 100644 --- a/packages/rust-scripts/src/bin/hotkeys.rs +++ b/packages/rust-scripts/src/bin/hotkeys.rs @@ -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) +}); diff --git a/packages/rust-scripts/src/bin/kassandra.rs b/packages/rust-scripts/src/bin/kassandra.rs new file mode 100644 index 00000000..c5f821be --- /dev/null +++ b/packages/rust-scripts/src/bin/kassandra.rs @@ -0,0 +1,7 @@ +extern crate rust_scripts; +#[macro_use] +extern crate error_chain; + +use rust_scripts::kassandra::kassandra; + +quick_main!(kassandra); diff --git a/packages/rust-scripts/src/generate.rs b/packages/rust-scripts/src/generate.rs new file mode 100644 index 00000000..d0e67aa7 --- /dev/null +++ b/packages/rust-scripts/src/generate.rs @@ -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(&mut self, new: Option) + where + T: Into; + /// 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(&mut self, new: Option) + where + T: Into; + /// 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(&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(&mut self, new: Option) + where + T: Into, + { + 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(&mut self, new: Option) + where + T: Into, + { + 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(&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) { + 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>, +) -> 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(&mut self, generators: T) -> Result<()> +where + T: IntoIterator, + T::Item: Into; +} + +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(&mut self, generators: T) -> Result<()> +where + T: IntoIterator, + T::Item: Into, +{ + check_ignores(self)?; + let mut orphans = HashMap::>::default(); + let mut create = Vec::::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) +} +} diff --git a/packages/rust-scripts/src/hotkeys.rs b/packages/rust-scripts/src/hotkeys.rs new file mode 100644 index 00000000..65b6faa9 --- /dev/null +++ b/packages/rust-scripts/src/hotkeys.rs @@ -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>(name: T, command: &str) -> Item { + let command = command.to_owned(); + ( + name.into(), + Do(Rc::new(move || { + str2cmd(&command).exec(); + Ok(Exit) + })), + ) +} + +pub fn term>(name: T, command: &str) -> Item { + run(name, &format!("urxvt -e {}", command)) +} + +pub fn holdterm>(name: T, command: &str) -> Item { + run(name, &format!("urxvt -hold -e {}", command)) +} + +pub fn menu>(name: T, options: Vec) -> Item { + let name = name.into(); + (name.clone(), Menu((name.clone(), options))) +} + +#[derive(Clone)] +pub enum Next { + Menu(Dialog), + Back, + Exit, + Stay, + Do(Rc Result>), +} + +type Dialog = (String, Vec); + +type Item = (String, Next); + +type Command = Vec; + +fn show_menu(dialog_provider: &mut T, menu: Dialog) -> Result { + 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::>(), + ) +} + +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(()) +} diff --git a/packages/rust-scripts/src/kassandra.rs b/packages/rust-scripts/src/kassandra.rs new file mode 100644 index 00000000..c866afbe --- /dev/null +++ b/packages/rust-scripts/src/kassandra.rs @@ -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 { + 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 { + 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>(dialog: &mut T, msg: S) -> Result { + Ok(TaskBuilder::default() + .description(dialog.input_line(msg, Vec::::default())?) + .build()?) +} + + +struct Kassandra { + state: State, + dialog: RofiDialogProvider, + cache: TaskCache, +} + + +impl Kassandra { + fn new() -> Result { + 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 bool>(&self, filter: T) -> Vec { + self.get_sorted_tasks(filter) + .into_iter() + .map(|x| x.uuid().clone()) + .collect() + } + + fn get_sorted_tasks 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>( + &mut self, + msg: T, + uuid: &Uuid, + ) -> Result> { + let msg = msg.into(); + let format_msg = |cache: &TaskCache, uuid, text| -> Result { + 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>); + 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 { + 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::>(); + 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() +} diff --git a/packages/rust-scripts/src/lib.rs b/packages/rust-scripts/src/lib.rs index af7f9a2f..3e74c17a 100644 --- a/packages/rust-scripts/src/lib.rs +++ b/packages/rust-scripts/src/lib.rs @@ -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 Next>), - } - - type Dialog = (String, Vec); - - type Item = (String, Next); - - type Command = Vec; - - type State = String; - - pub fn c(string: T) -> String - where - T: Into, - { - string.into() - } - - pub fn cmd(argv: S) -> Command - where - S: IntoIterator, - K: Into, - { - argv.into_iter().map(|x| x.into()).collect() - } - - - fn show_menu(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); } } } diff --git a/packages/rust-scripts/src/refresh.rs b/packages/rust-scripts/src/refresh.rs new file mode 100644 index 00000000..7f4e0ee8 --- /dev/null +++ b/packages/rust-scripts/src/refresh.rs @@ -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(&mut self, tasks: T, recurrence: Timer) -> Result<()> + where + T: IntoIterator; +} + +impl TaskRefresher for TaskCache { + fn reactivate(&mut self, tasks: T, recurrence: Timer) -> Result<()> + where + T: IntoIterator, + { + 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::>(); + for uuid in uuids { + let old = self.get_mut(&uuid).expect("BUG!"); + *old.status_mut() = TS::Pending; + old.set_end(None as Option); + } + self.generate(changes) + } +} diff --git a/packages/rust-scripts/src/tasktree.rs b/packages/rust-scripts/src/tasktree.rs new file mode 100644 index 00000000..5d7b27bb --- /dev/null +++ b/packages/rust-scripts/src/tasktree.rs @@ -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>; + fn set_partof(&mut self, Option); + fn add_tag>(&mut self, T); + fn remove_tag>(&mut self, T); + fn has_tag>(&self, T) -> bool; + fn tagged(&self) -> bool; + fn pending(&self) -> bool; + fn obsolete(&self) -> bool; +} + +impl TaskNode for Task { + fn partof(&self) -> Result> { + 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) { + 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>(&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>(&mut self, tag: T) { + if let Some(tags) = self.tags_mut() { + let tag = tag.into(); + tags.retain(|t| *t != tag); + } + } + + fn has_tag>(&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>; + fn get_parent_mut(&mut self, uuid: &Uuid) -> Result>; + 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; + fn is_project(&self, uuid: &Uuid) -> bool; + fn refresh_tree(&mut self); +} +fn get_project_name(cache: &TaskCache, task: &Task) -> Result> { + 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> { + 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> { + 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 { + 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::>(); + 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"); + } + } + } +} diff --git a/packages/rust-scripts/src/update.rs b/packages/rust-scripts/src/update.rs new file mode 100644 index 00000000..c863419a --- /dev/null +++ b/packages/rust-scripts/src/update.rs @@ -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 +where + T: IntoIterator, +{ + 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(()) +}