Mac OS cocoa app call command line tool fail in 10.15 - xcode

I want to create a cocoa app to call command line tool written use c++.
I can do it well when use Xcode to call command line tool.
but It fail when I close Xcode, double click the app and press Update button to
run command line tool.
I've tired to use NSTask to call command line tool but it still fail.
These is my code.
- (IBAction)Update:(id)sender {
[self performSelectorOnMainThread:#selector(IspUpdate) withObject:nil waitUntilDone:YES];
}
-(void)IspUpdate {
strCurDir = [[NSBundle mainBundle] bundlePath];
NSRange range = [strCurDir rangeOfString:#"/" options:NSBackwardsSearch];
NSRange rangeDir = NSMakeRange(0, range.location + 1);
strCurDir = [strCurDir substringWithRange:rangeDir];
NSString *strCmd = [strCurDir stringByAppendingString:#"ISPTool --isp --fw fw.bin --comm 6"];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
system([strCmd UTF8String]);
});
}

Please run this demo and see if it is similar to what you are trying to do. The code will allow you to run NSTask in the app's terminal window by clicking a button. Save the following code in a file called 'runCmd.m' and then compile from the command line using the instructions given below:
/*
Run from Terminal using: clang runCmd.m -fobjc-arc -framework Cocoa -o runCmd && ./runCmd
Should print current calendar when 'RunCommand' button is hit.
*/
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
-(void) runMyCmd;
-(void) buildMenu;
-(void) buildWindow;
#end
#implementation AppDelegate
- (void) runMyCmd {
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *args = [NSArray arrayWithObjects: #"-c", #"cal", nil];
[task setArguments: args];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
[task launch];
[task waitUntilExit];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
NSString *string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog (#"\n%#",string);
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [NSMenu new];
NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWindow {
#define _wndW 200
#define _wndH 150
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable
backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Test window"];
[window makeKeyAndOrderFront: nil];
// **** RunCmdButton **** //
NSButton *runBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, 60, 135, 30 )];
[runBtn setBezelStyle:NSBezelStyleRounded ];
[runBtn setTitle: #"RunCommand"];
[runBtn setAction: #selector(runMyCmd)];
[[window contentView] addSubview: runBtn];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 5, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAutoresizingMask: NSViewMinXMargin];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWindow];
}
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
#end
int main () {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}

Related

OSX Cocoa cursor in resources

ALL,
On Windows cursors are indicated by the cur file extension. Is there something similar for OSX Cocoa application?
Is there a way in XCode to edit such files, just like in MSVC there is some basic graphical editor?
Also, is there a special API which will allow loading such files on Cocoa? Or I can just load them as images?
TIA!!
EDIT:
In the meantime I think I found a good solution that will satisfy my needs
NSString *path;
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:cursor_file ofType:#"cur"];
if( path )
{
CGImageSourceRef image = CGImageSourceCreateWithURL( path, nil );
CFDictionaryRef properties =
CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
NSInteger x = properties["hotspotX"];
NSInteger y = properties["hotspotY"];
}
Except that it doesn't compile:
I need to import the file where CGImageSourceCreateWithURL() is declared, and I need to fix the last 2 lines, because the array subscript can't be strings.
Could someone help, please?
The following source code will create a custom NSCursor in Xcode from a .cur image, after retrieving its “hotspot” coordinates from the file located in the app bundle Resource folder. Replace the contents of the main.m file with the code below and delete the pre-supplied AppDelegate files to avoid duplicate symbols. You will need to copy/paste the .cur file into the Xcode project folder and also drag and drop it into the Project navigator. Xcode will not automatically place this file into the Resources folder of your application bundle (unlike a .png file), so copy/paste it there as well after you have compiled the app. Reference: Hotspot in Windows cursor .cur loaded by NSImage?
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
CGFloat x,y;
}
#end
#implementation AppDelegate
- (void) customCursor {
// **** Get HotSpot from .cur file **** //
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForImageResource:#"myCursor.cur"];
if( url ) {
CGImageSourceRef image = CGImageSourceCreateWithURL( (__bridge CFURLRef)url, nil );
NSDictionary *properties = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
x = [[properties objectForKey:#"hotspotX"]floatValue];
y = [[properties objectForKey:#"hotspotY"]floatValue];
CFBridgingRelease(image);
CFBridgingRelease((__bridge CFTypeRef _Nullable)(properties));
}
NSCursor *customCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:#"myCursor.cur"] hotSpot: NSMakePoint( x, y) ];
[customCursor set];
NSLog(#"x = %0.02f",x);
NSLog(#"y = %0.02f",y);
}
- (void) arrowCursor {
[[NSCursor arrowCursor] set];
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
NSMenu *appMenu = [NSMenu new];
[menuBarItem setSubmenu:appMenu];
[appMenu addItemWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
}
- (void) buildWnd {
#define _wndW 500
#define _wndH 250
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH ) styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Custom cursor"];
[window makeKeyAndOrderFront: nil];
// **** Button 1 **** //
NSButton *myBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, 30, 135, 30 )];
[myBtn setBezelStyle:NSBezelStyleRounded ];
[myBtn setTitle: #"Custom Cursor"];
[myBtn setAction: #selector (customCursor)];
[[window contentView] addSubview: myBtn];
// **** Button 2 **** //
NSButton *myBtn2 =[[NSButton alloc]initWithFrame:NSMakeRect( 190, 30, 135, 30 )];
[myBtn2 setBezelStyle:NSBezelStyleRounded ];
[myBtn2 setTitle: #"Arrow Cursor"];
[myBtn2 setAction: #selector (arrowCursor)];
[[window contentView] addSubview: myBtn2];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWnd];
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}

OSX App, NSTextField only responds to Force Click

I am writing my first OS X app and I am running into a problem. I am placing a NSTextField inside of a NSTableViewCell. A single click in the text field does nothing. A force click will, however, activate the textfield to enter text.
Is this because it's embedded in a cell?
So far my code is very simple:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:self.bounds];
mainTableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 1200, self.frame.size.height)];
mainTableView.autoresizingMask = NSViewWidthSizable|NSViewHeightSizable;
/// {creating columns here}
[tableContainer addSubview:mainTableView];
mainTableView.backgroundColor = [NSColor whiteColor];
mainTableView.rowHeight = 25;
[mainTableView setDelegate:self];
[mainTableView setDataSource:self];
[tableContainer setDocumentView:mainTableView];
[tableContainer setHasVerticalScroller:YES];
[self addSubview:tableContainer];
[mainTableView reloadData];
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
NSView *view = [[NSView alloc] initWithFrame:CGRectMake(0, 0, tableColumn.width, tableView.rowHeight)];
NSTextField *tf = [[NSTextField alloc] initWithFrame:view.bounds];
tf.stringValue = #"!";
tf.editable = YES;
tf.delegate = self;
[view addSubview:tf];
return view;
}

addSublayer over video preview in OSX

I'm trying to add a simple text label over a video preview. The video preview works fine, but the text label is no where to be found. The compiler isn't giving me any errors or warnings either. I know I'm mixing dot notation in there, but I'm not sure if that's my root cause or not.
This is a clip from my .m file:
#interface AVRecorderDocument ()
#property (retain) AVCaptureVideoPreviewLayer *previewLayer;
#property (nonatomic, strong) NSTextField *labelVideoOverlay;
#end
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
CALayer *previewViewLayer = [[self previewView] layer];
self.labelVideoOverlay = [[NSTextField alloc] initWithFrame:CGRectMake(100, 120, 200, 40)];
self.labelVideoOverlay.stringValue = #"TEST";
self.labelVideoOverlay.backgroundColor = [NSColor blackColor];
self.labelVideoOverlay.textColor = [NSColor whiteColor ];
[previewViewLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
AVCaptureVideoPreviewLayer *newPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self session]];
[newPreviewLayer setFrame:[previewViewLayer bounds]];
[newPreviewLayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
[previewViewLayer addSublayer: self.labelVideoOverlay.layer];
[previewViewLayer addSublayer:newPreviewLayer];
[self setPreviewLayer:newPreviewLayer];
[newPreviewLayer release];
}
It looks like although you've added the video preview as a sublayer, you still need to add the overlay as a subview to your main view. I did something similar like this recently (just with an UIImageView), but the principal should still be the same.
self.overlayImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"overlaygraphic.png"]];
self.overlayImageView.translatesAutoresizingMaskIntoConstraints = NO;
[[self view] addSubview:self.overlayImageView];
So, you could do something like:
self.labelVideoOverlay = [[NSTextField alloc] initWithFrame:CGRectMake(100, 120, 200, 40)];
self.labelVideoOverlay.stringValue = #"TEST";
self.labelVideoOverlay.backgroundColor = [NSColor blackColor];
self.labelVideoOverlay.textColor = [NSColor whiteColor ];
[[self view] addSubview:self.labelVideoOverlay];

How can I run a NSTask in the background and show the results in a modal NSWindow while running

I would like to execute a command with NSTask, and be able to see the progress in a modal window. For example if I execute 'ls -R /' i would like to see the chunks appearing in a NSTextView.
I came up with the following, and everything works fine, except the update part. The task get executed (with the spinning beachbal) and when it is finished i see the result appear in the textview.
#interface ICA_RunWindowController ()
#property (strong) IBOutlet NSTextView* textResult;
#property (strong) IBOutlet NSButton* buttonAbort;
#property (strong) IBOutlet NSButton* buttonOK;
- (IBAction) doOK:(id) sender;
- (IBAction) doAbort:(id) sender;
#end
#implementation ICA_RunWindowController {
NSTask * executionTask;
id taskObserver;
NSFileHandle * errorFile;
id errorObserver;
NSFileHandle * outputFile;
id outputObserver;
}
#synthesize textResult,buttonAbort,buttonOK;
- (IBAction)doOK:(id)sender {
[[self window] close];
[NSApp stopModal];
}
- (IBAction)doAbort:(id)sender {
[executionTask terminate];
}
- (void) taskCompleted {
NSLog(#"Task completed");
[[NSNotificationCenter defaultCenter] removeObserver:taskObserver];
[[NSNotificationCenter defaultCenter] removeObserver:errorObserver];
[[NSNotificationCenter defaultCenter] removeObserver:outputObserver];
[self outputAvailable];
[self errorAvailable];
executionTask = nil;
[buttonAbort setEnabled:NO];
[buttonOK setEnabled:YES];
}
- (void) appendText:(NSString *) text inColor:(NSColor *) textColor {
NSDictionary * makeUp = [NSDictionary dictionaryWithObject:textColor forKey:NSForegroundColorAttributeName];
NSAttributedString * extraText = [[NSAttributedString alloc] initWithString:text attributes:makeUp];
[textResult setEditable:YES];
[textResult setSelectedRange:NSMakeRange([[textResult textStorage] length], 0)];
[textResult insertText:extraText];
[textResult setEditable:NO];
[textResult display];
}
- (void) outputAvailable {
NSData * someData = [outputFile readDataToEndOfFile];
if ([someData length] > 0) {
NSLog(#"output Available");
NSString * someText = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
[self appendText:someText inColor:[NSColor blackColor]];
}
}
- (void) errorAvailable {
NSData * someData = [errorFile readDataToEndOfFile];
if ([someData length] > 0) {
NSLog(#"Error Available");
NSString * someText = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
[self appendText:someText inColor:[NSColor redColor]];
}
}
- (void) runCommand:(NSString *) command {
// make sure all views are initialized
[self showWindow:[self window]];
// some convience vars
NSArray * runLoopModes = #[NSDefaultRunLoopMode, NSRunLoopCommonModes];
NSNotificationCenter * defCenter = [NSNotificationCenter defaultCenter];
// create an task
executionTask = [[NSTask alloc] init];
// fill the parameters for the task
[executionTask setLaunchPath:#"/bin/sh"];
[executionTask setArguments:#[#"-c",command]];
// create an observer for Termination
taskObserver = [defCenter addObserverForName:NSTaskDidTerminateNotification
object:executionTask
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self taskCompleted];
}
];
// Create a pipe and a filehandle for reading errors
NSPipe * error = [[NSPipe alloc] init];
[executionTask setStandardError:error];
errorFile = [error fileHandleForReading];
errorObserver = [defCenter addObserverForName:NSFileHandleDataAvailableNotification
object:errorFile
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self errorAvailable];
[errorFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
}
];
[errorFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
// Create a pipe and a filehandle for reading output
NSPipe * output = [[NSPipe alloc] init];
[executionTask setStandardOutput:output];
outputFile = [output fileHandleForReading];
outputObserver = [defCenter addObserverForName:NSFileHandleDataAvailableNotification
object:outputFile
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self outputAvailable];
[outputFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
}
];
[outputFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
// start task
[executionTask launch];
// show our window as modal
[NSApp runModalForWindow:[self window]];
}
My question: Is it possible to update the output while the task is running? And, if yes, how could I achieve that?
A modal window runs the run loop in NSModalPanelRunLoopMode, so you need to add that to your runLoopModes.
You should not be getting the spinning beach ball. The cause is that you're calling -readDataToEndOfFile in your -outputAvailable and -errorAvailable methods. Given that you're using -waitForDataInBackgroundAndNotifyForModes:, you would use the -availableData method to get what data is available without blocking.
Alternatively, you could use -readInBackgroundAndNotifyForModes:, monitor the NSFileHandleReadCompletionNotification notification, and, in your handler, obtain the data from the notification object using [[note userInfo] objectForKey:NSFileHandleNotificationDataItem]. In other words, let NSFileHandle do the work of reading the data for you.
Either way, though, once you get the end-of-file indicator (an empty NSData), you should not re-issue the ...InBackgroundAndNotifyForModes: call. If you do, you'll busy-spin as it keeps feeding you the same end-of-file indicator over and over.
It shouldn't be necessary to manually -display your text view. Once you fix the blocking calls that were causing the spinning color wheel cursor, that will also allow the normal window updating to happen automatically.

Creating an OSX application without Xcode

I'm very new to OS X, and I'm trying to create a simple application without Xcode. I did found some other sites doing that, but I cannot attach event handlers to my button.
below is the code (crafted from other sites). It creates a window and a button, but I don't know how to attach that event to the button:
#import <Cocoa/Cocoa.h>
#interface myclass
-(void)buttonPressed;
#end
#implementation myclass
-(void)buttonPressed {
NSLog(#"Button pressed!");
//Do what You want here...
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Hi there."];
[alert runModal];
}
#end
int main ()
{
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [#"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
action:#selector(terminate:) keyEquivalent:#"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle:appName];
[window makeKeyAndOrderFront:nil];
int x = 10;
int y = 100;
int width = 130;
int height = 40;
NSButton *myButton = [[[NSButton alloc] initWithFrame:NSMakeRect(x, y, width, height)] autorelease];
[[window contentView] addSubview: myButton];
[myButton setTitle: #"Button title!"];
[myButton setButtonType:NSMomentaryLightButton]; //Set what type button You want
[myButton setBezelStyle:NSRoundedBezelStyle]; //Set what style You want
[myButton setAction:#selector(buttonPressed)];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}
First of all, don't avoid Xcode because you are a beginner. Being a beginner is one of the many reasons to use Xcode. Resorting to fully-manually-implemented code like what you have is a naive way to develop applications for OS X and you'll only encounter far more difficulties than it is worth, especially for anything non-trivial.
Having said that, the reason your button isn't doing anything is because the button doesn't have a target. All actions require a target. In your case, you want to create an instance of your myclass class (note that class names in Objective-C are conventionally named in upper camelcase, i.e. MyClass). Note that also your action method should take an argument (which is the sender of the action), even if it is unused.
- (void) buttonPressed:(id) sender
{
NSLog(#"Button pressed!");
//Do what You want here...
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Hi there."];
[alert runModal];
}
// ...
myclass *mc = [[myclass alloc] init];
[myButton setTarget:mc];
[myButton setAction:#selector(buttonPressed:)];
I can't stress enough how ridiculous all of this code is. Bite the bullet and dive into Xcode!

Resources