From 6815c53fde23ff1bc184c65fa124948a1d4ea141 Mon Sep 17 00:00:00 2001 From: mika-n Date: Sun, 11 Aug 2019 01:01:57 +0300 Subject: [PATCH] Added "run on key release" and "run synchronized" and "repeat while held" and "keep key state" support for macro special actions --- DS4Windows/DS4Control/Mapping.cs | 652 ++++++++++++++++++++++--------- DS4Windows/DS4Control/ScpUtil.cs | 10 + 2 files changed, 468 insertions(+), 194 deletions(-) diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index 28d25e6..bdf19d2 100644 --- a/DS4Windows/DS4Control/Mapping.cs +++ b/DS4Windows/DS4Control/Mapping.cs @@ -191,6 +191,7 @@ namespace DS4Windows public static bool[] pressedonce = new bool[261], macrodone = new bool[38]; static bool[] macroControl = new bool[25]; static uint macroCount = 0; + static Dictionary[] macroTaskQueue = new Dictionary[4] { new Dictionary(), new Dictionary(), new Dictionary(), new Dictionary() }; //actions public static int[] fadetimer = new int[4] { 0, 0, 0, 0 }; @@ -1240,11 +1241,12 @@ namespace DS4Windows bool active = getBoolMapping2(device, dcs.control, cState, eState, tp, fieldMapping); if (active) { - PlayMacro(device, macroControl, string.Join("/", (int[])action), dcs.control, keyType); + //PlayMacro(device, macroControl, string.Join("/", (int[])action), dcs.control, keyType); + PlayMacro(device, macroControl, String.Empty, null, (int[])action, dcs.control, keyType); } else { - EndMacro(device, macroControl, string.Join("/", (int[])action), dcs.control); + EndMacro(device, macroControl, /*string.Join("/",*/ (int[])action /*)*/, dcs.control); } // erase default mappings for things that are remapped @@ -1679,6 +1681,8 @@ namespace DS4Windows break; } } + if (action.typeID == SpecialAction.ActionTypeId.Macro && action.pressRelease && action.firstTouch) // DEBUG: Run macro when trigger keys are released + triggeractivated = !triggeractivated; } bool utriggeractivated = true; @@ -1773,22 +1777,52 @@ namespace DS4Windows else if (action.typeID == SpecialAction.ActionTypeId.Macro) { actionFound = true; - - if (!actionDone[index].dev[device]) + if (!action.pressRelease) { - DS4KeyType keyType = action.keyType; - actionDone[index].dev[device] = true; - //foreach (DS4Controls dc in action.trigger) - for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + // Macro run when trigger keys are pressed down (the default behaviour) + if (!actionDone[index].dev[device]) { - DS4Controls dc = action.trigger[i]; - resetToDefaultValue2(dc, MappedState, outputfieldMapping); - } + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } - PlayMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None, keyType); + //AppLogger.LogToGui($"DEBUG: PlayMacro={String.Join("/", action.macro)} trigger={action.controls}", false); + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, actionDone[index]); + } + else + { + if (!action.keyType.HasFlag(DS4KeyType.RepeatMacro)) + EndMacro(device, macroControl, action.macro, DS4Controls.None); + } + } + else + { + // Macro is run when trigger keys are released (optional behaviour of macro special action)) + if (action.firstTouch) + { + action.firstTouch = false; + if (!actionDone[index].dev[device]) + { + DS4KeyType keyType = action.keyType; + actionDone[index].dev[device] = true; + for (int i = 0, arlen = action.trigger.Count; i < arlen; i++) + { + DS4Controls dc = action.trigger[i]; + resetToDefaultValue2(dc, MappedState, outputfieldMapping); + } + + //AppLogger.LogToGui($"DEBUG: pressRelease PlayMacro={String.Join("/", action.macro)} trigger={action.controls}", false); + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, null); + //EndMacro(device, macroControl, action.macro, DS4Controls.None); + } + } + else + action.firstTouch = true; } - else - EndMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None); } else if (action.typeID == SpecialAction.ActionTypeId.Key) { @@ -2044,7 +2078,8 @@ namespace DS4Windows if ((DateTime.UtcNow - action.TimeofEnd) > TimeSpan.FromMilliseconds(150)) { if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + //PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); tappedOnce = false; action.tappedOnce = false; @@ -2070,7 +2105,8 @@ namespace DS4Windows } if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + //PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); firstTouch = false; action.firstTouch = false; @@ -2094,7 +2130,8 @@ namespace DS4Windows } if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + //PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); secondtouchbegin = false; action.secondtouchbegin = false; @@ -2194,203 +2231,412 @@ namespace DS4Windows } } - private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) + /* + private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) + { + if (macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) + { + string[] skeys; + int wait = 1000; + if (!string.IsNullOrEmpty(macro)) + { + skeys = macro.Split('/'); + ushort delay; + if (ushort.TryParse(skeys[skeys.Length - 1], out delay) && delay > 300) + wait = delay - 300; + } + AltTabSwapping(wait, device); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + } + else + { + string[] skeys; + int[] keys; + if (!string.IsNullOrEmpty(macro)) + { + skeys = macro.Split('/'); + keys = new int[skeys.Length]; + } + else + { + skeys = new string[0]; + keys = new int[0]; + } + for (int i = 0; i < keys.Length; i++) + keys[i] = int.Parse(skeys[i]); + bool[] keydown = new bool[286]; + if (control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) + { + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + foreach (int i in keys) + { + if (i >= 1000000000) + { + if (i > 1000000000) + { + string lb = i.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + DS4LightBar.forcelight[device] = true; + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); + } + else + { + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + } + } + else if (i >= 1000000) + { + DS4Device d = Program.rootHub.DS4Controllers[device]; + string r = i.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + d.setRumble(light, heavy); + } + else if (i >= 300) //ints over 300 used to delay + await Task.Delay(i - 300); + else if (!keydown[i]) + { + if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); //anything above 255 is not a keyvalue + else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); + else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); + else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); + else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); + else if (i == 261) { macroControl[0] = true; macroCount++; } + else if (i == 262) { macroControl[1] = true; macroCount++; } + else if (i == 263) { macroControl[2] = true; macroCount++; } + else if (i == 264) { macroControl[3] = true; macroCount++; } + else if (i == 265) { macroControl[4] = true; macroCount++; } + else if (i == 266) { macroControl[5] = true; macroCount++; } + else if (i == 267) { macroControl[6] = true; macroCount++; } + else if (i == 268) { macroControl[7] = true; macroCount++; } + else if (i == 269) { macroControl[8] = true; macroCount++; } + else if (i == 270) { macroControl[9] = true; macroCount++; } + else if (i == 271) { macroControl[10] = true; macroCount++; } + else if (i == 272) { macroControl[11] = true; macroCount++; } + else if (i == 273) { macroControl[12] = true; macroCount++; } + else if (i == 274) { macroControl[13] = true; macroCount++; } + else if (i == 275) { macroControl[14] = true; macroCount++; } + else if (i == 276) { macroControl[15] = true; macroCount++; } + else if (i == 277) { macroControl[16] = true; macroCount++; } + else if (i == 278) { macroControl[17] = true; macroCount++; } + else if (i == 279) { macroControl[18] = true; macroCount++; } + else if (i == 280) { macroControl[19] = true; macroCount++; } + else if (i == 281) { macroControl[20] = true; macroCount++; } + else if (i == 282) { macroControl[21] = true; macroCount++; } + else if (i == 283) { macroControl[22] = true; macroCount++; } + else if (i == 284) { macroControl[23] = true;macroCount++; } + else if (i == 285) { macroControl[24] = true; macroCount++; } + else if (keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyPress((ushort)i); + else + InputMethods.performKeyPress((ushort)i); + keydown[i] = true; + } + else + { + if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue + else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + else if (i == 261) { macroControl[0] = false; if (macroCount > 0) macroCount--; } + else if (i == 262) { macroControl[1] = false; if (macroCount > 0) macroCount--; } + else if (i == 263) { macroControl[2] = false; if (macroCount > 0) macroCount--; } + else if (i == 264) { macroControl[3] = false; if (macroCount > 0) macroCount--; } + else if (i == 265) { macroControl[4] = false; if (macroCount > 0) macroCount--; } + else if (i == 266) { macroControl[5] = false; if (macroCount > 0) macroCount--; } + else if (i == 267) { macroControl[6] = false; if (macroCount > 0) macroCount--; } + else if (i == 268) { macroControl[7] = false; if (macroCount > 0) macroCount--; } + else if (i == 269) { macroControl[8] = false; if (macroCount > 0) macroCount--; } + else if (i == 270) { macroControl[9] = false; if (macroCount > 0) macroCount--; } + else if (i == 271) { macroControl[10] = false; if (macroCount > 0) macroCount--; } + else if (i == 272) { macroControl[11] = false; if (macroCount > 0) macroCount--; } + else if (i == 273) { macroControl[12] = false; if (macroCount > 0) macroCount--; } + else if (i == 274) { macroControl[13] = false; if (macroCount > 0) macroCount--; } + else if (i == 275) { macroControl[14] = false; if (macroCount > 0) macroCount--; } + else if (i == 276) { macroControl[15] = false; if (macroCount > 0) macroCount--; } + else if (i == 277) { macroControl[16] = false; if (macroCount > 0) macroCount--; } + else if (i == 278) { macroControl[17] = false; if (macroCount > 0) macroCount--; } + else if (i == 279) { macroControl[18] = false; if (macroCount > 0) macroCount--; } + else if (i == 280) { macroControl[19] = false; if (macroCount > 0) macroCount--; } + else if (i == 281) { macroControl[20] = false; if (macroCount > 0) macroCount--; } + else if (i == 282) { macroControl[21] = false; if (macroCount > 0) macroCount--; } + else if (i == 283) { macroControl[22] = false; if (macroCount > 0) macroCount--; } + else if (i == 284) { macroControl[23] = false; if (macroCount > 0) macroCount--; } + else if (i == 285) { macroControl[24] = false; if (macroCount > 0) macroCount--; } + else if (keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyRelease((ushort)i); + else + InputMethods.performKeyRelease((ushort)i); + keydown[i] = false; + } + } + for (int i = 0, arlength = keydown.Length; i < arlength; i++) + { + if (keydown[i]) + { + if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue + else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); + else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); + else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); + else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); + else if (i == 261) { macroControl[0] = false; if (macroCount > 0) macroCount--; } + else if (i == 262) { macroControl[1] = false; if (macroCount > 0) macroCount--; } + else if (i == 263) { macroControl[2] = false; if (macroCount > 0) macroCount--; } + else if (i == 264) { macroControl[3] = false; if (macroCount > 0) macroCount--; } + else if (i == 265) { macroControl[4] = false; if (macroCount > 0) macroCount--; } + else if (i == 266) { macroControl[5] = false; if (macroCount > 0) macroCount--; } + else if (i == 267) { macroControl[6] = false; if (macroCount > 0) macroCount--; } + else if (i == 268) { macroControl[7] = false; if (macroCount > 0) macroCount--; } + else if (i == 269) { macroControl[8] = false; if (macroCount > 0) macroCount--; } + else if (i == 270) { macroControl[9] = false; if (macroCount > 0) macroCount--; } + else if (i == 271) { macroControl[10] = false; if (macroCount > 0) macroCount--; } + else if (i == 272) { macroControl[11] = false; if (macroCount > 0) macroCount--; } + else if (i == 273) { macroControl[12] = false; if (macroCount > 0) macroCount--; } + else if (i == 274) { macroControl[13] = false; if (macroCount > 0) macroCount--; } + else if (i == 275) { macroControl[14] = false; if (macroCount > 0) macroCount--; } + else if (i == 276) { macroControl[15] = false; if (macroCount > 0) macroCount--; } + else if (i == 277) { macroControl[16] = false; if (macroCount > 0) macroCount--; } + else if (i == 278) { macroControl[17] = false; if (macroCount > 0) macroCount--; } + else if (i == 279) { macroControl[18] = false; if (macroCount > 0) macroCount--; } + else if (i == 280) { macroControl[19] = false; if (macroCount > 0) macroCount--; } + else if (i == 281) { macroControl[20] = false; if (macroCount > 0) macroCount--; } + else if (i == 282) { macroControl[21] = false; if (macroCount > 0) macroCount--; } + else if (i == 283) { macroControl[22] = false; if (macroCount > 0) macroCount--; } + else if (i == 284) { macroControl[23] = false; if (macroCount > 0) macroCount--; } + else if (i == 285) { macroControl[24] = false; if (macroCount > 0) macroCount--; } + else if (keyType.HasFlag(DS4KeyType.ScanCode)) + InputMethods.performSCKeyRelease((ushort)i); + else + InputMethods.performKeyRelease((ushort)i); + } + } + + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + Program.rootHub.DS4Controllers[device].setRumble(0, 0); + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + await Task.Delay(50); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + } + } + } + */ + + // PlayMacro as a background task. If the macroPlayQueueName is defined then play macro only when the previous macro in the same queue + // has completed already (ie. if the same macro trigger key has both key down and key release macros then those are run synchronized). + private static void PlayMacro(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action = null, ActionState actionDoneState = null) { - if (macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18")) + if (action != null && action.synchronized) + { + // Run special action macros in synchronized order (ie. FirstIn-FirstOut). The trigger control name string is the execution queue identifier (ie. each unique trigger combination has an own synchronization queue). + if (!macroTaskQueue[device].TryGetValue(action.controls, out Task prevTask)) + macroTaskQueue[device].Add(action.controls, (Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState))) ); + else + macroTaskQueue[device][action.controls] = prevTask.ContinueWith((x) => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + else + // Run macro as "fire and forget" background task. No need to wait for completion of any of the other macros. + // If the same trigger macro is re-launched while previous macro is still running then the order of parallel macros is not guaranteed. + Task.Factory.StartNew(() => PlayMacroTask(device, macroControl, macroStr, macroLst, macroArr, control, keyType, action, actionDoneState)); + } + + // Play through a macro. The macro steps are defined either as string, List or Array object (always only one of those parameters is set to a valid value) + private static /*async*/ void PlayMacroTask(int device, bool[] macrocontrol, string macroStr, List macroLst, int[] macroArr, DS4Controls control, DS4KeyType keyType, SpecialAction action, ActionState actionDoneState) + { + if(!String.IsNullOrEmpty(macroStr)) { string[] skeys; - int wait = 1000; - if (!string.IsNullOrEmpty(macro)) - { - skeys = macro.Split('/'); - ushort delay; - if (ushort.TryParse(skeys[skeys.Length - 1], out delay) && delay > 300) - wait = delay - 300; - } + + skeys = macroStr.Split('/'); + macroArr = new int[skeys.Length]; + for (int i = 0; i < macroArr.Length; i++) + macroArr[i] = int.Parse(skeys[i]); + } + + // macro.StartsWith("164/9/9/164") || macro.StartsWith("18/9/9/18") + if ( (macroLst != null && macroLst.Count >= 4 && ((macroLst[0] == 164 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 164) || (macroLst[0] == 18 && macroLst[1] == 9 && macroLst[2] == 9 && macroLst[3] == 18))) + || (macroArr != null && macroArr.Length>= 4 && ((macroArr[0] == 164 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 164) || (macroArr[0] == 18 && macroArr[1] == 9 && macroArr[2] == 9 && macroArr[3] == 18))) + ) + { + int wait; + if(macroLst != null) + wait = macroLst[macroLst.Count - 1]; + else + wait = macroArr[macroArr.Length - 1]; + + if (wait <= 300 || wait > ushort.MaxValue) + wait = 1000; + else + wait -= 300; + AltTabSwapping(wait, device); if (control != DS4Controls.None) macrodone[DS4ControltoInt(control)] = true; } - else + else if(control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) { - string[] skeys; - int[] keys; - if (!string.IsNullOrEmpty(macro)) + int macroCodeValue; + bool[] keydown = new bool[286]; + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = true; + + // Play macro codes and simulate key down/up events (note! The same key may go through several up and down events during the same macro). + // If the return value is TRUE then this async method should do a asynchronized delay (the usual Thread.Sleep doesnt work here because it would block the main gamepad reading thread also). + if (macroLst != null) { - skeys = macro.Split('/'); - keys = new int[skeys.Length]; + for (int i = 0; i < macroLst.Count; i++) + { + macroCodeValue = macroLst[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + //await Task.Delay(macroCodeValue - 300); + Task.Delay(macroCodeValue - 300).Wait(); + } } else { - skeys = new string[0]; - keys = new int[0]; - } - for (int i = 0; i < keys.Length; i++) - keys[i] = int.Parse(skeys[i]); - bool[] keydown = new bool[286]; - if (control == DS4Controls.None || !macrodone[DS4ControltoInt(control)]) - { - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = true; - foreach (int i in keys) + for (int i = 0; i < macroArr.Length; i++) { - if (i >= 1000000000) - { - string lb = i.ToString().Substring(1); - if (i > 1000000000) - { - byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); - byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); - byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); - DS4LightBar.forcelight[device] = true; - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); - } - else - { - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - } - } - else if (i >= 1000000) - { - DS4Device d = Program.rootHub.DS4Controllers[device]; - string r = i.ToString().Substring(1); - byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); - byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); - d.setRumble(light, heavy); - } - else if (i >= 300) //ints over 300 used to delay - await Task.Delay(i - 300); - else if (!keydown[i]) - { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); - else if (i == 261) { macroControl[0] = true; macroCount++; } - else if (i == 262) { macroControl[1] = true; macroCount++; } - else if (i == 263) { macroControl[2] = true; macroCount++; } - else if (i == 264) { macroControl[3] = true; macroCount++; } - else if (i == 265) { macroControl[4] = true; macroCount++; } - else if (i == 266) { macroControl[5] = true; macroCount++; } - else if (i == 267) { macroControl[6] = true; macroCount++; } - else if (i == 268) { macroControl[7] = true; macroCount++; } - else if (i == 269) { macroControl[8] = true; macroCount++; } - else if (i == 270) { macroControl[9] = true; macroCount++; } - else if (i == 271) { macroControl[10] = true; macroCount++; } - else if (i == 272) { macroControl[11] = true; macroCount++; } - else if (i == 273) { macroControl[12] = true; macroCount++; } - else if (i == 274) { macroControl[13] = true; macroCount++; } - else if (i == 275) { macroControl[14] = true; macroCount++; } - else if (i == 276) { macroControl[15] = true; macroCount++; } - else if (i == 277) { macroControl[16] = true; macroCount++; } - else if (i == 278) { macroControl[17] = true; macroCount++; } - else if (i == 279) { macroControl[18] = true; macroCount++; } - else if (i == 280) { macroControl[19] = true; macroCount++; } - else if (i == 281) { macroControl[20] = true; macroCount++; } - else if (i == 282) { macroControl[21] = true; macroCount++; } - else if (i == 283) { macroControl[22] = true; macroCount++; } - else if (i == 284) { macroControl[23] = true;macroCount++; } - else if (i == 285) { macroControl[24] = true; macroCount++; } - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyPress((ushort)i); - else - InputMethods.performKeyPress((ushort)i); - keydown[i] = true; - } - else - { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) { macroControl[0] = false; if (macroCount > 0) macroCount--; } - else if (i == 262) { macroControl[1] = false; if (macroCount > 0) macroCount--; } - else if (i == 263) { macroControl[2] = false; if (macroCount > 0) macroCount--; } - else if (i == 264) { macroControl[3] = false; if (macroCount > 0) macroCount--; } - else if (i == 265) { macroControl[4] = false; if (macroCount > 0) macroCount--; } - else if (i == 266) { macroControl[5] = false; if (macroCount > 0) macroCount--; } - else if (i == 267) { macroControl[6] = false; if (macroCount > 0) macroCount--; } - else if (i == 268) { macroControl[7] = false; if (macroCount > 0) macroCount--; } - else if (i == 269) { macroControl[8] = false; if (macroCount > 0) macroCount--; } - else if (i == 270) { macroControl[9] = false; if (macroCount > 0) macroCount--; } - else if (i == 271) { macroControl[10] = false; if (macroCount > 0) macroCount--; } - else if (i == 272) { macroControl[11] = false; if (macroCount > 0) macroCount--; } - else if (i == 273) { macroControl[12] = false; if (macroCount > 0) macroCount--; } - else if (i == 274) { macroControl[13] = false; if (macroCount > 0) macroCount--; } - else if (i == 275) { macroControl[14] = false; if (macroCount > 0) macroCount--; } - else if (i == 276) { macroControl[15] = false; if (macroCount > 0) macroCount--; } - else if (i == 277) { macroControl[16] = false; if (macroCount > 0) macroCount--; } - else if (i == 278) { macroControl[17] = false; if (macroCount > 0) macroCount--; } - else if (i == 279) { macroControl[18] = false; if (macroCount > 0) macroCount--; } - else if (i == 280) { macroControl[19] = false; if (macroCount > 0) macroCount--; } - else if (i == 281) { macroControl[20] = false; if (macroCount > 0) macroCount--; } - else if (i == 282) { macroControl[21] = false; if (macroCount > 0) macroCount--; } - else if (i == 283) { macroControl[22] = false; if (macroCount > 0) macroCount--; } - else if (i == 284) { macroControl[23] = false; if (macroCount > 0) macroCount--; } - else if (i == 285) { macroControl[24] = false; if (macroCount > 0) macroCount--; } - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease((ushort)i); - else - InputMethods.performKeyRelease((ushort)i); - keydown[i] = false; - } + macroCodeValue = macroArr[i]; + if (PlayMacroCodeValue(device, macrocontrol, keyType, macroCodeValue, keydown)) + //await Task.Delay(macroCodeValue - 300); + Task.Delay(macroCodeValue - 300).Wait(); } + } + + // The macro is finished. If any of the keys is still in down state then release a key state (ie. simulate key up event) unless special action specified to keep the last state as it is left in a macro + if (action == null || !action.keepKeyState) + { for (int i = 0, arlength = keydown.Length; i < arlength; i++) { if (keydown[i]) - { - if (i == 256) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); //anything above 255 is not a keyvalue - else if (i == 257) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); - else if (i == 258) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); - else if (i == 259) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); - else if (i == 260) InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); - else if (i == 261) { macroControl[0] = false; if (macroCount > 0) macroCount--; } - else if (i == 262) { macroControl[1] = false; if (macroCount > 0) macroCount--; } - else if (i == 263) { macroControl[2] = false; if (macroCount > 0) macroCount--; } - else if (i == 264) { macroControl[3] = false; if (macroCount > 0) macroCount--; } - else if (i == 265) { macroControl[4] = false; if (macroCount > 0) macroCount--; } - else if (i == 266) { macroControl[5] = false; if (macroCount > 0) macroCount--; } - else if (i == 267) { macroControl[6] = false; if (macroCount > 0) macroCount--; } - else if (i == 268) { macroControl[7] = false; if (macroCount > 0) macroCount--; } - else if (i == 269) { macroControl[8] = false; if (macroCount > 0) macroCount--; } - else if (i == 270) { macroControl[9] = false; if (macroCount > 0) macroCount--; } - else if (i == 271) { macroControl[10] = false; if (macroCount > 0) macroCount--; } - else if (i == 272) { macroControl[11] = false; if (macroCount > 0) macroCount--; } - else if (i == 273) { macroControl[12] = false; if (macroCount > 0) macroCount--; } - else if (i == 274) { macroControl[13] = false; if (macroCount > 0) macroCount--; } - else if (i == 275) { macroControl[14] = false; if (macroCount > 0) macroCount--; } - else if (i == 276) { macroControl[15] = false; if (macroCount > 0) macroCount--; } - else if (i == 277) { macroControl[16] = false; if (macroCount > 0) macroCount--; } - else if (i == 278) { macroControl[17] = false; if (macroCount > 0) macroCount--; } - else if (i == 279) { macroControl[18] = false; if (macroCount > 0) macroCount--; } - else if (i == 280) { macroControl[19] = false; if (macroCount > 0) macroCount--; } - else if (i == 281) { macroControl[20] = false; if (macroCount > 0) macroCount--; } - else if (i == 282) { macroControl[21] = false; if (macroCount > 0) macroCount--; } - else if (i == 283) { macroControl[22] = false; if (macroCount > 0) macroCount--; } - else if (i == 284) { macroControl[23] = false; if (macroCount > 0) macroCount--; } - else if (i == 285) { macroControl[24] = false; if (macroCount > 0) macroCount--; } - else if (keyType.HasFlag(DS4KeyType.ScanCode)) - InputMethods.performSCKeyRelease((ushort)i); - else - InputMethods.performKeyRelease((ushort)i); - } - } - - DS4LightBar.forcedFlash[device] = 0; - DS4LightBar.forcelight[device] = false; - Program.rootHub.DS4Controllers[device].setRumble(0, 0); - if (keyType.HasFlag(DS4KeyType.HoldMacro)) - { - await Task.Delay(50); - if (control != DS4Controls.None) - macrodone[DS4ControltoInt(control)] = false; + PlayMacroCodeValue(device, macrocontrol, keyType, i, keydown); } } + + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + + // No need to zero out rumble after a macro because it may conflict with a game generated rumble events (ie. macro would stop a game generated rumble effect). + // If macro generated rumble effects then the macro proabbly sends a "rumble zero" event also. If it doesn't then a rumble watchdog timer will eventually stop the rumble. + //Program.rootHub.DS4Controllers[device].setRumble(0, 0); + + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + //await Task.Delay(50); + Task.Delay(50).Wait(); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } } + + // If a special action macro has "Repeat while held" option and actionDoneState object is defined then reset the action back to "not done" status in order to re-fire it if the trigger key is still held down + if (actionDoneState != null && keyType.HasFlag(DS4KeyType.RepeatMacro)) + actionDoneState.dev[device] = false; + } + + private static bool PlayMacroCodeValue(int device, bool[] macrocontrol, DS4KeyType keyType, int macroCodeValue, bool[] keydown) + { + bool doDelayOnCaller = false; + if (macroCodeValue >= 261 && macroCodeValue <= 285) + { + // Gamepad button up or down macro event. macroCodeValue index value is the button identifier (codeValue-261 = idx in 0..24 range) + if (!keydown[macroCodeValue]) + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = true; + macroCount++; + } + else + { + macroControl[macroCodeValue - 261] = keydown[macroCodeValue] = false; + if (macroCount > 0) macroCount--; + } + } + else if (macroCodeValue < 300) + { + // Keyboard key or mouse button macro event + if (!keydown[macroCodeValue]) + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTDOWN); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTDOWN); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEDOWN); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONDOWN, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyPress((ushort)macroCodeValue); + else InputMethods.performKeyPress((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = true; + } + else + { + switch (macroCodeValue) + { + //anything above 255 is not a keyvalue + case 256: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_LEFTUP); break; + case 257: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_RIGHTUP); break; + case 258: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_MIDDLEUP); break; + case 259: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 1); break; + case 260: InputMethods.MouseEvent(InputMethods.MOUSEEVENTF_XBUTTONUP, 2); break; + + default: + if (keyType.HasFlag(DS4KeyType.ScanCode)) InputMethods.performSCKeyRelease((ushort)macroCodeValue); + else InputMethods.performKeyRelease((ushort)macroCodeValue); + break; + } + keydown[macroCodeValue] = false; + } + } + else if (macroCodeValue >= 1000000000) + { + // Lightbar color event + if (macroCodeValue > 1000000000) + { + string lb = macroCodeValue.ToString().Substring(1); + byte r = (byte)(int.Parse(lb[0].ToString()) * 100 + int.Parse(lb[1].ToString()) * 10 + int.Parse(lb[2].ToString())); + byte g = (byte)(int.Parse(lb[3].ToString()) * 100 + int.Parse(lb[4].ToString()) * 10 + int.Parse(lb[5].ToString())); + byte b = (byte)(int.Parse(lb[6].ToString()) * 100 + int.Parse(lb[7].ToString()) * 10 + int.Parse(lb[8].ToString())); + DS4LightBar.forcelight[device] = true; + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcedColor[device] = new DS4Color(r, g, b); + } + else + { + DS4LightBar.forcedFlash[device] = 0; + DS4LightBar.forcelight[device] = false; + } + } + else if (macroCodeValue >= 1000000) + { + // Rumble event + DS4Device d = Program.rootHub.DS4Controllers[device]; + string r = macroCodeValue.ToString().Substring(1); + byte heavy = (byte)(int.Parse(r[0].ToString()) * 100 + int.Parse(r[1].ToString()) * 10 + int.Parse(r[2].ToString())); + byte light = (byte)(int.Parse(r[3].ToString()) * 100 + int.Parse(r[4].ToString()) * 10 + int.Parse(r[5].ToString())); + d.setRumble(light, heavy); + } + else + { + // Delay specification. Indicate to caller that it should do a delay of macroCodeValue-300 msecs + doDelayOnCaller = true; + } + + return doDelayOnCaller; } private static void EndMacro(int device, bool[] macrocontrol, string macro, DS4Controls control) @@ -2402,6 +2648,24 @@ namespace DS4Windows macrodone[DS4ControltoInt(control)] = false; } + private static void EndMacro(int device, bool[] macrocontrol, List macro, DS4Controls control) + { + if(macro.Count >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + + private static void EndMacro(int device, bool[] macrocontrol, int[] macro, DS4Controls control) + { + if (macro.Length >= 4 && ((macro[0] == 164 && macro[1] == 9 && macro[2] == 9 && macro[3] == 164) || (macro[0] == 18 && macro[1] == 9 && macro[2] == 9 && macro[3] == 18)) && !altTabDone) + AltTabSwappingRelease(); + + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } + private static void AltTabSwapping(int wait, int device) { if (altTabDone) diff --git a/DS4Windows/DS4Control/ScpUtil.cs b/DS4Windows/DS4Control/ScpUtil.cs index aaec4f4..4ca6698 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -4410,6 +4410,8 @@ namespace DS4Windows public DateTime TimeofEnd; public bool automaticUntrigger = false; public string prevProfileName; // Name of the previous profile where automaticUntrigger would jump back to (could be regular or temporary profile. Empty name is the same as regular profile) + public bool synchronized = false; // If the same trigger has both "key down" and "key released" macros then run those synchronized if this attribute is TRUE (ie. key down macro fully completed before running the key release macro) + public bool keepKeyState = false; // By default special action type "Macro" resets all keys used in the macro back to default "key up" state after completing the macro even when the macro itself doesn't do it explicitly. If this is TRUE then key states are NOT reset automatically (macro is expected to do it or to leave a key to down state on purpose) public SpecialAction(string name, string controls, string type, string details, double delay = 0, string extras = "") { @@ -4466,6 +4468,14 @@ namespace DS4Windows } if (extras.Contains("Scan Code")) keyType |= DS4KeyType.ScanCode; + if (extras.Contains("Release")) + pressRelease = true; + if (extras.Contains("Sync")) + synchronized = true; + if (extras.Contains("KeepKeyState")) + keepKeyState = true; + if (extras.Contains("Repeat")) + keyType |= DS4KeyType.RepeatMacro; } else if (type == "DisconnectBT") {