[futurebasic] Re: [FB] Line Input

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : November 2016 : Group Archive : Group : All Groups

From: Bernie <bernie.fblist@...>
Date: Sat, 5 Nov 2016 14:53:04 +0000
Brian wrote:

> On Nov 4, 2016, at 11:02 AM, Brian S <fblistserve@...> wrote:
>> 
>>> Brian wrote: For Bernie’s demo you would need to use the callback ( Bernie’s outputAvailable: method passes the data to the callback ).
>> 
>> Apparently outputCallback is not populated ( even if sent with the original BackgroundTaskRun() call which I modified to send a function name )
> 
> Okay, after review,  Bernie’s code works fine and my previous assertion of outputCallback failure is entirely incorrect. My apologies for the error.
> 
> Change Bernie’s call from:
> 
> BackgroundTaskRun( command, @fn MyTaskCompletedCallback, NULL, NULL, NULL )
> 
> to:
> 
> BackgroundTaskRun( command, @fn MyTaskCompletedCallback, NULL, @fn MyTaskCompletedCallback, NULL )
> 
> In your own code it would be preferable to use a different callback fn for the output callback but the idea is the same. The output would be found in the ’string’ variable in that function. 
> 
> btw: The 'ls -a' command was used for testing since it runs on any OS X release. 

As noted earlier, suggest removing the CFRelease in the callback.

// ---------------
include "NSLog.incl"

BeginCDeclaration
typedef void (*BackgroundTaskCallbackType)(void*,CFStringRef,void*);

@interface BackgroundTask : NSObject {
     NSTask *executionTask;
     NSObject *taskObserver;
     NSFileHandle *errorFile;
     NSObject *errorObserver;
     NSFileHandle *outputFile;
     NSObject *outputObserver;

     BackgroundTaskCallbackType completedCallback;
     BackgroundTaskCallbackType errorCallback;
     BackgroundTaskCallbackType outputCallback;
     void *userData;
}

@property (assign) NSTask *executionTask;
@property (assign) BackgroundTaskCallbackType completedCallback;
@property (assign) BackgroundTaskCallbackType errorCallback;
@property (assign) BackgroundTaskCallbackType outputCallback;
@property (assign) void *userData;

- (id)initWithCompletedCallback:(void *)fcb errorCallback:(void *)ecb outputCallback:(void *)ocb userData:(void *)ud;
- (void)runCommand:(NSString *)command;
- (void)terminate;
- (BOOL)isRunning;
@end

BackgroundTask *BackgroundTaskRun( CFStringRef command, void *completedCallback, void *errorCallback, void *outputCallback, void *userData );
void BackgroundTaskTerminate( BackgroundTask *bgt );
BOOL BackgroundTaskIsRunning( BackgroundTask *bgt );
EndC

BeginCFunction
@implementation BackgroundTask

@synthesize executionTask;
@synthesize completedCallback, errorCallback, outputCallback, userData;

- (id)initWithCompletedCallback:(void *)fcb errorCallback:(void *)ecb outputCallback:(void *)ocb userData:(void *)ud {
     if ( self = [super init] ) {
          self.completedCallback = fcb;
          self.errorCallback = ecb;
          self.outputCallback = ocb;
          self.userData = ud;
     }
     return self;
}

- (void)taskCompleted {
     if ( completedCallback != nil ) (*completedCallback)( self, CFSTR("Task completed."), userData );
     [[NSNotificationCenter defaultCenter] removeObserver:taskObserver];
     [[NSNotificationCenter defaultCenter] removeObserver:errorObserver];
     [[NSNotificationCenter defaultCenter] removeObserver:outputObserver];
     [self outputAvailable];
     [self errorAvailable];
     executionTask = nil;
}

- (void)errorAvailable {
     NSData *someData = [errorFile readDataToEndOfFile];
     if ([someData length] > 0) {
          if ( errorCallback != nil ) {
               NSString *string = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
               (*errorCallback)( self, (CFStringRef)string, userData );
               [string release];
          }
    }
}

- (BOOL)outputAvailable {
     NSData *someData = [outputFile availableData];
     if ([someData length] > 0) {
          if ( outputCallback != nil ) {
               NSString *string = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
               (*outputCallback)( self, (CFStringRef)string, userData );
               [string release];
          }
          return YES; 
     }
     return NO; 
} 

- (void)runCommand:(NSString *)command {
     // some convenience vars
     NSArray *runLoopModes = @[NSDefaultRunLoopMode, NSRunLoopCommonModes];
     NSNotificationCenter *defCenter = [NSNotificationCenter defaultCenter];

     // create task
     executionTask = [[NSTask alloc] init];

     // fill parameters for task
     [executionTask setLaunchPath:@"/bin/sh"];
     [executionTask setArguments:@[@"-c",command]];

     // create observer for termination
     taskObserver = [defCenter addObserverForName:NSTaskDidTerminateNotification
                                           object:executionTask 
                                            queue:[NSOperationQueue mainQueue]
                                       usingBlock:^(NSNotification *note) {
                         [self taskCompleted];        
                    }];

     // create pipe and filehandle for reading errors
     NSPipe *error = [NSPipe pipe];
     [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 pipe and filehandle for reading output
     NSPipe *output = [NSPipe pipe];
     [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];
}

- (void)terminate {
     [self.executionTask terminate];
}

- (BOOL)isRunning {
     return [self.executionTask isRunning];
}
@end

BackgroundTask *BackgroundTaskRun( CFStringRef command, void *completedCallback, void *errorCallback, void *outputCallback, void *userData )
{
     BackgroundTask *bgt = [[[BackgroundTask alloc] initWithCompletedCallback:completedCallback errorCallback:errorCallback outputCallback:outputCallback userData:userData] autorelease];
     [bgt runCommand:(NSString *)command];
     return bgt;
}

void BackgroundTaskTerminate( BackgroundTask *bgt )
{ [bgt terminate]; }

BOOL BackgroundTaskIsRunning( BackgroundTask *bgt )
{ return [bgt isRunning]; }
EndC

toolbox fn BackgroundTaskRun( CFStringRef command, ptr completedCallback, ptr errorCallback, ptr outputCallback, ptr userData ) = ptr
toolbox BackgroundTaskTerminate( ptr bgt )
toolbox fn BackgroundTaskIsRunning( ptr bgt ) = Boolean

_wBackgroundTaskDemo = 1
begin enum 1
_cStartTaskBtn
_cDoSomethingBtn
end enum

void local fn MyTaskCompletedCallback( bgt as ptr, string as CFStringRef, userData as ptr )
'~'1
NSLog(@"%@",string)
end fn

void local fn MyTaskOutputCallback( bgt as ptr, string as CFStringRef, userData as ptr )
'~'1
NSLog(@"%@",string)
end fn

void local fn BuildWindow
'~'1
dim as Rect   r

SetRect( r, 0, 0, 480, 360 )
appearance window -_wBackgroundTaskDemo, @"BackgroundTask Demo", @r, _kDocumentWindowClass, _kWindowCompositingAttribute
fn SetWindowBackground( _kThemeActiveDialogBackgroundBrush, _zTrue )

SetRect( r, 181, 136, 299, 156 )
appearance button _cStartTaskBtn,,,,, @"Start Task", @r, _kControlPushButtonProc

SetRect( r, 181, 231, 299, 251 )
appearance button _cDoSomethingBtn,,,,, @"Do Something", @r, _kControlPushButtonProc

window _wBackgroundTaskDemo
end fn

void local fn DoDialog
'~'1
dim as long          ev, id
dim as CFStringRef   command

ev = dialog( 0 )
id = dialog( ev )
select ( ev )
case _btnClick
select ( window(_outputWnd) )
case _wBackgroundTaskDemo
select ( id )
case _cStartTaskBtn
//command = @"log show --predicate 'subsystem == \"com.apple.TimeMachine\"' --info | grep 'upd: (' | cut -c 1-19,140-999"
command = @"ls"
fn BackgroundTaskRun( command, @fn MyTaskCompletedCallback, NULL, @fn MyTaskOutputCallback, NULL )
NSLog(@"Task is running in background.")

case _cDoSomethingBtn
NSLog(@"Do something.")

end select
end select
end select
end fn

fn BuildWindow()

on dialog fn DoDialog

RunApplicationEventLoop()