From 5a6bee4fe076e06514c6e4e538590dd3a2a5cdc3 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Fri, 6 Nov 2009 18:37:00 +0000 Subject: upgrade gridflow svn path=/trunk/; revision=12728 --- externals/gridflow/src/x11.cxx | 664 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 664 insertions(+) create mode 100644 externals/gridflow/src/x11.cxx (limited to 'externals/gridflow/src/x11.cxx') diff --git a/externals/gridflow/src/x11.cxx b/externals/gridflow/src/x11.cxx new file mode 100644 index 00000000..46778089 --- /dev/null +++ b/externals/gridflow/src/x11.cxx @@ -0,0 +1,664 @@ +/* + $Id: x11.c 4620 2009-11-01 21:16:58Z matju $ + + GridFlow + Copyright (c) 2001-2009 by Mathieu Bouchard + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + See file ../COPYING for further informations on licensing terms. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Note: some of the code was adapted from PDP's (the XVideo stuff). +*/ +#include "gridflow.hxx.fcs" +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#ifdef HAVE_X11_SHARED_MEMORY +#include +#include +#include +#endif +#ifdef HAVE_X11_XVIDEO +#include +#include +#endif + +/* X11 Error Handler type */ +typedef int (*XEH)(Display *, XErrorEvent *); + +struct FormatX11; +void FormatX11_call(FormatX11 *p); + +\class FormatX11 : Format { +/* at the Display/Screen level */ + Display *display; /* connection to xserver */ + Visual *visual; /* screen properties */ + Window root_window; + Colormap colormap;/* for 256-color mode */ + short depth; + bool use_stripes; /* use alternate conversion in 256-color mode */ + bool shared_memory; + bool xvideo; +/* at the Window level */ + Window window; /* X11 window number */ + Window parent; /* X11 window number of the parent */ + GC imagegc; /* X11 graphics context (like java.awt.Graphics) */ + XImage *ximage; /* X11 image descriptor */ + uint8 *image; /* the real data (that XImage binds to) */ + bool is_owner; + int32 pos[2]; + P bit_packing; + P dim; + bool lock_size; + bool override_redirect; + t_clock *clock; + std::string title; +#ifdef HAVE_X11_SHARED_MEMORY + XShmSegmentInfo *shm_info; /* to share memory with X11/Unix */ +#endif +#ifdef HAVE_X11_XVIDEO + int xv_format; + int xv_port; + XvImage *xvi; /* ils sont fous ces romains */ + unsigned char *data; + int last_encoding; +#endif + ~FormatX11 () { + clock_unset(clock); + if (is_owner) XDestroyWindow(display,window); + XSync(display,0); + dealloc_image(); + if (imagegc) XFreeGC(display,imagegc); + XCloseDisplay(display); + } + template void frame_by_type (T bogus); + void show_section(int x, int y, int sx, int sy); + void set_wm_hints (); + void dealloc_image (); + bool alloc_image (int sx, int sy); + void resize_window (int sx, int sy); + void open_display(const char *disp_string); + void report_pointer(int y, int x, int state); + void prepare_colormap(); + Window search_window_tree (Window xid, Atom key, const char *value, int level=0); + \constructor (...) { + shared_memory=false; xvideo=false; use_stripes=false; window=0; ximage=0; image=0; is_owner=true; + dim=0; lock_size=false; override_redirect=false; clock=0; imagegc=0; +#ifdef HAVE_X11_SHARED_MEMORY + shm_info=0; +#endif + int sy=240, sx=320; // defaults + argv++, argc--; + t_symbol *domain = argc<1 ? gensym("here") : argv[0]; + int i; + char host[256]; + if (domain==gensym("here")) { + open_display(0); + i=1; + } else if (domain==gensym("local")) { + if (argc<2) RAISE("open x11 local: not enough args"); + sprintf(host,":%d",int32(argv[1])); + open_display(host); + i=2; + } else if (domain==gensym("remote")) { + if (argc<3) RAISE("open x11 remote: not enough args"); + sprintf(host,"%s:%d",string(argv[1]).data(),int32(argv[2])); + open_display(host); + i=3; + } else if (domain==gensym("display")) { + if (argc<2) RAISE("open x11 display: not enough args"); + strcpy(host,string(argv[1]).data()); + for (int k=0; host[k]; k++) if (host[k]=='%') host[k]==':'; + post("mode `display', DISPLAY=`%s'",host); + open_display(host); + i=2; + } else RAISE("x11 destination syntax error"); + for(;i=argc) { + } else { + const t_atom2 &winspec = argv[i]; + if (winspec==gensym("root")) { + window = root_window; + is_owner = false; + } else if (winspec==gensym("embed")) { + string title = argv[i+1]; + sy = sx = pos[0] = pos[1] = 0; + parent = search_window_tree(root_window,XInternAtom(display,"WM_NAME",0),title.data()); + if (parent == 0xDeadBeef) RAISE("Window not found."); + } else if (winspec==gensym("embed_by_id")) { + const char *winspec2 = string(argv[i+1]).data(); + if (strncmp(winspec2,"0x",2)==0) { + parent = strtol(winspec2+2,0,16); + } else { + parent = atoi(winspec2); + } + } else { + if (winspec.a_type==A_SYMBOL) { + const char *winspec2 = string(winspec).data(); + if (strncmp(winspec2,"0x",2)==0) { + window = strtol(winspec2+2,0,16); + } else { + window = atoi(winspec2); // huh? + } + } else { + window = INT(winspec); + } + is_owner = false; + sy = sx = pos[0] = pos[1] = 0; + } + } + resize_window(sx,sy); // "resize" also takes care of creation + if (is_owner) { + Atom wmDeleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display,window,&wmDeleteAtom,1); + } + Visual *v = visual; + int disp_is_le = !ImageByteOrder(display); + int bpp = ximage->bits_per_pixel; + switch(visual->c_class) { + case TrueColor: case DirectColor: { + uint32 masks[3] = { v->red_mask, v->green_mask, v->blue_mask }; + bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks); + } break; + case PseudoColor: { + uint32 masks[3] = { 0x07, 0x38, 0xC0 }; // BBGGGRRR + bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks); + } break; + default: RAISE("huh?"); + } + clock = clock_new(this,(t_method)FormatX11_call); + clock_delay(clock,0); + show_section(0,0,sx,sy); + if ((mode&4)!=0) { + Window root; int x,y; unsigned sx,sy,sb,depth; + XGetGeometry(display,window,&root,&x,&y,&sx,&sy,&sb,&depth); + post("sx=%d sy=%d",sx,sy); + _0_out_size(argc,argv,sy,sx); + } + } + + \decl 0 bang (); + void call (); + \decl 0 out_size (int sy, int sx); + \decl 0 setcursor (int shape); + \decl 0 hidecursor (); + \decl 0 set_geometry (int y, int x, int sy, int sx); + \decl 0 move (int y, int x); + \decl 0 shared_memory (bool toggle); + \decl 0 xvideo (bool toggle); + \decl 0 title (string title=""); + \decl 0 warp (int y, int x); + \grin 0 int +}; + +/* ---------------------------------------------------------------- */ + +void FormatX11::show_section(int x, int y, int sx, int sy) { + if ((mode&2)==0) return; + int zy=dim->get(0), zx=dim->get(1); + if (y>zy||x>zx) return; + if (y+sy>zy) sy=zy-y; + if (x+sx>zx) sx=zx-x; +#ifndef HAVE_X11_XVIDEO + if (xvideo) RAISE("xvideo not available (recompile)"); +#endif +#ifndef HAVE_X11_SHARED_MEMORY + if (shared_memory) RAISE("xshm not available (recompile)"); +#endif + if (xvideo) { +#ifdef HAVE_X11_XVIDEO + if (shared_memory) { +#ifdef HAVE_X11_SHARED_MEMORY + +#endif // shm + } else { + XvPutImage(display,port,window,imagegc,ximage, + xvi, 0, 0, image_width, image_height, + drwX - (vo_panscan_x >> 1), drwY - (vo_panscan_y >> 1), + vo_dwidth + vo_panscan_x, + vo_dheight + vo_panscan_y); + + } +#endif // xvideo + } else { + if (shared_memory) { +#ifdef HAVE_X11_SHARED_MEMORY + XSync(display,False); + XShmPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy,False); + XFlush(display); + //XPutImage( display,window,imagegc,ximage,x,y,x,y,sx,sy); + // should completion events be waited for? looks like a bug +#endif // xshm + } else { + XPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy); + XFlush(display); + } + } +} + +/* window manager hints, defines the window as non-resizable */ +void FormatX11::set_wm_hints () { + if (!is_owner) return; + XWMHints wmh; + char buf[256],*bufp=buf; + if (title=="") { + sprintf(buf,"GridFlow (%d,%d,%d)",dim->get(0),dim->get(1),dim->get(2)); + } else { + sprintf(buf,"%.255s",title.data()); + } + XTextProperty wtitle; XStringListToTextProperty((char **)&bufp, 1, &wtitle); + XSizeHints sh; + sh.flags=PSize|PMaxSize|PMinSize; + sh.min_width = sh.max_width = sh.width = dim->get(1); + sh.min_height = sh.max_height = sh.height = dim->get(0); + wmh.input = True; + wmh.flags = InputHint; + XSetWMProperties(display,window,&wtitle,&wtitle,0,0,&sh,&wmh,0); + XFree(wtitle.value); // do i really have to do that? +} + +void FormatX11::report_pointer(int y, int x, int state) { + t_atom a[3]; + SETFLOAT(a+0,y); + SETFLOAT(a+1,x); + SETFLOAT(a+2,state); + outlet_anything(bself->outlets[0],gensym("position"),COUNT(a),a); +} + +void FormatX11::call() { + XEvent e; + for (;;) { + int xpending = XEventsQueued(display, QueuedAfterFlush); + if (!xpending) break; + XNextEvent(display,&e); + switch (e.type) { + case Expose:{ + XExposeEvent *ex = (XExposeEvent *)&e; + if (mode==2) show_section(ex->x,ex->y,ex->width,ex->height); + }break; + case ButtonPress:{ + XButtonEvent *eb = (XButtonEvent *)&e; + eb->state |= 128<button; + report_pointer(eb->y,eb->x,eb->state); + }break; + case ButtonRelease:{ + XButtonEvent *eb = (XButtonEvent *)&e; + eb->state &= ~(128<button); + report_pointer(eb->y,eb->x,eb->state); + }break; + case KeyPress: + case KeyRelease:{ + XKeyEvent *ek = (XKeyEvent *)&e; + //XLookupString(ek, buf, 63, 0, 0); + char *kss = XKeysymToString(XLookupKeysym(ek, 0)); + char buf[64]; + if (!kss) return; /* unknown keys ignored */ + if (isdigit(*kss)) sprintf(buf,"D%s",kss); else strcpy(buf,kss); + t_atom at[4]; + t_symbol *sel = gensym(const_cast(e.type==KeyPress ? "keypress" : "keyrelease")); + SETFLOAT(at+0,ek->y); + SETFLOAT(at+1,ek->x); + SETFLOAT(at+2,ek->state); + SETSYMBOL(at+3,gensym(buf)); + outlet_anything(bself->outlets[0],sel,4,at); + //XFree(kss); + }break; + case MotionNotify:{ + XMotionEvent *em = (XMotionEvent *)&e; + report_pointer(em->y,em->x,em->state); + }break; + case DestroyNotify:{ + post("This window is being closed, so this handler will close too!"); + delete this; /* really! what else could i do here anyway? */ + return; + }break; + case ConfigureNotify:break; // as if we cared + } + } + clock_delay(clock,20); +} +void FormatX11_call(FormatX11 *p) {p->call();} + +\def 0 bang () { + XGetSubImage(display, window, 0, 0, dim->get(1), dim->get(0), (unsigned)-1, ZPixmap, ximage, 0, 0); + GridOutlet out(this,0,dim,cast); + int sy=dim->get(0), sx=dim->get(1), bs=dim->prod(1); + uint8 b2[bs]; + for(int y=0; ybytes_per_line * y; + bit_packing->unpack(sx,b1,b2); + out.send(bs,b2); + } +} + +/* loathe Xlib's error handlers */ +static FormatX11 *current_x11; +static int FormatX11_error_handler (Display *d, XErrorEvent *xee) { + post("XErrorEvent: type=0x%08x display=0x%08x xid=0x%08x", + xee->type, xee->display, xee->resourceid); + post("... serial=0x%08x error=0x%08x request=0x%08lx minor=0x%08x", + xee->serial, xee->error_code, xee->request_code, xee->minor_code); + if (current_x11->shared_memory==1) { + post("(note: turning shm off)"); + current_x11->shared_memory = 0; + } + return 42; /* it seems that the return value is ignored. */ +} + +bool FormatX11::alloc_image (int sx, int sy) { + dim = new Dim(sy,sx,3); + dealloc_image(); + if (sx==0 || sy==0) return false; + current_x11 = this; + if (!shared_memory) { + ximage = XCreateImage(display,visual,depth,ZPixmap,0,0,sx,sy,8,0); + int size = ximage->bytes_per_line*ximage->height; + if (!ximage) RAISE("can't create image"); + image = new uint8[size]; + ximage->data = (int8 *)image; + } else { +#ifdef HAVE_X11_SHARED_MEMORY + shm_info = new XShmSegmentInfo; + ximage = XShmCreateImage(display,visual,depth,ZPixmap,0,shm_info,sx,sy); + if (!ximage) {post("x11: will retry without shared memory"); shared_memory=false;} + XSync(display,0); + if (!shared_memory) return alloc_image(sx,sy); + int size = ximage->bytes_per_line*ximage->height; + shm_info->shmid = shmget(IPC_PRIVATE,size,IPC_CREAT|0777); + if(shm_info->shmid < 0) RAISE("shmget() failed: %s",strerror(errno)); + ximage->data = shm_info->shmaddr = (char *)shmat(shm_info->shmid,0,0); + if ((long)(shm_info->shmaddr) == -1) RAISE("shmat() failed: %s",strerror(errno)); + image = (uint8 *)ximage->data; + shm_info->readOnly = False; + if (!XShmAttach(display, shm_info)) RAISE("ERROR: XShmAttach: big problem"); + XSync(display,0); // make sure the server picks it up + // yes, this can be done now. should cause auto-cleanup. + shmctl(shm_info->shmid,IPC_RMID,0); + if (!shared_memory) return alloc_image(sx,sy); +#endif + } +#ifdef HAVE_X11_XVIDEO + if (xvideo) { + unsigned int ver, rel, req, ev, err, i, j, adaptors, formats; + XvAdaptorInfo *ai; + if (Success != XvQueryExtension(display,&ver,&rel,&req,&ev,&err)) RAISE("XvQueryExtension problem"); + /* find + lock port */ + if (Success != XvQueryAdaptors(display,DefaultRootWindow(display),&adaptors,&ai)) RAISE("XvQueryAdaptors problem"); + for (i = 0; i < adaptors; i++) { + if (ai[i].type&XvInputMask && ai[i].type&XvImageMask) { + for (j=0; jdata); + XShmDetach(display,shm_info); + if (shm_info) {delete shm_info; shm_info=0;} + XFree(ximage); + ximage = 0; + image = 0; +#endif + } + if (xvideo) { +#ifdef HAVE_X11_XVIDEO + //if (data) delete[] data; + if (xvi) XFree(xvi); + xvi=0; + //data=0; +#endif + } +} + +void FormatX11::resize_window (int sx, int sy) { + if (sy<16) sy=16; if (sy>4096) RAISE("height too big"); + if (sx<16) sx=16; if (sx>4096) RAISE("width too big"); + alloc_image(sx,sy); + if (window) { + if (is_owner && !lock_size) { + set_wm_hints(); + XResizeWindow(display,window,sx,sy); + } + } else { + XSetWindowAttributes xswa; + xswa.do_not_propagate_mask = 0; //? + xswa.override_redirect = override_redirect; //#!@#$ + window = XCreateWindow(display, + parent, pos[1], pos[0], sx, sy, 0, + CopyFromParent, InputOutput, CopyFromParent, + CWOverrideRedirect|CWDontPropagate, &xswa); + if(!window) RAISE("can't create window"); + set_wm_hints(); + + XSelectInput(display, window, + ExposureMask|StructureNotifyMask|PointerMotionMask| + ButtonPressMask|ButtonReleaseMask|ButtonMotionMask| + KeyPressMask|KeyReleaseMask); + + if (is_owner) XMapRaised(display, window); + imagegc = XCreateGC(display, window, 0, NULL); + if (visual->c_class == PseudoColor) prepare_colormap(); + } + XSync(display,0); +} + +GRID_INLET(0) { + if (in->dim->n != 3) + RAISE("expecting 3 dimensions: rows,columns,channels"); + if (in->dim->get(2)!=3 && in->dim->get(2)!=4) + RAISE("expecting 3 or 4 channels: red,green,blue,ignored (got %d)",in->dim->get(2)); + int sx = in->dim->get(1), osx = dim->get(1); + int sy = in->dim->get(0), osy = dim->get(0); + in->set_chunk(1); + if (sx!=osx || sy!=osy) resize_window(sx,sy); + if (in->dim->get(2)!=bit_packing->size) { + bit_packing->mask[3]=0; + bit_packing = new BitPacking(bit_packing->endian, + bit_packing->bytes, in->dim->get(2), bit_packing->mask); + } +} GRID_FLOW { + int bypl = ximage->bytes_per_line; + int sxc = in->dim->prod(1); + int sx = in->dim->get(1); + int y = dex/sxc; + for (; n>0; y++, data+=sxc, n-=sxc) { + // convert line + if (use_stripes) { + int o=y*bypl; + for (int x=0, i=0, k=y%3; x>2; + } + } else { + bit_packing->pack(sx, data, image+y*bypl); + } + } +} GRID_FINISH { + show_section(0,0,in->dim->get(1),in->dim->get(0)); +} GRID_END + +\def 0 out_size (int sy, int sx) { resize_window(sx,sy); } + +\def 0 setcursor (int shape) { + shape = 2*(shape&63); + Cursor c = XCreateFontCursor(display,shape); + XDefineCursor(display,window,c); + XFlush(display); +} + +\def 0 hidecursor () { + Font font = XLoadFont(display,"fixed"); + XColor color; /* bogus */ + Cursor c = XCreateGlyphCursor(display,font,font,' ',' ',&color,&color); + XDefineCursor(display,window,c); + XFlush(display); +} + +void FormatX11::prepare_colormap() { + Colormap colormap = XCreateColormap(display,window,visual,AllocAll); + XColor colors[256]; + if (use_stripes) { + for (int i=0; i<192; i++) { + int k=(i&63)*0xffff/63; + colors[i].pixel = i; + colors[i].red = (i>>6)==0 ? k : 0; + colors[i].green = (i>>6)==1 ? k : 0; + colors[i].blue = (i>>6)==2 ? k : 0; + colors[i].flags = DoRed | DoGreen | DoBlue; + } + XStoreColors(display,colormap,colors,192); + } else { + for (int i=0; i<256; i++) { + colors[i].pixel = i; + colors[i].red = ((i>>0)&7)*0xffff/7; + colors[i].green = ((i>>3)&7)*0xffff/7; + colors[i].blue = ((i>>6)&3)*0xffff/3; + colors[i].flags = DoRed | DoGreen | DoBlue; + } + XStoreColors(display,colormap,colors,256); + } + XSetWindowColormap(display,window,colormap); +} + +void FormatX11::open_display(const char *disp_string) { + display = XOpenDisplay(disp_string); + if(!display) RAISE("ERROR: opening X11 display: %s",strerror(errno)); + // btw don't expect too much from Xlib error handling. + // Xlib, you are so free of the ravages of intelligence... + XSetErrorHandler(FormatX11_error_handler); + Screen *screen = DefaultScreenOfDisplay(display); + int screen_num = DefaultScreen(display); + visual = DefaultVisual(display, screen_num); + root_window = DefaultRootWindow(display); + depth = DefaultDepthOfScreen(screen); + colormap = 0; + + switch(visual->c_class) { + // without colormap + case TrueColor: case DirectColor: break; + // with colormap + case PseudoColor: if (depth!=8) RAISE("ERROR: with colormap, only supported depth is 8 (got %d)", depth); break; + default: RAISE("ERROR: visual type not supported (got %d)", visual->c_class); + } + +#if defined(HAVE_X11_XVIDEO) + xvideo = true; +#elif defined(HAVE_X11_SHARED_MEMORY) + shared_memory = !! XShmQueryExtension(display); +#else + shared_memory = false; +#endif +} + +Window FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level) { + if (level>2) return 0xDeadBeef; + Window root_r, parent_r; + Window *children_r; + unsigned int nchildren_r; + XQueryTree(display,xid,&root_r,&parent_r,&children_r,&nchildren_r); + Window target = 0xDeadBeef; + for (int i=0; i<(int)nchildren_r; i++) { + Atom actual_type_r; + int actual_format_r; + unsigned long nitems_r, bytes_after_r; + unsigned char *prop_r; + XGetWindowProperty(display,children_r[i],key,0,666,0,AnyPropertyType, + &actual_type_r,&actual_format_r,&nitems_r,&bytes_after_r,&prop_r); + uint32 value_l = strlen(value); + bool match = prop_r && nitems_r>=value_l && + strncmp((char *)prop_r+nitems_r-value_l,value,value_l)==0; + XFree(prop_r); + if (match) {target=children_r[i]; break;} + target = search_window_tree(children_r[i],key,value,level+1); + if (target != 0xDeadBeef) break; + } + if (children_r) XFree(children_r); + return target; +} + +\def 0 move (int y, int x) { + pos[0]=y; pos[1]=x; + XMoveWindow(display,window,x,y); + XFlush(display); +} + +\def 0 set_geometry (int y, int x, int sy, int sx) { + pos[0]=y; pos[1]=x; + XMoveWindow(display,window,x,y); + resize_window(sx,sy); + XFlush(display); +} + +\def 0 shared_memory (bool toggle) {shared_memory = toggle;} +\def 0 xvideo (bool toggle) {xvideo = toggle;} + +\def 0 warp (int y, int x) { + XWarpPointer(display,None,None,0,0,0,0,x,y); + XFlush(display); +} + +\def 0 title (string title="") {this->title = title; set_wm_hints();} + +\end class FormatX11 {install_format("#io.x11",6,"");} +void startup_x11 () { + \startall +} + -- cgit v1.2.1