Screenshots on Mac OX X 10.7 Lion lazarus / 39-Graphics ---------------------------------------------------------------------- tristan_2468 · 2012-05-17 09:45 Hi, I have been using the following code to take screenshots of my app window for the last few years, with no problems. With Mac OS X 10.7 Lion however, the resulting image is all black. procedure TfrmMain.CaptureScreenshot(var Image: TBitmap); var   BMP: TBitmap;   ScreenDC: HDC; begin   BMP := TBitmap.Create;   ScreenDC := GetDC(0);   BMP.LoadFromDevice(ScreenDC);   ReleaseDC(0, ScreenDC);   Image.SetSize(Self.Width, Self.Height);   Image.Canvas.CopyRect(Rect(0, 0, Self.Width - 1, Self.Height - 1), BMP.Canvas,     Rect(Self.Left, Self.Top, Self.Left + Self.Width - 1, Self.Top + Self.Height - 1 + 22));   BMP.Free; end; It is the example code from the Lazarus Wiki on taking screenshots. I have reported in the bug here: http://bugs.freepascal.org/view.php?id=21845 and it was mentioned that Apple broke this ability with 10.7, and that I can use LCL-CustomDrawn-Cocoa or a "new [method] which works only in 10.6+" I was wondering if anyone had managed to get this to work and could provide me with some example code or pointers where to find the information for the technique. I would prefer to keep my app compiled against the Carbon widgetset if I can. Thank you, Tristan ---------------------------------------------------------------------- Shebuka · 2012-05-17 10:14 I can confirm this (i'm using this for a while, but never noticed that on Lion it's not working...) ---------------------------------------------------------------------- jwdietrich · 2012-05-17 12:11 I think this is the result of rigid security measures that were introduced with Mac OS 10.7. Obviously, screen access has been blocked by Lion's sandboxing mechanism. ---------------------------------------------------------------------- felipemdc · 2012-05-17 13:59 Quote from: jwdietrich on May 17, 2012, 02:11:23 pmI think this is the result of rigid security measures that were introduced with Mac OS 10.7. Obviously, screen access has been blocked by Lion's sandboxing mechanism. Screen access is not blocked, they just changed the APIs which do it. The old way stopped working and the new one works only in 10.6 and this is the nasty part =( Anyway, LCL-CustomDrawn-Cocoa shows how to use the new way. ---------------------------------------------------------------------- tristan_2468 · 2012-05-17 14:27 Quote from: felipemdc on May 17, 2012, 03:59:14 pmAnyway, LCL- CustomDrawn-Cocoa shows how to use the new way. Could you please give me a link to a code example of capturing the screen? Thanks, Tristan ---------------------------------------------------------------------- felipemdc · 2012-05-17 14:55 Quote from: tristan_2468 on May 17, 2012, 04:27:02 pmCould you please give me a link to a code example of capturing the screen? Just read the Lazarus source code, here: http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/lcl/interfaces/cust omdrawn/customdrawnlclintf_cocoa.inc?view=markup&root=lazarus Line  164 function TCDWidgetSet.RawImage_FromDevice(out ARawImage: TRawImage; ADC: HDC; const ARect: TRect): Boolean; There is a large comment right before this line which explains the process. After it there is the code of how to do it using CoreGraphic APIs Patches for LCL-Carbon to implement this are welcome. It would need to detect the Mac OS X version and dynamically load the routine CGDisplayCreateImage. In Mac OS X <= 10.5 it should use the current method. ---------------------------------------------------------------------- Shebuka · 2013-07-10 08:08 Resuming this old problem, was it patched? I'm trying to implement this but having trouble with dynamically load the CGDisplayCreateImage routine... ---------------------------------------------------------------------- Hansaplast · 2013-07-15 21:35 +1 here. Only getting a black image (looking into the suggested code from link now.) ---------------------------------------------------------------------- Hansaplast · 2013-10-03 17:15 Does anybody know if there is an update on this situation? ---------------------------------------------------------------------- BigChimp · 2013-10-04 07:43 Well, nobody posted here, there's no link to a bug report, so my guess would be a resounding NO. ---------------------------------------------------------------------- Hansaplast · 2013-10-04 17:50 Thanks BigChimp. I did search the forum and used Google for other sources: no progress. The link to the bug is not showing any progress either. I just wasn't sure if I overlooked something. I haven't been able to get the suggested approach to work (my lack of knowledge I'm sure) either. Thanks again though! ---------------------------------------------------------------------- Shebuka · 2014-04-17 10:06 Now we finally target 10.6 as minimum OS version so here it is: Code: [Select]uses   LCLIntf, GraphType, IntfGraphics, CGContext, CGImage, CGGeometry, CGDirectDisplay, CGBitmapContext, CGColorSpace, procedure TGuiMgr.desktop_screen_grab_mnu_mnuClick(Sender: TObject); var   MyBitmap: TBitmap;   //BMPHandle, BMPMaskHandle: HBITMAP;   //ScreenDC: HDC;   WrkJpg: TJpegImage;   ReceiverDirPath, ScreenPathName: String;   screenBitmapRawImage: TRawImage;   monRect: TRect;   screenBitmapWidth, screenBitmapHeight: Integer;   screenBitmapContext: CGContextRef;   screenshotImage: CGImageRef;   lRect: CGRect;   screenImage: TLazIntfImage;   lScreenRawImage: TRawImage;   lDataLength: SizeInt; begin   try        ReceiverDirPath := TrimFilename(GetUserDir + PathDelim + 'Downloads' + PathDelim + 'my_app_name');       if not DirectoryExists(ReceiverDirPath) then         ForceDirectories(ReceiverDirPath);       ScreenPathName := ReceiverDirPath + PathDelim + 'Screen-' + UT_CondEventTime() + '[' + IntToStr(Random(899)+100) + '].jpg';    // UT_CondEventTime() returns string with current date       Application.Minimize;       Application.ProcessMessages;       Sleep(32);       monRect := Screen.WorkareaRect;       screenBitmapWidth := monRect.Right;       screenBitmapHeight := monRect.Bottom;       screenBitmapRawImage.Init;       screenBitmapRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(scr eenBitmapWidth, screenBitmapHeight);       // Take the screenshot       screenshotImage := CGDisplayCreateImage(CGMainDisplayID()); // Requires 10.6+       // Draw it to our screen bitmap       lRect := CGRectMake(0, 0, screenBitmapWidth, screenBitmapHeight);       screenBitmapRawImage.CreateData(True);       screenBitmapContext := CGBitmapContextCreate(screenBitmapRawImage.Data,                                                    screenBitmapWidth,                                                    screenBitmapHeight,                                                    8,                                                    ScreenBitmapWidth * 4,                                                    CGColorSpaceCreateDeviceRGB(),                                                    kCGImageAlphaNoneSkipFirst//,kCGImageAlphaPremultipliedFirst                                                    );       screenImage := TLazIntfImage.Create(screenBitmapRawImage, True);       CGContextDrawImage(screenBitmapContext, lRect, screenshotImage);       // Now copy the data       screenImage.GetRawImage(lScreenRawImage, False);       lDataLength := Min(Int64(lScreenRawImage.DataSize), Int64(screenBitmapRawImage.DataSize));       System.Move(lScreenRawImage.Data^, screenBitmapRawImage.Data^, lDataLength);       MyBitmap := TBitmap.Create;       MyBitmap.LoadFromRawImage(screenBitmapRawImage, true);       Application.Restore;       Application.BringToFront;       // Per avere un elenco dei vari formati disponibili (png, bmp, xmp, ecc.):       // http://wiki.lazarus.freepascal.org/Developing_with_Graphics#I mage_formats       WrkJpg := TJpegImage.Create;       try         WrkJpg.CompressionQuality := 80;         WrkJpg.Assign(MyBitmap);         WrkJpg.SaveToFile(Utf8ToSys(ScreenPathName));       finally         FreeAndNil(WrkJpg);       end;       FreeAndNil(MyBitmap);       //FreeAndNil(screenImage);              //This is commented for a reason, don't uncomment it       //FreeAndNil(screenBitmapContext);      //This is commented for a reason, don't uncomment it       //FreeAndNil(screenBitmapRawImage);     //This is commented for a reason, don't uncomment it       //FreeAndNil(screenshotImage);          //This is commented for a reason, don't uncomment it   except     on e: Exception do       SendLogMsg('TGuiMgr.desktop_screen_grab_mnu_mnuClick => raised: ' + e.Message, LOG_ERROR, LOG_CTX_GUI);   end; end; Comments and suggestions with improvements are welcome ---------------------------------------------------------------------- skalogryz · 2014-04-17 10:53 sleep(32)? ---------------------------------------------------------------------- Shebuka · 2014-04-17 10:57 Quote from: skalogryz on April 17, 2014, 12:53:32 pmsleep(32)? It's time for minimize animation to execute, without it, depending on machine load, i get at random screenshots with application in foreground. ---------------------------------------------------------------------- taazz · 2014-04-17 10:59 isn't this the opposite of what the OP wanted ee he wants a screen shot of his application not the desktop. ---------------------------------------------------------------------- skalogryz · 2014-04-17 11:06 Quote from: taazz on April 17, 2014, 12:59:50 pmisn't this the opposite of what the OP wanted ee he wants a screen shot of his application not the desktop. Tricky question: what if an application has mulitple windows? Tricky answer: loop through wach of the form, and cut the screenshot by bounding rect. Whatever pixel doesn't belong to the application can be changed to white with 0 alpha-channel The next tricky question: multiple monitors? 2: Shebuka, doesn't sound right to me... to be honest. But I have a better example myself ---------------------------------------------------------------------- Shebuka · 2014-04-17 11:20 Quote from: taazz on April 17, 2014, 12:59:50 pmisn't this the opposite of what the OP wanted ee he wants a screen shot of his application not the desktop. Comment the Application.minimize part and you get application screenshot, same behavior as the legacy code (you get the whole desktop screenshot, with all stuff behind application). Quote from: skalogryz on April 17, 2014, 01:06:41 pm2: Shebuka, doesn't sound right to me... to be honest. But I have a better example myself What example? ---------------------------------------------------------------------- skalogryz · 2014-04-17 13:25 Quote from: Shebuka on April 17, 2014, 01:20:42 pmQuote from: skalogryz on April 17, 2014, 01:06:41 pm2: Shebuka, doesn't sound right to me... to be honest. But I have a better example myself What example? Sorry, I meant to say "But I don't have a better example" ---------------------------------------------------------------------- nitram · 2014-12-07 16:48 Shebuka: Modified your Code for a PixelPicker. Just need to read out Pixels by      TGColor := screenImage.TColors[MousePosX,MousePosY]; in addition. Now I come to following lines of your code...       //FreeAndNil(screenImage);              //This is commented for a reason, don't uncomment it       //FreeAndNil(screenBitmapContext);      //This is commented for a reason, don't uncomment it       //FreeAndNil(screenBitmapRawImage);     //This is commented for a reason, don't uncomment it       //FreeAndNil(screenshotImage);          //This is commented for a reason, don't uncomment it Has somebody an idea why free methods of above does not work. Even     screenImage.free; does not work. As I do several screenshots per second I get soon a memory overflow.... Lazarus 1.2.6, Mac mini, yosemite ---------------------------------------------------------------------- felipemdc · 2015-02-12 22:23 2 things: 1> In my original answer indeed I thought the issue was about a desktop screenshot, I totally missed that he wanted a window screenshot 2> LCL-Cocoa supports desktop screenshot using the normal method