initial OS X screenshot support
authorTony Cook <tony@develop-help.com>
Sat, 9 Oct 2010 03:29:23 +0000 (03:29 +0000)
committerTony Cook <tony@develop-help.com>
Sat, 9 Oct 2010 03:29:23 +0000 (03:29 +0000)
Makefile.PL
Screenshot.pm
Screenshot.xs
imss.h
scdarwin.c [new file with mode: 0644]

index 8f301be..8138880 100644 (file)
@@ -18,6 +18,7 @@ GetOptions("incpath=s", \@incpaths,
 my @objs = qw/Screenshot.o/;
 my @cflags;
 my @lflags;
+my @lddlflags;
 my %seen_incdir;
 my %seen_libdir;
 my $X11_lib = $^O eq 'cygwin' ? 'X11.dll' : 'X11';
@@ -41,6 +42,13 @@ if (find_header('windows.h', "Win32 header")
   print "Found Win32\n";
 }
 
+if ($^O eq "darwin") {
+  push @objs, "scdarwin.o";
+  push @cflags, "-DSS_DARWIN";
+  push @lddlflags, qw/-framework OpenGL -framework Cocoa/;
+  print "Found OS X\n";
+}
+
 unless (@objs > 1) {
   die <<DEAD;
 OS unsupported: Headers or libraries not found for a supported GUI
@@ -49,6 +57,7 @@ Sorry, I can't find headers or libraries for a supported GUI
 You need to install development headers and libraries for your GUI
 For Win32: Platform SDK or a substitute
 For X11: X11 headers and libraries, eg. the libX11-dev package on Debian
+For OS X: Install Xcode
 
 DEAD
 }
@@ -68,6 +77,10 @@ my %opts =
 $opts{LIBS} = "@lflags" if @lflags;
 $opts{INC} .= " @cflags" if @cflags;
 
+if (@lddlflags) {
+  $opts{LDDLFLAGS} = $Config{lddlflags} . " @lddlflags";
+}
+
 # avoid "... isn't numberic in numeric gt ..." warnings for dev versions
 my $eu_mm_version = eval $ExtUtils::MakeMaker::VERSION;
 if ($eu_mm_version > 6.06) {
index 54e887a..768c6a9 100644 (file)
@@ -83,7 +83,9 @@ sub screenshot {
     $result =
       defined &_win32 ? _win32(0, $opts{decor}, $opts{left}, $opts{top},
                               $opts{right}, $opts{bottom}) :
-       defined &_x11 ? _x11($opts{display}, 0, $opts{left}, $opts{top},
+      defined &_darwin ? _darwin($opts{left}, $opts{top},
+                                $opts{right}, $opts{bottom}) :
+      defined &_x11 ? _x11($opts{display}, 0, $opts{left}, $opts{top},
                             $opts{right}, $opts{bottom}) :
           die "No drivers enabled\n";
   }
index 335b3d4..dd9dd0c 100644 (file)
@@ -48,5 +48,16 @@ imss_x11_close(display)
 
 #endif
 
+#ifdef SS_DARWIN
+
+Imager::ImgRaw
+imss_darwin(left = 0, top = 0, right = 0, bottom = 0)
+       int left
+       int top
+       int right
+       int bottom
+
+#endif
+
 BOOT:
        PERL_INITIALIZE_IMAGER_CALLBACKS;
\ No newline at end of file
diff --git a/imss.h b/imss.h
index 1ecb490..c464b13 100644 (file)
--- a/imss.h
+++ b/imss.h
@@ -12,4 +12,7 @@ imss_x11_open(char const *display_name);
 extern void
 imss_x11_close(unsigned long display);
 
+extern i_img *
+imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom);
+
 #endif
diff --git a/scdarwin.c b/scdarwin.c
new file mode 100644 (file)
index 0000000..30512d0
--- /dev/null
@@ -0,0 +1,132 @@
+/* Darwin support via OpenGL */
+#include "imext.h"
+#include "imss.h"
+#include <ApplicationServices/ApplicationServices.h>
+#include "OpenGL/OpenGL.h"
+#include "OpenGL/gl.h"
+#include "OpenGL/glu.h"
+#include "OpenGL/glext.h"
+
+i_img *
+imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom) {
+  CGDisplayCount count;
+  CGDisplayErr err;
+  CGRect rect;
+  CGLPixelFormatObj pix;
+  GLint npix;
+  CGLContextObj ctx;
+  i_img *im;
+  CGDirectDisplayID disp;
+  i_img_dim screen_width, screen_height;
+  i_img_dim width, height;
+
+  CGLPixelFormatAttribute pix_attrs[] =
+    {
+      kCGLPFADisplayMask, 0, /* filled in later */
+      kCGLPFAColorSize, 24,
+      kCGLPFAAlphaSize, 0,
+      kCGLPFAFullScreen,
+      0
+    };
+
+  i_clear_error();
+
+  disp = CGMainDisplayID();
+  
+  /* for now, only interested in the first display */
+  rect = CGDisplayBounds(disp);
+  screen_width = rect.size.width;
+  screen_height = rect.size.height;
+
+  /* adjust negative/zero values to window size */
+  if (left < 0)
+    left += screen_width;
+  if (top < 0)
+    top += screen_height;
+  if (right <= 0)
+    right += screen_width;
+  if (bottom <= 0)
+    bottom += screen_height;
+  
+  /* clamp */
+  if (left < 0)
+    left = 0;
+  if (right > screen_width)
+    right = screen_width;
+  if (top < 0)
+    top = 0;
+  if (bottom > screen_height)
+    bottom = screen_height;
+
+  /* validate */
+  if (right <= left || bottom <= top) {
+    i_push_error(0, "image would be empty");
+    return NULL;
+  }
+
+  width = right - left;
+  height = bottom - top;
+
+  /* select a pixel format */
+  pix_attrs[1] = CGDisplayIDToOpenGLDisplayMask(disp);
+  err = CGLChoosePixelFormat(pix_attrs, &pix, &npix);
+  if (err) {
+    i_push_errorf(err, "CGLChoosePixelFormat: %d", (int)err);
+    return NULL;
+  }
+  if (!npix) {
+    i_push_error(0, "No pixel format found");
+    return NULL;
+  }
+
+  /* make ourselves a context */
+  err = CGLCreateContext(pix, NULL, &ctx);
+  CGLDestroyPixelFormat(pix);
+  if (err) {
+    i_push_errorf(err, "CGLCreateContext: %d", (int)err);
+    return NULL;
+  }
+
+  err = CGLSetCurrentContext(ctx);
+  if (err) {
+    i_push_errorf(err, "CGLSetCurrentContext: %d", (int)err);
+    return NULL;
+  }
+
+  err = CGLSetFullScreen(ctx);
+  if (err) {
+    i_push_errorf(err, "CGLSetFullScreen: %d", (int)err);
+    return NULL;
+  }
+
+  /* capture */
+  im = i_img_8_new(width, height, 3);
+  if (im) {
+    size_t line_size = width * 4; 
+    size_t buf_size = line_size * height;
+    unsigned char *buf = malloc(buf_size);
+    i_img_dim y = height - 1;
+    i_color *bufp = (i_color *)buf; /* hackish */
+
+    /* GL has the vertical axis going from bottom to top, so translate it */
+
+    glReadBuffer(GL_FRONT);
+    glReadPixels(left, screen_height - top - height, width, height,
+                GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buf);
+
+    /* transfer */
+    while (y >= 0) {
+      i_plin(im, 0, width, y, bufp);
+      bufp += width;
+      --y;
+    }
+    
+    free(buf);
+  }
+
+  /* clean up */
+  CGLSetCurrentContext(NULL);
+  CGLDestroyContext(ctx);
+
+  return im;
+}