I have seen many post on this topic. But haven't found a clear answer anywhere.
Is there a way to toggle CAPS LOCK in Objective-C or C code? I am not looking for a solution using X11 libs. I am not bothered about the LED on/off status. But just the functionality of CAPS LOCK (changing the case of letters and printing the special characters on number keys).
Why is CGEvent not supporting this the way it does for other keys?
var ioConnect: io_connect_t = .init(0)
let ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass))
IOServiceOpen(ioService, mach_task_self_, UInt32(kIOHIDParamConnectType), &ioConnect)
var modifierLockState = false
IOHIDGetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), &modifierLockState)
modifierLockState.toggle()
IOHIDSetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), modifierLockState)
IOServiceClose(ioConnect)
The following method also works when you want CapsLock to toggle the current keyboard language (if you have the CapsLock key configured to do that).
CFMutableDictionaryRef mdict = IOServiceMatching(kIOHIDSystemClass);
io_service_t ios = IOServiceGetMatchingService(kIOMasterPortDefault, (CFDictionaryRef)mdict);
if (ios) {
io_connect_t ioc = 0;
IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, &ioc);
if (ioc) {
NXEventData event{};
IOGPoint loc{};
// press CapsLock key
UInt32 evtInfo = NX_KEYTYPE_CAPS_LOCK << 16 | NX_KEYDOWN << 8;
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
event.compound.misc.L[0] = evtInfo;
IOHIDPostEvent(ioc, NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE);
// release CapsLock key
evtInfo = NX_KEYTYPE_CAPS_LOCK << 16 | NX_KEYUP << 8;
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
event.compound.misc.L[0] = evtInfo;
IOHIDPostEvent(ioc, NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE);
IOServiceClose(ioc);
}
}
I got this working, after a long struggle.
Invoke the method given below twice. Once for up event and another for down event. For example for simulating CAPS A, we need to do the following.
[self handleKeyEventWithCapsOn:0 andKeyDown:NO];
[self handleKeyEventWithCapsOn:0 andKeyDown:YES];
0 is the keycode for 'a'.
- (void) handleKeyEventWithCapsOn:(int) keyCode andKeyDown:(BOOL)keyDown
{
if(keyDown)
{
CGEventRef eventDown;
eventDown = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)keyCode, true);
CGEventSetFlags(eventDown, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, eventDown);
CFRelease(eventDown);
}
else
{
CGEventRef eventUp;
eventUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)keyCode, false);
CGEventSetFlags(eventUp, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, eventUp);
// SHIFT Up Event
CGEventRef eShiftUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, eShiftUp);
CFRelease(eventUp);
CFRelease(eShiftUp);
}
}
Related
I have some code I've been using to get the current keyboard layout and convert a virtual key code into a string. This works great in most situations, but I'm having trouble with some specific cases. The one that brought this to light is the accent key next to the backspace key on german QWERTZ keyboards. http://en.wikipedia.org/wiki/File:KB_Germany.svg
That key generates the VK code I'd expect kVK_ANSI_Equal but when using a QWERTZ keyboard layout I get no description back. Its ending up as a dead key because its supposed to be composed with another key. Is there any way to catch these cases and do the proper conversion?
My current code is below.
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
if(keyboardLayout)
{
UInt32 deadKeyState = 0;
UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];
OSStatus status = UCKeyTranslate(keyboardLayout,
keyCode, kUCKeyActionDown, 0,
LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
&deadKeyState,
maxStringLength,
&actualStringLength, unicodeString);
if(actualStringLength > 0 && status == noErr)
return [[NSString stringWithCharacters:unicodeString length:(NSInteger)actualStringLength] uppercaseString];
}
That key is a dead key, as you can see if you try it yourself or look at the Keyboard Viewer with the German layout active.
On the Mac, the way to enter a dead key's actual character, without composing it with another character, is to press a space after it. So try that: Turn off kUCKeyTranslateNoDeadKeysBit, and if UCKeyTranslate sets the dead-key state, translate a space after it.
EDIT (added by asker)
Just for future people, here is the fixed code with the right solution.
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
if(keyboardLayout)
{
UInt32 deadKeyState = 0;
UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];
OSStatus status = UCKeyTranslate(keyboardLayout,
keyCode, kUCKeyActionDown, 0,
LMGetKbdType(), 0,
&deadKeyState,
maxStringLength,
&actualStringLength, unicodeString);
if (actualStringLength == 0 && deadKeyState)
{
status = UCKeyTranslate(keyboardLayout,
kVK_Space, kUCKeyActionDown, 0,
LMGetKbdType(), 0,
&deadKeyState,
maxStringLength,
&actualStringLength, unicodeString);
}
if(actualStringLength > 0 && status == noErr)
return [[NSString stringWithCharacters:unicodeString length:(NSUInteger)actualStringLength] uppercaseString];
}
Hy i wanted to toggle a GPIO pin always when i send a package. But I have a weird behafure sometimes my pin gets toggled and sometimes not. I checked my Kernel logs and there is no Information that the pin could not be toggled.
My Ops look like this:
static const struct net_device_ops stmuart_netdev_ops = {
.ndo_init = stmuart_netdev_init,
.ndo_uninit = stmuart_netdev_uninit,
.ndo_open = stmuart_netdev_open,
.ndo_stop = stmuart_netdev_close,
.ndo_start_xmit = stmuart_netdev_xmit,
.ndo_set_mac_address = eth_mac_addr,
.ndo_tx_timeout = stmuart_netdev_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
};
So as far as i know the ndo_start_xmit method is called as soon as i want to send a package. so i only toggle my pin when this method is called. But when i look on my Oscilloscope i can see that the pin is only sometimes toggled.
My method for sending looks like this:
static netdev_tx_t
stmuart_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_device_stats *n_stats = &dev->stats;
struct stmuart *stm = netdev_priv(dev);
u8 pad_len = 0;
int written;
u8 *pos;
spin_lock(&stm->lock);
gpiod_set_value(stm->rts_gpio, 1);
WARN_ON(stm->tx_left);
if (!netif_running(dev)) {
spin_unlock(&stm->lock);
netdev_warn(stm->net_dev, "xmit: iface is down\n");
goto out;
}
pos = stm->tx_buffer;
if (skb->len < STMFRM_MIN_LEN)
pad_len = STMFRM_MIN_LEN - skb->len;
pos += stmfrm_create_header(pos, skb->len + pad_len);
memcpy(pos, skb->data, skb->len);
pos += skb->len;
if (pad_len) {
memset(pos, 0, pad_len);
pos += pad_len;
}
pos += stmfrm_create_footer(pos);
netif_stop_queue(stm->net_dev);
written = serdev_device_write_buf(stm->serdev, stm->tx_buffer,
pos - stm->tx_buffer);
if (written > 0) {
stm->tx_left = (pos - stm->tx_buffer) - written;
stm->tx_head = stm->tx_buffer + written;
n_stats->tx_bytes += written;
}
spin_unlock(&stm->lock);
out:
gpiod_set_value(stm->rts_gpio, 0);
netif_trans_update(dev);
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
So i checked the netdevice.h but there i did not saw another method that gets called when the net device wants to send a package.
It seems to me like always when the netdevice sends a ARP-Request the pin gets not toggled.
I'm writing an app in swift that lives in the menu bar at the top of the screen. I need both a global and local event monitor to open the popover on a specific key press. There is no problem with the local event monitor, but when the user hits the key command (cmd+shift+8) from inside an app like Finder, the popover opens but the mac error "Funk" sound is played as well. Is there any way I can disable this? Perhaps some way for the app to eat the sound, or register it as a valid keyboard shortcut so the sound is never played?
Here is the code:
NSEvent.addGlobalMonitorForEvents(matching: NSEventMask.keyDown, handler: {(event: NSEvent!) -> Void in
if (event.keyCode == 28 && event.modifierFlags.contains(NSEventModifierFlags.command) && event.modifierFlags.contains(NSEventModifierFlags.shift)){
self.togglePopover(sender: self)
}
});
NSEvent.addLocalMonitorForEvents(matching: NSEventMask.keyDown, handler: {(event: NSEvent!) -> NSEvent? in
if (event.keyCode == 28 && event.modifierFlags.contains(NSEventModifierFlags.command) && event.modifierFlags.contains(NSEventModifierFlags.shift)){
self.togglePopover(sender: self)
}
return event
});
I ended up using MASShortcut as a workaround solution to this issue.
In your addGlobalMonitorForEventsMatchingMask handler, save the current volume level and turn the volume down on both channels. You can do you own event processing in the handler before or after turning down the volume.
Before returning from the handler, restore the original volume, but include a delay to give the OS time to process the event (it will send the "funk," but you won't hear it).
One side effect: if you're listening to something (e.g., music), that will be briefly silenced, too.
My event code:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown
handler:^(NSEvent *event) {
if ( event.type == NSEventTypeKeyDown ) {
if ( event.keyCode == 106 ) { // F16
// process the event here
[self adjustVolume:#(NO)];
[self performSelector:#selector(adjustVolume:) withObject:#(YES) afterDelay:0.3];
// 0.2 seconds was too soon
}
}
}];
My volume adjustment code:
- (void)adjustVolume:(NSNumber *)offOn
{
// get audio device...
AudioObjectPropertyAddress getDefaultOutputDevicePropertyAddress = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
AudioDeviceID defaultOutputDeviceID;
UInt32 infoSize = sizeof(defaultOutputDeviceID);
AudioObjectGetPropertyData(kAudioObjectSystemObject,
&getDefaultOutputDevicePropertyAddress,
0, NULL,
&infoSize, &defaultOutputDeviceID);
// structurs to access the left/right volume setting
AudioObjectPropertyAddress volumePropertyAddress1 = {
kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeOutput,
1 /* left */
};
AudioObjectPropertyAddress volumePropertyAddress2 = {
kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeOutput,
2 /* right */
};
// save the original volume (assumes left/right are the same
static Float32 volumeOriginal; // could be an iVar
if ( offOn.boolValue == NO ) { // turn off
UInt32 volumedataSize = sizeof(volumeOriginal);
AudioObjectGetPropertyData(defaultOutputDeviceID,
&volumePropertyAddress1,
0, NULL,
&volumedataSize, &volumeOriginal);
//NSLog(#"volumeOriginal %f",volumeOriginal);
// turn off both channels
Float32 volume = 0.0;
AudioObjectSetPropertyData(defaultOutputDeviceID,
&volumePropertyAddress1,
0, NULL,
sizeof(volume), &volume);
AudioObjectSetPropertyData(defaultOutputDeviceID,
&volumePropertyAddress2,
0, NULL,
sizeof(volume), &volume);
} else { // restore
//NSLog(#"restoring volume");
AudioObjectSetPropertyData(defaultOutputDeviceID,
&volumePropertyAddress1,
0, NULL,
sizeof(volumeOriginal), &volumeOriginal);
AudioObjectSetPropertyData(defaultOutputDeviceID,
&volumePropertyAddress2,
0, NULL,
sizeof(volumeOriginal), &volumeOriginal);
}
}
With recognition to Thomas O'Dell for getting me started on this Change OS X system volume programmatically
I want to simulate a click on the icon of an app on the home screen of jailbroken iPhone. I used the code below, but it could not take effect. Did I do something wrong?
Is the method to getFrontMostAppPort() right? Event if I tried to use GSSendSystemEvent(), it had no effect.
BTW, I mean jailbroken device. Could anyone give me a help? I very much appreciate it.
// Framework Paths
#define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"
static mach_port_t getFrontMostAppPort() {
bool locked;
bool passcode;
mach_port_t *port;
void *lib = dlopen(SBSERVPATH, RTLD_LAZY);
int (*SBSSpringBoardServerPort)() = dlsym(lib, "SBSSpringBoardServerPort");
void* (*SBGetScreenLockStatus)(mach_port_t* port, bool *lockStatus, bool *passcodeEnabled) = dlsym(lib, "SBGetScreenLockStatus");
port = (mach_port_t *)SBSSpringBoardServerPort();
dlclose(lib);
SBGetScreenLockStatus(port, &locked, &passcode);
void *(*SBFrontmostApplicationDisplayIdentifier)(mach_port_t *port, char *result) = dlsym(lib, "SBFrontmostApplicationDisplayIdentifier");
char appId[256];
memset(appId, 0, sizeof(appId));
SBFrontmostApplicationDisplayIdentifier(port, appId);
NSString * frontmostApp=[NSString stringWithFormat:#"%s",appId];
if([frontmostApp length] == 0 || locked)
return GSGetPurpleSystemEventPort();
else
return GSCopyPurpleNamedPort(appId);
}
static void sendTouchEvent(GSHandInfoType handInfoType, CGPoint point) {
uint8_t touchEvent[sizeof(GSEventRecord) + sizeof(GSHandInfo) + sizeof(GSPathInfo)];
// structure of touch GSEvent
struct GSTouchEvent {
GSEventRecord record;
GSHandInfo handInfo;
} * event = (struct GSTouchEvent*) &touchEvent;
bzero(touchEvent, sizeof(touchEvent));
// set up GSEvent
event->record.type = kGSEventHand;
event->record.subtype = kGSEventSubTypeUnknown;
event->record.windowLocation = point;
event->record.timestamp = GSCurrentEventTimestamp();
event->record.infoSize = sizeof(GSHandInfo) + sizeof(GSPathInfo);
event->handInfo.type = handInfoType;
event->handInfo.x52 = 1;
bzero(&event->handInfo.pathInfos[0], sizeof(GSPathInfo));
event->handInfo.pathInfos[0].pathIndex = 1;
event->handInfo.pathInfos[0].pathIdentity = 2;
event->handInfo.pathInfos[0].pathProximity = (handInfoType == kGSHandInfoTypeTouchDown || handInfoType == kGSHandInfoTypeTouchDragged || handInfoType == kGSHandInfoTypeTouchMoved) ? 0x03 : 0x00;;
event->handInfo.pathInfos[0].pathLocation = point;
mach_port_t port = (mach_port_t)getFrontMostAppPort();
GSSendEvent((GSEventRecord *)event, port);
}
// why nothing happened?
static clickOnHome() {
sendTouchEvent(kGSHandInfoTypeTouchDown, CGPointMake(100, 200));
sleep(1);
sendTouchEvent(kGSHandInfoTypeTouchUp, CGPointMake(100, 200));
}
Yes, I think your getFrontMostAppPort() method is fine, if you got it from here.
I'm trying to understand what you want:
If you simply want to open up a particular app (by bundleId), then I would recommend using the command-line open utility available on Cydia. You can call this programmatically with system(), or an exec call. Or, if you want to build this "open" capability into your own app, see my answer here.
Now, maybe you are writing code for an app or tweak that's in the background, not the foreground app. And, maybe you really do need to touch a specific {x,y} coordinate, not open an app by its bundleId. If you really want that, this code works for me (based on this answer ... with corrections):
static void sendTouchEvent(GSHandInfoType handInfoType, CGPoint point) {
uint8_t gsTouchEvent[sizeof(GSEventRecord) + sizeof(GSHandInfo) + sizeof(GSPathInfo)];
// structure of touch GSEvent
struct GSTouchEvent {
GSEventRecord record;
GSHandInfo handInfo;
} * touchEvent = (struct GSTouchEvent*) &gsTouchEvent;
bzero(touchEvent, sizeof(touchEvent));
touchEvent->record.type = kGSEventHand;
touchEvent->record.subtype = kGSEventSubTypeUnknown;
touchEvent->record.location = point;
touchEvent->record.windowLocation = point;
touchEvent->record.infoSize = sizeof(GSHandInfo) + sizeof(GSPathInfo);
touchEvent->record.timestamp = GSCurrentEventTimestamp();
//touchEvent->record.window = winRef;
//touchEvent->record.senderPID = 919;
bzero(&touchEvent->handInfo, sizeof(GSHandInfo));
bzero(&touchEvent->handInfo.pathInfos[0], sizeof(GSPathInfo));
GSHandInfo touchEventHandInfo;
touchEventHandInfo._0x5C = 0;
touchEventHandInfo.deltaX = 0;
touchEventHandInfo.deltaY = 0;
touchEventHandInfo.height = 0;
touchEventHandInfo.width = 0;
touchEvent->handInfo = touchEventHandInfo;
touchEvent->handInfo.type = (handInfoType == kGSHandInfoTypeTouchDown) ? 2 : 1;
touchEvent->handInfo.deltaX = 1;
touchEvent->handInfo.deltaY = 1;
touchEvent->handInfo.pathInfosCount = 0;
touchEvent->handInfo.pathInfos[0].pathIndex = 1;
touchEvent->handInfo.pathInfos[0].pathIdentity = 2;
touchEvent->handInfo.pathInfos[0].pathProximity = (handInfoType == kGSHandInfoTypeTouchDown || handInfoType == kGSHandInfoTypeTouchDragged || handInfoType == kGSHandInfoTypeTouchMoved) ? 0x03: 0x00;
touchEvent->handInfo.x52 = 1; // iOS 5+, I believe
touchEvent->handInfo.pathInfos[0].pathLocation = point;
//touchEvent->handInfo.pathInfos[0].pathWindow = winRef;
GSEventRecord* record = (GSEventRecord*) touchEvent;
record->timestamp = GSCurrentEventTimestamp();
GSSendEvent(record, getFrontMostAppPort());
}
- (void) simulateHomeScreenTouch {
CGPoint location = CGPointMake(50, 50);
sendTouchEvent(kGSHandInfoTypeTouchDown, location);
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
sendTouchEvent(kGSHandInfoTypeTouchUp, location);
});
}
This will send a touch down/up event pair to SpringBoard, as long as SpringBoard is the frontmost app (no other app open). Also, you can see where the handInfoType value is mapped to 2 or 1 for touch down and up events. The code above assumes that those are the only two types of events you're generating.
Note: I first tried with your coordinates of x=100, y=200, and nothing happened. I think that coordinate is between two home screen icons. Using other coordinates (e.g. x=50, y=50) works fine.
Note II: I don't believe this solution actually requires jailbreaking. It uses Private APIs, so it can't be submitted to the App Store, but this should work for jailed phones in an enterprise, or hobbyist deployment.
I find MPMoviePlayerController.h,there is
enum {
MPMovieLoadStateUnknown = 0,
MPMovieLoadStatePlayable = 1 << 0,
MPMovieLoadStatePlaythroughOK = 1 << 1, // Playback will be automatically started in this state when shouldAutoplay is YES
MPMovieLoadStateStalled = 1 << 2, // Playback will be automatically paused in this state, if started
};
typedef NSInteger MPMovieLoadState;
but when i did
NSLog(#"%d",player.loadState)
it prints out 5 or sometimes 3,how did it happen?As i know the loadstate has value of 0,1,2,4 refer to developer documentation.
Thank you!
The playState is a bitmask. Any number of bits can be set, such as
MPMovieLoadStatePlaythroughOK | MPMovieLoadStatePlayable
Check for states like this:
MPMovieLoadState state = [playerController loadState];
if( state & MPMovieLoadStatePlaythroughOK ) {
NSLog(#"State is Playthrough OK");
}