Author Topic: Determine If Variable Hotkey Pressed or Not?  (Read 1251 times)

Starblue7

  • Full Member
  • ***
  • Posts: 147
Determine If Variable Hotkey Pressed or Not?
« on: February 12, 2025, 09:26:07 PM »
Was looking around, but couldn't find a clear enough way to do this.

I have a Variable Hotkey called VK_FWD

This is defined as the Arrow Up key or ARROWU

I'd like to detect, in a conditional, whether the VK_FWD Variable Hotkey is in a pressed state or not.

I was thinking something like:  {STATE_KEYSTATE:VK_FWD} = '1'   (then pressed)
But that isn't working.

Could you please help correct my format on this, and or, is it even possible?
As Variable Hotkeys could be a combination, like:  RCTRL + F1
So the KEYSTATE would need to know if both the RCTRL and F1 keys are both pressed.

Thanks muchos.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4836
  • RTFM
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #1 on: February 12, 2025, 11:57:12 PM »
Variable hotkeys as a feature was designed as command triggers, specifically, so there is no state token for them.

To properly check the key states , you'd need to write a parser that takes each key in the combination, and checks them individually using the "{STATE_KEYSTATE:}" token, assuming that is something you know how to do.

Barring that, you could assign the variable hotkey to a set of two commands, where one triggers on press, and the other on release, and have them set a variable.
This method could get desynchronized, however, if for whatever reason the release isn't detected (E.G. if hotkeys are suspended while a key is down).

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #2 on: February 13, 2025, 05:42:47 AM »
Yeah, the desync thing is something I'd be worried about.

The parse idea.  Well hoping wouldn't need to do that.
But if all keys must be surrounded by brackets [ ]  then the parser could pull that out and feed that into a KEYSTATE check.
Was kinda hoping wouldn't need to do that.
Maybe this wheel has already been created somewhere so I wouldn't need to recreate it.

SemlerPDX

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 302
  • Upstanding Lunatic
    • My AVCS Homepage
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #3 on: February 13, 2025, 01:29:34 PM »
I created my own variable hotkey push-to-talk system for one of my profiles, though it's quite proprietary to my own profile systems (and written back when I was refamiliarizing myself with VB.net instead of C#, works for over a thousand current users but my methods makes me cringe nowadays).  That said, free free to review it for how I do certain things if you want:
AVCS QCC PTT Key Monitor v3 for VoiceAttack on pastebin.com

For keyboard keys, this is what it boils down to, where earlier methods/expressions gather the value of "checkKeyDown" to something like:
Code: [Select]
STATE_KEYSTATE:144(no square brackets, just the key code and expected token format for a key state)

Code: (C#) [Select]
if (VA.Utility.ParseTokens("{" + checkKeyDown + "}") == "1")
{
    keyDown = true;
}
...or more simply:
Code: (C#) [Select]
keyDown = (VA.Utility.ParseTokens("{" + checkKeyDown + "}") == "1");


...and then, of course, subsequent logic could act upon this as appropriate, essentially ending with something resulting in this:
Code: (C#) [Select]
Thread.Sleep(keyDown ? 150 : 500);

if (VA.State.GetListeningEnabled() != keyDown)
{
    VA.State.SetListeningEnabled(keyDown);
}
...I use a larger sleep time on the PTT release than for the press, you might consider this as well.


I gather what key was pressed in order to set it to a number similar this:
Code: (C#) [Select]
private string GetKeyboardKey()
{
    string checkKeyDown = string.Empty;

    if (VA.Utility.ParseTokens("{STATE_ANYKEYDOWN}") == "0")
    {
        return checkKeyDown;
    }

    for (int k = 1; k <= 254; k++)
    {
        if (VA.Utility.ParseTokens("{STATE_KEYSTATE:" + k.ToString() + "}") == "1")
        {
            return "STATE_KEYSTATE:" + k.ToString();
        }
    }

    return checkKeyDown;
}

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #4 on: February 13, 2025, 03:22:58 PM »
Thanks.  Will take a look at this.

I was thinking of just making a call to a master command that would either contain the VA commands to parse, or the C# code to do it, passing the Variable Hotkey in question in the call, and then the C# would parse and check if any of the keys are in a PRESS state or not.

Since Variable Hotkeys take Brackets '[ ]' as well as just a key letter, like 'u', I'm gonna need to look for any open/close brackets and pull the in-between value, as well as value without open/close brackets.

I guess if I wanted to use the virtual key code, then I'd need a mapping between that and the text(key) I pull out of the Variable Hotkey.  Hmm

Thanks for giving me some ideas about it.

SemlerPDX

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 302
  • Upstanding Lunatic
    • My AVCS Homepage
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #5 on: February 14, 2025, 04:13:17 PM »
...
...passing the Variable Hotkey in question in the call, and then the C# would parse and check if any of the keys are in a PRESS state or not.

...gonna need to look for any open/close brackets and pull the in-between value, as well as value without open/close brackets.

...to use the virtual key code, then I'd need a mapping between that and the text(key) I pull out of the Variable Hotkey.

None of this is really needed for a single key contained in the VK_FWD text variable.  First of all, if you have a variable already containing the key you want to press, you can simply nest that into the state token (even if the value of VK_FWD is surrounded in square brackets):

Code: [Select]
{STATE_KEYSTATE:{TXT:VK_FWD}}
...all this is because (as noted in the VA Manual under "Key State Token Parameter Values" around page 200):
Quote
...if you wanted to see if the F10 key is down, simply use the following token: {STATE_KEYSTATE:F10}.
Note: For convenience, if the key parameter is surrounded by square brackets (‘[‘ and ‘]’),
the brackets will be automatically removed before processing.

meaning all of these are valid options..
Code: [Select]
{STATE_KEYSTATE:F1}
{STATE_KEYSTATE:[F1]}
{STATE_KEYSTATE:{TXT:VK_FWD}}
(which for the UP arrow key is the same as the following, too)
{STATE_KEYSTATE:[ARROWU]}
{STATE_KEYSTATE:38}

(and, as an example of actions in a command)
Code: [Select]
Begin Text Compare : [{STATE_KEYSTATE:{TXT:VK_FWD}}] Equals '1'
  Start VoiceAttack listening
Else
  Stop VoiceAttack listening
End Condition

(and also, as an example of an inline function executed from a command, running in the background so long as VoiceAttack is running)
Code: (C#) [Select]
using System;
using System.Threading;

public class VAInline
{
  public void main()
  {
    while(true)
    {
      var keyDown = (VA.Utility.ParseTokens("{STATE_KEYSTATE:{TXT:VK_FWD}}") == "1");
      Thread.Sleep(keyDown ? 150 : 500);

      if (VA.State.GetListeningEnabled() != keyDown)
      {
        VA.State.SetListeningEnabled(keyDown);
      }

      // Add a small delay to reduce CPU usage
      Thread.Sleep(25);
    }
  }
}
   
...although, as you stated previously that you might have more than just a single key, but like a key combo, you'd need to parse that out as you described.
...
As Variable Hotkeys could be a combination, like:  RCTRL + F1
So the KEYSTATE would need to know if both the RCTRL and F1 keys are both pressed.
One way would be to use Regular Expressions (RegEx), and then you can simply monitor either one or both keys to have been pressed, and toggle the listening mode respectively (if not already in the correct mode).

Here's a working example you can test and tinker with, and/or alter as needed for different functionality -- uncomment various "TESTING" variables at the top of the main() method to see how these work, using the "Test Run" button to start/stop this script from within the inline function editor:
Code: (C#) [Select]
using System;
using System.Threading;
using System.Text.RegularExpressions;

public class VAInline
{
  public string[] ParseVariableHotkeys(string input)
  {
    // Regex to capture either bracketed keys or single characters
    var matches = Regex.Matches(input, @"(\[[A-Z0-9]+\])|(\d+)|(\w)");

    string[] keys = new string[matches.Count];

    for (int i = 0; i < matches.Count; i++)
    {
      keys[i] = matches[i].Value;
    }

    return keys;
  }
 
  public void main()
  {
    /// TESTING:
    //VA.SetText("VK_FWD", "[RCTRL][F1]");
    //VA.SetText("VK_FWD", "[LCTRL][ENTER]");
    //VA.SetText("VK_FWD", "[ALT]3");
    //VA.SetText("VK_FWD", "t");
    //VA.SetText("VK_FWD", "[ARROWU]");
    VA.SetText("VK_FWD", "38"); // The virtual key code for Arrow-UP is 38
   
   
    // Gather the string value(s) contained in the Variable Hotkey "VK_FWD"
    string[] keys = ParseVariableHotkeys(VA.GetText("VK_FWD") ?? string.Empty);
   
    // Guard Clause to ensure there is at least one key and less than 3 set in "VK_FWD"
    if (keys.Length == 0 || keys.Length > 2)
    {
      VA.WriteToLog("Invalid or no valid keys parsed from VK_FWD.", "red");
      return;
    }
   
    bool hasSecondKey = keys.Length > 1;

    VA.WriteToLog("Push to Talk Key Monitor is now Active", "green");

    while (true)
    {
      bool key1Down = (VA.Utility.ParseTokens("{STATE_KEYSTATE:" + keys[0] + "}") == "1");
      bool key2Down = hasSecondKey ? (VA.Utility.ParseTokens("{STATE_KEYSTATE:" + keys[1] + "}") == "1") : true;
     
      bool keysPressed = key1Down && key2Down;

      Thread.Sleep(keysPressed ? 150 : 500);

      if (VA.State.GetListeningEnabled() != keysPressed)
      {
        VA.State.SetListeningEnabled(keysPressed);
      }
     
      // Add a small delay to reduce CPU usage
      Thread.Sleep(25);
    }
  }
}

I hope that helps you accomplish your current goals, and gives you an idea on how you can accomplish similar advanced goals in future.

Best wishes and have fun!!   8)

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #6 on: February 14, 2025, 10:45:03 PM »
Thanks.

Well, I have multiple Variable Hotkeys that are a combination, like:  LALT + ARROWU  or RSHIFT + U

Thanks for your example code below.
It looks good and I may apply it.

Besides a Variable Hotkey by Variable Hotkey basis, I'm thinking may just do a blanket call to force release of ALL keys.

I've written some code in C# to do this, but it doesn't work.  i.e.  the W key is made to be in a Pressed state via Voice Attack Command.
Subsequent, the C# is called to release the W key, but in my testing, the C# code won't release the W key.

So it seems like something is holding on to the Press of the W key and the C# code can't do anything about it.

If you have any ideas..
This is going beyond the scope of this post.  Please feel free to DM me if you want.

Code: [Select]
using System;
using System.Runtime.InteropServices;

public class VAInline
{
    [DllImport("user32.dll")]
    private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct INPUT
    {
        public uint type;
        public KEYBDINPUT ki;
    }

    private const int INPUT_KEYBOARD = 1;
    private const uint KEYEVENTF_KEYUP = 0x0002;
    private const ushort VK_W = 0x57; // Virtual key code for 'W'

    public static void main()
    {
        INPUT[] inputs = new INPUT[1];
        inputs[0] = new INPUT
        {
            type = INPUT_KEYBOARD,
            ki = new KEYBDINPUT
            {
                wVk = VK_W,
                dwFlags = KEYEVENTF_KEYUP
            }
        };

        SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
    }
}




SemlerPDX

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 302
  • Upstanding Lunatic
    • My AVCS Homepage
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #7 on: February 15, 2025, 03:56:34 PM »
This is going beyond the scope of this post.  Please feel free to DM me if you want.

Not at all, this is exactly what this/these forums are designed for, and I'm happy to help here if I can.  It may also help others in the future who may be after the same sort of help.  S'all good.

...
...the W key is made to be in a Pressed state via Voice Attack Command.
...in my testing, the C# code won't release the W key.

So it seems like something is holding on to the Press of the W key and the C# code can't do anything about it.

Yes.  You are telling VoiceAttack to continually hold down the W key, then expecting to send some key up event to release it without telling VoiceAttack to stop holding down that same key.

You can either manually press the key and release it to stop the action you issued to VoiceAttack to hold it, or you can specifically send a Keypress action to release that key (or variable hotkey), or you can use the "Stop all commands processing" action to effectively release any/all keys that have been told to be held by VoiceAttack:

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #8 on: February 15, 2025, 05:38:07 PM »
Well.  I've got some commands running in VA that I'd like to not stop if I could help it.

I'm wondering then, if a better approach would be to loop through all keys to see which ones are pressed, and send a keypress action for any that are in a pressed down state.
This may seem silly, but my profile has become complex that some keys end up remaining to be pressed, or somehow get stuck.  So part of this is to help debug and not just clean the slate.

So maybe I'll try to create a VB or C# routine to do the above, and spit out a report of what keys were found to be pressed.

Any suggestions on the language or implementation?  Or am I over thinking this?

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #9 on: February 15, 2025, 07:17:31 PM »
I'm wondering if there's any way to provide the key code of the key,  like 87 is for the W key, and use that in some KEYPRESS to simulate it?

Note.  I'm steering away form parsing Variable Hotkeys and looking towards a more blanket solution to identify what keys are in a pressed state, and send a keystroke for that key to release it.  As it were my pressing the key on the keyboard myself.

If there's no way via code to do something like:  VAProxy.Command.Keypress(KeyCode),  then I guess I have
to loop through to find keys that are pressed.  Like if I find W (87) key pressed, I then have to pass
that to a variable hotkey and then execute a command with a Press (variable hotkey).

Seems messy, and wish there was just a direct way to call up the Voice Attack Keypress from the code itself.
« Last Edit: February 15, 2025, 08:39:37 PM by Starblue7 »

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #10 on: February 16, 2025, 05:13:35 PM »
So, what I would REALLY like is a way via Inline Code (C# or VB) to instruct Voice Attack to release a key that it is currently pressing.  Without having to Call(Execute) a Voice Attack Command from the Code.

But rather, to do it directly from the code itself, with something like:

vaProxy.Keypress(KeyCode, 0)  0 = release, 1 = press


Is there NO possible way to do this directly from the code?  Must I have to rely on calling a Voice Attack Command to do this?


SemlerPDX

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 302
  • Upstanding Lunatic
    • My AVCS Homepage
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #11 on: February 17, 2025, 12:23:15 PM »
When you are already telling VoiceAttack to hold a key, afaik, there is no way for you to issue the release without telling VoiceAttack to stop holding a key.  There should be no mystery which key(s) are pressed if you are designing this entire system yourself.

You have variables which will contain/hold the value of various keys, and you have systems which could end up with these keys being held using the VoiceAttack action to "Press and hold" one or more of these variable hotkeys.  If you have commands potentially running in the background that should not be stopped by the "Stop all commands processing" action, then the programmatic way to release them would be to iterate over all your variable hotkeys and issue the action to release them using the VoiceAttack Keypress (release) action.

If you needed to do this all from code directly, cutting out VoiceAttack actions for the key hold AND release, only then might you also be able to issue the release programmatically.  This gets rather advanced very quickly, and this is where VoiceAttack shines in making what are programmatically advanced and difficult concepts easy and approachable:  as I said above, you know every hotkey variable keyword that could contain a key, and could use VoiceAttack actions to press AND/OR release any or all of these simply using VoiceAttack Keypress actions in your own custom "stop keypresses" command.

[edit:  it's been brought to my attention that I may be mistaken in my understanding of what is happening when we tell VoiceAttack to hold a key down, just FYI.  I'm still learning more about the nuances of what is happening behind the scenes and made a few false-positive conclusions.  Hoping this can be cleared up here by those who know more before this thread is concluded]
« Last Edit: February 21, 2025, 04:08:26 PM by SemlerPDX »

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #12 on: February 20, 2025, 05:25:03 AM »
Thanks for further response.

From what I'm gathering, right now, is that if I initiate a keypress and hold from a Voice Attack command, that keypress/hold will remain that state, because it's coming from Voice Attack.
Sendkeys, windows api/dlls and other methods programmatically from inline containers won't get Voice Attack to release the identified key.
So the only thing I could think of is if we had some type of attribute in the VA Class that would allow us, as Voice Attack, to make the release.  Something like VAProxy.Keypress(key,0)  0 = release, 1 = press/hold   Something like that.
Without something like the above, I guess it's currently not possible using an Inline Container within Voice Attack to do this.
Instead, I'd have to build an array of any keys found in a pressed state, and call and execute a command in Voice Attack to do this.  Seems potentially not very performant and unnecessary hoops.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4836
  • RTFM
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #13 on: February 20, 2025, 06:31:03 AM »
As far as I'm aware the Windows keyboard system effectively works on a "last known status" principle. So when VoiceAttack instructs Windows to press a key, that key remains pressed only because that is the last known state.

VoiceAttack isn't actively holding the key down, nor does it have any authority over when it can be released, E.G. by the physical keyboard or other software (whether said software is running as part of VoiceAttack's own process or not)


Have you verified that your method for releasing keys actually works? E.G. by physically holding down a key, instructing a release in software, and then checking its state (while still holding it down)?

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #14 on: February 20, 2025, 03:33:34 PM »
I have a keyboard monitor that will visually show me if a key is in an active keypress state or not.

In my testing, I had Voice Attack (Via Command) press and hold a key down.
I tried via C# Inline Code to send a key release and press and release event to said key being pressed.
Neither indicated that the key was actually put back to a released state.

Yet, If I execute a VA Command from Code, passing the keyname to the VA Command to 'Release' the key, that works.

VA is running as admin as well. 

So far, I can only assume that the Inline Code needs to tell Voice Attack to release the key.  So far I've had no other success.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4836
  • RTFM
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #15 on: February 21, 2025, 03:24:32 AM »
In my testing, I had Voice Attack (Via Command) press and hold a key down.
I tried via C# Inline Code to send a key release and press and release event to said key being pressed.
Neither indicated that the key was actually put back to a released state.
I'm specifically asking about a key that isn't "held down" by VoiceAttack.

From what you've mentioned so far, there is no evidence that your inline function is actually working at all, regardless of whether VoiceAttack is involved.

SemlerPDX

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 302
  • Upstanding Lunatic
    • My AVCS Homepage
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #16 on: February 21, 2025, 03:16:53 PM »
In my testing, I had Voice Attack (Via Command) press and hold a key down.
I tried via C# Inline Code to send a key release and press and release event to said key being pressed.
Neither indicated that the key was actually put back to a released state.

...

So far, I can only assume that the Inline Code needs to tell Voice Attack to release the key.  So far I've had no other success.

Ya know, I hadn't considered what Pfeil stated above, so went back to some testing.  Your original C# script doesn't function to press (and/or release) a key at all.  You should be able to see the letter being typed into something like Notepad, and that does not happen with the above script.

However, this will work to press and release the indicated key, and you can play with this altering as needed (such as removing the 'press' line of code to simply issue a release).

I have already tested this, and it functionally releases any key that VoiceAttack had previously been told to hold down (whether you send a key down & up event, or just send the key up event):
Code: (C#) [Select]
using System;
using System.Threading;
using System.Runtime.InteropServices;

public class VAInline
{
  [DllImport("user32.dll", SetLastError = true)]
  private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

  private const uint KEYEVENTF_KEYUP = 0x0002;
  private const ushort VK_W = 0x57; // Virtual key code for 'W'

  public void main()
  {
    //Send a key down event for "W"  (Not needed, can remove for final testing)
    keybd_event((byte)VK_W, 0, 0, UIntPtr.Zero);

    Thread.Sleep(150); //Hold the key down for 150ms  (Not needed, can remove for final testing)

    //Send a key up event for "W"
    keybd_event((byte)VK_W, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
  }
}

Starblue7

  • Full Member
  • ***
  • Posts: 147
Re: Determine If Variable Hotkey Pressed or Not?
« Reply #17 on: Today at 07:01:40 AM »
Thanks SemlerPDX,

I'll take a look again.  Appreciate your help.