I've created an Xcode project using Swift and a privileged Helper tool using Objective-C. The helper tool works fine within a project which has also been created in Objective-C but it doesn't seem to work within a project created with Swift.
The service itself is being installed. I can see the helper binary within the /Library/PrivilegedHelperTools directory and it's permissions seem to be okay (as well as the user: root). Removing the helper by using launchctl results in re-installing the tool when my project runs (that works as expected) but I can't call any method of the helper tool.
There is neither any exception being thrown nor does any other error occur (at least there seem to be no error as the Console shows nothing as well).
Does anybody know whether this might be an issue with Swift? Because running the same helper tool within another project (written in Objective-C) works well.
I could figure out what the problem was. The helper tool has a main.m wich contains a main() method. I just forgot to fill it with code that creates an instance of my helper class and trigger its listener:
#import <Foundation/Foundation.h>
#import "Helper.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
Helper *helper = [[Helper alloc] init];
[helper run];
}
return EXIT_FAILURE;
}
This code causes the Helper instance to run in an infinite loop waiting for incoming connections (from Helper.h):
- (void)run
{
[_listener resume];
[[NSRunLoop currentRunLoop] run];
}
_listener is an instance of NSXPCListener.
Related
"[GCController controllers]" does not contain any controllers that were connected prior to application launch
TLDR;
I am trying to implement gamepad input on macOS using the Game Controller Framework. When invoked in my code, [GameController controllers] always returns an empty list until new controllers are connected. It never reflects gamepads connected to macOS prior to application launch, except if you disconnect them and reconnect them while the app is running. Does anyone know what I need to do to make controllers populate with pre-launch connections?
Full question
Now that Apple has added support for Xbox and Playstation controllers to the GameController framework, I'm trying to use it for gamepad input on a C++ game engine I'm developing. I'm using the framework instead of IOKit in order to "future-proof" my games to support additional controller types in the future, as well as to simplify my own input handling code.
Like many other game engines, I've foregone using NSApplicationMain() and nib files in favor of implementing my own event loop and setting up my game window programmatically. While my "Windows style" event loop appears to be working correctly, I've discovered that [GCController controllers] does not. The array it returns is always empty at launch, and will only ever reflect controllers that are connected while the game is running. Disconnecting a pre-connected controller does not trigger my GCControllerDidDisconnectNotification callback.
Here is a simplified version of my event loop:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// Create application
[NSApplication sharedApplication];
// Set up custom app delegate
CustomAppDelegate * delegate = [[CustomAppDelegate alloc] init];
[NSApp setDelegate:delegate];
// Activate and launch app
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
[NSApp activateIgnoringOtherApps:YES]; // Strictly speaking, not necessary
[NSApp finishLaunching]; // NSMenu is set up at this point in applicationWillFinishLaunching:.
// Initialize game engine (window is created here)
GenericEngineCode_Init(); // <-- Where I want to call [GCController controllers]
NSEvent *e;
do
{
do
{
// Pump messages
e = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e)
{
[NSApp sendEvent: e];
[NSApp updateWindows];
}
} while (e);
} while (GenericEngineCode_Run()); // Steps the engine, returns false when quitting.
GenericEngineCode_Cleanup();
}
return 0;
}
I've confirmed that even when using [NSApp run] instead of [NSApp finishLaunching], the behavior is the same. As best as I can tell, the problem is that there's something NSApplicationMain() does that I'm not doing, but that function is a black box -- I can't identify what I need to do to get controllers to populate correctly. Does anyone know what I'm missing?
The closest thing I could find to an explanation of this problem is this answer, which suggests that my app isn't getting didBecomeActive notifications, or that at the least, the private _GCControllerManager isn't getting a CBApplicationDidBecomeActive message. I'm not a professional macOS developer, though: I don't know if this actually applies to my situation, or how I'd go about correcting the problem if it does.
After a huge amount of time searching, I found the answer on my own. It turns out that my code wasn't the problem -- the problem was that my Info.plist file was having its CFBundleIdentifier value stripped out due to a problem with my build system. It appears that the Game Controller Framework needs the bundle identifier to correctly populate [GCController controllers] at launch. While a missing CFBundleIdentifier would have been a problem anyway, as a Windows person it didn't occur to me that the identifier might be used for things besides the App Store, so I let it slide until now.
If someone else has this problem, make sure that CFBundleIdentifier isn't missing or empty in Info.plist in your assembled app bundle. In my case with Premake, I had to manually set PRODUCT_BUNDLE_IDENTIFIER with xcodebuildsettings so that $(PRODUCT_BUNDLE_IDENTIFIER) would get properly replaced in Info.plist.
I make a little tool built on "Command Line Tool" project.
Everything works well except the Cursor in this tool.
Here is the launch code:
int main(int argc, const char * argv[]) {
NSApplication * app = [NSApplication sharedApplication];
MyDelegate * delegate = [[MyDelegate alloc] init];
app.delegate = delegate;
[app run];
return 0;
}
And in MyDelegate, I create window, view, etc. However I override the resetCursorRects function in the View, but nothing happened.
If the same code run in an Application project, everything will be OK.
All the thing I tried include:
Make a subclass from NSApplication and override run function to handle uesr event.
Make a NSTrackingArea in the view to update cursor.
Perform selector(run) on mainThread and wait until done.
However they didn't work at all.
Now all the reason I guessed is the function NSApplicationMain(argc, argv) is not equal with [app run].
What's the difference? Can any one help me?
PS: If I use NSApplicationMain(argc, argv), xcode will give me an error that I must have a bundle and Info.plist. I don't want create an application, because it's to fat.
A program which is not a bundled app is, by default, a background-only process. A bundled app is, by default, a foreground-capable process. (One can set the LSUIElement key in an app's Info.plist to change that.)
Only the active app can set the cursor. A background-only or UI element (a.k.a. accessory) process can't become the active app.
If you want your program to be able to be activated and control the cursor, you can use [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] to make it a foreground-capable app process. Note that this will make it appear in the Dock and Command-Tab application switcher (but with a generic-executable icon).
Of course, it's just easier and simpler to make your program a full-fledged bundled app.
I am writing an iOS app for the iPhone in Xcode and I have created some classes as well as their methods inside their respective .h and .m files (that's two classes so basically I have two pairs of .h & .m files)
I now I want to start writing my main loop that will be executed whenever the user hits on the play button, but where exactly do I do that ?
Do I do that in ViewController.m ? e.g. inside this method :
- (IBAction)playPressed:(UIButton *)sender
{
// main loop executed in here ?
// or simply message to the main loop to start executing is *sent* from here ?
}
I read about a similar question in here and someone was suggesting AppDelegate. Now would that be AppDelegate.m or AppDelegate.h ? And if that's the case do I just start writing code or do I include everything inside something like :
int main(int argc, char **argv)
{
....
}
in the Appdelegate file?
I tried to simply start instantiating classes and declaring generic methods (not belonging to any particular class that is..) in a game.m file I created and I get a initializer element is not a compile-time constant warning as soon as I try instantiating anything
Any help? Coming from c++ it would really help me to clarify once and for all in which file exactly to write my main loop and whether I should wrap it in some kind of an int main() function..
thanks!
PS :
Just in case it makes any difference, my ViewController will only consist of a play button that would start the execution of my main loop whenever its pressed, and a stop button that would terminate the execution of the main loop
I have created their respective methods in ViewController.m :
- (IBAction)playPressed:(UIButton *)sender
{
//
}
- (IBAction)stopPressed:(UIButton *)sender
{
// ??
}
which are for the time being empty :)
The programming methodoly on iOS is different from the C++ methodoly.
In C++ , indeed , you would have to make an infinite loop and get the touches , draw everything , etc at each frame.
Until the player presses "exit" and you break the loop.
On iOS , things are done differently:
You already have a main.m file in which you have a main function.
That starts up the app delegate. That app delegate tells you when the app finished launching , goes to background , comes in foreground , etc.
When the app finished launching , you go to your first actual screen.
There , you ADD subviews. You don't draw them at each frame. That is done automatically for you once you have added the view to a parent view.
The programming on iOS is based on events. You don't have to check for touches and see if the
touch location is on a button and then call the method of that button.
Instead , you set a callback method for the button and it's called automatically for you once the button is pressed.
Of course , you first need to alloc the button and add it to a parent view.
Once you get used to this event based programming model , you will for sure like it.
At the start it may seam very different and maybe it doesn't make sense to you , but don't worry.
Comming from a C++ background is surely a good start.
Cheers,
George
EDIT: In that case , I can give more specific info:
So , you go from the AppDelegate in your first screen. Let's call it MainAppScreen.
Now , you need to add those 2 buttons and set selectors ( callback methods ) for them. I can see you already did that.
Now :
- (IBAction)playPressed:(UIButton *)sender
{
running = TRUE;
[self performSelectorInBackground:#selector(myLoop) withObject:nil];
}
- (IBAction)stopPressed:(UIButton *)sender
{
running = FALSE;
}
- (void) myLoop
{
while(running)
{
//this is your loop. You can code in here.
}
}
Where running is an instance variable in the MainAppScreen class.
Hope this helps.
Cheers!
Every iOS app, as well as every executable file has an entry point - this is the main(). You can't have more than one entry points of an executable.And if you look closely into the project you will see that there is an automatically generated main.m file in the Supporting Files group in Xcode's navigator, which looks like this:
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
}
}
What you want to do is not clear enough, but it's a good start reading about the structure and the lifecycle of iOS apps, objective-c syntax, get familiar with the UIKit and at least some of the frameworks Apple provide.
You don't have a main in iOS apps (well, technically you do have a main, but you don't have to worry about writing it). That's all handled for you. The runloop is all done for you too. All you have to do is create your button and then tell it (via addTarget method) which method to run when it gets pressed.
Update:
This is pseudo(ish) code for what you'd need to do....
[startButton addTarget:#selector(startPressed:)];
[stopButton addTarget:#selector(stopPressed:)];
-(void)startPressed {
backgroundThread = [[NSThread alloc] initWithWhateverYouWantToRun];
[backgroundThread start];
}
-(void)stopPressed {
[backgroundThread stop];
}
In your background thread, if you want to update the UI, you would call sendMessageOnMainThread (or something similar - can't remember the exact details at the moment!)
In main function body, I need to call system(RELATIVE_PATH + /MyShellScript.sh). The script and other related stuff resides in main project bundle. How to get relative path? IMHO it should be possible to extract it from main parameter - "char *argv[]"...
What exactly are you trying to do? What is your shell script doing, and why are you calling it from main()? You should not normally need to modify the main() function in a Cocoa app.
Generally, you should avoid using a shell script unless absolutely necessary, but if you must then you should create an NSTask object and use that to execute your script. If you want to run it at launch, you should place your code to launch your script in the application delegate's applicationDidFinishLaunching: method.
You can get the path to resources inside your app's bundle using the various methods of NSBundle:
NSString* path = [[NSBundle mainBundle] pathForResource:#"MyShellScript" ofType:#"sh"];
int main (int argc, const char * argv[])
{
NSLog(#"%s", argv[0]);
return 0;
}
output
2012-01-24 21:33:22.015 CLIXMPLParser[13042:707] /Users/zrxq/Library/Developer/Xcode/DerivedData/CLIXMPLParser-dsibnvwgoqvjnvbhlxkyzcojqnwr/Build/Products/Debug/CLIXMPLParser
I'm writing an OS X Service with MacRuby. It upcases the selected text. It mostly works, but… well, here's all of it:
#!/usr/local/bin/macruby
# encoding: UTF-8
framework 'Foundation'
framework 'AppKit'
class KCUpcase
def upcase(pasteboard, userData: s_userdata, error: s_error)
incoming_string = pasteboard.stringForType "public.utf8-plain-text"
outgoing_string = incoming_string.upcase
pasteboard.clearContents
pasteboard.setString(outgoing_string, forType: "public.utf8-plain-text")
end
end
NSLog "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
NSLog "Registered…"
NSRunLoop.currentRunLoop\
.acceptInputForMode(NSDefaultRunLoopMode,
beforeDate:NSDate.dateWithTimeIntervalSinceNow(10.0))
NSLog "Done."
It's just a Foundation tool, not part of an Application.
Now, see the NSRunLoop… line? That doesn't really work. The program exits imediately. I suppose the loop runs once and then exits. Anyhoo, the fact is that it's definititely not waiting 10s for input. So, here's what I did instead:
NSRunLoop.currentRunLoop.runUntilDate NSDate.dateWithTimeIntervalSinceNow(60.0)
And that works, but naturally the program sticks around for 60s, and it's a kludge. So I implemented the whole thing in Objective C (Including KCUpcase, which is not shown). And… it works. With manual memory management. Once I switch to GC (-fobjc-gc-only), it exits imediately same as the MacRuby version.
#import <Foundation/Foundation.h>
#import "KCUpcase.h"
int main (int argc, const char * argv[]) {
NSLog(#"Starting…");
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
NSLog(#"Registered…");
[[NSRunLoop currentRunLoop]
acceptInputForMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(#"Done.");
return 0;
}
But, alas, the fix is easy: because this is a Foundation tool (not an NSApplication), it seems I have to start GC manually by calling objc_startCollectorThread. Here:
#import <objc/objc-auto.h>
// ...
NSLog(#"Starting…");
objc_startCollectorThread();
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
// ...
Ok, but what's up with MacRuby then? Let's throw it into the mix:
#import <MacRuby/MacRuby.h>
// ...
NSLog(#"Starting…");
objc_startCollectorThread(); // This magic stops working once we add MacRuby
[[MacRuby sharedRuntime] evaluateString: #"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
// ...
And, again, it's not waiting in the loop. And, again, ussing the runUntilDate: kludge instead of acceptInputForMode:beforeDate: works:
NSLog(#"Starting…");
[[MacRuby sharedRuntime] evaluateString: #"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
NSLog(#"Registered…");
// Hmmm…
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(#"Done.");
return 0;
So, I suppose I'm missing something terribly obvious. Please enlighten me.
And by the way, the full MacRuby version of the project is available here (download) with a Rake task that'll build and install it in ~/Library/Services. Then you need to enable its checkbox in Services in the Keyboard Preference Pane (once).
(or git clone git://gist.github.com/537075.git)
Aside: Interestingly, I tried calling NSLog inside the MacRuby string, and it raised NoMethodError. What gives?
It's a bit weird but here is a workaround:
framework 'Foundation'
framework 'AppKit'
class KCUpcase
def upcase(pasteboard, userData: s_userdata, error: s_error)
incoming_string = pasteboard.stringForType "public.utf8-plain-text"
outgoing_string = incoming_string.upcase
pasteboard.clearContents
pasteboard.setString(outgoing_string, forType: "public.utf8-plain-text")
end
end
puts "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
puts "Registered…"
later = NSDate.dateWithTimeIntervalSinceNow(5)
NSRunLoop.currentRunLoop.runUntilDate later
puts "Done"
Basically, you need to define the timestamp before you dispatch the runloop request otherwise the main loop exists before getting the instruction. As you spotted, this is not really a MacRuby bug but still, hopefully that helps.
acceptInputForMode:beforeDate: only runs the loop once. As soon as any input (other than a timer) is processed, it exits. runUntilDate: however continues running the loop until the date is reached.