--- grabber-x11.cc Sat May 30 14:55:30 1998 +++ /home/old_wd0f/ports/mbone/vic/work.luigi/vic-2.8-luigi/grabber-x11.cc Fri Apr 10 21:32:11 1998 @@ -0,0 +1,1332 @@ +/* + * Copyright (c) 1998 Luigi Rizzo + * grabber-x11.cc for vic + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jim Lowe + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "grabber.h" +#include "Tcl.h" +#include "device-input.h" +#include "module.h" + +#include +#include +#include +#include +#include +/*** #include "sized_types.h" ***/ + +/* + Netvideo version 3.3 + Written by Ron Frederick + + Machine-specific sized integer type definitions + Video utility definitions +*/ + +/* + * Copyright (c) Xerox Corporation 1992. All rights reserved. + * + * License is granted to copy, to use, and to make and to use derivative + * works for research and evaluation purposes, provided that Xerox is + * acknowledged in all documentation pertaining to any such copy or derivative + * work. Xerox grants no other licenses expressed or implied. The Xerox trade + * name should not be used in any advertising without its written permission. + * + * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE + * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE + * FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without + * express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this software. + */ + +typedef signed char int8; /* 8 bit signed int */ +typedef short int16; /* 16 bit signed int */ +typedef int int32; /* 32 bit signed int */ +#if defined(__alpha) +typedef long int64; /* 64 bit signed int */ +#endif + +typedef unsigned char uint8; /* 8 bit unsigned int */ +typedef unsigned short uint16; /* 16 bit unsigned int */ +typedef unsigned int uint32; /* 32 bit unsigned int */ +#if defined(__alpha) +typedef unsigned long uint64; /* 64 bit unsigned int */ +#endif + +/* Mildly gross but moderately portable test for littleendian machines */ +#define LITTLEENDIAN (ntohl(0x12345678) != 0x12345678) + +typedef struct { + XImage *image; + void *shminfo; +} ximage_t; + +/*************************/ + +#define VID_SMALL 0x01 +#define VID_MEDIUM 0x02 +#define VID_LARGE 0x04 +#define VID_SIZEMASK 0x07 + +#define VID_GREYSCALE 0x08 +#define VID_COLOR 0x10 + +#define X11GRAB_FIXED 0 +#define X11GRAB_POINTER 1 +#define X11GRAB_WINDOW 2 + + +/*XXX*/ +#define NTSC_WIDTH 320 +#define NTSC_HEIGHT 240 +#define PAL_WIDTH 384 +#define PAL_HEIGHT 288 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 + + +class X11Grabber : public Grabber { + public: + X11Grabber(const char* name, const char* format); + virtual ~X11Grabber(); + virtual void start(); + virtual void stop(); + protected: + virtual int command(int argc, const char*const* argv); + virtual int capture(); + virtual int grab(); + void format(); + void setsize(); + + void X11Grab_ComputeYUVTable(void) ; + int X11Grab_MSBWhite1(void); + int X11Grab_LSBWhite1(void); + int X11Grab_MSBBlack1(void); + int X11Grab_LSBBlack1(void); + int X11Grab_Pseudo8(void); + int X11Grab_RGB16(void); + int X11Grab_TrueXBGR24(void); + + int X11Grab_Initialize(Window rw, int w, int h); + int (X11Grabber::*c_grab)(void); + + uint8 *rgb2y_ ; + int8 *rgb2u_ ; + int8 *rgb2v_ ; + + ximage_t *ximage_ ; + + Display *dpy_ ; + int mode_; /* input mode */ + Window theroot_ ; + +// Tcl_Interp *interp_=NULL; + + int screen, xerror ; + Window vRoot_ ; + Window rootwin_ ; + Colormap colormap; + Visual *root_vis; + XVisualInfo root_visinfo; + + int ncolors_ ; + int black, white; + XColor *color ; + uint8 *col2y_ ; + uint16 *col2rgb16_ ; + + u_int basewidth_; /* Height of frame to be captured */ + u_int baseheight_; /* Width of frame to be captured */ + u_int decimate_; /* division of base sizes */ + + int x_origin_, y_origin_, width_, height_; + int root_depth_, root_width, root_height; +}; + +class X11Device : public InputDevice { + public: + X11Device(const char* nickname); + virtual int command(int argc, const char*const* argv); + protected: + const char* name_; +}; + +static X11Device find_x11_devices("x11"); + +X11Device::X11Device(const char* nickname): + InputDevice(nickname), name_(nickname) +{ + if (free) + attributes_ = "\ +size {large normal small cif} \ +format {422}" ; + else + attributes_ = "disabled"; +} + +extern "C" { +/*** most of this taken from nv:x11-grab.c ***/ +extern ximage_t *VidUtil_AllocXImage(Display *dpy, Visual *vis, int depth, + int width, int height, int readonly); + +#if 0 /* debugging stuff */ +static int my_Tcl_Eval(Tcl_Interp *interp, char *cmd) +{ + fprintf(stderr,"Tcl_Eval <%s>\n", cmd); + Tcl_Eval(interp, cmd); +} +#define Tcl_Eval my_Tcl_Eval +#endif + +static Window +VirtualRootWindow(Display *dpy, int screen) +{ + static Display *last_dpy=(Display *)NULL; + static int last_screen = -1; + static Window vRoot=None; + + Atom __SWM_VROOT=None; + Window rw, p, *child; + unsigned int i, nChildren; + + if ((dpy != last_dpy) || (screen != last_screen)) { + vRoot = RootWindow(dpy, screen); + + /* go look for a virtual root */ + __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False); + XQueryTree(dpy, vRoot, &rw, &p, &child, &nChildren); + for (i=0; i> 5 ; + color[i].blue = (color[i].blue & 0xf800) >> 11 ; + col2rgb16_[i] = color[i].red + color[i].green + color[i].blue; + col2y_[i] = rgb2y_[col2rgb16_[i]]; + } + break; + case TrueColor: + fprintf(stderr, "TrueColor...\n"); + break; + case DirectColor: + fprintf(stderr, "DirectColor...\n"); + break; + } +} + +/* + * these are the grabbing functions for the various video formats + */ + +int +X11Grabber::X11Grab_MSBWhite1() +{ + int x, y, row; + uint8 *data=(uint8 *)ximage_->image->data, *yp= frame_; + + for (y=0; y>7); + yp[1] = 255 * ((row & 0x40)>>6); + yp[2] = 255 * ((row & 0x20)>>5); + yp[3] = 255 * ((row & 0x10)>>4); + yp[4] = 255 * ((row & 0x08)>>3); + yp[5] = 255 * ((row & 0x04)>>2); + yp[6] = 255 * ((row & 0x02)>>1); + yp[7] = 255 * (row & 0x01); + yp += 8; + } + } + + return 1; +} + +int +X11Grabber::X11Grab_MSBBlack1() +{ + int x, y, row; + uint8 *data=(uint8 *)ximage_->image->data, *yp= frame_; + + for (y=0; y>7); + yp[1] = 255 - 255 * ((row & 0x40)>>6); + yp[2] = 255 - 255 * ((row & 0x20)>>5); + yp[3] = 255 - 255 * ((row & 0x10)>>4); + yp[4] = 255 - 255 * ((row & 0x08)>>3); + yp[5] = 255 - 255 * ((row & 0x04)>>2); + yp[6] = 255 - 255 * ((row & 0x02)>>1); + yp[7] = 255 - 255 * (row & 0x01); + yp += 8; + } + } + + return 1; +} + +int +X11Grabber::X11Grab_LSBWhite1() +{ + int x, y, row; + uint8 *data=(uint8 *)ximage_->image->data, *yp= frame_ ; + + for (y=0; y>7); + yp[6] = 255 * ((row & 0x40)>>6); + yp[5] = 255 * ((row & 0x20)>>5); + yp[4] = 255 * ((row & 0x10)>>4); + yp[3] = 255 * ((row & 0x08)>>3); + yp[2] = 255 * ((row & 0x04)>>2); + yp[1] = 255 * ((row & 0x02)>>1); + yp[0] = 255 * (row & 0x01); + yp += 8; + } + } + + return 1; +} + +int +X11Grabber::X11Grab_LSBBlack1() +{ + int x, y, row; + uint8 *data=(uint8 *)ximage_->image->data, *yp= frame_; + + for (y=0; y>7); + yp[6] = 255 - 255 * ((row & 0x40)>>6); + yp[5] = 255 - 255 * ((row & 0x20)>>5); + yp[4] = 255 - 255 * ((row & 0x10)>>4); + yp[3] = 255 - 255 * ((row & 0x08)>>3); + yp[2] = 255 - 255 * ((row & 0x04)>>2); + yp[1] = 255 - 255 * ((row & 0x02)>>1); + yp[0] = 255 - 255 * (row & 0x01); + yp += 8; + } + } + + return 1; +} + +int +X11Grabber::X11Grab_Pseudo8() +{ + int x, y, p0, p1, p2, p3 ; + uint8 *data=(uint8 *)ximage_->image->data, *yp=frame_ ; + uint8 *up= (uint8 *)yp + framesize_ ; + uint8 *vp= up + (framesize_ >> 2) ; + + X11Grab_ComputeYUVTable(); + + for (y=0; y> 1) & 0x7bef ) + ( (p1 >> 1) & 0x7bef ) ; + p2 = ( (p2 >> 1) & 0x7bef ) + ( (p3 >> 1) & 0x7bef ) ; + p0 = ( (p0 >> 1) & 0x7bef ) + ( (p2 >> 1) & 0x7bef ) ; +#else /* take the darkest... */ + if (yp[1] < yp[0]) p0 = p1 ; + if (rgb2y_[p2] < rgb2y_[p0]) p0 = p2 ; + if (rgb2y_[p3] < rgb2y_[p0]) p0 = p3 ; +#endif + *up++ = rgb2u_[ p0 ]; + *vp++ = rgb2v_[ p0 ]; + + data += 2; + yp += 2 ; + } + for (x=0; x> 2) ; + uint16 *data=(uint16 *)ximage_->image->data, p0, p1, p2, p3; + + for (y=0; y> 1) & 0x7bef ) + ( (p1 >> 1) & 0x7bef ) ; + p2 = ( (p2 >> 1) & 0x7bef ) + ( (p3 >> 1) & 0x7bef ) ; + p0 = ( (p0 >> 1) & 0x7bef ) + ( (p2 >> 1) & 0x7bef ) ; +#else /* take the darkest... */ + if (yp[1] < yp[0]) p0 = p1 ; + if (rgb2y_[p2] < rgb2y_[p0]) p0 = p2 ; + if (rgb2y_[p3] < rgb2y_[p0]) p0 = p3 ; +#endif + *up++ = rgb2u_[ p0 ]; + *vp++ = rgb2v_[ p0 ]; + yp += 2 ; + } + for (x=0; x> 2) ; + uint16 p0, p1 ; + uint32 *data=(uint32 *)ximage_->image->data, d ; + + for (y=0; y>5) & 0x7e0) | ((p0>>19) & 0x1f); + *yp++ = rgb2y_[ p0 ]; + + d = *data++ ; + p1 = ((d<<8) & 0xf100) | ((p0>>5) & 0x7e0) | ((p0>>19) & 0x1f); + *yp++ = rgb2y_[ p1 ]; + + /* average the two pixels... */ + p0 = ( (p0 >> 1) & 0x7bef ) + ( (p1 >> 1) & 0x7bef ) ; + *up++ = rgb2u_[ p0 ]; + } + for (x=0; x>5) & 0x7e0) | ((p0>>19) & 0x1f); + *yp++ = rgb2y_[ p0 ]; + + d = *data++ ; + p1 = ((d<<8) & 0xf100) | ((p0>>5) & 0x7e0) | ((p0>>19) & 0x1f); + *yp++ = rgb2y_[ p1 ]; + + /* average the two pixels... */ + p0 = ( (p0 >> 1) & 0x7bef ) + ( (p1 >> 1) & 0x7bef ) ; + *vp++ = rgb2v_[ p0 ]; + } + } + + return 1; +} + +/* + * initialization of frame grabber... + */ +int +X11Grabber::X11Grab_Initialize(Window rw, int w, int h) +{ + int config = 0 ; + XWindowAttributes wattr; + + if (theroot_ != rw) { + theroot_ = rw; + XGetWindowAttributes(dpy_, theroot_, &wattr); + screen = XScreenNumberOfScreen(wattr.screen); + colormap = DefaultColormapOfScreen(wattr.screen); + ncolors_ = CellsOfScreen(wattr.screen); + black = BlackPixelOfScreen(wattr.screen); + white = WhitePixelOfScreen(wattr.screen); + root_depth_ = wattr.depth; + root_width = wattr.width; + root_height = wattr.height; + root_vis = wattr.visual; + vRoot_ = VirtualRootWindow(dpy_, screen); + + if (color != NULL) { + free(color); + free(col2y_); + free(col2rgb16_); + } + color = (XColor *) malloc(ncolors_*sizeof(XColor)); + col2y_ = (uint8 *) malloc(ncolors_*sizeof(uint8)); + col2rgb16_ = (uint16 *) malloc(ncolors_*sizeof(uint16)); + + XMatchVisualInfo(dpy_, screen, root_depth_, root_vis->c_class, + &root_visinfo); + switch (root_depth_) { + case 1: + if (white == 1) { + c_grab = (LITTLEENDIAN) ? X11Grab_LSBWhite1 : X11Grab_MSBWhite1; + } else { + c_grab = (LITTLEENDIAN) ? X11Grab_LSBBlack1 : X11Grab_MSBBlack1; + } + config = VID_GREYSCALE; + break; + + case 8: + switch (root_visinfo.c_class) { + case PseudoColor: + case GrayScale: + case StaticColor: + case StaticGray: + c_grab = X11Grab_Pseudo8; + break; + default: + c_grab = NULL; + break; + } + config = VID_GREYSCALE|VID_COLOR; + break; + + case 16: + c_grab = X11Grab_RGB16; + break ; + + case 24: + if ((root_visinfo.c_class == TrueColor) && + (root_visinfo.green_mask = 0xff00) && +#ifdef __FreeBSD__ + (root_visinfo.red_mask == 0xff0000) && + (root_visinfo.blue_mask == 0xff)) +#else + (root_visinfo.red_mask == 0xff) && + (root_visinfo.blue_mask == 0xff0000)) +#endif + { + c_grab = X11Grab_TrueXBGR24; + } else + c_grab = NULL; + config = VID_GREYSCALE|VID_COLOR; + break; + + default: + fprintf(stderr, "don't know how to grab %d bits\n", + root_depth_); + c_grab = NULL; + break; + } + } + + if ((ximage_ == NULL) || (width_ != w) || (height_ != h)) { + width_ = w; + height_ = h; + if (ximage_ != NULL) + VidUtil_DestroyXImage(dpy_, ximage_); + ximage_ = VidUtil_AllocXImage(dpy_, root_vis, root_depth_, w, h, False); + } + return (c_grab == NULL) ? 0 : config|VID_SMALL|VID_MEDIUM|VID_LARGE; +} + +extern "C" { +extern void VidUtil_Init(Display *dpy); +extern void VidUtil_DestroyXImage(Display *dpy, ximage_t *ximage); + +#ifdef UNUSED /* not yet... */ +static int +ErrHandler1(ClientData clientData, XErrorEvent *errevp) +{ + xerror = 1; + return 0; +} + +static int +X11Grab_MakeBox(unsigned int x1, unsigned int y1, + unsigned int x2, unsigned int y2, + int *xp, int *yp, int *wp, int *hp) +{ + int w, h; + + w = x2-x1; + if (w < 0) { + *xp = x2; + *wp = -w; + } else { + *xp = x1; + *wp = w; + } + + h = y2-y1; + if (h < 0) { + *yp = y2; + *hp = -h; + } else { + *yp = y1; + *hp = h; + } +} + +static int +X11Grab_UpdatePos(Window rw, int x, int y, int w, int h) +{ + static char cmd[256]; + + if (w < 8) w = 8; + if (h < 8) h = 8; + + if (w > root_width/8*8) w = root_width/8*8; + if (h > root_height/8*8) h = root_height/8*8; + + w = (w+7)/8*8; + h = (h+7)/8*8; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + if (x > root_width-w) x = root_width-w; + if (y > root_height-h) y = root_height-h; + + sprintf(cmd, "x11grabUpdatePos %d %d %d %d", x, y, w, h); + (void) Tcl_Eval(interp, cmd); + + x_origin = x; + y_origin = y; + + if ((root != rw) || (width != w) || (height != h)) { + X11Grab_Initialize(rw, w, h); + return 0; + } else + return 1; +} + +static int +X11Grab_FollowPointer(void) +{ + Window rw, cw; + int x, y, wx, wy; + unsigned int mask; + + XQueryPointer(dpy, root, &rw, &cw, &x, &y, &wx, &wy, &mask); + + if (x < x_origin+width/4) + x = x-width/4; + else if (x >= x_origin+3*width/4) + x = x-3*width/4; + else + x = x_origin; + + if (y < y_origin+height/4) + y = y-height/4; + else if (y >= y_origin+3*height/4) + y = y-3*height/4; + else + y = y_origin; + + return X11Grab_UpdatePos(rw, x, y, width, height); +} + +static int +X11Grab_FollowWindow(void) +{ + int x, y, w, h; + XWindowAttributes wattr, vRoot_wattr; + Tk_ErrorHandler handler; + + handler = Tk_CreateErrorHandler(dpy, -1, -1, -1, ErrHandler1, NULL); + xerror = 0; + XGetWindowAttributes(dpy, target, &wattr); + XSync(dpy, False); + Tk_DeleteErrorHandler(handler); + if ((target == None) || xerror) { + target = None; + (void) Tcl_Eval(interp, + ".grabControls.x11grab.row1.mode.window config -state disabled"); + (void) Tcl_Eval(interp, "set x11grabMode fixed"); + return 1; + } else { + XGetWindowAttributes(dpy, vRoot, &vRoot_wattr); + x = wattr.x+vRoot_wattr.x; + y = wattr.y+vRoot_wattr.y; + w = wattr.width+2*wattr.border_width; + h = wattr.height+2*wattr.border_width; + + return X11Grab_UpdatePos(root, x, y, w, h); + } +} +#endif /* UNUSED ... */ + + +#ifdef UNUSED +/*ARGSUSED*/ +static int +X11Grab_SetXCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int x; + + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " x\"", NULL); + return TCL_ERROR; + } + + x = atoi(argv[1]); + (void) X11Grab_UpdatePos(root, x, y_origin, width, height); + + return TCL_OK; +} + +/*ARGSUSED*/ +static int +X11Grab_SetYCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int y; + + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " y\"", NULL); + return TCL_ERROR; + } + + y = atoi(argv[1]); + (void) X11Grab_UpdatePos(root, x_origin, y, width, height); + + return TCL_OK; +} + +/*ARGSUSED*/ +static int X11Grab_SetWCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int w; + + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " width\"", NULL); + return TCL_ERROR; + } + + w = atoi(argv[1]); + (void) X11Grab_UpdatePos(root, x_origin, y_origin, w, height); + + return TCL_OK; +} + +/*ARGSUSED*/ +static int +X11Grab_SetHCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int h; + + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " height\"", NULL); + return TCL_ERROR; + } + + h = atoi(argv[1]); + (void) X11Grab_UpdatePos(root, x_origin, y_origin, width, h); + + return TCL_OK; +} + +/*ARGSUSED*/ +static int +X11Grab_SetRegionCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + unsigned int rootx, rooty; + int x, y, w, h, boxDrawn=0; + GC xorGC; + Cursor cursor; + XEvent event; + + cursor = XCreateFontCursor(dpy, XC_cross); + + if (XGrabPointer(dpy, root, False, ButtonPressMask, GrabModeAsync, + GrabModeAsync, root, cursor, CurrentTime)!=GrabSuccess) { + Tcl_AppendResult(interp, argv[0], ": can't grab mouse", NULL); + return TCL_ERROR; + } + + xorGC = XCreateGC(dpy, root, 0, NULL); + XSetSubwindowMode(dpy, xorGC, IncludeInferiors); + XSetForeground(dpy, xorGC, -1); + XSetFunction(dpy, xorGC, GXxor); + + XMaskEvent(dpy, ButtonPressMask, &event); + rootx = event.xbutton.x_root; + rooty = event.xbutton.y_root; + + XChangeActivePointerGrab(dpy, ButtonMotionMask|ButtonReleaseMask, cursor, + CurrentTime); + + while (1) { + XNextEvent(dpy, &event); + switch (event.type) { + case MotionNotify: + if (boxDrawn) { + XDrawRectangle(dpy, root, xorGC, x, y, w, h); + boxDrawn = 0; + } + while (XCheckTypedEvent(dpy, MotionNotify, &event)) ; + X11Grab_MakeBox(rootx, rooty, event.xbutton.x_root, + event.xbutton.y_root, &x, &y, &w, &h); + XDrawRectangle(dpy, root, xorGC, x, y, w, h); + boxDrawn = 1; + break; + case ButtonRelease: + if (boxDrawn) { + XDrawRectangle(dpy, root, xorGC, x, y, w, h); + boxDrawn = 0; + } + XFlush(dpy); + X11Grab_MakeBox(rootx, rooty, event.xmotion.x_root, + event.xmotion.y_root, &x, &y, &w, &h); + XUngrabPointer(dpy, CurrentTime); + XFreeGC(dpy, xorGC); + XFreeCursor(dpy, cursor); + (void) Tcl_Eval(interp, "set x11grabMode fixed"); + (void) X11Grab_UpdatePos(root, x, y, w, h); + return TCL_OK; + } + } +} + +/*ARGSUSED*/ +static int X11Grab_SetWindowCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int buttons=0; + Cursor cursor; + XEvent event; + + cursor = XCreateFontCursor(dpy, XC_crosshair); + target = None; + + if (XGrabPointer(dpy, vRoot, False, ButtonPressMask|ButtonReleaseMask, + GrabModeSync, GrabModeAsync, root, cursor, + CurrentTime) != GrabSuccess) { + Tcl_AppendResult(interp, argv[0], ": can't grab mouse", NULL); + return TCL_ERROR; + } + + while ((target == None) || (buttons != 0)) { + XAllowEvents(dpy, SyncPointer, CurrentTime); + XWindowEvent(dpy, vRoot, ButtonPressMask|ButtonReleaseMask, &event); + switch (event.type) { + case ButtonPress: + if (target == None) target = event.xbutton.subwindow; + buttons++; + break; + case ButtonRelease: + if (buttons > 0) buttons--; + break; + } + } + + XUngrabPointer(dpy, CurrentTime); + XFreeCursor(dpy, cursor); + + (void) Tcl_Eval(interp, + ".grabControls.x11grab.row1.mode.window config -state normal"); + (void) Tcl_Eval(interp, "set x11grabMode window"); + (void) X11Grab_FollowWindow(); + return TCL_OK; +} + +int +X11Grab_Probe(Tk_Window tkMainWin) +{ + Window rw; + interp = Tcl_CreateInterp(); + + if (tkMainWin == NULL) return 0; + + Tcl_TraceVar(interp, "x11grabMode", TCL_TRACE_WRITES, X11Grab_TraceMode, + NULL); + Tcl_CreateCommand(interp, "x11grabSetX", X11Grab_SetXCmd, 0, NULL); + Tcl_CreateCommand(interp, "x11grabSetY", X11Grab_SetYCmd, 0, NULL); + Tcl_CreateCommand(interp, "x11grabSetW", X11Grab_SetWCmd, 0, NULL); + Tcl_CreateCommand(interp, "x11grabSetH", X11Grab_SetHCmd, 0, NULL); + Tcl_CreateCommand(interp, "x11grabSetRegion", X11Grab_SetRegionCmd, 0, + NULL); + Tcl_CreateCommand(interp, "x11grabSetWindow", X11Grab_SetWindowCmd, 0, + NULL); + dpy = Tk_Display(tkMainWin); + rootwin = rw = RootWindow(dpy, Tk_ScreenNumber(tkMainWin)); + VidUtil_Init(dpy); + return X11Grab_Initialize(rw, width, height); +} +#endif /* UNUSED */ + +} /* end extern "C" block */ + + +int +X11Device::command(int argc, const char*const* argv) +{ + Tcl& tcl = Tcl::instance(); + + if ((argc == 3) && (strcmp(argv[1], "open") == 0)) { + TclObject* o = new X11Grabber(name_, argv[2]); + if (o != 0) + tcl.result(o->name()); + return (TCL_OK); + } + return (InputDevice::command(argc, argv)); +} + +X11Grabber::X11Grabber(const char* name, const char* format) +{ + c_grab = NULL ; /* XXX */ + theroot_ = None ; /* XXX */ + ximage_ = NULL ; + color = NULL ; + col2y_ = NULL ; + col2rgb16_ = NULL ; + + width_ = 320 ; + height_ = 240 ; + x_origin_ = y_origin_ = 0 ; /* XXX */ + + if (strcmp(format, "422") && strcmp(format, "cif")) { + fprintf(stderr, + "vic: x11Grabber: unsupported format: %s\n", + format); + abort(); + } + + Tk_Window tkMainWin = Tcl::instance().tkmain() ; + Window rw ; + + dpy_ = Tk_Display(tkMainWin); + rootwin_ = rw = RootWindow(dpy_, Tk_ScreenNumber(tkMainWin)); + + /* Initialize the RGB565 to YUV tables */ + int i, r, g, b, y, u, v; + + rgb2y_ = new uint8[65536] ; + rgb2u_ = new uint8[65536] ; + rgb2v_ = new uint8[65536] ; + + i = 0; + for (r=4; r<256; r+=8) { + for (g=3; g<256; g+=4) { /* XXX */ + for (b=4; b<256; b+=8) { + y = (38*r+75*g+15*b+64)/128; + u = 74*(b-y)/128; + if (u > 127) u = 127 ; + else if (u< -128) u = -128 ; + v = (93*(r-y)/128); + if (v > 127) v = 127 ; + else if (v< -128) v = -128 ; + rgb2y_[i] = y ; + rgb2u_[i] = u ^ 0x80 ; /* was u */ + rgb2v_[i] = v ^ 0x80 ; + i++; + } + } + } + + X11Grab_Initialize(rw, width_, height_); + + mode_ = X11GRAB_FIXED; /* XXX */ + decimate_ = 1; /* XXX */ + basewidth_ = PAL_WIDTH * 2; + baseheight_ = PAL_HEIGHT * 2; + +} + +X11Grabber::~X11Grabber() +{ + if (ximage_ != NULL) + VidUtil_DestroyXImage(dpy_, ximage_); + free(rgb2y_); + free(rgb2u_); + free(rgb2v_); +} + +void +X11Grabber::setsize() +{ + int rows, columns; + + rows = (baseheight_ / decimate_) &~0xf; /* 0xf, ugh! */ + columns = (basewidth_ / decimate_) &~0xf; + + /* XXX set size of captured window ? */ + + set_size_422(columns, rows); /* was 422... */ + X11Grab_Initialize(rootwin_, columns, rows); /* XXX */ + + allocref(); /* allocate reference frame */ +} + +void +X11Grabber::format() +{ + + baseheight_ = CIF_HEIGHT * 2; + basewidth_ = CIF_WIDTH * 2; + + setsize(); +} + + +void +X11Grabber::start() +{ + format(); + /* XXX prepare for continuous capture */ + Grabber::start(); +} + +void +X11Grabber::stop() +{ + /* XXX stop capture */ + VidUtil_DestroyXImage(dpy_, ximage_); + ximage_ = NULL ; + Grabber::stop(); +} + +int +X11Grabber::command(int argc, const char*const* argv) +{ + if (argc >= 3) { + if (strcmp(argv[1], "decimate") == 0) { + int dec = atoi(argv[2]); + Tcl& tcl = Tcl::instance(); + if (dec <= 0) { + tcl.resultf("%s: divide by zero", argv[0]); + return (TCL_ERROR); + } + if (dec != decimate_) { + decimate_ = dec; + if(running_) { + stop(); + setsize(); + start(); + } + } + return (TCL_OK); + } else if (strcmp(argv[1], "fixed") == 0) { + mode_ = X11GRAB_FIXED; + + int x = atoi(argv[2]); + int y = atoi(argv[3]); + if (x >= 0 && *argv[2] != '-' && x + width_ <= root_width) + x_origin_ = x ; + else if ( x <= 0 && -x + width_ <= root_width ) + x_origin_ = root_width + x - width_ ; + if (y >= 0 && *argv[3] != '-' && y + height_ <= root_height) + y_origin_ = y ; + else if (y <= 0 && -y + height_ <= root_height ) + y_origin_ = root_height + y - height_ ; + fprintf(stderr, "x11 fixed %d %d (root %dx%d)\n", + x_origin_, y_origin_, root_width, root_height); + return (TCL_OK); + } else if (!strcmp(argv[2], "pointer")) { + mode_ = X11GRAB_POINTER; + return (TCL_OK); + } else if (!strcmp(argv[2], "window")) { + mode_ = X11GRAB_WINDOW; + return (TCL_OK); + } else if (strcmp(argv[1], "format") == 0 || + strcmp(argv[1], "type") == 0) { + if (running_) + format(); + return (TCL_OK); + } else if (strcmp(argv[1], "contrast") == 0) { + contrast(atof(argv[2])); + return (TCL_OK); + } + } else if (argc == 2) { + if (strcmp(argv[1], "format") == 0 || + strcmp(argv[1], "type") == 0) { + return (TCL_OK); + } + } + return (Grabber::command(argc, argv)); +} + +/* + * captures in CIF or 411 -- color info is half the luma info. + */ +int +X11Grabber::capture() +{ + int dograb = 0 ; + +#define MY_T uint8 + + + switch (mode_) { + case X11GRAB_FIXED: + dograb = 1; + break; +#if 0 /* not yet... */ + case X11GRAB_POINTER: + dograb = X11Grab_FollowPointer(); + break; + case X11GRAB_WINDOW: + dograb = X11Grab_FollowWindow(); + break; +#endif + } + + if (1 || dograb) { + XImage *image=ximage_->image; + +#ifdef USE_SHM + if (ximage_->shminfo != NULL) + XShmGetImage(dpy_, theroot_, image, x_origin_, y_origin_,AllPlanes); + else +#endif + XGetSubImage(dpy_, theroot_, x_origin_, y_origin_, + image->width, image->height, AllPlanes, + ZPixmap, image, 0, 0); + c_grab(); + return 1 ; + } else + return 0; +} + +int X11Grabber::grab() +{ + if (capture() == 0) + return (0); + suppress(frame_); + saveblks(frame_); + YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_); + return (target_->consume(&f)); +} + +extern "C" { + +#include +#ifdef USE_SHM +#include +#if defined(sun) && !defined(__svr4__) +int shmget(key_t, int, int); +char *shmat(int, char*, int); +int shmdt(char*); +int shmctl(int, int, struct shmid_ds*); +#endif +#ifdef __osf__ +int XShmGetEventBase(struct _XDisplay *); +#else +int XShmGetEventBase(Display *); +#endif +#ifdef sgi +#define XShmAttach __XShmAttach__ +#define XShmDetach __XShmDetach__ +#define XShmPutImage __XShmPutImage__ +#endif +#include +#ifdef sgi +#undef XShmAttach +#undef XShmDetach +#undef XShmPutImage +int XShmAttach(Display*, XShmSegmentInfo*); +int XShmDetach(Display*, XShmSegmentInfo*); +int XShmPutImage(Display*, Drawable, GC, XImage*, int, int, int, int, + int, int, int); +#endif +#endif + + +/*ARGSUSED*/ +static int +ErrHandler(ClientData clientData, XErrorEvent *errevp) +{ + return 0; +} + +ximage_t * +VidUtil_AllocXImage(Display *dpy, Visual *vis, int depth, int width, + int height, int readonly) +{ + ximage_t *ximage; + int ximage_size; + Tk_ErrorHandler handler; + + ximage = (ximage_t *) malloc(sizeof(ximage_t)); + if (ximage == NULL) + return NULL; + +#ifdef USE_SHM + if (1) { + XShmSegmentInfo *shminfo; + + ximage->shminfo = shminfo = + (XShmSegmentInfo *) malloc(sizeof(XShmSegmentInfo)); + + ximage->image = XShmCreateImage(dpy, vis, depth, ZPixmap, 0, shminfo, + width, height); + ximage_size = ximage->image->bytes_per_line * ximage->image->height; + + shminfo->shmid = shmget(IPC_PRIVATE, ximage_size, IPC_CREAT|0777); + if (shminfo->shmid != -1) { + shminfo->shmaddr = ximage->image->data = + (char *) shmat(shminfo->shmid, 0, 0); + shminfo->readOnly = readonly; + + handler = Tk_CreateErrorHandler(dpy, -1, -1, -1, ErrHandler, NULL); + XShmAttach(dpy, shminfo); + XSync(dpy, False); + shmctl(shminfo->shmid, IPC_RMID, 0); /* so it goes away on exit */ + Tk_DeleteErrorHandler(handler); + if (0) { /* so it goes away on exit... */ + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid, IPC_RMID, 0); + XDestroyImage(ximage->image); + free(shminfo); + } + return ximage; + } else { + XDestroyImage(ximage->image); + free(shminfo); + ximage->shminfo = NULL ; + /* XXX hmmm... something more ? */ + } + } +#endif + { + ximage->image = XCreateImage(dpy, vis, depth, ZPixmap, 0, NULL, width, + height, (depth == 24) ? 32 : depth, 0); + ximage_size = ximage->image->bytes_per_line * ximage->image->height; + ximage->image->data = (char *) malloc(ximage_size); + + ximage->shminfo = NULL; + } + + return ximage; +} + +void +VidUtil_DestroyXImage(Display *dpy, ximage_t *ximage) +{ +#ifdef USE_SHM + if (ximage->shminfo != NULL) { + XShmSegmentInfo *shminfo=(XShmSegmentInfo *)ximage->shminfo; + + XShmDetach(dpy, shminfo); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid, IPC_RMID, 0); + free(shminfo); + } + ximage->shminfo = NULL ; +#endif + + XDestroyImage(ximage->image); + free(ximage); +} + + +} /* end extern "C" block */