From 87cd516f0ee3359feb0dd4396488b74755ab57dc Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Tue, 16 Jan 2007 09:33:50 +0000 Subject: [PATCH] - add support for getting a subimage of the window - include the interface header in the implementations so we get errors when they don't match (doh!) - return something from the X11 error handler - add X11/Xutil.h include for XDestroyImage() - handle the default (no id or hwnd) case correctly --- Changes | 8 +++++ Screenshot.pm | 91 ++++++++++++++++++++++++++++++++++++++++++++------- Screenshot.xs | 12 +++++-- imss.h | 4 +-- scwin32.c | 42 +++++++++++++++++++++--- scx11.c | 56 +++++++++++++++++++++++++------ 6 files changed, 182 insertions(+), 31 deletions(-) diff --git a/Changes b/Changes index 6b7a8da..27c9be7 100755 --- a/Changes +++ b/Changes @@ -1,3 +1,11 @@ +0.004 + - add support for getting a subimage of the window + - include the interface header in the implementations so we + get errors when they don't match (doh!) + - return something from the X11 error handler + - add X11/Xutil.h include for XDestroyImage() + - handle the default (no id or hwnd) case correctly + 0.003 12 Jan 2007 - 0.003 release - ignore some VC++ generated junk in MANIFEST.SKIP diff --git a/Screenshot.pm b/Screenshot.pm index fb43237..28b6e24 100644 --- a/Screenshot.pm +++ b/Screenshot.pm @@ -27,24 +27,28 @@ sub screenshot { # lose the class if called as a method @_ % 2 == 1 and shift; - my %opts = (decor => 0, display => 0, @_); + my %opts = + ( + decor => 0, + display => 0, + left => 0, + top => 0, + right => 0, + bottom => 0, + @_); my $result; - if (!@_) { - my $result = - defined &_win32 ? _win32(0) : - defined &_x11 ? _x11($opts{display}, 0) : - die "No drivers enabled\n"; - } if (defined $opts{hwnd}) { defined &_win32 or die "Win32 driver not enabled\n"; - $result = _win32($opts{hwnd}, $opts{decor}); + $result = _win32($opts{hwnd}, $opts{decor}, $opts{left}, $opts{top}, + $opts{right}, $opts{bottom}); } elsif (defined $opts{id}) { # X11 window id defined &_x11 or die "X11 driver not enabled\n"; - $result = _x11($opts{display}, $opts{id}); + $result = _x11($opts{display}, $opts{id}, $opts{left}, $opts{top}, + $opts{right}, $opts{bottom}); } elsif ($opts{widget}) { # Perl/Tk widget @@ -55,7 +59,8 @@ sub screenshot { Imager->_set_error("Win32 Tk and Win32 support not built"); return; } - $result = _win32(hex($opts{widget}->id)); + $result = _win32(hex($opts{widget}->id), $opts{decor}, + $opts{left}, $opts{top}, $opts{right}, $opts{bottom}); } elsif ($sys eq 'x11') { unless (defined &_x11) { @@ -68,13 +73,22 @@ sub screenshot { and $id_hex = $opts{widget}->frame; # is there a way to get the display pointer from Tk? - $result = _x11(0, hex($id_hex)); + $result = _x11($opts{display}, hex($id_hex), $opts{left}, $opts{top}, + $opts{right}, $opts{bottom}); } else { Imager->_set_error("Unsupported windowing system '$sys'"); return; } } + else { + $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}, + $opts{right}, $opts{bottom}) : + die "No drivers enabled\n"; + } unless ($result) { Imager->_set_error(Imager->_error_as_msg()); @@ -170,7 +184,7 @@ Note: taking a screenshot of a remote display is slow. =item screenshot -If no parameters are supplied: +If no C or C parameter is supplied: =over @@ -188,6 +202,46 @@ otherwise, die. =back +You can also supply the following parameters to retrieve a subset of +the window: + +=over + +=item * + +left + +=item * + +top + +=item * + +right + +=item * + +bottom + +=back + +If left or top is negative, then treat that as from the right/bottom +edge of the window. + +If right ot bottom is zero or negative then treat as from the +right/bottom edge of the window. + +So setting all 4 values to 0 retrieves the whole window. + + # a 10-pixel wide right edge of the window + my $right_10 = screenshot(left => -10, ...); + + # the top-left 100x100 portion of the window + my $topleft_100 = screenshot(right => 100, bottom => 100, ...); + + # 10x10 pixel at the bottom right corner + my $bott_right_10 = screenshot(left => -10, top => -10, ...); + =item have_win32 Returns true if Win32 support is available. @@ -210,6 +264,19 @@ Closes a display returned by Imager::Screenshot::x11_open(). =back +=head1 CAVEATS + +It's possible to have more than one grab driver available, for +example, Win32 and X11, and which is used can have an effect on the +result. + +Under Win32, if there's a screesaver running, then you grab the +results of the screensaver. + +Grabbing the root window on a rootless server (eg. Cygwin/X) may not +grab the background. In fact, when I tested under Cygwin/X I got the +xterm window contents even when the Windows screensaver was running. + =head1 LICENSE Imager::Screenshot is licensed under the same terms as Perl itself. diff --git a/Screenshot.xs b/Screenshot.xs index 6d56685..e21cca2 100644 --- a/Screenshot.xs +++ b/Screenshot.xs @@ -16,18 +16,26 @@ PROTOTYPES: DISABLE #ifdef SS_WIN32 Imager -imss_win32(hwnd, include_decor = 0) +imss_win32(hwnd, include_decor = 0, left = 0, top = 0, right = 0, bottom = 0) unsigned hwnd int include_decor + int left + int top + int right + int bottom #endif #ifdef SS_X11 Imager -imss_x11(display, window_id) +imss_x11(display, window_id, left = 0, top = 0, right = 0, bottom = 0) unsigned long display int window_id + int left + int top + int right + int bottom unsigned long imss_x11_open(display_name = NULL) diff --git a/imss.h b/imss.h index 8a0ced2..1ecb490 100644 --- a/imss.h +++ b/imss.h @@ -2,10 +2,10 @@ #define IMSS_H extern i_img * -imss_win32(unsigned hwnd, int include_decor); +imss_win32(unsigned hwnd, int include_decor, int left, int top, int right, int bottom); extern i_img * -imss_x11(unsigned long display, int window_id); +imss_x11(unsigned long display, int window_id, int left, int top, int right, int bottom); extern unsigned long imss_x11_open(char const *display_name); diff --git a/scwin32.c b/scwin32.c index dad6436..13c726d 100644 --- a/scwin32.c +++ b/scwin32.c @@ -1,17 +1,20 @@ #include "imext.h" #include #include +#include "imss.h" i_img * -imss_win32(unsigned hwnd_u, int include_decor) { +imss_win32(unsigned hwnd_u, int include_decor, int left, int top, + int right, int bottom) { HWND hwnd = (HWND)hwnd_u; HDC wdc, bmdc; RECT rect; HBITMAP work_bmp, old_dc_bmp; - int width, height; + int window_width, window_height; BITMAPINFO bmi; unsigned char *di_bits; i_img *result = NULL; + int width, height; i_clear_error(); @@ -31,12 +34,41 @@ imss_win32(unsigned hwnd_u, int include_decor) { return NULL; } - width = rect.right - rect.left; - height = rect.bottom - rect.top; + window_width = rect.right - rect.left; + window_height = rect.bottom - rect.top; + + /* adjust negative/zero values to window size */ + if (left < 0) + left += window_width; + if (top < 0) + top += window_height; + if (right <= 0) + right += window_width; + if (bottom <= 0) + bottom += window_height; + + /* clamp */ + if (left < 0) + left = 0; + if (right > window_width) + right = window_width; + if (top < 0) + top = 0; + if (bottom > window_height) + bottom = window_height; + + /* validate */ + if (right <= left || bottom <= top) { + i_push_error(0, "image would be empty"); + return NULL; + } + width = right - left; + height = bottom - top; + work_bmp = CreateCompatibleBitmap(wdc, width, height); bmdc = CreateCompatibleDC(wdc); old_dc_bmp = SelectObject(bmdc, work_bmp); - BitBlt(bmdc, 0, 0, width, height, wdc, 0, 0, SRCCOPY); + BitBlt(bmdc, 0, 0, width, height, wdc, left, top, SRCCOPY); /* make a dib */ memset(&bmi, 0, sizeof(bmi)); diff --git a/scx11.c b/scx11.c index bdea60f..64df505 100644 --- a/scx11.c +++ b/scx11.c @@ -1,5 +1,7 @@ #include "imext.h" #include +#include +#include "imss.h" static int @@ -8,10 +10,13 @@ my_handler(Display *display, XErrorEvent *error) { XGetErrorText(display, error->error_code, buffer, sizeof(buffer)); i_push_error(error->error_code, buffer); + + return 0; } i_img * -imss_x11(unsigned long display_ul, unsigned long window_id) { +imss_x11(unsigned long display_ul, int window_id, + int left, int top, int right, int bottom) { Display *display = (Display *)display_ul; int own_display = 0; /* non-zero if we connect */ GC gc; @@ -22,6 +27,7 @@ imss_x11(unsigned long display_ul, unsigned long window_id) { int x, y; XColor *colors; XErrorHandler old_handler; + int width, height; i_clear_error(); @@ -51,7 +57,37 @@ imss_x11(unsigned long display_ul, unsigned long window_id) { return NULL; } - image = XGetImage(display, window_id, 0, 0, attr.width, attr.height, + /* adjust negative/zero values to window size */ + if (left < 0) + left += attr.width; + if (top < 0) + top += attr.height; + if (right <= 0) + right += attr.width; + if (bottom <= 0) + bottom += attr.height; + + /* clamp */ + if (left < 0) + left = 0; + if (right > attr.width) + right = attr.width; + if (top < 0) + top = 0; + if (bottom > attr.height) + bottom = attr.height; + + /* validate */ + if (right <= left || bottom <= top) { + XSetErrorHandler(old_handler); + if (own_display) + XCloseDisplay(display); + i_push_error(0, "image would be empty"); + return NULL; + } + width = right - left; + height = bottom - top; + image = XGetImage(display, window_id, left, top, width, height, -1, ZPixmap); if (!image) { XSetErrorHandler(old_handler); @@ -61,24 +97,24 @@ imss_x11(unsigned long display_ul, unsigned long window_id) { return NULL; } - result = i_img_8_new(attr.width, attr.height, 3); - line = mymalloc(sizeof(i_color) * attr.width); - colors = mymalloc(sizeof(XColor) * attr.width); - for (y = 0; y < attr.height; ++y) { + result = i_img_8_new(width, height, 3); + line = mymalloc(sizeof(i_color) * width); + colors = mymalloc(sizeof(XColor) * width); + for (y = 0; y < height; ++y) { cp = line; /* XQueryColors seems to be a round-trip, so do one big request instead of one per pixel */ - for (x = 0; x < attr.width; ++x) { + for (x = 0; x < width; ++x) { colors[x].pixel = XGetPixel(image, x, y); } - XQueryColors(display, attr.colormap, colors, attr.width); - for (x = 0; x < attr.width; ++x) { + XQueryColors(display, attr.colormap, colors, width); + for (x = 0; x < width; ++x) { cp->rgb.r = colors[x].red >> 8; cp->rgb.g = colors[x].green >> 8; cp->rgb.b = colors[x].blue >> 8; ++cp; } - i_plin(result, 0, attr.width, y, line); + i_plin(result, 0, width, y, line); } myfree(line); myfree(colors); -- 2.39.2