Compare commits

...

5 Commits

Author SHA1 Message Date
Colin b4653b20a2 moby: button map: move vol{up,down}_hold to only act in power-off, else they may falsely trigger during other power-on actions 2024-04-22 05:13:29 +00:00
Colin 2c011df252 moby: tweak button mappings
- power hold: toggle media
- power x1 -> hold: kill

now nothing in screenoff uses power x2, which means we can get to
screen-on without waiting for any timeout.
2024-04-22 05:10:35 +00:00
Colin 9d472bb290 sane-input-handler: clean up suggestedPrograms 2024-04-22 04:13:56 +00:00
Colin 95b21cbed9 moby: update improve button mappings
- power + volup: screenshot
- power + voldown: camera
- volup_hold: file browser
- remove modal media controls
2024-04-22 04:05:52 +00:00
Colin 82007c9b40 bonsai: store the config in ~/.config to allow easier online editing 2024-04-22 04:05:15 +00:00
4 changed files with 113 additions and 89 deletions

View File

@ -99,18 +99,12 @@ in
type = types.listOf transitionType;
default = [];
};
configFile = mkOption {
type = types.path;
default = pkgs.writeText "bonsai_tree.json" (builtins.toJSON cfg.config.transitions);
description = ''
configuration file to pass to bonsai.
usually auto-generated from the sibling options; exposed mainly for debugging or convenience.
'';
};
};
};
};
fs.".config/bonsai/bonsai_tree.json".symlink.text = builtins.toJSON cfg.config.transitions;
sandbox.method = "bwrap";
sandbox.extraRuntimePaths = [
"/" #< just needs "bonsai", but needs to create it first...
@ -120,7 +114,7 @@ in
description = "bonsai: programmable input dispatcher";
partOf = [ "graphical-session" ];
# nice -n -11 chosen arbitrarily. i hope this will allow for faster response to inputs, but without audio underruns (pipewire is -21, dino -15-ish)
command = "nice -n -11 bonsaid -t ${cfg.config.configFile}";
command = "nice -n -11 bonsaid -t $HOME/.config/bonsai/bonsai_tree.json";
cleanupCommand = "rm -f $XDG_RUNTIME_DIR/bonsai";
};
};

View File

@ -37,6 +37,18 @@ let
hash = "sha256-gz3N4uo7IWzzqaPHHVhby/e9NbtzcFJRQwgdNYxO/Yw=";
})
];
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
pkgs.copyDesktopItems
];
desktopItems = (upstream.desktopItems or []) ++ [
(pkgs.makeDesktopItem {
name = "rofi-filebrowser";
# alternatively: `rofi -modes filebrowser -show`, however this would require theme tweaking to look good
exec = "rofi -combi-modes filebrowser -show";
desktopName = "rofi filebrowser";
})
];
});
# rofi-emoji = pkgs.rofi-emoji.override {
# # plugins must be compiled against the same rofi they're loaded by

View File

@ -94,12 +94,17 @@ in
};
suggestedPrograms = [
"bonsai"
# dependencies which get pulled in unconditionally:
"killall"
"playerctl"
"procps"
"sane-open-desktop"
"sway"
"wireplumber"
# optional integrations:
"megapixels"
"rofi"
"xdg-terminal-exec"
"wvkbd"
];
sandbox.method = "bwrap";
@ -138,32 +143,27 @@ in
sane.programs.bonsai.config.transitions = lib.mkIf cfg.enabled (friendlyToBonsai {
# map sequences of "events" to an argument to pass to sane-input-handler
# map: power (short), power (short) x2, power (long)
power_pressed.timeout.ms = 900; # press w/o release. this is a long timeout because it's tied to the "kill window" action.
power_pressed.timeout.trigger = "powerhold";
power_pressed.power_released.timeout.trigger = "powerbutton_one";
power_pressed.power_released.timeout.ms = 300;
power_pressed.power_released.power_pressed.trigger = "powerbutton_two";
# map power (short) -> volup/voldown
power_pressed.power_released.volup_pressed.trigger = "powerbutton_volup";
power_pressed.power_released.voldown_pressed.trigger = "powerbutton_voldown";
# map: power (tap), power (tap) x2
power_pressed.power_released.trigger = "power_tap_1";
power_pressed.power_released.timeout.ms = 600; # max time within which a second power press will be recognized
power_pressed.power_released.power_pressed.power_released.trigger = "power_tap_2";
# map power (hold), power tap -> hold:
power_pressed.timeout.trigger = "power_hold";
power_pressed.timeout.ms = 600;
power_pressed.power_released.power_pressed.timeout.trigger = "power_tap_1_hold";
power_pressed.power_released.power_pressed.timeout.ms = 750; # this is a long timeout because it's tied to the "kill window" action.
# map: power (tap) -> volup/voldown
power_pressed.power_released.volup_pressed.trigger = "power_then_volup";
power_pressed.power_released.voldown_pressed.trigger = "power_then_voldown";
# map: power + volup/voldown
power_pressed.volup_pressed.trigger = "power_and_volup";
power_pressed.voldown_pressed.trigger = "power_and_voldown";
# map: volume taps and holds
volup_pressed = (recurseHold "volup" {}) // {
# this either becomes volup_hold_* (via recurseHold, above) or:
# - a short volup_tap_1 followed by:
# - a *finalized* volup_1 (i.e. end of action)
# - more taps/holds, in which case we prefix it with `modal_<action>`
# to denote that we very explicitly entered this state.
#
# it's clunky: i do it this way so that voldown can map to keyboard/terminal in unlock mode
# but trigger media controls in screenoff
# in a way which *still* allows media controls if explicitly entered into via a tap on volup first
volup_released = (volumeActions { prefix = "modal_"; }) // {
trigger = "volup_tap_1";
timeout.ms = 300;
timeout.trigger = "volup_1";
};
volup_pressed = (volumeActions {}).volup_pressed // {
trigger = "volup_start";
};
voldown_pressed = (volumeActions {}).voldown_pressed // {
trigger = "voldown_start";

View File

@ -12,27 +12,46 @@
#
# example of a design which considers these things:
# - when unlocked:
# - volup toggle -> app menu
# - voldown press -> keyboard
# - voldown hold -> terminal
# - power x2 -> screenoff
# - hold power -> kill app
# - volup tap -> app menu
# - volup hold -> file browser
# - voldown press -> keyboard
# - voldown hold -> terminal
# - power x2 -> screenoff
# - power tap->hold -> kill app
# - power,volup -> screen rotate CCW
# - power,voldown -> screen rotate CW
# - power+volup -> screenshot
# - power+voldown -> camera
# - when locked:
# - volup tap -> volume up
# - volup hold -> media seek forward
# - voldown tap -> volume down
# - voldown hold -> media seek backward
# - power x1 -> screen on
# - power x2 -> play/pause media
# some trickiness allows for media controls in unlocked mode:
# - volup tap -> enter media mode
# - i.e. in this state, vol tap/hold is mapped to volume/seek
# - if, after entering media mode, no more taps occur, then we trigger the default app-menu action
# - volup tap -> volume up
# - volup hold -> media seek forward
# - voldown tap -> volume down
# - voldown hold -> media seek backward
# - power tap -> screen on
# - power hold -> play/pause media
# limitations/downsides:
# - power mappings means phone is artificially slow to unlock.
# - media controls when unlocked have quirks:
# - mashing voldown to decrease the volume will leave you with a toggled keyboard.
# - seeking backward isn't possible except by first tapping volup.
# - voldown hold is over eager: easy to open terminals when phone is slow.
# - remap to voldown tap->hold ?
#
# EXAMPLE EVENT FIRINGS:
# - double-tap voldown:
# - voldown_start
# - voldown_tap_1
# - voldown_tap_2
# - hold voldown:
# - voldown_start
# - voldown_hold_1
# - voldown_hold_2
# - voldown_hold_3
# - hold power:
# - power_hold (notice: it doesn't fire power_start)
# - double-tap power:
# - power_tap_1
# - power_tap_2
# - power tap-then-hold:
# - power_tap_1
# - power_tap_1_hold
# increments to use for volume adjustment (in %)
@ -106,82 +125,81 @@ handleWith() {
dispatchDefault() {
case "$action" in
"powerbutton_one")
# power once => unlock
handleWith allOn
;;
"powerbutton_two")
"power_tap_2")
# power twice => screenoff
handleWith allOff
;;
# powerbutton_three: intentional no-op because overloading the kill-window handler is risky
"power_hold")
# power twice => toggle media player
handleWith playerctl play-pause
;;
volup_tap*|modal_volup_tap*)
volup_tap*)
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%+
;;
voldown_tap*|modal_voldown_tap*)
voldown_tap*)
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%-
;;
volup_hold*|modal_volup_hold*)
handleWith playerctl position 30+
;;
voldown_hold*|modal_voldown_hold*)
handleWith playerctl position 10-
;;
esac
}
dispatchOff() {
case "$action" in
"powerbutton_two")
# power twice => toggle media player
handleWith playerctl play-pause
;;
"powerhold")
# power toggle during deep sleep often gets misread as power hold, so treat same
"power_tap_1")
# power once => unlock
handleWith allOn
;;
volup_hold*)
handleWith playerctl position 30+
;;
voldown_hold*)
handleWith playerctl position 10-
;;
esac
}
dispatchOn() {
case "$action" in
# powerbutton_one: intentional default to no-op
# powerbutton_two: intentional default to screenoff
"powerhold")
# power thrice: kill active window
# power_tap_1: intentional default to no-op (it's important this be unmapped, because events can be misordered with power_tap_1 arriving *after* power_tap_2)
# power_tap_2: intentional default to screenoff
"power_tap_1_hold")
# power tap->hold: kill active window
# TODO: disable this if locked (with e.g. schlock, swaylock, etc)
handleWith swaymsg kill
;;
"powerbutton_volup")
"power_and_volup")
# power (hold) -> volup: take screenshot
handleWith sane-open-desktop sane-screenshot.desktop
;;
"power_and_voldown")
# power (hold) -> voldown: open camera
handleWith sane-open-desktop org.postmarketos.Megapixels.desktop
;;
"power_then_volup")
# power (tap) -> volup: rotate CCW
handleWith swaymsg -- output '-' transform 90 anticlockwise
;;
"powerbutton_voldown")
"power_then_voldown")
# power (tap) -> voldown: rotate CW
handleWith swaymsg -- output '-' transform 90 clockwise
;;
"volup_tap_1")
# swallow: this could be the start to a media control (multi taps / holds),
# or it could be just a single tap -> release, handled next/below
handleWith ignore
;;
"volup_1")
# volume up once: system menu
handleWith sane-open-desktop rofi.desktop
;;
"volup_hold_1")
# volume up hold: just browse files
handleWith sane-open-desktop rofi-filebrowser.desktop
;;
"voldown_start")
# volume down once: toggle keyboard
handleWith toggleKeyboard
;;
"voldown_hold_2")
"voldown_hold_1")
# hold voldown to launch terminal
# note we already triggered the keyboard; that's fine: usually keyboard + terminal go together :)
# voldown_hold_1 frequently triggers during short taps meant only to reveal the keyboard,
# so prefer a longer hold duration
handleWith sane-open-desktop xdg-terminal-exec.desktop
;;
"voldown_tap_1")
@ -197,8 +215,8 @@ dispatchOn() {
dispatchInhibited() {
case "$action" in
"powerhold")
# power thrice: escape hatch in case rofi has hung
"power_hold")
# power hold: escape hatch in case rofi has hung
handleWith killall -9 rofi
;;
*)