diff --git a/DS4Windows/DS4Control/Mapping.cs b/DS4Windows/DS4Control/Mapping.cs index f2cc624..5cdbc85 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 }; @@ -1562,11 +1563,11 @@ 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.Empty, null, (int[])action, dcs.control, keyType); } else { - EndMacro(device, macroControl, string.Join("/", (int[])action), dcs.control); + EndMacro(device, macroControl, (int[])action, dcs.control); } // erase default mappings for things that are remapped @@ -2001,6 +2002,10 @@ namespace DS4Windows break; } } + + // If special action macro is set to run on key release then activate the trigger status only when the trigger key is released + if (action.typeID == SpecialAction.ActionTypeId.Macro && action.pressRelease && action.firstTouch) + triggeractivated = !triggeractivated; } bool utriggeractivated = true; @@ -2095,22 +2100,49 @@ 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); + 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); + } + + PlayMacro(device, macroControl, String.Empty, action.macro, null, DS4Controls.None, keyType, action, null); + } + } + else + action.firstTouch = true; } - else - EndMacro(device, macroControl, String.Join("/", action.macro), DS4Controls.None); } else if (action.typeID == SpecialAction.ActionTypeId.Key) { @@ -2366,7 +2398,7 @@ namespace DS4Windows if ((DateTime.UtcNow - action.TimeofEnd) > TimeSpan.FromMilliseconds(150)) { if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); tappedOnce = false; action.tappedOnce = false; @@ -2392,7 +2424,7 @@ namespace DS4Windows } if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); firstTouch = false; action.firstTouch = false; @@ -2416,7 +2448,7 @@ namespace DS4Windows } if (macro != "") - PlayMacro(device, macroControl, macro, DS4Controls.None, DS4KeyType.None); + PlayMacro(device, macroControl, macro, null, null, DS4Controls.None, DS4KeyType.None); secondtouchbegin = false; action.secondtouchbegin = false; @@ -2516,203 +2548,209 @@ namespace DS4Windows } } - private static async void PlayMacro(int device, bool[] macrocontrol, string macro, DS4Controls control, DS4KeyType keyType) + // Play macro as a background task. Optionally the new macro play waits for completion of a previous macro execution (synchronized macro special action). + // Macro steps are defined either as macrostr string value, macroLst list object or as macroArr integer array. Only one of these should have a valid macro definition when this method is called. + // If the macro definition is a macroStr string value then it will be converted as integer array on the fl. If steps are already defined as list or array of integers then there is no need to do type cast conversion. + 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 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 method should do a asynchronized delay (the usual Thread.Sleep doesnt work here because it would block the main gamepad reading thread). + 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)) + 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)) + 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; + + // Commented out rumble reset. 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 generates rumble effects then the macro can stop the rumble as a last step or wait for rumble watchdog timer to do it after few seconds. + //Program.rootHub.DS4Controllers[device].setRumble(0, 0); + + if (keyType.HasFlag(DS4KeyType.HoldMacro)) + { + Task.Delay(50).Wait(); + if (control != DS4Controls.None) + macrodone[DS4ControltoInt(control)] = false; + } } + + // If a special action type of 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) @@ -2724,6 +2762,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 af798b2..cdd8faf 100644 --- a/DS4Windows/DS4Control/ScpUtil.cs +++ b/DS4Windows/DS4Control/ScpUtil.cs @@ -4412,6 +4412,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 = "") { @@ -4468,6 +4470,14 @@ namespace DS4Windows } if (extras.Contains("Scan Code")) keyType |= DS4KeyType.ScanCode; + if (extras.Contains("RunOnRelease")) + 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") { diff --git a/DS4Windows/DS4Forms/SpecActions.Designer.cs b/DS4Windows/DS4Forms/SpecActions.Designer.cs index 5e7c6cb..17d8b56 100644 --- a/DS4Windows/DS4Forms/SpecActions.Designer.cs +++ b/DS4Windows/DS4Forms/SpecActions.Designer.cs @@ -55,9 +55,14 @@ this.lbHoldForProg = new System.Windows.Forms.Label(); this.lbSecsProg = new System.Windows.Forms.Label(); this.pnlMacro = new System.Windows.Forms.Panel(); + this.cBMacroRepeat = new System.Windows.Forms.CheckBox(); + this.cBMacroKeepKeyState = new System.Windows.Forms.CheckBox(); + this.cBMacroSyncRun = new System.Windows.Forms.CheckBox(); + this.cBMacroRunOnRelease = new System.Windows.Forms.CheckBox(); this.cBMacroScanCode = new System.Windows.Forms.CheckBox(); this.lbMacroRecorded = new System.Windows.Forms.Label(); this.pnlProfile = new System.Windows.Forms.Panel(); + this.cbProfileAutoUntrigger = new System.Windows.Forms.CheckBox(); this.lbUnloadTipProfile = new System.Windows.Forms.Label(); this.pnlDisconnectBT = new System.Windows.Forms.Panel(); this.nUDDCBT = new System.Windows.Forms.NumericUpDown(); @@ -86,7 +91,7 @@ this.lbDTapDVR = new System.Windows.Forms.Label(); this.lbHoldDVR = new System.Windows.Forms.Label(); this.lbTapDVR = new System.Windows.Forms.Label(); - this.cbProfileAutoUntrigger = new System.Windows.Forms.CheckBox(); + this.tp = new System.Windows.Forms.ToolTip(this.components); ((System.ComponentModel.ISupportInitialize)(this.pBProgram)).BeginInit(); this.pnlProgram.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.nUDProg)).BeginInit(); @@ -365,12 +370,40 @@ // // pnlMacro // + this.pnlMacro.Controls.Add(this.cBMacroRepeat); + this.pnlMacro.Controls.Add(this.cBMacroKeepKeyState); + this.pnlMacro.Controls.Add(this.cBMacroSyncRun); + this.pnlMacro.Controls.Add(this.cBMacroRunOnRelease); this.pnlMacro.Controls.Add(this.cBMacroScanCode); this.pnlMacro.Controls.Add(this.btnRecordMacro); this.pnlMacro.Controls.Add(this.lbMacroRecorded); resources.ApplyResources(this.pnlMacro, "pnlMacro"); this.pnlMacro.Name = "pnlMacro"; // + // cBMacroRepeat + // + resources.ApplyResources(this.cBMacroRepeat, "cBMacroRepeat"); + this.cBMacroRepeat.Name = "cBMacroRepeat"; + this.cBMacroRepeat.UseVisualStyleBackColor = true; + // + // cBMacroKeepKeyState + // + resources.ApplyResources(this.cBMacroKeepKeyState, "cBMacroKeepKeyState"); + this.cBMacroKeepKeyState.Name = "cBMacroKeepKeyState"; + this.cBMacroKeepKeyState.UseVisualStyleBackColor = true; + // + // cBMacroSyncRun + // + resources.ApplyResources(this.cBMacroSyncRun, "cBMacroSyncRun"); + this.cBMacroSyncRun.Name = "cBMacroSyncRun"; + this.cBMacroSyncRun.UseVisualStyleBackColor = true; + // + // cBMacroRunOnRelease + // + resources.ApplyResources(this.cBMacroRunOnRelease, "cBMacroRunOnRelease"); + this.cBMacroRunOnRelease.Name = "cBMacroRunOnRelease"; + this.cBMacroRunOnRelease.UseVisualStyleBackColor = true; + // // cBMacroScanCode // resources.ApplyResources(this.cBMacroScanCode, "cBMacroScanCode"); @@ -391,6 +424,12 @@ resources.ApplyResources(this.pnlProfile, "pnlProfile"); this.pnlProfile.Name = "pnlProfile"; // + // cbProfileAutoUntrigger + // + resources.ApplyResources(this.cbProfileAutoUntrigger, "cbProfileAutoUntrigger"); + this.cbProfileAutoUntrigger.Name = "cbProfileAutoUntrigger"; + this.cbProfileAutoUntrigger.UseVisualStyleBackColor = true; + // // lbUnloadTipProfile // resources.ApplyResources(this.lbUnloadTipProfile, "lbUnloadTipProfile"); @@ -598,17 +637,12 @@ resources.ApplyResources(this.lbTapDVR, "lbTapDVR"); this.lbTapDVR.Name = "lbTapDVR"; // - // cbProfileAutoUntrigger - // - resources.ApplyResources(this.cbProfileAutoUntrigger, "cbProfileAutoUntrigger"); - this.cbProfileAutoUntrigger.Name = "cbProfileAutoUntrigger"; - this.cbProfileAutoUntrigger.UseVisualStyleBackColor = true; - // // SpecActions // resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.SystemColors.Control; + this.Controls.Add(this.pnlMacro); this.Controls.Add(this.pnlGameDVR); this.Controls.Add(this.tBName); this.Controls.Add(this.cBActions); @@ -619,7 +653,6 @@ this.Controls.Add(this.lVUnloadTrigger); this.Controls.Add(this.btnBorder); this.Controls.Add(this.pnlKeys); - this.Controls.Add(this.pnlMacro); this.Controls.Add(this.pnlProfile); this.Controls.Add(this.pnlProgram); this.Controls.Add(this.pnlBatteryCheck); @@ -633,6 +666,7 @@ this.pnlMacro.ResumeLayout(false); this.pnlMacro.PerformLayout(); this.pnlProfile.ResumeLayout(false); + this.pnlProfile.PerformLayout(); this.pnlDisconnectBT.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.nUDDCBT)).EndInit(); this.pnlKeys.ResumeLayout(false); @@ -707,5 +741,10 @@ public System.Windows.Forms.Button btnHoldT; public System.Windows.Forms.Button btnSTapT; private System.Windows.Forms.CheckBox cbProfileAutoUntrigger; + private System.Windows.Forms.CheckBox cBMacroKeepKeyState; + private System.Windows.Forms.CheckBox cBMacroSyncRun; + private System.Windows.Forms.CheckBox cBMacroRunOnRelease; + private System.Windows.Forms.CheckBox cBMacroRepeat; + private System.Windows.Forms.ToolTip tp; } } \ No newline at end of file diff --git a/DS4Windows/DS4Forms/SpecActions.cs b/DS4Windows/DS4Forms/SpecActions.cs index 7bcd4b2..ac99cad 100644 --- a/DS4Windows/DS4Forms/SpecActions.cs +++ b/DS4Windows/DS4Forms/SpecActions.cs @@ -61,6 +61,13 @@ namespace DS4Windows.Forms advColorDialog = new AdvancedColorDialog(); advColorDialog.FullOpen = true; advColorDialog.OnUpdateColor += new AdvancedColorDialog.ColorUpdateHandler(this.advColorDialog_OnUpdateColor); + + tp.SetToolTip(cBMacroScanCode, Properties.Resources.MacroScanCodeTip); + tp.SetToolTip(cBMacroRunOnRelease, Properties.Resources.MacroRunOnReleaseTip); + tp.SetToolTip(cBMacroRepeat, Properties.Resources.MacroRepeatTip); + tp.SetToolTip(cBMacroSyncRun, Properties.Resources.MacroSynchronizedRunTip); + tp.SetToolTip(cBMacroKeepKeyState, Properties.Resources.MacroKeepKeyStateTip); + } void LoadAction() @@ -81,6 +88,10 @@ namespace DS4Windows.Forms macrostag = act.macro; lbMacroRecorded.Text = "Macro Recored"; cBMacroScanCode.Checked = act.keyType.HasFlag(DS4KeyType.ScanCode); + cBMacroRunOnRelease.Checked = act.pressRelease; + cBMacroSyncRun.Checked = act.synchronized; + cBMacroKeepKeyState.Checked = act.keepKeyState; + cBMacroRepeat.Checked = act.keyType.HasFlag(DS4KeyType.RepeatMacro); break; case "Program": cBActions.SelectedIndex = 2; @@ -251,7 +262,13 @@ namespace DS4Windows.Forms actRe = true; if (!string.IsNullOrEmpty(oldprofilename) && oldprofilename != tBName.Text) Global.RemoveAction(oldprofilename); - Global.SaveAction(tBName.Text, String.Join("/", controls), cBActions.SelectedIndex, String.Join("/", macrostag), edit, (cBMacroScanCode.Checked ? "Scan Code" : "")); + Global.SaveAction(tBName.Text, String.Join("/", controls), cBActions.SelectedIndex, String.Join("/", macrostag), edit, String.Join("/", new string[] { + (cBMacroScanCode.Checked ? "Scan Code" : null), + (cBMacroRunOnRelease.Checked ? "RunOnRelease" : null), + (cBMacroSyncRun.Checked ? "Sync" : null), + (cBMacroKeepKeyState.Checked ? "KeepKeyState" : null), + (cBMacroRepeat.Checked ? "Repeat" : null) }.Where(s => !String.IsNullOrEmpty(s)) + )); } break; case 2: diff --git a/DS4Windows/DS4Forms/SpecActions.fi.resx b/DS4Windows/DS4Forms/SpecActions.fi.resx index b406394..1694072 100644 --- a/DS4Windows/DS4Forms/SpecActions.fi.resx +++ b/DS4Windows/DS4Forms/SpecActions.fi.resx @@ -202,6 +202,21 @@ Kytke pois painamalla - Poiskytkentä liipaisimen vapautuksella + Poiskytkentä painikkeen vapautuksella + + + Sixaxis rattiohjainemuloinnin kalibrointi + + + Säilytä napin tila + + + Toista painettaessa + + + Suorita painikkeen vapautuksella + + + Synkronoitu suoritus \ No newline at end of file diff --git a/DS4Windows/DS4Forms/SpecActions.resx b/DS4Windows/DS4Forms/SpecActions.resx index 66c2e08..a3e233a 100644 --- a/DS4Windows/DS4Forms/SpecActions.resx +++ b/DS4Windows/DS4Forms/SpecActions.resx @@ -814,7 +814,7 @@ $this - 6 + 7 NoControl @@ -841,7 +841,7 @@ pnlMacro - 1 + 5 NoControl @@ -889,7 +889,7 @@ pnlProfile - 1 + 2 Bottom, Left @@ -922,7 +922,7 @@ $this - 4 + 5 Bottom, Left @@ -952,7 +952,7 @@ $this - 3 + 4 NoControl @@ -982,7 +982,7 @@ $this - 5 + 6 Top, Left, Right @@ -1006,7 +1006,7 @@ $this - 1 + 2 -Select an Action- @@ -1054,7 +1054,7 @@ $this - 2 + 3 17, 17 @@ -1147,7 +1147,7 @@ $this - 8 + 9 NoControl @@ -1174,7 +1174,7 @@ pnlProfile - 2 + 3 Top, Bottom, Left @@ -1870,7 +1870,7 @@ $this - 7 + 8 4, 91 @@ -2031,6 +2031,114 @@ 12 + + True + + + 4, 120 + + + 111, 17 + + + 21 + + + Repeat while held + + + cBMacroRepeat + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pnlMacro + + + 0 + + + True + + + 4, 97 + + + 97, 17 + + + 20 + + + Keep key state + + + cBMacroKeepKeyState + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pnlMacro + + + 1 + + + True + + + 4, 74 + + + 108, 17 + + + 19 + + + Synchronized run + + + cBMacroSyncRun + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pnlMacro + + + 2 + + + True + + + 4, 51 + + + 130, 17 + + + 18 + + + Run on trigger release + + + cBMacroRunOnRelease + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pnlMacro + + + 3 + True @@ -2038,7 +2146,7 @@ NoControl - 4, 30 + 4, 29 79, 17 @@ -2059,13 +2167,13 @@ pnlMacro - 0 + 4 NoControl - 0, 50 + 0, 141 153, 23 @@ -2086,13 +2194,13 @@ pnlMacro - 2 + 6 206, 58 - 163, 94 + 163, 165 261 @@ -2110,66 +2218,6 @@ $this - 10 - - - NoControl - - - 0, 95 - - - 100, 23 - - - 259 - - - lbUnloadTipProfile - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - pnlProfile - - - 0 - - - 206, 58 - - - 161, 121 - - - 262 - - - False - - - pnlProfile - - - System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 11 - - - cbProfileAutoUntrigger - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - pnlProfile - - 0 @@ -2199,6 +2247,54 @@ 0 + + NoControl + + + 0, 95 + + + 100, 23 + + + 259 + + + lbUnloadTipProfile + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + pnlProfile + + + 1 + + + 206, 58 + + + 161, 121 + + + 262 + + + False + + + pnlProfile + + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 11 + 56, 3 @@ -2446,7 +2542,7 @@ $this - 9 + 10 NoControl @@ -2941,8 +3037,11 @@ $this - 0 + 1 + + 267, 17 + True @@ -2979,6 +3078,12 @@ System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + tp + + + System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + SpecActions diff --git a/DS4Windows/Properties/Resources.Designer.cs b/DS4Windows/Properties/Resources.Designer.cs index 667fa6d..dabc9b6 100644 --- a/DS4Windows/Properties/Resources.Designer.cs +++ b/DS4Windows/Properties/Resources.Designer.cs @@ -1280,6 +1280,15 @@ namespace DS4Windows.Properties { } } + /// + /// Looks up a localized string similar to Keep the last key state when macro execution is completed (ie. if a key is left in down state then it is not automatically reset back to default state). + /// + public static string MacroKeepKeyStateTip { + get { + return ResourceManager.GetString("MacroKeepKeyStateTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Macro Recorded. /// @@ -1289,6 +1298,42 @@ namespace DS4Windows.Properties { } } + /// + /// Looks up a localized string similar to Repeat a macro while the trigger key is held down. + /// + public static string MacroRepeatTip { + get { + return ResourceManager.GetString("MacroRepeatTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Run a macro on the trigger key release. + /// + public static string MacroRunOnReleaseTip { + get { + return ResourceManager.GetString("MacroRunOnReleaseTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use a scan code value of keyboard keys in a macro. + /// + public static string MacroScanCodeTip { + get { + return ResourceManager.GetString("MacroScanCodeTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Run multiple macros in synchronized order if the same trigger has several macros (key down macro completed before key release macro is run). + /// + public static string MacroSynchronizedRunTip { + get { + return ResourceManager.GetString("MacroSynchronizedRunTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Make a New Profile. /// diff --git a/DS4Windows/Properties/Resources.fi.resx b/DS4Windows/Properties/Resources.fi.resx index b17be00..778fb9d 100644 --- a/DS4Windows/Properties/Resources.fi.resx +++ b/DS4Windows/Properties/Resources.fi.resx @@ -615,4 +615,19 @@ Salli kosketuslevyhiiriohjaimen päälle-pois kytkentä PS + kosketuslevynapautuksella. + + Käytä scan code näppäinkoodeja makrossa + + + Suorita makro painikkeen vapautuksella + + + Suorita useampi makro synkronoidussa järjestyksessä (esim painikkeen aktivoinnin makro suoritetaan kokonaisuudessa ennen painikkeen vapautuksen makroa) + + + Toista makron suoritusta painikkeen painamisen ajan + + + Säilytä napin tilatieto kun makron suoritus on päättynyt (jos makro jättää napin Painettu-tilaan, niin makron suoritus ei automaattisesti palauta napin tilatietoa takaisin oletustilaan) + \ No newline at end of file diff --git a/DS4Windows/Properties/Resources.resx b/DS4Windows/Properties/Resources.resx index aef6749..930ad32 100644 --- a/DS4Windows/Properties/Resources.resx +++ b/DS4Windows/Properties/Resources.resx @@ -832,4 +832,19 @@ If enabled then Log tab page shows detailed messages of auto-profile events. + + Use a scan code value of keyboard keys in a macro + + + Run a macro on the trigger key release + + + Run multiple macros in synchronized order if the same trigger has several macros (key down macro completed before key release macro is run) + + + Repeat a macro while the trigger key is held down + + + Keep the last key state when macro execution is completed (ie. if a key is left in down state then it is not automatically reset back to default state) + \ No newline at end of file