// Parse a key macro and return a string of tokens public string[] ValidateMacro(string macro) { // This will track modifier keys to ensure they're all terminated List <string> Modifiers = new List <string>(); // Holds series of tokens to be returned List <string> KeyTokens = new List <string>(); // Split input into words string[] tokens = macro.Split(' '); if (tokens.Length < 1) { throw new FormatException("Key macro is blank"); } // Iterate over words foreach (string token in tokens) { string tokenU = token.ToUpper(); // Check if word is a valid key if (SendRawInput.KeyData.ContainsKey(tokenU)) { // Add it to the return value KeyTokens.Add(tokenU); SendRawInput.KeyInfo key = SendRawInput.KeyData[tokenU]; // If the key is a modifier and isn't in the Modifiers list, add it. // Otherwise, remove it. // This will be used to ensure all modifiers are terminated. if (key.Modifier) { if (!Modifiers.Contains(tokenU)) { Modifiers.Add(tokenU); } else { Modifiers.Remove(tokenU); } } } else { // Was not a valid key; give up and throw exception (since otherwise we'd need to // return an empty string, which is vague) throw new FormatException("Invalid key name: " + token); } } // If there are any unmatched modifier keys (pressed but never released), release them now if (Modifiers.Count > 0) { #if DEBUG DbgW("Modifier keys " + string.Join(",", Modifiers) + " were not terminated, terminating automatically."); #endif // Add each modifier to the returned string foreach (string Modifier in Modifiers) { KeyTokens.Add(Modifier); } } return(KeyTokens.ToArray()); /* * Note for later reference: there are a lot of ways of making modifier keys work, and at some later date, * if anyone has interest in this project , I would love to see this language replaced with one that does a * cleaner job with them, but parsers are the opposite of my forte, so this isn't going to be clean. * * A good way to handle this would be to say that a single modifier by itself is an instantaneous keypress, * a modifier prefixed e.g. CTRL {A A S} will send Ctrl keydown, A A S as keypresses, then Ctrl keyup. But * the code for that is ugly and confusing to my head, so I'm just going to make every modifier a toggle. * * Every modifier is flagged as such in the key value structure, and has a state field. when it's in a * macro it will toggle the state every time it's called; macro validation will fail if not all modifiers are * released after being set. This is probably adequate long term, frankly. */ }
// Common function for triggering button events so delay & instant buttons use the same code private void ButtonEvent(int index) { // New sendkey APIs //SendRawInput.SendKeyDown(SendRawInput.KeyCodes.DIK_LCONTROL); //SendRawInput.SendKeyPress(SendRawInput.KeyCodes.DIK_V, 50); //SendRawInput.SendKeyUp(SendRawInput.KeyCodes.DIK_LCONTROL); // Don't actually send keys if "Enable binds" is unchecked if (ChkBinds.Checked == false) { DbgW("Binds disabled; not sending keys"); return; } string macro = ActionStrings[index].Text; string[] MacroTokens; // Validate the macro and get an array of valid keycodes back try { MacroTokens = ValidateMacro(macro); } catch (FormatException e) { DbgW("Macro for button " + index.ToString() + " is invalid: " + e.Message); return; } // This will track modifier keys to ensure they're all terminated List <string> Modifiers = new List <string>(); DbgW("Sending keys: " + string.Join(",", MacroTokens)); // Send the actual keystrokes foreach (string Token in MacroTokens) { SendRawInput.KeyInfo key = SendRawInput.KeyData[Token]; // Track which modifiers are down and send the appropriate Down or Up if (key.Modifier) { if (!Modifiers.Contains(Token)) { Modifiers.Add(Token); SendRawInput.SendKeyDown(Token); } else { Modifiers.Remove(Token); SendRawInput.SendKeyUp(Token); } } else { // It's a normal key, just press it // Currently there is a 50ms hardcoded delay; this will be improved later SendRawInput.SendKeyPress(Token, 50); } } // Play default sound if (ButtonState[index].SoundEffect == true) { PlaySound("coinin"); } }