×Закрыть

[Objective-C] keyDown NSEvent хелп

в общем задумал, сделать простенькую программку под OSX, push-to-talk для скайпа.

стантарно в Skype 5 есть такая функция, но крайне не удобно ctrl+cmd+options+up...

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

в общем задача заставить кнопку на клавиатуре делать действие, что-то типо

www.developers-life.com/...​-handling-key-events.html

помогите как нибуть упростить и показать какой кусок кода куда вписывать.

👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Есть другой вариант сделать глобальный хук
тут пример как сделать перехват сообщений по нажатию на медиа кнопки

stackoverflow.com/...-play-pause-key

В ответе пишется, что нужно наследовать NSApplication и указать свой класс в главном plist’e, «Principal class».

ЗЫ Прикольно, ссылка на мой блог :)

Угу. Только такое работает только, если твое приложение активно (т.е., глобальных хоткеев не выйдет). Иначе, ивенты идут мимо него. На данный момент единственный не deprecated способ — через NSEvent global monitor. Самый гитбкий, но уже deprecated (и не факт, тчо пустят в app store) — карбоновские ивенты.

Если сделать приложение как агент, то будут приходить

Интересно. Агент — это плист с <key>RunAtLoad</key> юзать в бутах?

Если же ты имеешь в виду LSUIElement, то у меня сомнения. Дока также подтверждает, что только по клику приходят ивенты.

LSUIElement (String — Mac OS X) specifies whether the application runs as an agent application. If this key is set to “1”, Launch Services runs the application as an agent application. Agent applications do not appear in the Dock or in the Force Quit window. Although they typically run as background applications, they can come to the foreground to present a user interface if desired. A click on a window belonging to an agent application brings that application forward to handle events.

З.Ы. Только что из интереса проверил, глобал ивенты не приходят, если аппликуха не активирована (ativateignoringotherapps).Если у тебя все работает, то код в студию.

В моем примере все работало так, как и предполагалось. Т.е. реагирвоало только на keydown. Странно.

Долго мучил этот globalmonitor, то на Up не работает, то на зажатой кнопке сразу и Up и Down спамит, в обшем сделал по примеру NDHotKeyEvent,
если кому интересно
сорс cl.ly/3mLf
апп cl.ly/3mSJ

с 5ым скайпом легко словить not responding, на 2.8 все отлично

У меня нет опыта Mac-разработки, поэтому всё только по докам. Судя по тому, что я вижу с помощью этих методов можно ловить глобально только key down (но не key up). И что значит

Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).

я понятия не имею.
Судя по доке, план должен быть примерно такой:
0) Добавлять в SkypeController.h addGlobalMonitorForEventsMatchingMask blah-blah-blah не надо.
Добавляем туда id globalSubscription; для хранения подписки

1) где-то в районе старта приложения пишем что-то вроде

globalSubscription = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDown handler:^(NSEvent* event)  { [self keyDownEvent:event]; }];

2) где-то в районе выходы из приложения

[NSEvent removeMonitor:globalSubscription];

И еще совет, поменьше пользуйся интерфейс билдером и клепанием формочек, т.к. в итоге твой сплав кода и формошлепания будет тяжко читать. Даже сейчас уже приходидца лазить и смотреть в ИБ на связи, тчобы понять, что у тебя и как. ЛИчно я предпочитаю элементы окон раскидывать и селекторы в иб с делегатами, но все взаимосвязи между окнами выполнять в коде.

Первое, читай в мане, что такое блоки: Blocks Programming Topics.
Объявлять не надо, передавать надо указатель на блок, а не селектор, смотри в примере: cl.ly/3lHv

Стоит учесть, что, чтобы тебе делать логгирование не своего приложения, тебе надо будет либо работать по этом примеру: caffeinatedcocoa.com/blog p=12 и тогда пользователь должен будет ввести рутовый пароль, либо отсылать пользователя, чтобы он в Universal Access в System Preferences включал Enable Assistive Devices и требовать, чтобы пользователь перезагрузил приложение или перезагружать его форсированно, т.к. у яблока есть баг, что ты не будешь ловить ивенты, пока приложение не будет перезагружено после включения вспомогательных устройств. Этот пример рассматривает вариант с ручной перезагрузкой приложения и включения ассистивных девайсов.

почитал про addGlobalMonitorForEventsMatchingMask: handler: ,
но так и не понял как его подсунуть,
добавил
// SkypeController.h
+ (id) addGlobalMonitorForEventsMatchingMask: (NSEventMask) mask handler: (void) keyDownEvent: (NSEvent *) pEvent;
+ (id) addGlobalMonitorForEventsMatchingMask: (NSEventMask) mask handler: (void) keyDownEvent: (NSEvent *) pEvent; ,
но как в SkypeController.m писать?
я в obj-c совсем новичек, подробнее=true;

новый source: cl.ly/3l12

Экий ты быстрый. А я только собрался качать твое творение;
Если не активно, то регистрируй сей селектор: addGlobalMonitorForEventsMatchingMask: handler:, для активного окна надо использовать локальные мониторы, т.к. глобальный не будет вызывадца, когда активно окно твоей аппликухи. Если хочешь, можешь сходить пониже до CGEventов.
Если тебе необходимо, чтобы оно висело в бекграунде, то тебе нужен демон (бекграундное приложение, запускаемое на старте и висящее в памяти до перезагрузки), который будет не связан с твоим приложением, но сможет его запускать в случае необходимости или самостоятельно обрабатывать нажатия.

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

А можно поподробнее про демона?

разобрался, но работает только если окно активно, а как заставить его работать если окно не активно?

Оке. Я часика в 2 ночи гляну. Сейчас занят еще.

gonzo, нужно повесить действие на 1 клавишу, но чтобы клавиша выполняла действие даже если программа висит на заднем плане, как Ventrilo, но так както используется Universal Access.
trimm, , вставил код такого типа, повесил на delete (пробовал вещать на tab, enter, backspace, также пробовал NSUpArrowFunctionKey вместо ucicharcode). кнопки не хотят выполнять действие, в логах пусто. — (void) keyDown: (NSEvent *) theEvent {
NSString *theArrow = [theEvent charactersIgnoringModifiers];
unichar keyChar = [theArrow characterAtIndex: 0];
if (keyChar == 0×007f) {
NSLog (@"Success! Microphone is on! ");
[SkypeAPI sendSkypeCommand: @"SET MUTE OFF"];
} else {
NSLog (@"Success! Microphone is off! ");
[SkypeAPI sendSkypeCommand: @"SET MUTE ON"];
} }
Такое ощущение, что чего-то не хватает.
прилагаю проект, если это поможет делу

cl.ly/3lEG

— (void) keyDown: (NSEvent *) theEvent {
NSString *theArrow = [theEvent charactersIgnoringModifiers];
unichar keyChar = [theArrow characterAtIndex: 0];
if (keyChar == unicodeChar) {
NSLog (@"Button was tapped");
} else {
[super keyDown: theEvent];
} }
Вместо unicodeChar вставь номер юникодового символа (unsigned short, если мне память не изменяет), который отвечает за твою кнопку, которая тебе нужна. Этот вариант подходит в том случае, если тебе нужна реакция на одну кнопку, а не на комбинацию.
Если не хочешь дальше по респондерам передавать сие событие, то выкинь кусок с супером.
Писал по доке без проверки на работоспособность.
Тут — некоторые юникодовые чары, если не буквы тыкать хочешь.

developer.apple.com/...d/20000367-SW46

Вам надо комбинации клавиш перехватывать для одного приложения или вообще в системе?

Подписаться на комментарии