How to change mouse settings programmatically in macOS using IOKit - objective-c

The functions IOHIDGetAccelerationWithKey and IOHIDSetAccelerationWithKey are deprecated since macOS 10.12, therefore I am trying to implement the same using other IO*-methods.
I have never worked with IOKit, thus, all I can do is google for functions and try to get it to work.
Now I found this: Can't edit IORegistryEntry which has an example of how to change TrackpadThreeFingerSwipe property, however it is using a function which is not defined for me: getEVSHandle. Googling for it reveals only that it should be Found in the MachineSettings framework, however I can't seem to add any "MachineSettings" framework in Xcode 11.
What should I do? Current code is like:
#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDLib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSInteger value = -65536;
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &value);
CFMutableDictionaryRef propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, NULL, NULL);
CFDictionarySetValue(propertyDict, #"HIDMouseAcceleration", number);
io_connect_t connect = getEVSHandle(); // ???
if (!connect)
{
NSLog(#"Unable to get EVS handle");
}
res = IOConnectSetCFProperties(connect, propertyDict);
if (res != KERN_SUCCESS)
{
NSLog(#"Failed to set mouse acceleration (%d)", res);
}
IOObjectRelease(service);
CFRelease(propertyDict);
}
return 0;
}

The following works (tested with Xcode 11.2 / macOS 10.15)
#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDLib.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
io_service_t service = IORegistryEntryFromPath(kIOMasterPortDefault,
kIOServicePlane ":/IOResources/IOHIDSystem");
NSDictionary *parameters = (__bridge NSDictionary *)IORegistryEntryCreateCFProperty(service,
CFSTR(kIOHIDParametersKey), kCFAllocatorDefault, kNilOptions);
NSLog(#"%#", parameters);
NSMutableDictionary *newParameters = [parameters mutableCopy];
newParameters[#"HIDMouseAcceleration"] = #(12345);
kern_return_t result = IORegistryEntrySetCFProperty(service,
CFSTR(kIOHIDParametersKey), (__bridge CFDictionaryRef)newParameters);
NSLog(kIOReturnSuccess == result ? #"Updated" : #"Failed");
IOObjectRelease(service);
}
return 0;
}

Related

Setting media playback info in MacOs not working

I'm a newbie in ObjC and MacOs development.
My final goal is understand how setting playback info works from ObjC to try to implement that later on Rust (using generated ObjC runtime bindings).
So right now I'm trying to write small piece of code on ObjC that would set playback info (without actually playing anything).
I have found https://github.com/MarshallOfSound/electron-media-service/blob/master/src/darwin/service.mm and used it as a base.
Here's code I have right now:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <objc/runtime.h>
#include "MediaPlayer/MPNowPlayingInfoCenter.h"
#include "MediaPlayer/MPRemoteCommandCenter.h"
#include "MediaPlayer/MPRemoteCommand.h"
#include "MediaPlayer/MPMediaItem.h"
#include "MediaPlayer/MPRemoteCommandEvent.h"
#interface NativeMediaController : NSObject { }
#end
#implementation NativeMediaController
- (MPRemoteCommandHandlerStatus)remotePlay {
NSLog(#"Play");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePause {
NSLog(#"Pause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteTogglePlayPause {
NSLog(#"PlayPause");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteNext {
NSLog(#"Next");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remotePrev {
NSLog(#"Previous");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)remoteChangePlaybackPosition:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"ChangePlaybackPosition");
return MPRemoteCommandHandlerStatusSuccess;
}
- (MPRemoteCommandHandlerStatus)move:(MPChangePlaybackPositionCommandEvent*)event {
NSLog(#"Move");
return MPRemoteCommandHandlerStatusSuccess;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
NativeMediaController* controller = [[NativeMediaController alloc] init];
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[remoteCommandCenter playCommand].enabled = true;
[remoteCommandCenter pauseCommand].enabled = true;
[remoteCommandCenter togglePlayPauseCommand].enabled = true;
[remoteCommandCenter changePlaybackPositionCommand].enabled = true;
[remoteCommandCenter nextTrackCommand].enabled = true;
[remoteCommandCenter previousTrackCommand].enabled = true;
[[remoteCommandCenter playCommand] addTarget:controller action:#selector(remotePlay)];
[[remoteCommandCenter pauseCommand] addTarget:controller action:#selector(remotePause)];
[[remoteCommandCenter togglePlayPauseCommand] addTarget:controller action:#selector(remoteTogglePlayPause)];
[[remoteCommandCenter changePlaybackPositionCommand] addTarget:controller action:#selector(remoteChangePlaybackPosition:)];
[[remoteCommandCenter nextTrackCommand] addTarget:controller action:#selector(remoteNext)];
[[remoteCommandCenter previousTrackCommand] addTarget:controller action:#selector(remotePrev)];
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
[songInfo setObject:[NSString stringWithUTF8String:"Test title"] forKey:MPMediaItemPropertyTitle];
[songInfo setObject:[NSString stringWithUTF8String:"Test artist"] forKey:MPMediaItemPropertyArtist];
[songInfo setObject:[NSString stringWithUTF8String:"Test albumtitle"] forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:[NSNumber numberWithFloat:60.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[songInfo setObject:[NSNumber numberWithFloat:360.0] forKey:MPMediaItemPropertyPlaybackDuration];
[songInfo setObject:[NSNumber numberWithFloat:112233] forKey:MPMediaItemPropertyPersistentID];
[MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePlaying;
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
NSLog(#"End!");
char input[50] = {0};
printf("Enter anything to quit: ");
scanf("%s", input);
}
return 0;
}
When this code is executed I see no effect in MacOs playing info widget.
Unfortunately right now I have no idea how to debug this or where can I find better ObjC example.
Finally, I found the reason that caused my problem playing with Apple Swift example.
There're two requirements to show playback widget (i didn't find them documented anywhere) that have been missing:
You should register a callback for at least one action (play, pause)
You should build your application as an App, not as Console. If your program is not shown in the dock as a running app - the playback widget wouldn't work.

Call to AXIsProcessTrustedWithOptions does not work after change setting value

I am testing accessibility permissions using AXIsProcessTrustedWithOptions.
When app starts, value is correctly returned. However if I change the setting in System Preferences -> Security & Privacy while app is running call to API is still returning the previous value.
Is that a bug?
Here is example code:
bool checkIfAccessibilityEnabledAndDisplayPopup()
{
// Method to check if accessibility is enabled
// Passing YES to kAXTrustedCheckOptionPrompt forces showing popup
NSDictionary *options = #{(bridge id)kAXTrustedCheckOptionPrompt: #YES};
Boolean accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
return (!!accessibilityEnabled);
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSLog(#"Starting");
for (int i=0;i<100;i) {
checkIfAccessibilityEnabledAndDisplayPopup();
[NSThread sleepForTimeInterval:20.0f];
NSDictionary *options = #{(__bridge id)kAXTrustedCheckOptionPrompt: #NO};
Boolean b = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);
Boolean b1 = AXIsProcessTrusted();
NSLog(#"accessibility AXIsProcessTrustedWithOptions--> %d AXIsProcessTrusted --->
%d", b, b1);
}
}

Using NSUInteger Input to insertObject:___ atIndex:___

I'm trying to create a simple commandline tic-tac-toe game using an NSMutableArray.
Created a class called "Board" with the method "getPosition"
(I'm assuming this is the best way to get a user input)
I'm asking for position, then casting from int to NSUInteger)
#import "Board.h"
#implementation Board
-(void)getPosition;
{
int enteredPosition;
scanf("%i", &enteredPosition);
NSUInteger nsEnteredPosition = (NSUInteger ) enteredPosition;
NSLog(#"Position = %lu", (unsigned long)nsEnteredPosition);
}
#import <Foundation/Foundation.h>
#import "Board.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSString *currentPlayer;
NSMutableArray *gameBoard=[[NSMutableArray alloc] initWithCapacity:9];
for(int i; i<=2; i++)
{
if(i %2)
{
currentPlayer=#"X";
}
else
{
currentPlayer=#"O";
}
NSLog(#"Player %#, select an open spot 1 - 9 on the board", currentPlayer);
Board *currentPosition = [[Board alloc] init];
[currentPosition getPosition];
[gameBoard insertObject:currentPlayer atIndex:currentPosition]; //this is where i have a problem
}
As I understand it atIndex requires an NSUInteger parameter, but I'm receiving the error message:
"Incompatible pointer to integer conversion sending 'Board *_strong"
to parameter of type 'NSUInteger' (aka 'unassigned long')
You're using currentPosition as your index which is a Board object. Perhaps [currentPosition getPosition] is supposed to return an NSUInteger. If so, try rewriting the last portion of your code like this:
Board *theBoard = [[Board alloc] init];
NSUInteger currentPosition = [theBoard getPosition];
[gameBoard insertObject:currentPlayer atIndex:currentPosition]; //this is where i have a problem

Objective-C: printing enum values

I was trying to print out the enum constant in Objective-C on Xcode.
The code:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
enum boolean{
no, yes
};
NSLog(#"%d", yes);
}
return 0;
}
I ran this code and all the console is showing me is "(lldb)".
Is it the syntax that I got wrong?
Or am I missing something here?
Also, I tried it different way using typedef:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
typedef enum {
no, yes
} boolean;
boolean boolVal = yes;
NSLog(#"%d", boolVal);
}
return 0;
}
I suspect I did something wrong with printing out the value, with NSLog().
But I have tried using %i, %#, %d. But the output was same, (lldb).
Are there any different ways to print out the enum values?
You have to give the members of the enum values is you want to print them. Try the following.
enum boolean {
no = 0,
yes = 1
};
NSLog(#"yes = %d",yes);
The previous code outputs the following.
yes = 1

CoreMidi.framework sending midi commands

there is an app called FreeStyler, that you can control using midi commands. In my mac app I want to send midi signals.
Can someone show an example of this?
Elijah
This is what it took to send a note to my blofeld synth. I hope it helps. You can use MIDIObjectGetProperties to find the uniqueIDs for all the midi devices connected to your mac.
#import <Foundation/Foundation.h>
#import <CoreMIDI/CoreMIDI.h>
MIDIEndpointRef getEndpointWithUniqueID(MIDIUniqueID id){
MIDIObjectRef endPoint;
MIDIObjectType foundObj;
MIDIObjectFindByUniqueID(id, &endPoint, &foundObj);
return (MIDIEndpointRef) endPoint;
}
MIDIClientRef getMidiClient(){
MIDIClientRef midiClient;
NSString *outPortName =#"blofeldOut";
MIDIClientCreate((CFStringRef)outPortName, NULL, NULL, &midiClient);
return midiClient;
}
MIDIPortRef getOutPutPort(){
MIDIPortRef outPort;
NSString *outPortName =#"blofeldOut";
MIDIOutputPortCreate(getMidiClient(), (CFStringRef)outPortName, &outPort);
return outPort;
}
MIDIPacketList getMidiPacketList(){
MIDIPacketList packetList;
packetList.numPackets = 1;
MIDIPacket* firstPacket = &packetList.packet[0];
firstPacket->timeStamp = 0; // send immediately
firstPacket->length = 3;
firstPacket->data[0] = 0x90;
firstPacket->data[1] = 60;
firstPacket->data[2] = 64;
// TODO: add end note sequence
return packetList;
}
void play_note(void) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MIDIPacketList packetList=getMidiPacketList();
MIDIUniqueID blofeldEndpointID = -934632258;
MIDIEndpointRef blofeldEndpoint = getEndpointWithUniqueID(blofeldEndpointID);
MIDISend(getOutPutPort(), blofeldEndpoint, &packetList);
MIDIEndpointDispose(blofeldEndpoint);
[pool drain];
}
int main (int argc, const char * argv[]) {
play_note();
return 0;
}
Your application will need to use the CoreMIDI framework to send or receive MIDI, which I can tell you from experience is not a lot of fun to work with directly. You might want to try the vvopensource framework, which is a MIDI framework designed for cocoa.