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; type = types.listOf transitionType;
default = []; 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.method = "bwrap";
sandbox.extraRuntimePaths = [ sandbox.extraRuntimePaths = [
"/" #< just needs "bonsai", but needs to create it first... "/" #< just needs "bonsai", but needs to create it first...
@ -120,7 +114,7 @@ in
description = "bonsai: programmable input dispatcher"; description = "bonsai: programmable input dispatcher";
partOf = [ "graphical-session" ]; 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) # 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"; cleanupCommand = "rm -f $XDG_RUNTIME_DIR/bonsai";
}; };
}; };

View File

@ -37,6 +37,18 @@ let
hash = "sha256-gz3N4uo7IWzzqaPHHVhby/e9NbtzcFJRQwgdNYxO/Yw="; 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 { # rofi-emoji = pkgs.rofi-emoji.override {
# # plugins must be compiled against the same rofi they're loaded by # # plugins must be compiled against the same rofi they're loaded by

View File

@ -94,12 +94,17 @@ in
}; };
suggestedPrograms = [ suggestedPrograms = [
"bonsai" "bonsai"
# dependencies which get pulled in unconditionally:
"killall" "killall"
"playerctl" "playerctl"
"procps" "procps"
"sane-open-desktop" "sane-open-desktop"
"sway" "sway"
"wireplumber" "wireplumber"
# optional integrations:
"megapixels"
"rofi"
"xdg-terminal-exec"
"wvkbd" "wvkbd"
]; ];
sandbox.method = "bwrap"; sandbox.method = "bwrap";
@ -138,32 +143,27 @@ in
sane.programs.bonsai.config.transitions = lib.mkIf cfg.enabled (friendlyToBonsai { sane.programs.bonsai.config.transitions = lib.mkIf cfg.enabled (friendlyToBonsai {
# map sequences of "events" to an argument to pass to sane-input-handler # map sequences of "events" to an argument to pass to sane-input-handler
# map: power (short), power (short) x2, power (long) # map: power (tap), power (tap) x2
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.power_released.trigger = "power_tap_1";
power_pressed.timeout.trigger = "powerhold"; power_pressed.power_released.timeout.ms = 600; # max time within which a second power press will be recognized
power_pressed.power_released.timeout.trigger = "powerbutton_one"; power_pressed.power_released.power_pressed.power_released.trigger = "power_tap_2";
power_pressed.power_released.timeout.ms = 300; # map power (hold), power tap -> hold:
power_pressed.power_released.power_pressed.trigger = "powerbutton_two"; power_pressed.timeout.trigger = "power_hold";
# map power (short) -> volup/voldown power_pressed.timeout.ms = 600;
power_pressed.power_released.volup_pressed.trigger = "powerbutton_volup"; power_pressed.power_released.power_pressed.timeout.trigger = "power_tap_1_hold";
power_pressed.power_released.voldown_pressed.trigger = "powerbutton_voldown"; 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 # map: volume taps and holds
volup_pressed = (recurseHold "volup" {}) // { volup_pressed = (volumeActions {}).volup_pressed // {
# this either becomes volup_hold_* (via recurseHold, above) or: trigger = "volup_start";
# - 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";
};
}; };
voldown_pressed = (volumeActions {}).voldown_pressed // { voldown_pressed = (volumeActions {}).voldown_pressed // {
trigger = "voldown_start"; trigger = "voldown_start";

View File

@ -12,27 +12,46 @@
# #
# example of a design which considers these things: # example of a design which considers these things:
# - when unlocked: # - when unlocked:
# - volup toggle -> app menu # - volup tap -> app menu
# - voldown press -> keyboard # - volup hold -> file browser
# - voldown hold -> terminal # - voldown press -> keyboard
# - power x2 -> screenoff # - voldown hold -> terminal
# - hold power -> kill app # - 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: # - when locked:
# - volup tap -> volume up # - volup tap -> volume up
# - volup hold -> media seek forward # - volup hold -> media seek forward
# - voldown tap -> volume down # - voldown tap -> volume down
# - voldown hold -> media seek backward # - voldown hold -> media seek backward
# - power x1 -> screen on # - power tap -> screen on
# - power x2 -> play/pause media # - power hold -> 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
# limitations/downsides: # limitations/downsides:
# - power mappings means phone is artificially slow to unlock. # - voldown hold is over eager: easy to open terminals when phone is slow.
# - media controls when unlocked have quirks: # - remap to voldown tap->hold ?
# - mashing voldown to decrease the volume will leave you with a toggled keyboard. #
# - seeking backward isn't possible except by first tapping volup. # 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 %) # increments to use for volume adjustment (in %)
@ -106,82 +125,81 @@ handleWith() {
dispatchDefault() { dispatchDefault() {
case "$action" in case "$action" in
"powerbutton_one") "power_tap_2")
# power once => unlock
handleWith allOn
;;
"powerbutton_two")
# power twice => screenoff # power twice => screenoff
handleWith allOff 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"%+ 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"%- 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 esac
} }
dispatchOff() { dispatchOff() {
case "$action" in case "$action" in
"powerbutton_two") "power_tap_1")
# power twice => toggle media player # power once => unlock
handleWith playerctl play-pause
;;
"powerhold")
# power toggle during deep sleep often gets misread as power hold, so treat same
handleWith allOn handleWith allOn
;; ;;
volup_hold*)
handleWith playerctl position 30+
;;
voldown_hold*)
handleWith playerctl position 10-
;;
esac esac
} }
dispatchOn() { dispatchOn() {
case "$action" in case "$action" in
# powerbutton_one: intentional default to no-op # 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)
# powerbutton_two: intentional default to screenoff # power_tap_2: intentional default to screenoff
"powerhold") "power_tap_1_hold")
# power thrice: kill active window # power tap->hold: kill active window
# TODO: disable this if locked (with e.g. schlock, swaylock, etc) # TODO: disable this if locked (with e.g. schlock, swaylock, etc)
handleWith swaymsg kill 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 # power (tap) -> volup: rotate CCW
handleWith swaymsg -- output '-' transform 90 anticlockwise handleWith swaymsg -- output '-' transform 90 anticlockwise
;; ;;
"powerbutton_voldown") "power_then_voldown")
# power (tap) -> voldown: rotate CW # power (tap) -> voldown: rotate CW
handleWith swaymsg -- output '-' transform 90 clockwise handleWith swaymsg -- output '-' transform 90 clockwise
;; ;;
"volup_tap_1") "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 # volume up once: system menu
handleWith sane-open-desktop rofi.desktop handleWith sane-open-desktop rofi.desktop
;; ;;
"volup_hold_1")
# volume up hold: just browse files
handleWith sane-open-desktop rofi-filebrowser.desktop
;;
"voldown_start") "voldown_start")
# volume down once: toggle keyboard # volume down once: toggle keyboard
handleWith toggleKeyboard handleWith toggleKeyboard
;; ;;
"voldown_hold_2") "voldown_hold_1")
# hold voldown to launch terminal # hold voldown to launch terminal
# note we already triggered the keyboard; that's fine: usually keyboard + terminal go together :) # 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 handleWith sane-open-desktop xdg-terminal-exec.desktop
;; ;;
"voldown_tap_1") "voldown_tap_1")
@ -197,8 +215,8 @@ dispatchOn() {
dispatchInhibited() { dispatchInhibited() {
case "$action" in case "$action" in
"powerhold") "power_hold")
# power thrice: escape hatch in case rofi has hung # power hold: escape hatch in case rofi has hung
handleWith killall -9 rofi handleWith killall -9 rofi
;; ;;
*) *)