/// <summary> /// Sends key. /// Not used for keys whose scancode can depend on keyboard layout. To get scancode, uses keyboard layout of current thread. /// </summary> /// <param name="k"></param> /// <param name="downUp">1 down, 2 up, 0 down-up.</param> internal static void SendKey(KKey k, int downUp = 0) { uint f = 0; if (KeyTypes_.IsExtended(k)) { f |= Api.KEYEVENTF_EXTENDEDKEY; } ushort scan = VkToSc(k); if (0 == (downUp & 2)) { SendKeyEventRaw(k, scan, f); } if (0 == (downUp & 1)) { SendKeyEventRaw(k, scan, f | Api.KEYEVENTF_KEYUP); } }
/// <summary> /// Adds single key, specified as <see cref="KKey"/>, to the internal collection. It will be sent by <see cref="Send"/>. /// Returns self. /// </summary> /// <param name="key">Virtual-key code, as <see cref="KKey"/> or int like <c>(KKey)200</c>. Valid values are 1-255.</param> /// <param name="down">true - key down; false - key up; null (default) - key down-up.</param> /// <exception cref="ArgumentException">Invalid <i>key</i> (0).</exception> public AKeys AddKey(KKey key, bool?down = null) { _ThrowIfSending(); if (key == 0) { throw new ArgumentException("Invalid value.", nameof(key)); } bool isPair; _KFlags f = 0; if (!(isPair = (down == null)) && !down.GetValueOrDefault()) { f |= _KFlags.Up; } if (KeyTypes_.IsExtended(key)) { f |= _KFlags.Extended; } return(_AddKey(new _KEvent(isPair, key, f))); }
//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. } }