CGEventPost - возможная ошибка при имитации событий клавиатуры?

У меня очень простой кусок кода, который предназначен для имитации событий клавиатуры. В простом примере ниже следует ввести "Cz" - клавиша смены идет вниз, клавиша c спускается, c появляется и сдвигается вверх. Затем клавиша z переходит вниз и вверх.

Кажется, что иногда порядок путается. Когда я создаю таймер для вызова этой процедуры каждую секунду, вывод должен быть CzCzCzCz.... Но вот что я получаю:

CZcZCZCzczCzczCzczCZCZCzCz

Я запустил его еще раз:

CzCzCzCzCZCzCZCzCZCzCZCzCZCzCzCz

Different. И одинаково неправильно.

Код:

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? Я думаю, что это может быть ошибка - где я могу сообщить об этом?

Ответы

Ответ 1

Я нашел надежный способ опубликовать измененные события клавиатуры - он не следует примеру в документации Apple (что не работает), но, похоже, имеет смысл, а главное, WORKS.

Вместо того, чтобы отправлять сообщения "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);

Не забудьте освободить свои события, как только вы закончите с ними.

Я надеюсь, что это полезно кому-то - мне потребовалось много времени, чтобы заставить это работать.

Ответ 2

Самый чистый способ для этого - побитовое ИЛИ - текущие флаги модификатора с флагом желаемого модификатора (-ов), например:

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);

Ответ 4

Недавно у меня была аналогичная проблема. Вместо передачи NULL в качестве первого аргумента CGEventCreateKeyboardEvent создайте источник события, например:

CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);

и использовать его во время создания и публикации событий. CFRelease это, когда вы закончите с ним.

Ответ 5

Как и другой комментарий, относящийся к другим решениям здесь, после использования прописных букв с помощью маски 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);

Ответ 6

надежное обходное решение:

+(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"];