CGEventPost - возможная ошибка при симуляции событий клавиатуры?
У меня есть очень простой кусок кода, который предназначен для имитации событий клавиатуры. В приведенном ниже простом примере следует ввести "Cz" - клавиша Shift нажимается, клавиша "C" нажимается, "C" поднимается и "Shift" поднимается Затем клавиша z идет вниз и вверх.
Кажется, что иногда порядок запутывается все же. Когда я создаю таймер для вызова этой подпрограммы каждую секунду, вывод должен быть CzCzCzCz.... Но вот что я получаю:
CZcZCZCzczCzczCzczCZCZCzCz
Я запустлю это снова:
CzCzCzCzCZCzCZCzCZCzCZCzCZCzCzCz
Разные. И в равной степени неправильно.
Код:
e1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, true);
CGEventPost(kCGSessionEventTap, e1);
CFRelease(e1);
e2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, true);
CGEventPost(kCGSessionEventTap, e2);
CFRelease(e2);
e3 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, false);
CGEventPost(kCGSessionEventTap, e3);
CFRelease(e3);
e4 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e4);
CFRelease(e4);
e7 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);
CGEventPost(kCGSessionEventTap, e7);
CFRelease(e7);
e8 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventPost(kCGSessionEventTap, e8);
CFRelease(e8);
Есть ли что-то, чего мне не хватает в том, как реализовать keydown и keyup для клавиши Shift? Я думаю, что это может быть ошибкой - куда мне сообщить об этом?
6 ответов
Я нашел надежный способ публикации измененных событий клавиатуры - он не следует примеру в документации Apple (который не работает), но, кажется, имеет смысл, и что самое важное, РАБОТАЕТ.
Вместо того, чтобы отправлять сообщения "Shift Key Down" и "Shift Key Up" (как указано в документации), вам нужно установить флаг-модификатор на нажатие клавиши. Вот как вывести заглавные буквы Z.
CGEventRef event1, event2;
event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);//'z' keydown event
CGEventSetFlags(event1, kCGEventFlagMaskShift);//set shift key down for above event
CGEventPost(kCGSessionEventTap, event1);//post event
Затем я отпускаю клавишу 'z' для полноты (также устанавливая флаг Shift, хотя и не уверен, что это правильно).
event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventSetFlags(event2, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, event2);
Наконец (и причудливо) вам действительно нужно отправить событие 'key up' для клавиши Shift:
e5 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e5);
Не забудьте опубликовать свои события, как только вы закончите с ними.
Надеюсь, это кому-нибудь пригодится - мне потребовалось много времени, чтобы заставить это работать.
Самый чистый способ для этого - побитовое ИЛИ, помечая флаги текущего модификатора флагом желаемого модификатора (ов), например:
CGEventFlags flags = kCGEventFlagMaskShift;
CGEventRef ev;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
//press down
ev = CGEventCreateKeyboardEvent (source, keyCode, true);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
//press up
ev = CGEventCreateKeyboardEvent (source, keyCode, false);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
CFRelease(source);
У меня недавно была похожая проблема. Вместо того, чтобы передавать NULL в качестве первого аргумента CGEventCreateKeyboardEvent, создайте источник события, подобный этому:
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
и использовать его при создании и публикации событий. CF Отпустите его, когда закончите.
Как и в другом комментарии, относящемся к другим решениям, приведенным здесь, после использования заглавной буквы с маской Shift, последовательный вызов приведет к тому, что любой другой предполагаемый неизмененный символ будет превращен в сдвинутые символы. Я подумал, что вызов CGEventCreateKeyboardEvent каким-то образом сохраняет предыдущие маски, поэтому я намеренно очистил маску модификатора:
CGEventRef event1, event2;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
CGEventFlags flags;
event1 = CGEventCreateKeyboardEvent (source, keyCode, true);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event1);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event1);
CGEventSetFlags(event1, flags);
CGEventPost(kCGHIDEventTap,event1);
event2 = CGEventCreateKeyboardEvent (source, keyCode, false);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event2);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event2);
CGEventSetFlags(event2, flags);
CGEventPost(kCGHIDEventTap,event2);
CFRelease(event1);
CFRelease(event2);
CFRelease(source);
Надежный обходной путь:
+(void)runScript:(NSString*)scriptText
{
NSDictionary *error = nil;
NSAppleEventDescriptor *appleEventDescriptor;
NSAppleScript *appleScript;
appleScript = [[NSAppleScript alloc] initWithSource:scriptText];
appleEventDescriptor = [appleScript executeAndReturnError:&error];
}
[self runScript:@"tell application \"System Events\" to key code "c" using shift down"];
[self runScript:@"tell application \"System Events\" to key code "z"];