No internet connection
  1. Home
  2. Ideas

Deck modifier key implementation

By Martin Wrang @Martin_Wrang
    2023-08-31 21:35:46.020Z

    Are there any plans to integrate keyboard modifiers into the deck designer?

    I know the common workaround of using event.keyboardState in a script to trigger different actions depending on the modifier. Or even running a background script that shows different decks depending on held modifiers. I just don't think those solutions are very elegant, efficient or flexible.

    Being able to trigger completely different commands from the same deck button, depending on which modifier key(s) are held, would make decks many times more powerful (up to 240 commands on a 15 button Stream Deck). The deck icons should of course update as modifiers are held.

    Using the same modifier/layer buttons as in the 'Keyboard Map' pane to show different "layers" of a deck in the deck designer would make it very easy to configure.

    • 8 replies
    1. M
      Martin Wrang @Martin_Wrang
        2023-08-31 21:53:40.711Z

        As an example:

        Deck with no modifiers:

        Deck with ctrl held:

        Deck with ctrl+alt+cmd held:

        Currently using the top row of buttons to trigger different decks, giving a sort of tabs/panes functionality.

        1. O
          In reply toMartin_Wrang:

          You explained something I've been toying with asking for for a while perfectly! 100% to this. I know it would throw some current scripts that use modifier keys already for a loop. But some sort of alternate DECK+ like this would be super intuitive for most people, even if you did have to start over on some of your more advanced scripts that already take advantage of modifier keys.

          1. M
            In reply toMartin_Wrang:
            Martin Wrang @Martin_Wrang
              2023-08-31 22:07:54.435Z

              I saw your post from September 2021 about this sort of thing too. Seems like a logical evolution of decks to me.

              Incorporating the functionality described in this post into the decks themselves, effectively turning 1 deck into 16 deck layers is basically what I'm talking about:
              How to use keyboard modifier keys with Stream Deck and other devices to create multifunctional buttons and "layers". #post-9

              1. Yeah I built the not recommended 'multi-function for dummies' package for this reason.

                But it's Clunky to build with and doesn't do the cool ICON Switching which I think would be CLUTCH.

                1. In reply toMartin_Wrang:

                  I gotta say.. this is working really well, except for a weird hiccup I'm going to ask @Kitch about next Zoom Meetup I can make. You're right it's a LOT more Deck Managing. And if it was simply built in the way you've suggested it'd be way more elegant.

                  But I've put this on a window trigger for protools 'Edit:' window and it's damn snappy. I might spend a day fleshing out decks to go with it at some point.

                  @JesperA You're kinda a Genius.

                  var app = sf.ui.proTools
                  
                  var normal = 'user:ckrmaawmn00025r10ez7e20qj:cku5ow80o0002gx109hlb1g33'
                  var shift = 'user:ckrmaawmn00025r10ez7e20qj:clk8ltq0g0001kr10xeuck7af'
                  var ctrl = 'user:ckrmaawmn00025r10ez7e20qj:ckzj48zkf000pdt10wxqfhi2p'
                  
                  // If finder is unhiden start loop
                  sf.engine.runInBackground(function () {
                      while (true) {
                          // This checks for a user cancellation.
                          sf.engine.checkForCancellation();
                  
                          // This will break the loop if the window includes the title Plugin is in focus.  
                          if (app.focusedWindow.title.value == 'Plug-in*') 
                              break;
                          
                          // This will break the loop if the window includes the title Plugin is in focus.  
                          if (app.focusedWindow.title.value == 'Audiosuite*') 
                              break;
                  
                          // This will break the loop if finder is not in focus 
                          if (sf.ui.frontmostApp.activeBundleID != app.activeBundleID)
                              break;
                  
                  if (event.keyboardState.asString == 'shift') sf.soundflow.runCommand({ commandId: shift })
                  else if (event.keyboardState.asString == 'ctrl') sf.soundflow.runCommand({ commandId: ctrl })
                  else sf.soundflow.runCommand({ commandId: normal })
                  
                  
                  // This will do so we check every 100 ms if there's been a "change"
                          sf.wait({
                              executionMode: 'Background',
                              intervalMs: 100,
                              onError: 'Continue'
                          });
                      }
                  });
                  
                  1. MMartin Wrang @Martin_Wrang
                      2023-09-01 08:02:29.683Z

                      I'll try this approach as a temporary solution until we hopefully have the feature implemented!

                      1. @Martin_Wrang, @Kitch has been improving this script, see below, obviously add your own decks to the ID's instead.

                        It now has an on and off button too which is always nice.

                        globalState.isModifierDeckRunning = true;
                        
                        const proTools = sf.ui.proTools;
                        
                        let lastHeldModifier = event.keyboardState.asString;
                        
                        const deckIds = {
                            "shift": `user:ckrmaawmn00025r10ez7e20qj:clm06bmrw000dot10nwwhmynu`,
                            "ctrl": `user:ckrmaawmn00025r10ez7e20qj:ckzj48zkf000pdt10wxqfhi2p`,
                            "alt": `user:ckrmaawmn00025r10ez7e20qj:cl9oga5zr0001zs10jwta5f15`,
                            "": `user:ckrmaawmn00025r10ez7e20qj:cku5ow80o0002gx109hlb1g33`,
                        };
                        
                        function showDeck(modifierDeckId) {
                            sf.soundflow.runCommand({
                                commandId: modifierDeckId
                            });
                        }
                        
                        function main() {
                            let updatedKeyboardModifier = event.keyboardState.asString;
                        
                            if (updatedKeyboardModifier !== lastHeldModifier) {
                                // This will break the loop if the window includes the title Plugin is in focus.
                                if (proTools.focusedWindow.title.value.startsWith("Plug-in")) return;
                        
                                // This will break the loop if the window includes the title AudioSuite is in focus.
                                if (proTools.focusedWindow.title.value.startsWith("Audiosuite")) return;
                        
                                // This will break the loop if protools is not in focus
                                if (sf.ui.frontmostApp.activeBundleID !== proTools.activeBundleID) return;
                        
                                // Show Deck
                                deckIds[updatedKeyboardModifier] && showDeck(deckIds[updatedKeyboardModifier]);
                        
                                lastHeldModifier = updatedKeyboardModifier;
                            }
                        }
                        
                        function runForever(name, action, interval, timeout) {
                            let now = (new Date).valueOf();
                            if (now - globalState[name] < timeout) throw 0; //Exit if we were invoked again inside the timeout
                        
                            globalState[name] = now;
                            sf.engine.runInBackground(function () {
                                try {
                                    while (true) {
                                        sf.engine.checkForCancellation();
                                        globalState[name] = (new Date).valueOf();
                        
                                        if (globalState.isModifierDeckRunning) action();
                        
                                        sf.wait({ intervalMs: interval, executionMode: 'Background' });
                                    }
                                } finally {
                                    globalState[name] = null;
                                }
                            });
                        }
                        
                        runForever("isDeckFollowRunning", main, 100, 5000);
                        

                        You can then use a separate script to turn it off.

                        globalState.isModifierDeckRunning = !globalState.isModifierDeckRunning;
                        
                      2. Thanks Owen ☺️

                        The newly talk about Meta Commands will be the way to get icon switching more natively. Basically it can change the icons on single keys, rather than us changing a full deck (having to replicate and maintain them etc).
                        You can see this in some of the new videos showing the Stream Deck Plus features posted on the SF instagram/Facebook.

                        I don't know though when Meta Commands will be general available and have a simple implementation.
                        So for now I will say it's definitely still worth it to build a temporary solution, since it works pretty well..

                        Best, Jesper