2

I have been trying to figure out how to create an OS X routine in objective C that:

  • run in the background (separate thread) and

  • repeatedly (timed or when certain criteria are met) captures several defined subsets of a specified window/screen (i.e. without user interaction) and

  • save the images into a specified path.

Similar the output of a "pre-programmed loop of cmd-shift-4 + mouseDown-drag".

Neither SonofGrab nor ScreenSnapshot from Mac Developer Library seem to indicate how this could be done, but I suspect CGImageRef is one/the(?) way to go about it.

  1. Does anyone know how to do it?

  2. Are there other possible ways besides CGImageRef to solve this problem? Could it for example be done via command-line tools and NSTask?

  3. If so, what are the advantages/disadvantages with the different methods in terms of speed and complexity?

userjuicer
  • 123
  • 2
  • 9

1 Answers1

2

Assuming you have the logic required to do a screengrab (which you can see in SonOfGrab), you just use a dispatch queue or a timer to do the job.

void MyTakeScreengrab();

dispatch_queue_t  queue    = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,  0);

dispatch_source_t timerSrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0,queue);

dispatch_source_set_timer(timerSrc, 
      dispatch_time(DISPATCH_TIME_NOW,0) /* start on */,
      SECONDS_PER_GRAB * NSEC_PER_SEC /* interval */, 
      NSEC_PER_SEC /* leeway */);

dispatch_source_set_event_handler_f(timerSrc, MyTakeScreengrab);

dispatch_resume(timerSrc);

You can use an NSTimer as well.

-(void)setup {
    _timer = [[NSTimer scheduledTimerWithTimeInterval: (NSTimerInterval)SECONDS_PER_GRAB 
            target: self 
            selector: @selector(takeScreegrabOnBGThread:) 
            userInfo: @{ @"folderPath" : MyFolderPath(), 
                         @"imageSettings" : MYCGImageSettings() }  
            repeats: YES];
    [_timer fire];
}

-(void)takeScreengrabOnBGThread:(id)userInfo {
    [self performSelectorInBackground:@selector(takeScreengrabBlocking:) 
            withObject:userInfo];
}

-(void)takeScreengrabBlocking:(id)userInfo {

    MyTakeScreengab(userInfo);
}

To use the terminal to do this, the command you're looking for is screencapture(1).

iluvcapra
  • 9,436
  • 2
  • 30
  • 32
  • Thanks, I can use that. HOWEVER, the big question is how do you get a screenshot of lets say AXPosition x=193.00 y=365.00; AXSize w=492.00 h=63.00? As far as I can tell SonOfGrab only provide you with full screenshot (cmd-shift-3) or 'windowshot' (cmd-shift-4 + spacebar). – userjuicer Feb 08 '13 at 01:35
  • 1
    If you want a part of the screen, you just use `CGDisplayCreateImageForRect()` – iluvcapra Feb 08 '13 at 02:05
  • If you were hellbent to avoid using `CGImageRef`s at all costs (USE THEM!!), you could create a grab of the entire screen with `screengrab(1)`, open it with the `NSImage` API and then crop it using a bitmap graphics context. yuck. – iluvcapra Feb 08 '13 at 02:36
  • It seems like CGDisplayCreateImageForRect() might be the way to go. I have no desire in grabbing more of the screen than I need. It will just slow down things. Is it possible to set the (x,y) in relation to a specific window instead of the screen? – userjuicer Feb 08 '13 at 03:27
  • Also, would you say using `CGDisplayCreateImageForRect()` would result in a faster routine than Unix command-line tools and NSTask? – userjuicer Feb 08 '13 at 03:33
  • 1
    To grab just a window you would use `CGWindowListCreateImage()` -- that still requires screen coordinates, but to transform NSWindow-relative coordinates to screen coordinates you just use `-[NSWindow convertRectToScreen:]` – iluvcapra Feb 08 '13 at 16:32