diff --git a/hosts/common/programs/rofi/default.nix b/hosts/common/programs/rofi/default.nix index 881ce6de1..d0cdbb8a0 100644 --- a/hosts/common/programs/rofi/default.nix +++ b/hosts/common/programs/rofi/default.nix @@ -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 diff --git a/hosts/common/programs/sane-input-handler/default.nix b/hosts/common/programs/sane-input-handler/default.nix index 6d3b8b460..1fe044c42 100644 --- a/hosts/common/programs/sane-input-handler/default.nix +++ b/hosts/common/programs/sane-input-handler/default.nix @@ -138,32 +138,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"; + # map: power (short), power (short) x2 + power_pressed.power_released.timeout.trigger = "power_tap_1_final"; 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"; + power_pressed.power_released.power_pressed.trigger = "power_tap_2_final"; + # map: power (short) -> 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: 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.power_released.trigger = "power_hold"; + ## remap power + volup/voldown on the long power, to decrease accidental "kill window"s + power_pressed.timeout.volup_pressed.trigger = "power_and_volup"; + power_pressed.timeout.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_` - # 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"; diff --git a/hosts/common/programs/sane-input-handler/sane-input-handler b/hosts/common/programs/sane-input-handler/sane-input-handler index bbbf2ce45..6cf26d5da 100755 --- a/hosts/common/programs/sane-input-handler/sane-input-handler +++ b/hosts/common/programs/sane-input-handler/sane-input-handler @@ -12,11 +12,16 @@ # # example of a design which considers these things: # - when unlocked: -# - volup toggle -> app menu +# - volup tap -> app menu +# - volup hold -> file browser # - voldown press -> keyboard # - voldown hold -> terminal # - power x2 -> screenoff -# - hold power -> kill app +# - power 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 @@ -24,15 +29,31 @@ # - 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 # 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. +# - alternate mapping could resolve this: +# - power_tap_1: screen on (i.e. immediately after releasing power, if not held long) +# - power_hold_1: play/pause media (which now works in any mode) +# - power_tap_2: screen off +# - power_tap_1_hold_1: kill +# - by hiding this behind the tap, it decreases the chance of accidental kills +# - "tap-then-hold" idiom could make sense for keyboard/terminal, too: voldown_tap_1_hold_1: terminal, instead of voldown_hold -- decreases accidental terminal launches +# - power-hold for kill means messing up the compound operations has bad consequences +# +# 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_2_final (notice: it doesn't fire power_tap_1) # increments to use for volume adjustment (in %) @@ -106,27 +127,26 @@ handleWith() { dispatchDefault() { case "$action" in - "powerbutton_one") + "power_tap_1_final") # power once => unlock handleWith allOn ;; - "powerbutton_two") + "power_tap_2_final") # power twice => screenoff handleWith allOff ;; - # powerbutton_three: intentional no-op because overloading the kill-window handler is risky - 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*) + volup_hold*) handleWith playerctl position 30+ ;; - voldown_hold*|modal_voldown_hold*) + voldown_hold*) handleWith playerctl position 10- ;; esac @@ -134,11 +154,11 @@ dispatchDefault() { dispatchOff() { case "$action" in - "powerbutton_two") + "power_tap_2_final") # power twice => toggle media player handleWith playerctl play-pause ;; - "powerhold") + "power_hold") # power toggle during deep sleep often gets misread as power hold, so treat same handleWith allOn ;; @@ -147,41 +167,46 @@ dispatchOff() { dispatchOn() { case "$action" in - # powerbutton_one: intentional default to no-op - # powerbutton_two: intentional default to screenoff - "powerhold") + # power_tap_1_final: intentional default to no-op + # power_tap_2_final: intentional default to screenoff + "power_hold") # power thrice: 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 +222,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 ;; *)