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"];
Другие вопросы по тегам