/* * Grace - GRaphing, Advanced Computation and Exploration of data * * Home page: http://plasma-gate.weizmann.ac.il/Grace/ * * Copyright (c) 1991-1995 Paul J Turner, Portland, OR * Copyright (c) 1996-2003 Grace Development Team * * Maintained by Evgeny Stambulchik * * * All Rights Reserved * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * * driver for X11 for Grace * */ #include #include #include "defines.h" #include #include #include "globals.h" #include "utils.h" #include "device.h" #include "devlist.h" #include "draw.h" #include "graphs.h" #include "patterns.h" #include "x11drv.h" #include "protos.h" extern Display *disp; extern Window xwin; Window root; int screennumber; GC gc, gcxor; int depth; static Visual *visual; static int pixel_size; int install_cmap = CMAP_INSTALL_AUTO; static int private_cmap = FALSE; unsigned long xvlibcolors[MAXCOLORS]; Colormap cmap; static Pixmap curtile; static Pixmap displaybuff = (Pixmap) NULL; static int xlibcolor; static int xlibbgcolor; static int xlibpatno; static int xliblinewidth; static int xliblinestyle; static int xlibfillrule; static int xlibarcfillmode; static int xliblinecap; static int xliblinejoin; unsigned int win_h = 0, win_w = 0; #define win_scale ((win_h < win_w) ? win_h:win_w) Pixmap resize_bufpixmap(unsigned int w, unsigned int h); static Device_entry dev_x11 = {DEVICE_TERM, "X11", xlibinitgraphics, NULL, NULL, "", FALSE, TRUE, {DEFAULT_PAGE_WIDTH, DEFAULT_PAGE_HEIGHT, 72.0}, NULL }; int register_x11_drv(void) { long mrsize; int max_path_limit; /* XExtendedMaxRequestSize() appeared in X11R6 */ #if XlibSpecificationRelease > 5 mrsize = XExtendedMaxRequestSize(disp); #else mrsize = 0; #endif if (mrsize <= 0) { mrsize = XMaxRequestSize(disp); } max_path_limit = (mrsize - 3)/2; if (max_path_limit < get_max_path_limit()) { char buf[128]; sprintf(buf, "Setting max drawing path length to %d (limited by the X server)", max_path_limit); errmsg(buf); set_max_path_limit(max_path_limit); } dev_x11.pg.dpi = rint(MM_PER_INCH*DisplayWidth(disp, screennumber)/ DisplayWidthMM(disp, screennumber)); return register_device(dev_x11); } int xlibinit(void) { XGCValues gc_val; XPixmapFormatValues *pmf; int i, n; screennumber = DefaultScreen(disp); visual = DefaultVisual(disp, screennumber); root = RootWindow(disp, screennumber); gc = DefaultGC(disp, screennumber); depth = DisplayPlanes(disp, screennumber); pixel_size = 0; pmf = XListPixmapFormats (disp, &n); if (pmf) { for (i = 0; i < n; i++) { if (pmf[i].depth == depth) { pixel_size = pmf[i].bits_per_pixel/8; break; } } XFree ((char *) pmf); } if (pixel_size == 0) { monomode = TRUE; } /* * init colormap */ cmap = DefaultColormap(disp, screennumber); /* redefine colormap, if needed */ if (install_cmap == CMAP_INSTALL_ALWAYS) { cmap = XCopyColormapAndFree(disp, cmap); private_cmap = TRUE; } xlibinitcmap(); /* * set GCs */ gc_val.foreground = xvlibcolors[0]; gc_val.background = xvlibcolors[1]; if (invert) { gc_val.function = GXinvert; } else { gc_val.function = GXxor; } gcxor = XCreateGC(disp, root, GCFunction | GCForeground, &gc_val); displaybuff = resize_bufpixmap(win_w, win_h); /* * tile pixmap for patterns */ curtile = XCreatePixmap(disp, root, 16, 16, depth); if (curtile == (Pixmap) NULL) { errmsg("Error allocating tile pixmap"); return RETURN_FAILURE; } /* * disable font AA in mono mode */ if (monomode == TRUE) { Device_entry dev; dev = get_device_props(tdevice); dev.fontaa = FALSE; set_device_props(tdevice, dev); } return RETURN_SUCCESS; } int xconvxlib(double x) { return ((int) rint(win_scale * x)); } int yconvxlib(double y) { return ((int) rint(win_h - win_scale * y)); } void xlibVPoint2dev(VPoint vp, int *x, int *y) { *x = xconvxlib(vp.x); *y = yconvxlib(vp.y); } XPoint VPoint2XPoint(VPoint vp) { XPoint xp; xp.x = xconvxlib(vp.x); xp.y = yconvxlib(vp.y); return(xp); } /* * xlibdev2VPoint - given (x,y) in screen coordinates, return the * viewport coordinates */ VPoint xlibdev2VPoint(int x, int y) { VPoint vp; if (win_scale == 0) { vp.x = (double) 0.0; vp.y = (double) 0.0; } else { vp.x = (double) x / win_scale; vp.y = (double) (win_h - y) / win_scale; } return (vp); } void xlibupdatecmap(void) { /* TODO: replace!!! */ if (inwin) { xlibinitcmap(); } } void xlibinitcmap(void) { int i; RGB *prgb; XColor xc[MAXCOLORS]; for (i = 0; i < MAXCOLORS; i++) { xc[i].pixel = 0; xc[i].flags = DoRed | DoGreen | DoBlue; } for (i = 0; i < number_of_colors(); i++) { /* even in mono, b&w must be allocated */ if (monomode == FALSE || i < 2) { prgb = get_rgb(i); if (prgb != NULL) { xc[i].red = prgb->red << (16 - GRACE_BPP); xc[i].green = prgb->green << (16 - GRACE_BPP); xc[i].blue = prgb->blue << (16 - GRACE_BPP); if (XAllocColor(disp, cmap, &xc[i])) { xvlibcolors[i] = xc[i].pixel; } else { if (install_cmap != CMAP_INSTALL_NEVER && private_cmap == FALSE) { cmap = XCopyColormapAndFree(disp, cmap); private_cmap = TRUE; /* will try to allocate the same color * in the private colormap */ i--; } else { /* really bad */ xvlibcolors[i] = xvlibcolors[1]; /* * errmsg("Can't allocate color"); */ } } } } else { xvlibcolors[i] = xvlibcolors[1]; } } } int xlibinitgraphics(void) { int i, j; double step; XPoint xp; if (inwin == FALSE) { return RETURN_FAILURE; } xlibcolor = BAD_COLOR; xlibbgcolor = BAD_COLOR; xlibpatno = -1; xliblinewidth = -1; xliblinestyle = -1; xlibfillrule = -1; xlibarcfillmode = -1; xliblinecap = -1; xliblinejoin = -1; /* device-dependent routines */ devupdatecmap = xlibupdatecmap; devdrawpixel = xlibdrawpixel; devdrawpolyline = xlibdrawpolyline; devfillpolygon = xlibfillpolygon; devdrawarc = xlibdrawarc; devfillarc = xlibfillarc; devputpixmap = xlibputpixmap; devleavegraphics = xlibleavegraphics; /* init settings specific to X11 driver */ if (get_pagelayout() == PAGE_FIXED) { sync_canvas_size(&win_w, &win_h, FALSE); } else { sync_canvas_size(&win_w, &win_h, TRUE); } displaybuff = resize_bufpixmap(win_w, win_h); xlibupdatecmap(); XSetForeground(disp, gc, xvlibcolors[0]); XSetFillStyle(disp, gc, FillSolid); XFillRectangle(disp, displaybuff, gc, 0, 0, win_w, win_h); XSetForeground(disp, gc, xvlibcolors[1]); step = (double) win_scale/10; for (i = 0; i < win_w/step; i++) { for (j = 0; j < win_h/step; j++) { xp.x = rint(i*step); xp.y = win_h - rint(j*step); XDrawPoint(disp, displaybuff, gc, xp.x, xp.y); } } XSetLineAttributes(disp, gc, 1, LineSolid, CapButt, JoinMiter); XDrawRectangle(disp, displaybuff, gc, 0, 0, win_w - 1, win_h - 1); return RETURN_SUCCESS; } void xlib_setpen(void) { int fg, bg, p; Pixmap ptmp; fg = getcolor(); bg = getbgcolor(); p = getpattern(); if ((fg == xlibcolor) && (bg == xlibbgcolor) && (p == xlibpatno)) { return; } if (fg != xlibcolor) { XSetForeground(disp, gc, xvlibcolors[fg]); xlibcolor = fg; } if (bg != xlibbgcolor) { XSetBackground(disp, gc, xvlibcolors[bg]); xlibbgcolor = bg; } if (p >= number_of_patterns() || p < 0) { p = 0; } xlibpatno = p; if (p == 0) { /* TODO: transparency !!!*/ return; } else if (p == 1) { /* To make X faster */ XSetFillStyle(disp, gc, FillSolid); } else { /* TODO: implement cache ? */ ptmp = XCreateBitmapFromData(disp, root, (char *) pat_bits[p], 16, 16); XCopyPlane(disp, ptmp, curtile, gc, 0, 0, 16, 16, 0, 0, 1); XFreePixmap(disp, ptmp); /* * XSetFillStyle(disp, gc, FillStippled); * XSetStipple(disp, gc, curstipple); */ XSetFillStyle(disp, gc, FillTiled); XSetTile(disp, gc, curtile); return; } } void xlib_setdrawbrush(void) { unsigned int iw; int style; int lc, lj; int i, scale, darr_len; char *xdarr; xlib_setpen(); iw = (unsigned int) rint(getlinewidth()*win_scale); if (iw == 1) { iw = 0; } style = getlinestyle(); lc = getlinecap(); lj = getlinejoin(); switch (lc) { case LINECAP_BUTT: lc = CapButt; break; case LINECAP_ROUND: lc = CapRound; break; case LINECAP_PROJ: lc = CapProjecting; break; } switch (lj) { case LINEJOIN_MITER: lj = JoinMiter; break; case LINEJOIN_ROUND: lj = JoinRound; break; case LINEJOIN_BEVEL: lj = JoinBevel; break; } if (iw != xliblinewidth || style != xliblinestyle || lc != xliblinecap || lj != xliblinejoin) { if (style > 1) { darr_len = dash_array_length[style]; xdarr = xmalloc(darr_len*SIZEOF_CHAR); if (xdarr == NULL) { return; } scale = MAX2(1, iw); for (i = 0; i < darr_len; i++) { xdarr[i] = scale*dash_array[style][i]; } XSetLineAttributes(disp, gc, iw, LineOnOffDash, lc, lj); XSetDashes(disp, gc, 0, xdarr, darr_len); xfree(xdarr); } else if (style == 1) { XSetLineAttributes(disp, gc, iw, LineSolid, lc, lj); } xliblinestyle = style; xliblinewidth = iw; xliblinecap = lc; xliblinejoin = lj; } return; } void xlibdrawpixel(VPoint vp) { XPoint xp; xp = VPoint2XPoint(vp); xlib_setpen(); XDrawPoint(disp, displaybuff, gc, xp.x, xp.y); } void xlibdrawpolyline(VPoint *vps, int n, int mode) { int i, xn = n; XPoint *p; if (n <= 1 || getlinestyle() == 0 || getpattern() == 0) { return; } if (mode == POLYLINE_CLOSED) { xn++; } p = xmalloc(xn*sizeof(XPoint)); if (p == NULL) { return; } for (i = 0; i < n; i++) { p[i] = VPoint2XPoint(vps[i]); } if (mode == POLYLINE_CLOSED) { p[n] = p[0]; } xlib_setdrawbrush(); XDrawLines(disp, displaybuff, gc, p, xn, CoordModeOrigin); xfree(p); } void xlibfillpolygon(VPoint *vps, int npoints) { int i; XPoint *p; if (npoints < 3 || getpattern() == 0) { return; } p = (XPoint *) xmalloc(npoints*sizeof(XPoint)); if (p == NULL) { return; } for (i = 0; i < npoints; i++) { p[i] = VPoint2XPoint(vps[i]); } xlib_setpen(); if (getfillrule() != xlibfillrule) { xlibfillrule = getfillrule(); if (getfillrule() == FILLRULE_WINDING) { XSetFillRule(disp, gc, WindingRule); } else { XSetFillRule(disp, gc, EvenOddRule); } } XFillPolygon(disp, displaybuff, gc, p, npoints, Complex, CoordModeOrigin); xfree(p); } /* * xlibdrawarc */ void xlibdrawarc(VPoint vp1, VPoint vp2, int angle1, int angle2) { int x1, y1, x2, y2; xlibVPoint2dev(vp1, &x1, &y2); xlibVPoint2dev(vp2, &x2, &y1); if (getlinestyle() == 0 || getpattern() == 0) { return; } xlib_setdrawbrush(); if (x1 != x2 || y1 != y2) { XDrawArc(disp, displaybuff, gc, MIN2(x1, x2), MIN2(y1, y2), abs(x2 - x1), abs(y2 - y1), 64 * angle1, 64 * (angle2 - angle1)); } else { /* zero radius */ XDrawPoint(disp, displaybuff, gc, x1, y1); } } /* * xlibfillarc */ void xlibfillarc(VPoint vp1, VPoint vp2, int angle1, int angle2, int mode) { int x1, y1, x2, y2; xlibVPoint2dev(vp1, &x1, &y2); xlibVPoint2dev(vp2, &x2, &y1); if (getpattern() != 0) { xlib_setpen(); if (x1 != x2 || y1 != y2) { if (xlibarcfillmode != mode) { xlibarcfillmode = mode; if (mode == ARCFILL_CHORD) { XSetArcMode(disp, gc, ArcChord); } else { XSetArcMode(disp, gc, ArcPieSlice); } } XFillArc(disp, displaybuff, gc, MIN2(x1, x2), MIN2(y1, y2), abs(x2 - x1), abs(y2 - y1), 64 * angle1, 64 * (angle2 - angle1)); } else { /* zero radius */ XDrawPoint(disp, displaybuff, gc, x1, y1); } } } void xlibputpixmap(VPoint vp, int width, int height, char *databits, int pixmap_bpp, int bitmap_pad, int pixmap_type) { int j, k, l; XPoint xp; static XImage *ximage; Pixmap clipmask = 0; char *pixmap_ptr; char *clipmask_ptr = NULL; int line_off; int cindex, fg, bg; xp = VPoint2XPoint(vp); if (pixmap_bpp != 1) { if (monomode == TRUE) { /* TODO: dither pixmaps on mono displays */ return; } pixmap_ptr = xcalloc(PAD(width, 8) * height, pixel_size); if (pixmap_ptr == NULL) { errmsg("xmalloc failed in xlibputpixmap()"); return; } /* re-index pixmap */ for (k = 0; k < height; k++) { for (j = 0; j < width; j++) { cindex = (unsigned char) (databits)[k*width+j]; for (l = 0; l < pixel_size; l++) { pixmap_ptr[pixel_size*(k*width+j) + l] = (char) (xvlibcolors[cindex] >> (8*l)); } } } ximage=XCreateImage(disp, visual, depth, ZPixmap, 0, pixmap_ptr, width, height, bitmap_pad, /* lines padded to bytes */ 0 /* number of bytes per line */ ); if (pixmap_type == PIXMAP_TRANSPARENT) { clipmask_ptr = xcalloc((PAD(width, 8)>>3) * height, SIZEOF_CHAR); if (clipmask_ptr == NULL) { errmsg("xmalloc failed in xlibputpixmap()"); return; } else { /* Note: We pad the clipmask always to byte boundary */ bg = getbgcolor(); for (k = 0; k < height; k++) { line_off = k*(PAD(width, 8) >> 3); for (j = 0; j < width; j++) { cindex = (unsigned char) (databits)[k*width+j]; if (cindex != bg) { clipmask_ptr[line_off+(j>>3)] |= (0x01 << (j%8)); } } } clipmask=XCreateBitmapFromData(disp, root, clipmask_ptr, width, height); xfree(clipmask_ptr); } } } else { pixmap_ptr = xcalloc((PAD(width, bitmap_pad)>>3) * height, sizeof(unsigned char)); if (pixmap_ptr == NULL) { errmsg("xmalloc failed in xlibputpixmap()"); return; } memcpy(pixmap_ptr, databits, ((PAD(width, bitmap_pad)>>3) * height)); fg = getcolor(); if (fg != xlibcolor) { XSetForeground(disp, gc, xvlibcolors[fg]); xlibcolor = fg; } ximage=XCreateImage(disp, visual, 1, XYBitmap, 0, pixmap_ptr, width, height, bitmap_pad, /* lines padded to bytes */ 0 /* number of bytes per line */ ); if (pixmap_type == PIXMAP_TRANSPARENT) { clipmask=XCreateBitmapFromData(disp, root, pixmap_ptr, PAD(width, bitmap_pad), height); } } if (pixmap_type == PIXMAP_TRANSPARENT) { XSetClipMask(disp, gc, clipmask); XSetClipOrigin(disp, gc, xp.x, xp.y); } /* Force bit and byte order */ ximage->bitmap_bit_order=LSBFirst; ximage->byte_order=LSBFirst; XPutImage(disp, displaybuff, gc, ximage, 0, 0, xp.x, xp.y, width, height); XDestroyImage(ximage); if (pixmap_type == PIXMAP_TRANSPARENT) { XFreePixmap(disp, clipmask); clipmask = 0; XSetClipMask(disp, gc, None); XSetClipOrigin(disp, gc, 0, 0); } } void xlibleavegraphics(void) { int cg = get_cg(); if (is_graph_hidden(cg) == FALSE) { draw_focus(cg); } reset_crosshair(); xlibredraw(xwin, 0, 0, win_w, win_h); XFlush(disp); }