/// <summary> /// Presses Ctrl+key. Does not release. /// If enter is true, Release will press Enter. /// </summary> public void Press(KKey key, OptKey opt, AWnd wFocus, bool enter = false) { _scan = VkToSc(_vk = key, Api.GetKeyboardLayout(wFocus.ThreadId)); _enter = enter; _opt = opt; SendCtrl(true); Internal_.Sleep(opt.KeySpeedClipboard); //need 1 ms for IE address bar, 100 ms for BlueStacks SendKeyEventRaw(_vk, _scan, 0); }
//Caller should set k.scan; this func doesn't. unsafe static void _SendKey2(_KEvent k, _KEvent kNext, bool isLast, OptKey opt) { var ki = new Api.INPUTK(k.vk, k.scan, (uint)k.SIFlags); int count = 1, sleep = opt.KeySpeed; if (isLast) { if (!k.IsPair) { sleep = Internal_.LimitSleepTime(sleep) - opt.SleepFinally; } } else { if (kNext.IsRepeat) { count = kNext.repeat; } else if (!k.IsPair) { //If this is pair, sleep between down and up, and don't sleep after up. //Else if repeat, sleep always. //Else in most cases don't need to sleep. In some cases need, but can limit the time. // For example, in Ctrl+C normally would not need to sleep after Ctrl down and Ctrl up. // However some apps/controls then may not work. Maybe they process mod and nonmod keys somehow async. // For example, Ctrl+C in IE address bar often does not work if there is no sleep after Ctrl down. Always works if 1 ms. sleep = Internal_.LimitSleepTime(sleep); if (kNext.IsKey) { bool thisMod = KeyTypes_.IsMod(k.vk), nextMod = KeyTypes_.IsMod(kNext.vk); if (!k.IsUp) { if (kNext.IsUp) { sleep = opt.KeySpeed; } else if (thisMod == nextMod) { sleep = 0; } } else { if (!thisMod || nextMod) { sleep = 0; } } } else if (kNext.IsSleep) { sleep = sleep - kNext.sleep; } } } if (sleep < 0) { sleep = 0; } //var s = (k.vk).ToString(); //if(k.IsPair) AOutput.Write($"{s}<{sleep}>"); //else { var ud = k.IsUp ? '-' : '+'; if(sleep > 0) AOutput.Write($"{s}{ud} {sleep}"); else AOutput.Write($"{s}{ud}"); } for (int r = 0; r < count; r++) { //APerf.First(); Api.SendInput(&ki); //APerf.Next(); if (sleep > 0) { Internal_.Sleep(sleep); } if (k.IsPair) { ki.dwFlags |= Api.KEYEVENTF_KEYUP; Api.SendInput(&ki); ki.dwFlags &= ~Api.KEYEVENTF_KEYUP; } //APerf.NW(); //speed: min 400 mcs for each event. Often > 1000. Does not depend on whether all events sent by single SendInput call. } }
/// <summary> /// Sends keys, text and executes other events added with the <b>AddX</b> functions. /// </summary> /// <param name="canSendAgain">Don't clear the internal collection. If true, this function then can be called again (eg in loop) to send/execute the same keys etc. If false (default), clears the added keys etc; then you can call <b>AddX</b> functions and <b>Send</b> again.</param> /// <exception cref="ArgumentException"><i>canSendAgain</i> is true and keys end with + or (.</exception> public void Send(bool canSendAgain = false) { _ThrowIfSending(); if (_a.Count == 0) { return; } if (canSendAgain) { if (_pstate.paren || _pstate.plus) { throw new ArgumentException("canSendAgain cannot be true if keys ends with + or ("); } } //AOutput.Write("-- _parsing.mod --"); //AOutput.Write(_parsing.mod); _AddModUp(); //add mod-up events if need, eg Ctrl-up after "Ctrl+A" //AOutput.Write("-- _a --"); //AOutput.Write(_a); //APerf.First(); int sleepFinally = 0; bool restoreCapsLock = false; var bi = new AInputBlocker() { ResendBlockedKeys = true }; try { _sending = true; //AOutput.Write("{"); if (!Options.NoBlockInput) { bi.Start(BIEvents.Keys); } restoreCapsLock = Internal_.ReleaseModAndCapsLock(Options); //APerf.Next(); for (int i = 0; i < _a.Count; i++) { var k = _a[i]; switch (k.Type) { case _KType.Sleep: if (i == _a.Count - 1) { sleepFinally = k.sleep; } else { Internal_.Sleep(k.sleep); } break; case _KType.Repeat: Debug.Assert(i > 0 && _a[i - 1].IsKey); break; case _KType.Callback: (_GetData(k.data) as Action)(); break; case _KType.Text: _SendText(k); break; default: _SendKey(k, i); break; } } //APerf.Next(); sleepFinally += _GetOptionsAndWndFocused(out _, false).SleepFinally; } finally { if (restoreCapsLock) { Internal_.SendKey(KKey.CapsLock); } _sending = false; bi.Dispose(); //APerf.NW(); //AOutput.Write("}"); //if canSendAgain, can be used like: AddX(); for(...) Send(); //else can be used like: AddX(); Send(); AddX(); Send(); if (!canSendAgain) { _a.Clear(); _data = null; _sstate.Clear(); //and don't clear _pstate } } if (sleepFinally > 0) { Internal_.Sleep(sleepFinally); } //_SyncWait(); //CONSIDER: instead of SleepFinally use TimeSyncFinally, default 100 ms. Eg send a sync key and wait max TimeSyncFinally ms. // Don't sync after each (or some) sent key. Usually it does not make sense. The final sync/sleep is useful if next statement is not an input function. //Sync problems: // Tried many ways, nothing is good enough. The test code now is in the "Unused" project. // The best would be non-LL keyboard hook that sets event when receives our sent special key-up. Especially when combined with 'get thread CPU usage' while waiting for the event. However these hooks don't work eg in Store apps. //Better add a Sync function (AKeys.Sync) or/and special key name, let users do it explicitly where need. }