How to generate keystroke with repeat in macOS application with objective-c?
I already manage to generate the keystroke:
CGEventRef keyup, keydown;
uint64_t keyModifier=0;
VK = kVK_RightArrow;
keyModifier = 0;
keydown = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)VK, true);
CGEventSetFlags( keydown, keyModifier);
keyup = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)VK, false);
CGEventSetFlags( keyup, keyModifier);
// press the key
// forward them to the frontmost app
CGEventPostToPid (pid, keydown);
// and finally behave friendly
CFRelease(keydown);
....
// somewhere else with time delay
// release the key
// forward them to the frontmost app
CGEventPostToPid (pid, keyup);
// and finally behave friendly
CFRelease(keyup);
Using this code the equivalent keyboard right key is pressed then released, but no repeat although there is enough time between the 2 events.
Related
My window calls hide() when a QEvent::FocusOut is received. Simultaniously I want its visibility to be toggled if a hotkey is pressed. Now I have the following problem: Pressing the hotkey, registered with XGrabKex(...) seems to steel the focus of my app. Resulting in an unwanted behaviour. If my app is visible the hotkeyevent steels focus, which results in a QEvent::FocusOut, which hides my app, and after that the hotkey is received which toggles visibility (shows) my app. I.e. my app does not hide when pressing the hotkey.
Is there a way to tell the x window system to not steel the focus when a grabbed key is pressed? Or are there other possible solutions to this problem?
A couple of different methods.
Use XQueryKeymap to see which keys are pressed. For instance, when you get a FocusOut event, call XQueryKeymap and see if your hotkey is pressed. If it is not, hide the window; if it is, don't hide it and wait for the hotkey event.
Delay hiding on FocusOut by 100 or so milliseconds. Cancel hiding if you either get the hot key or get your focus back during this time interval.
Look also here for useful info.
Finally got it to work in a "proper" way:
bool MainWidget::nativeEvent(const QByteArray &eventType, void *message, long *)
{
#ifdef Q_OS_LINUX
if (eventType == "xcb_generic_event_t")
{
xcb_generic_event_t* event = static_cast<xcb_generic_event_t *>(message);
switch (event->response_type & 127)
{
case XCB_FOCUS_OUT: {
xcb_focus_out_event_t *fe = (xcb_focus_out_event_t *)event;
if ((fe->mode==XCB_NOTIFY_MODE_GRAB && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR)
|| (fe->mode==XCB_NOTIFY_MODE_NORMAL && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR ))
hide();
break;
}
}
}
#endif
return false;
}
I'm trying to implement a screensaver simulator on Mac OS X , I managed to disable the effect of pressing command+Q that was causing the application to exit, so now if it's in the full screen mode, it will not respond to the quit keyboard shortcut.
But, my problem is in handling the shortcut of ( Command+ Shift+Q) that pops up the confirmation dialog of Max OS X that warns exiting all the apps and logging of the system.
Can anyone help me in preventing the effect of command+shift+q shortcut while being in full screen mode ?
thanks
This is the best answer for this question
First in your applicationDidFinishedLoad function, add this peace of code to create an event tap and add the event tap in the current run loop
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
// Create an event tap. We are interested in key presses.
eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
eventMask, myCGEventCallback, NULL);
if (!eventTap) {
fprintf(stderr, "failed to create event tap\n");
exit(1);
}
// Create a run loop source.
runLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, eventTap, 0);
// Add to the current run loop.
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
// Enable the event tap.
CGEventTapEnable(eventTap, true);
then in your class, you can implement the call back function called myCGEventCallback like this
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *refcon)
{
// Paranoid sanity check.
// type will be key down or key up, you can discard the command + q by setting the kecode to be -1 like this
if (((type == kCGEventKeyDown) || (type == kCGEventKeyUp)))
{
CGEventSetIntegerValueField(
event, kCGKeyboardEventKeycode, (int64_t)-1);
return event;
}
// The incoming keycode.
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(
event, kCGKeyboardEventKeycode);
// Swap 'a' (keycode=0) and 'z' (keycode=6).
if (keycode == (CGKeyCode)0)
keycode = (CGKeyCode)6;
else if (keycode == (CGKeyCode)6)
keycode = (CGKeyCode)0;
// Set the modified keycode field in the event.
CGEventSetIntegerValueField(
event, kCGKeyboardEventKeycode, (int64_t)keycode);
// We must return the event for it to be useful.
return event;
}
for the original code
Check Here
Thanks
I need to send mouse click events to an arbitrary process (not necessarily the front one) without bringing that process's window to the front.
This code works for sending a mouse click and letting the window server send it to whatever process it decides has focus:
#include <ApplicationServices/ApplicationServices.h>
int
main()
{
CGEventRef down, up;
down = CGEventCreateMouseEvent(
NULL,
kCGEventLeftMouseDown,
CGPointMake(16, 36),
kCGMouseButtonLeft
);
up = CGEventCreateMouseEvent(
NULL,
kCGEventLeftMouseUp,
CGPointMake(16, 36),
kCGMouseButtonLeft
);
CGEventPost(kCGHIDEventTap, down);
CGEventPost(kCGHIDEventTap, up);
CFRelease(down);
CFRelease(up);
return 0;
}
I can send mouse click events via CGEventPost() just fine, but that requires the target process to have focus (which I am trying to avoid).
I can send keyboard events via CGEventPostToPSN() just fine, and as far as I can tell, mouse move events work too, what I'm having trouble with is mouse down/up events.
This code (pretty much the above, the only difference being that I specify myself which process to send the events to) does not work and I don't even know how to find out where it breaks down.
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h> /* for ProcessSerialNumber stuff */
#include <stdio.h>
int
main()
{
ProcessSerialNumber psn;
CGEventRef down, up;
if (GetFrontProcess(&psn) != noErr) {
printf("Unable to get front process\n");
return 1;
}
down = CGEventCreateMouseEvent(
NULL,
kCGEventLeftMouseDown,
CGPointMake(16, 36),
kCGMouseButtonLeft
);
up = CGEventCreateMouseEvent(
NULL,
kCGEventLeftMouseUp,
CGPointMake(16, 36),
kCGMouseButtonLeft
);
CGEventPostToPSN(&psn, down);
CGEventPostToPSN(&psn, up);
CFRelease(down);
CFRelease(up);
return 0;
}
I've been stuck on this for a few days now, and I can't seem to figure it out. According to the documentation this is (as far as I can tell) exactly how it is supposed to be done.
I have tested this code on Snow Leopard and Lion, with the same results.
Could somebody who has had success with clicking via CGEventPostToPSN() please shed some insight on the proper way to accomplish this?
I have been successful sending keystrokes in order to automate a particular software package for drawing that I use. This software relies a lot of keyboard shortcuts so I wrote something that could call some of these keyboard shortcuts in order to streamline my workflow. As I said, this has worked out good.
My library is a Cocoa library that is loaded as a plugin to the software package. Here is a snippet of code that I have been using for sending my keystrokes.
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef eventDown = CGEventCreateKeyboardEvent(eventSource, (CGKeyCode)1, true);
CGEventRef eventUp = CGEventCreateKeyboardEvent(eventSource, (CGKeyCode)1, false);
//setting the process number somewhere else
CGEventPostToPSN(&number, eventDown);
CGEventPostToPSN(&number, eventUp);
For some procedures in the drawing package if you continue to hold the Shift key then you activate a special tool. I have been unable to simulate this. I thought I could send the Shift key and say that I wanted it to auto-repeat but that doesn't seem to work. I have been using the following code to set the auto-repeat:
//This is done before sending the key
CGEventSetIntegerValueField(eventDown, kCGKeyboardEventAutorepeat, 1);
In my testing I have been unable to make any key auto-repeat. It just send the key once and that is it.
Is there anyone that have been successful autorepeating a key using the above method? I have searched the Internet for answers but all I have found are some unanswered questions from 2008... Any help is greatly appreciated.
Thanks,
mobbe
The code that OP finally came up with to solve the problem (transferred here from a comment under other answer):
CGEventRef flagsChanged = CGEventCreate(eventSource);
CGEventSetType(flagsChanged, kCGEventFlagsChanged);
CGEventSetIntegerValueField(flagsChanged, kCGKeyboardEventKeycode, 56);
CGEventSetFlags(flagsChanged, 131330);
CGEventPostToPSN(&number, flagsChanged);
CFRelease(flagsChanged); CFRelease(eventSource);
131330 is a constant indicating the Shift key; it is related to NSShiftKeyMask and kCGEventFlagMaskShift, which are 131072 (0x00020000). 131330 - 256 - 2 == 131072.
UPDATE: the Shift key's code isn't 56, according to Events.h:
...
kVK_Shift = 0x38,
...
(EDIT: of course those of you who are paying attention (I wasn't) realize that HEX 38 == DEC 56.)
I also realized how to get modifier key presses: flagsChanged:. So using this keycode, I can catch Shift key presses in flagsChanged:, and the repeat works fine; I also get repeated key events for "normal" keys in keyDown: and keyUp: without difficulty.
It sounds like you may not have access to/want to change the event-handling code (to add flagsChanged:) though, so if that keycode doesn't work for you, I'm stumped and can only say "Good luck!"
I believe that the field you're setting is used to indicate not that the event should be repeated by the system, but that an event is an auto-repeat of a previous event. You still have to generate each event yourself. Something like (EDITED to use a timer instead of a loop):
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef eventDown = CGEventCreateKeyboardEvent(eventSource, (CGKeyCode)1, true);
// Post the initial keydown
CGEventPostToPSN(&pidNumber, eventDown);
// Keep track of how many Shift keydown events have been sent
shiftKeyRepeatCount = 1;
[NSTimer scheduledTimerWithTimeInterval:0.3 // I don't know exactly what the interval should be, of course
target:self
selector:#selector(repeatShiftKeyDown:)
userInfo:nil
repeats:YES];
CFRelease(eventDown);
- (void)repeatShiftKeyDown:(NSTimer *)tim {
if( shiftKeyRepeatCount >= kRepeatCountForSpecialTool ){
[tim invalidate];
[self sendShiftKeyUp];
return;
}
shiftKeyRepeatCount++;
GEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef eventDown = CGEventCreateKeyboardEvent(eventSource, (CGKeyCode)1, true);
// Set the auto-repeat field
CGEventSetIntegerValueField(eventDown, kCGKeyboardEventAutorepeat, 1);
CGEventPostToPSN(&pidNumber, eventDown);
CFRelease(eventDown);
}
I'm not certain whether you can reuse the first event with a changed field or you'll need to generate a new event to use when repeating.
The iPhone app that I released is a wireless game controller, it translates touches on the device into key-presses on the networked Mac. This allowed for playing emulator (e.g. Nestopia) games using the iPhone as a controller. Of course, the day that I released it coincided with an os x update. After installing this update, the simulated key-presses no longer work in Nestopia! The crazier thing is, when I go to 'File > Open' within Nestopia, I can cycle through the file list by hitting the up-arrow on my iphone controller; i.e. the simulated key-presses work in menu items, but not in the game itself. The code that I use to simulate keys is below. Given the list of changes here, can anyone identify which change would cause this problem?
Thanks!!
#define UP false
#define DOWN true
-(void)sendKey:(CGKeyCode)keycode andKeyDirection:(BOOL)keydirection{
CGEventRef eventRef = CGEventCreateKeyboardEvent(NULL, keycode, keydirection);
CGEventPost(kCGSessionEventTap, eventRef);
CFRelease(eventRef);
}
The author of Mac Nestopia is using an older call, GetKeys(), to capture key events. As of 10.6.3, GetKeys does not catch generated key presses using the methods detailed in this post. The workaround I found was to use this instead:
-(void)sendKey:(CGKeyCode)keycode andKeyDirection:(BOOL)keydirection{
AXUIElementRef axSystemWideElement = AXUIElementCreateSystemWide();
AXError err = AXUIElementPostKeyboardEvent(axSystemWideElement, 0, keycode, keydirection);
if (err != kAXErrorSuccess)
NSLog(#" Did not post key press!");
}
Huge thanks to Richard Bannister for his quick email responses!
I think it's a problem with your code and not with 10.6.3. I have an app I'm writing that simulates key presses, and I've upgraded to 10.6.3, and my simulated key presses still work just fine.
Here's what I'm doing:
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
CGEventRef keyDownPress = CGEventCreateKeyboardEvent(source, (CGKeyCode)keyCode, YES);
CGEventSetFlags(keyDownPress, (CGEventFlags)flags);
CGEventRef keyUpPress = CGEventCreateKeyboardEvent(source, (CGKeyCode)keyCode, NO);
CGEventPost(kCGAnnotatedSessionEventTap, keyDownPress);
CGEventPost(kCGAnnotatedSessionEventTap, keyUpPress);
CFRelease(keyDownPress);
CFRelease(keyUpPress);
CFRelease(source);