From 5e2a1bc9e56003349e533f7e5841041ba5c04e28 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Tue, 4 Oct 2005 02:02:15 +0000 Subject: starting to commit gridflow 0.8.0 ... if you know how to use "cvs import" please mail me and i'll use it for 0.8.1 svn path=/trunk/; revision=3646 --- externals/gridflow/format/x11.c | 655 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 externals/gridflow/format/x11.c (limited to 'externals/gridflow/format/x11.c') diff --git a/externals/gridflow/format/x11.c b/externals/gridflow/format/x11.c new file mode 100644 index 00000000..62041f06 --- /dev/null +++ b/externals/gridflow/format/x11.c @@ -0,0 +1,655 @@ +/* + $Id: x11.c,v 1.1 2005-10-04 02:02:15 matju Exp $ + + GridFlow + Copyright (c) 2001,2002,2003 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. +*/ + +#include "../base/grid.h.fcs" +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* X11 Error Handler type */ +typedef int (*XEH)(Display *, XErrorEvent *); + +#ifdef HAVE_X11_SHARED_MEMORY +#include +#include +#include +#endif + +\class FormatX11 < Format +struct FormatX11 : Format { + template void frame_by_type (T bogus); +/* at the Display/Screen level */ + Display *display; /* connection to xserver */ + Visual *visual; /* screen properties */ + Window root_window; + Colormap colormap; /* for 256-color mode */ + long black,white; /* color numbers in default colormap */ + short depth; + bool use_shm; /* should use shared memory? */ + bool use_stripes; /* use alternate conversion in 256-color mode */ + +/* at the Window level */ + int autodraw; /* how much to send to the display at once */ + 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 */ + char *name; /* window name (for use by window manager) */ + Pt image; /* the real data (that XImage binds to) */ + bool is_owner; + int pos_x, pos_y; + + P bit_packing; + P dim; + bool lock_size; + bool override_redirect; + +#ifdef HAVE_X11_SHARED_MEMORY + XShmSegmentInfo *shm_info; /* to share memory with X11/Unix */ +#endif + + Atom wmProtocolsAtom; + Atom wmDeleteAtom; + + FormatX11 () : use_stripes(false), + autodraw(1), window(0), ximage(0), name(0), image(Pt()), is_owner(true), + dim(0), lock_size(false), override_redirect(false) +#ifdef HAVE_X11_SHARED_MEMORY + , shm_info(0) +#endif + {} + + void show_section(int x, int y, int sx, int sy); + void set_wm_hints (int sx, int sy); + 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 FormatX11::search_window_tree (Window xid, Atom key, const char *value, int level=0); + + \decl void initialize (...); + \decl void frame (); + \decl void close (); + \decl void call (); + \decl void _0_out_size (int sy, int sx); + \decl void _0_draw (); + \decl void _0_autodraw (int autodraw); + \decl void _0_setcursor (int shape); + \decl void _0_hidecursor (); + \decl void _0_set_geometry (int y, int x, int sy, int sx); + \decl void _0_fall_thru (int flag); + \grin 0 int +}; + +/* ---------------------------------------------------------------- */ + +void FormatX11::show_section(int x, int y, int sx, int sy) { + if (y>dim->get(0)||x>dim->get(1)) return; + if (y+sy>dim->get(0)) sy=dim->get(0)-y; + if (x+sx>dim->get(1)) sx=dim->get(1)-x; +#ifdef HAVE_X11_SHARED_MEMORY + if (use_shm) { + XSync(display,False); + XShmPutImage(display,window,imagegc,ximage,x,y,x,y,sx,sy,False); + /* should completion events be waited for? looks like a bug */ + } else +#endif + 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 (int sx, int sy) { + if (!is_owner) return; + XWMHints wmhints; + XTextProperty window_name, icon_name; + XSizeHints hints; + // enable recommended, maximum and minimum size + hints.flags=PSize|PMaxSize|PMinSize; + // set those + hints.min_width = hints.max_width = hints.width = sx; + hints.min_height = hints.max_height = hints.height = sy; + + wmhints.input = True; + wmhints.flags = InputHint; + XStringListToTextProperty((char **)&name, 1, &window_name); + XStringListToTextProperty((char **)&name, 1, &icon_name); + XSetWMProperties(display, window, + &window_name, &icon_name, NULL, 0, &hints, &wmhints, NULL); +} + +void FormatX11::report_pointer(int y, int x, int state) { + Ruby argv[5] = { + INT2NUM(0), SYM(position), + INT2NUM(y), INT2NUM(x), INT2NUM(state) }; + send_out(COUNT(argv),argv); +} + +\def void 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 (rb_ivar_get(rself,SI(@mode)) == SYM(out)) { + 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); + Ruby argv[6] = { + INT2NUM(0), e.type==KeyPress ? SYM(keypress) : SYM(keyrelease), + INT2NUM(ek->y), INT2NUM(ek->x), INT2NUM(ek->state), + rb_funcall(rb_str_new2(buf),SI(intern),0) }; + send_out(COUNT(argv),argv); + //XFree(kss); + }break; + case MotionNotify:{ + XMotionEvent *em = (XMotionEvent *)&e; + report_pointer(em->y,em->x,em->state); + }break; + case DestroyNotify:{ + gfpost("This window is being closed, so this handler will close too!"); + rb_funcall(rself,SI(close),0); + return; + }break; + case ConfigureNotify:break; // as if we cared + case ClientMessage:{ + // tnx to vektor&walken + /* + if (e.xclient.message_type==wmProtocolsAtom + && e.xclient.format==32 + && (Atom)(e.xclient.data.l[0])==wmDeleteAtom) { + gfpost("This window is being closed, so this handler will close too!"); + rb_funcall(rself,SI(close),0); + return; + } + */ + }break; + } + } + IEVAL(rself,"@clock.delay 20"); +} + +\def void frame () { + XGetSubImage(display, window, + 0, 0, dim->get(1), dim->get(0), + (unsigned)-1, ZPixmap, ximage, 0, 0); + GridOutlet out(this,0,dim,NumberTypeE_find(rb_ivar_get(rself,SI(@cast)))); + int sy=dim->get(0), sx=dim->get(1); + int bs=dim->prod(1); + STACK_ARRAY(uint8,b2,bs); + for(int y=0; y b1 = Pt(image,ximage->bytes_per_line*dim->get(0)) + + ximage->bytes_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) { + gfpost("X11 reports Error: display=0x%08x",(int)d); + gfpost("serial=0x%08x error=0x%08x request=0x%08lx minor=0x%08x", + xee->serial, xee->error_code, xee->request_code, xee->minor_code); + current_x11->use_shm = false; + return 42; /* it seems that the return value is ignored. */ +} + +void FormatX11::dealloc_image () { + if (!ximage) return; + if (use_shm) { + #ifdef HAVE_X11_SHARED_MEMORY + shmdt(ximage->data); + XShmDetach(display,shm_info); + if (shm_info) {delete shm_info; shm_info=0;} + //ximage->data = new char[1]; // bogus + //ximage->data = 0; + //XDestroyImage(ximage); + XFree(ximage); + ximage = 0; + image = Pt(); + #endif + } else { + //XDestroyImage(ximage); + XFree(ximage); + ximage = 0; + image = Pt(); + } +} + +bool FormatX11::alloc_image (int sx, int sy) { + dim = new Dim(sy, sx, 3); + dealloc_image(); + if (sx==0 || sy==0) return false; +#ifdef HAVE_X11_SHARED_MEMORY + if (use_shm) { + shm_info = new XShmSegmentInfo; + ximage = XShmCreateImage(display,visual,depth,ZPixmap,0,shm_info,sx,sy); + if (!ximage) { + gfpost("shm got disabled (1), retrying..."); + return alloc_image(sx,sy);} + shm_info->shmid = shmget(IPC_PRIVATE, + ximage->bytes_per_line*ximage->height, IPC_CREAT|0777); + if(shm_info->shmid < 0) + RAISE("ERROR: shmget failed: %s",strerror(errno)); + ximage->data = shm_info->shmaddr = + (char *)shmat(shm_info->shmid,0,0); + image = Pt((uint8 *)ximage->data, + ximage->bytes_per_line*sy); + shm_info->readOnly = False; + current_x11 = this; + //XSetErrorHandler(FormatX11_error_handler); + if (!XShmAttach(display, shm_info)) RAISE("ERROR: XShmAttach: big problem"); + XSync(display,0); // make sure the server picks it up + //XSetErrorHandler(0); + /* yes, this can be done now. should cause auto-cleanup. */ + shmctl(shm_info->shmid,IPC_RMID,0); + if (!use_shm) { + gfpost("shm got disabled (2), retrying..."); + return alloc_image(sx,sy);} + } else +#endif + ximage = XCreateImage(display,visual,depth,ZPixmap,0,0,sx,sy,8,0); + if (!ximage) RAISE("can't create image"); + image = ARRAY_NEW(uint8,ximage->bytes_per_line*sy); + ximage->data = (int8 *)image; + int status = XInitImage(ximage); + if (status!=1) gfpost("XInitImage returned: %d", status); + return true; +} + +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 (name) delete name; + name = new char[64]; + sprintf(name,"GridFlow (%d,%d,3)",sy,sx); + if (window) { + if (is_owner && !lock_size) { + set_wm_hints(sx,sy); + XResizeWindow(display,window,sx,sy); + } + } else { + XSetWindowAttributes xswa; + xswa.do_not_propagate_mask = 0; //? + xswa.override_redirect = override_redirect; //#!@#$ + window = XCreateWindow(display, + parent, pos_x, pos_y, sx, sy, 0, + CopyFromParent, InputOutput, CopyFromParent, + CWOverrideRedirect|CWDontPropagate, &xswa); + if(!window) RAISE("can't create window"); + set_wm_hints(sx,sy); + if (is_owner) { + // fall_thru 0 + XSelectInput(display, window, + ExposureMask | StructureNotifyMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | + KeyPressMask | KeyReleaseMask); + XMapRaised(display, window); + } else { + // fall_thru 1 + XSelectInput(display, window, + ExposureMask | StructureNotifyMask); + } + imagegc = XCreateGC(display, window, 0, NULL); + if (visual->c_class == PseudoColor) prepare_colormap(); + } + XSync(display,0); +} + +GRID_INLET(FormatX11,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 sxc = in->dim->prod(1); + int sx = in->dim->get(1), osx = dim->get(1); + int sy = in->dim->get(0), osy = dim->get(0); + in->set_factor(sxc); + 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 = in->dex/sxc; + int oy = y; + 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); + } + } + if (autodraw==2) show_section(0,oy,sx,y-oy); +} GRID_FINISH { + if (autodraw==1) show_section(0,0,in->dim->get(1),in->dim->get(0)); +} GRID_END + +\def void close () { + if (!this) RAISE("stupid error: trying to close display NULL. =)"); + bit_packing=0; + IEVAL(rself,"@clock.unset"); + if (is_owner) XDestroyWindow(display,window); + XSync(display,0); + dealloc_image(); + XCloseDisplay(display); + display=0; + rb_call_super(argc,argv); +} + +\def void _0_out_size (int sy, int sx) { resize_window(sx,sy); } +\def void _0_draw () { show_section(0,0,dim->get(1),dim->get(0)); } + +\def void _0_autodraw (int autodraw) { + if (autodraw<0 || autodraw>2) + RAISE("autodraw=%d is out of range",autodraw); + this->autodraw = autodraw; +} + +\def void _0_setcursor (int shape) { + shape = 2*(shape&63); + Cursor c = XCreateFontCursor(display,shape); + XDefineCursor(display,window,c); + XFlush(display); +} + +\def void _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) { + int screen_num; + Screen *screen; + + // Open an X11 connection + 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 = DefaultScreenOfDisplay(display); + screen_num = DefaultScreen(display); + visual = DefaultVisual(display, screen_num); + white = XWhitePixel(display,screen_num); + black = XBlackPixel(display,screen_num); + root_window = DefaultRootWindow(display); + depth = DefaultDepthOfScreen(screen); + colormap = 0; + + switch(visual->c_class) { + case TrueColor: case DirectColor: /* without colormap */ + break; + case PseudoColor: /* with colormap */ + 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); + } + +#ifdef HAVE_X11_SHARED_MEMORY + use_shm = !! XShmQueryExtension(display); +#else + use_shm = 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 void _0_set_geometry (int y, int x, int sy, int sx) { + pos_x = x; + pos_y = y; + XMoveWindow(display,window,x,y); + resize_window(sx,sy); + XFlush(display); +} + +\def void _0_fall_thru (int flag) { + if (flag) { + gfpost("falling through!"); + XSelectInput(display, window, + ExposureMask | StructureNotifyMask); + } else { + gfpost("NOT falling through!"); + XSelectInput(display, window, + ExposureMask | StructureNotifyMask | + PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); + } + XFlush(display); +} + +\def void initialize (...) { + int sy=240, sx=320; // defaults + rb_call_super(argc,argv); + argv++, argc--; + VALUE domain = argc<1 ? SYM(here) : argv[0]; + int i; + if (domain==SYM(here)) { + open_display(0); + i=1; + } else if (domain==SYM(local)) { + if (argc<2) RAISE("open x11 local: not enough args"); + char host[256]; + int dispnum = NUM2INT(argv[1]); + sprintf(host,":%d",dispnum); + open_display(host); + i=2; + } else if (domain==SYM(remote)) { + if (argc<3) RAISE("open x11 remote: not enough args"); + char host[256]; + strcpy(host,rb_sym_name(argv[1])); + int dispnum = NUM2INT(argv[2]); + sprintf(host+strlen(host),":%d",dispnum); + open_display(host); + i=3; + } else if (domain==SYM(display)) { + if (argc<2) RAISE("open x11 display: not enough args"); + char host[256]; + strcpy(host,rb_sym_name(argv[1])); + for (int k=0; host[k]; k++) if (host[k]=='%') host[k]==':'; + gfpost("mode `display', DISPLAY=`%s'",host); + open_display(host); + i=2; + } else { + RAISE("x11 destination syntax error"); + } + + for(;i=argc) { + } else { + VALUE winspec = argv[i]; + if (winspec==SYM(root)) { + window = root_window; + is_owner = false; + } else if (winspec==SYM(embed)) { + Ruby title_s = rb_funcall(argv[i+1],SI(to_s),0); + char *title = strdup(rb_str_ptr(title_s)); + sy = sx = pos_y = pos_x = 0; + parent = search_window_tree(root_window,XInternAtom(display,"WM_NAME",0),title); + free(title); + if (parent == 0xDeadBeef) RAISE("Window not found."); + } else if (winspec==SYM(embed_by_id)) { + const char *winspec2 = rb_sym_name(argv[i+1]); + if (strncmp(winspec2,"0x",2)==0) { + parent = strtol(winspec2+2,0,16); + } else { + parent = atoi(winspec2); + } + } else { + if (TYPE(winspec)==T_SYMBOL) { + const char *winspec2 = rb_sym_name(winspec); + 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_y = pos_x = 0; + } + } + + // "resize" also takes care of creation + resize_window(sx,sy); + + if (is_owner) { + wmProtocolsAtom = XInternAtom(display, "WM_PROTOCOLS", False); + 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 }; + bit_packing = new BitPacking(disp_is_le, bpp/8, 3, masks); + } break; + } + IEVAL(rself,"@clock = Clock.new self; @clock.delay 0"); +} + +\classinfo { + IEVAL(rself,"install '#io:x11',1,1;@mode=6;@comment='X Window System Version 11.5'"); +} +\end class FormatX11 +void startup_x11 () { + \startall +} -- cgit v1.2.1