diff options
Diffstat (limited to 'jwxyz/jwxyz-common.c')
-rw-r--r-- | jwxyz/jwxyz-common.c | 1828 |
1 files changed, 1828 insertions, 0 deletions
diff --git a/jwxyz/jwxyz-common.c b/jwxyz/jwxyz-common.c new file mode 100644 index 0000000..1a31ae3 --- /dev/null +++ b/jwxyz/jwxyz-common.c @@ -0,0 +1,1828 @@ +/* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +/* JWXYZ Is Not Xlib. + + But it's a bunch of function definitions that bear some resemblance to + Xlib and that do Cocoa-ish or OpenGL-ish things that bear some resemblance + to the things that Xlib might have done. + + This is the version of jwxyz for Android. The version used by MacOS + and iOS is in jwxyz.m. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_JWXYZ /* whole file */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "jwxyzI.h" +#include "pow2.h" +#include "utf8wc.h" +#include "xft.h" + +/* There's only one Window for a given jwxyz_Display. */ +#define assert_window(dpy, w) \ + Assert (w == RootWindow (dpy, 0), "not a window") + +#define VTBL JWXYZ_VTBL(dpy) + +struct jwxyz_Font { + Display *dpy; + void *native_font; + int refcount; // for deciding when to release the native font + int ascent, descent; + char *xa_font; + + // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics. + // But we need the metrics on both of them, so they go here. + XFontStruct metrics; +}; + +struct jwxyz_XFontSet { + XFontStruct *font; +}; + + +void +Log (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + Logv (fmt, args); + va_end (args); +} + + +int +XDisplayWidth (Display *dpy, int screen) +{ + return jwxyz_frame (XRootWindow (dpy, 0))->width; +} + +int +XDisplayHeight (Display *dpy, int screen) +{ + return jwxyz_frame (XRootWindow (dpy, 0))->height; +} + + +/* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */ +static int +size_mm (Display *dpy, unsigned size) +{ + /* ((mm / inch) / (points / inch)) * dots / (dots / points) */ + return (25.4 / 72) * size / jwxyz_scale (XRootWindow (dpy,0)) + 0.5; +} + +int +XDisplayWidthMM (Display *dpy, int screen) +{ + return size_mm (dpy, XDisplayWidth (dpy, screen)); +} + +int +XDisplayHeightMM (Display *dpy, int screen) +{ + return size_mm (dpy, XDisplayHeight (dpy, screen)); +} + +unsigned long +XBlackPixelOfScreen(Screen *screen) +{ + return DefaultVisualOfScreen (screen)->alpha_mask; +} + +unsigned long +XWhitePixelOfScreen(Screen *screen) +{ + Visual *v = DefaultVisualOfScreen (screen); + return (v->red_mask | v->green_mask |v->blue_mask | v->alpha_mask); +} + +unsigned long +XCellsOfScreen(Screen *screen) +{ + Visual *v = DefaultVisualOfScreen (screen); + return (v->red_mask | v->green_mask |v->blue_mask); +} + +void +jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth, + Bool alpha_allowed_p) +{ + Assert (depth == 1 || depth == visual_depth(NULL, NULL), + "invalid depth: %d", depth); + + if (depth == 1) + Assert ((pixel == 0 || pixel == 1), "bogus mono pixel: 0x%08X", pixel); + else if (!alpha_allowed_p) + Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)), + "bogus color pixel: 0x%08X", pixel); +} + + +int +XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y) +{ + XPoint p; + p.x = x; + p.y = y; + return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin); +} + + +Bool +jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc, + int x, int y, unsigned width, unsigned height) +{ + XGCValues *gcv = VTBL->gc_gcv (gc); + + if (gcv->function == GXset || gcv->function == GXclear) { + // "set" and "clear" are dumb drawing modes that ignore the source + // bits and just draw solid rectangles. + unsigned depth = VTBL->gc_depth (gc); + jwxyz_fill_rect (dpy, d, 0, x, y, width, height, + (gcv->function == GXset + ? (depth == 1 ? 1 : WhitePixel(dpy,0)) + : (depth == 1 ? 0 : BlackPixel(dpy,0)))); + return True; + } + + return False; +} + + +int +XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, + int src_x, int src_y, + unsigned int width, unsigned int height, + int dst_x, int dst_y) +{ + Assert (gc, "no GC"); + Assert ((width < 65535), "improbably large width"); + Assert ((height < 65535), "improbably large height"); + Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x"); + Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y"); + Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x"); + Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y"); + + if (width == 0 || height == 0) + return 0; + + if (jwxyz_dumb_drawing_mode (dpy, dst, gc, dst_x, dst_y, width, height)) + return 0; + + XRectangle src_frame, dst_frame; // Sizes and origins of the two drawables + Bool clipped = False; // Whether we did any clipping of the rects. + + src_frame = *jwxyz_frame (src); + dst_frame = *jwxyz_frame (dst); + + // Initialize src_rect... + // + src_x += src_frame.x; + src_y += src_frame.y; + if (src_y < -65535) Assert(0, "src.origin.y went nuts"); + + // Initialize dst_rect... + // + dst_x += dst_frame.x; + dst_y += dst_frame.y; + if (dst_y < -65535) Assert(0, "dst.origin.y went nuts"); + + // Use signed width and height for this... + int width0 = width, height0 = height; + + // Clip rects to frames... + // + +# define CLIP(THIS,THAT,VAL,SIZE) do { \ + int off = THIS##_##VAL; \ + if (off < 0) { \ + clipped = True; \ + SIZE##0 += off; \ + THIS##_##VAL -= off; \ + THAT##_##VAL -= off; \ + } \ + off = (( THIS##_##VAL + SIZE##0) - \ + (THIS##_frame.VAL + THIS##_frame.SIZE)); \ + if (off > 0) { \ + clipped = True; \ + SIZE##0 -= off; \ + }} while(0) + + CLIP (dst, src, x, width); + CLIP (dst, src, y, height); + + // Not actually the original dst_rect, just the one before it's clipped to + // the src_frame. + int orig_dst_x = dst_x; + int orig_dst_y = dst_y; + int orig_width = width0; + int orig_height = height0; + + if (width0 <= 0 || height0 <= 0) + return 0; + + CLIP (src, dst, x, width); + CLIP (src, dst, y, height); +# undef CLIP + + // Sort-of-special case where no pixels can be grabbed from the source, + // and the whole destination is filled with the background color. + if (width0 <= 0 || height0 <= 0) { + width0 = 0; + height0 = 0; + } else { + VTBL->copy_area (dpy, src, dst, gc, + src_x, src_y, width0, height0, dst_x, dst_y); + } + + // If either the src or dst rects did not lie within their drawables, then + // we have adjusted both the src and dst rects to account for the clipping; + // that means we need to clear to the background, so that clipped bits end + // up in the bg color instead of simply not being copied. + // + // This has to happen after the copy, because if it happens before, the + // cleared area will get grabbed if it overlaps with the source rectangle. + // + if (clipped && dst == XRootWindow (dpy,0)) { + int dst_x0 = dst_x; + int dst_y0 = dst_y; + + Assert (orig_dst_x >= 0 && + orig_dst_x + orig_width <= dst_frame.width && + orig_dst_y >= 0 && + orig_dst_y + orig_height <= dst_frame.height, + "wrong dimensions"); + + XRectangle rects[4]; + XRectangle *rects_end = rects; + + if (orig_dst_y < dst_y0) { + rects_end->x = orig_dst_x; + rects_end->y = orig_dst_y; + rects_end->width = orig_width; + rects_end->height = dst_y0 - orig_dst_y; + ++rects_end; + } + + if (orig_dst_y + orig_height > dst_y0 + height0) { + rects_end->x = orig_dst_x; + rects_end->y = dst_y0 + height0; + rects_end->width = orig_width; + rects_end->height = orig_dst_y + orig_height - dst_y0 - height0; + ++rects_end; + } + + if (orig_dst_x < dst_x0) { + rects_end->x = orig_dst_x; + rects_end->y = dst_y0; + rects_end->width = dst_x0 - orig_dst_x; + rects_end->height = height0; + ++rects_end; + } + + if (dst_x0 + width0 < orig_dst_x + orig_width) { + rects_end->x = dst_x0 + width0; + rects_end->y = dst_y0; + rects_end->width = orig_dst_x + orig_width - dst_x0 - width0; + rects_end->height = height0; + ++rects_end; + } + + XGCValues *gcv = VTBL->gc_gcv (gc); + int old_function = gcv->function; + gcv->function = GXcopy; + VTBL->fill_rects (dpy, dst, gc, rects, rects_end - rects, + *VTBL->window_background (dpy)); + gcv->function = old_function; + } + + return 0; +} + + +void +jwxyz_blit (const void *src_data, ptrdiff_t src_pitch, + unsigned src_x, unsigned src_y, + void *dst_data, ptrdiff_t dst_pitch, + unsigned dst_x, unsigned dst_y, + unsigned width, unsigned height) +{ + Bool same = src_data == dst_data; + src_data = SEEK_XY (src_data, src_pitch, src_x, src_y); + dst_data = SEEK_XY (dst_data, dst_pitch, dst_x, dst_y); + + size_t bytes = width * 4; + + if (same && dst_y > src_y) { + // Copy upwards if the areas might overlap. + src_data += src_pitch * (height - 1); + dst_data += dst_pitch * (height - 1); + src_pitch = -src_pitch; + dst_pitch = -dst_pitch; + } + + while (height) { + // memcpy is an alias for memmove on macOS. + memmove (dst_data, src_data, bytes); + src_data += src_pitch; + dst_data += dst_pitch; + --height; + } +} + + +int +XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc, + int src_x, int src_y, + unsigned width, int height, + int dest_x, int dest_y, unsigned long plane) +{ + Assert ((VTBL->gc_depth (gc) == 1 || plane == 1), "hairy plane mask!"); + + // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg, + // not to white/black. + return XCopyArea (dpy, src, dest, gc, + src_x, src_y, width, height, dest_x, dest_y); +} + + +int +XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) +{ + XSegment segment; + segment.x1 = x1; + segment.y1 = y1; + segment.x2 = x2; + segment.y2 = y2; + XDrawSegments (dpy, d, gc, &segment, 1); + return 0; +} + + +int +XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) +{ + Assert (w == XRootWindow (dpy,0), "not a window"); + jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False); + *VTBL->window_background (dpy) = pixel; + return 0; +} + +void +jwxyz_fill_rect (Display *dpy, Drawable d, GC gc, + int x, int y, unsigned int width, unsigned int height, + unsigned long pixel) +{ + XRectangle r = {x, y, width, height}; + VTBL->fill_rects (dpy, d, gc, &r, 1, pixel); +} + +int +XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height) +{ + jwxyz_fill_rect (dpy, d, gc, x, y, width, height, + VTBL->gc_gcv (gc)->foreground); + return 0; +} + +int +XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height) +{ + XPoint points[5] = { + {x, y}, + {x, y + height}, + {x + width, y + height}, + {x + width, y}, + {x, y} + }; + + XDrawLines(dpy, d, gc, points, 5, CoordModeOrigin); + return 0; +} + +int +XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) +{ + VTBL->fill_rects (dpy, d, gc, rects, n, VTBL->gc_gcv (gc)->foreground); + return 0; +} + + +int +XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) +{ + Assert(win == XRootWindow(dpy,0), "XClearArea: not a window"); + Assert(!exp, "XClearArea: exposures unsupported"); + jwxyz_fill_rect (dpy, win, 0, x, y, w, h, *VTBL->window_background (dpy)); + return 0; +} + + +int +XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height, int angle1, int angle2) +{ + return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, + False); +} + +int +XFillArc (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height, int angle1, int angle2) +{ + return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, + True); +} + +int +XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) +{ + int i; + for (i = 0; i < narcs; i++) + VTBL->draw_arc (dpy, d, gc, + arcs[i].x, arcs[i].y, + arcs[i].width, arcs[i].height, + arcs[i].angle1, arcs[i].angle2, + False); + return 0; +} + +int +XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) +{ + int i; + for (i = 0; i < narcs; i++) + VTBL->draw_arc (dpy, d, gc, + arcs[i].x, arcs[i].y, + arcs[i].width, arcs[i].height, + arcs[i].angle1, arcs[i].angle2, + True); + return 0; +} + +void +jwxyz_gcv_defaults (Display *dpy, XGCValues *gcv, int depth) +{ + memset (gcv, 0, sizeof(*gcv)); + gcv->function = GXcopy; + gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0)); + gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0)); + gcv->line_width = 1; + gcv->cap_style = CapButt; + gcv->join_style = JoinMiter; + gcv->fill_rule = EvenOddRule; + + gcv->alpha_allowed_p = False; + gcv->antialias_p = True; +} + + +int +XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from) +{ + if (! mask) return 0; + Assert (gc && from, "no gc"); + if (!gc || !from) return 0; + + XGCValues *to = VTBL->gc_gcv (gc); + unsigned depth = VTBL->gc_depth (gc); + + if (mask & GCFunction) to->function = from->function; + if (mask & GCForeground) to->foreground = from->foreground; + if (mask & GCBackground) to->background = from->background; + if (mask & GCLineWidth) to->line_width = from->line_width; + if (mask & GCCapStyle) to->cap_style = from->cap_style; + if (mask & GCJoinStyle) to->join_style = from->join_style; + if (mask & GCFillRule) to->fill_rule = from->fill_rule; + if (mask & GCClipXOrigin) to->clip_x_origin = from->clip_x_origin; + if (mask & GCClipYOrigin) to->clip_y_origin = from->clip_y_origin; + if (mask & GCSubwindowMode) to->subwindow_mode = from->subwindow_mode; + + if (mask & GCClipMask) XSetClipMask (dpy, gc, from->clip_mask); + if (mask & GCFont) XSetFont (dpy, gc, from->font); + + if (mask & GCForeground) + jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p); + if (mask & GCBackground) + jwxyz_validate_pixel (dpy, from->background, depth, to->alpha_allowed_p); + + Assert ((! (mask & (GCLineStyle | + GCPlaneMask | + GCFillStyle | + GCTile | + GCStipple | + GCTileStipXOrigin | + GCTileStipYOrigin | + GCGraphicsExposures | + GCDashOffset | + GCDashList | + GCArcMode))), + "unimplemented gcvalues mask"); + + return 0; +} + + +Status +XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa) +{ + assert_window(dpy, w); + memset (xgwa, 0, sizeof(*xgwa)); + const XRectangle *frame = jwxyz_frame (w); + xgwa->x = frame->x; + xgwa->y = frame->y; + xgwa->width = frame->width; + xgwa->height = frame->height; + xgwa->depth = visual_depth (NULL, NULL); + xgwa->screen = DefaultScreenOfDisplay (dpy); + xgwa->visual = XDefaultVisualOfScreen (xgwa->screen); + return 0; +} + +Status +XGetGeometry (Display *dpy, Drawable d, Window *root_ret, + int *x_ret, int *y_ret, + unsigned int *w_ret, unsigned int *h_ret, + unsigned int *bw_ret, unsigned int *d_ret) +{ + const XRectangle *frame = jwxyz_frame (d); + *x_ret = frame->x; + *y_ret = frame->y; + *w_ret = frame->width; + *h_ret = frame->height; + *d_ret = jwxyz_drawable_depth (d); + *root_ret = RootWindow (dpy, 0); + *bw_ret = 0; + return True; +} + + +Status +XAllocColor (Display *dpy, Colormap cmap, XColor *color) +{ + Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy)); + color->pixel = + (((color->red << 16) >> (31 - i_log2(v->red_mask))) & v->red_mask) | + (((color->green << 16) >> (31 - i_log2(v->green_mask))) & v->green_mask) | + (((color->blue << 16) >> (31 - i_log2(v->blue_mask))) & v->blue_mask) | + v->alpha_mask; + return 1; +} + +Status +XAllocColorCells (Display *dpy, Colormap cmap, Bool contig, + unsigned long *pmret, unsigned int npl, + unsigned long *pxret, unsigned int npx) +{ + return 0; +} + +int +XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n) +{ + Assert(0, "XStoreColors called"); + return 0; +} + +int +XStoreColor (Display *dpy, Colormap cmap, XColor *c) +{ + Assert(0, "XStoreColor called"); + return 0; +} + +int +XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels, + unsigned long planes) +{ + return 0; +} + +Status +XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret) +{ + unsigned char r=0, g=0, b=0; + if (*spec == '#' && strlen(spec) == 7) { + static unsigned const char hex[] = { // yeah yeah, shoot me. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char *uspec = (const unsigned char *)spec; + r = (hex[uspec[1]] << 4) | hex[uspec[2]]; + g = (hex[uspec[3]] << 4) | hex[uspec[4]]; + b = (hex[uspec[5]] << 4) | hex[uspec[6]]; + } else if (!strcasecmp(spec,"black")) { + // r = g = b = 0; + } else if (!strcasecmp(spec,"white")) { + r = g = b = 255; + } else if (!strcasecmp(spec,"red")) { + r = 255; + } else if (!strcasecmp(spec,"green")) { + g = 255; + } else if (!strcasecmp(spec,"blue")) { + b = 255; + } else if (!strcasecmp(spec,"cyan")) { + g = b = 255; + } else if (!strcasecmp(spec,"magenta")) { + r = b = 255; + } else if (!strcasecmp(spec,"yellow")) { + r = g = 255; + } else { + return 0; + } + + ret->red = (r << 8) | r; + ret->green = (g << 8) | g; + ret->blue = (b << 8) | b; + ret->flags = DoRed|DoGreen|DoBlue; + return 1; +} + +Status +XAllocNamedColor (Display *dpy, Colormap cmap, char *name, + XColor *screen_ret, XColor *exact_ret) +{ + if (! XParseColor (dpy, cmap, name, screen_ret)) + return False; + *exact_ret = *screen_ret; + return XAllocColor (dpy, cmap, screen_ret); +} + +int +XQueryColor (Display *dpy, Colormap cmap, XColor *color) +{ + jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False); + uint16_t rgba[4]; + JWXYZ_QUERY_COLOR (dpy, color->pixel, 0xffffull, rgba); + color->red = rgba[0]; + color->green = rgba[1]; + color->blue = rgba[2]; + color->flags = DoRed|DoGreen|DoBlue; + return 0; +} + +int +XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n) +{ + int i; + for (i = 0; i < n; i++) + XQueryColor (dpy, cmap, &c[i]); + return 0; +} + + +static unsigned long +ximage_getpixel_1 (XImage *ximage, int x, int y) +{ + return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1); +} + +static int +ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel) +{ + if (pixel) + ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7)); + else + ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7)); + + return 0; +} + +static unsigned long +ximage_getpixel_8 (XImage *ximage, int x, int y) +{ + return ((unsigned long) + *((uint8_t *) ximage->data + + (y * ximage->bytes_per_line) + + x)); +} + +static int +ximage_putpixel_8 (XImage *ximage, int x, int y, unsigned long pixel) +{ + *((uint8_t *) ximage->data + + (y * ximage->bytes_per_line) + + x) = (uint8_t) pixel; + return 0; +} + + +static unsigned long +ximage_getpixel_32 (XImage *ximage, int x, int y) +{ + return ((unsigned long) + *((uint32_t *) ximage->data + + (y * (ximage->bytes_per_line >> 2)) + + x)); +} + +static int +ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel) +{ + *((uint32_t *) ximage->data + + (y * (ximage->bytes_per_line >> 2)) + + x) = (uint32_t) pixel; + return 0; +} + + +Status +XInitImage (XImage *ximage) +{ + if (!ximage->bytes_per_line) + ximage->bytes_per_line = (ximage->depth == 1 ? (ximage->width + 7) / 8 : + ximage->depth == 8 ? ximage->width : + ximage->width * 4); + + if (ximage->depth == 1) { + ximage->f.put_pixel = ximage_putpixel_1; + ximage->f.get_pixel = ximage_getpixel_1; + } else if (ximage->depth == 32 || ximage->depth == 24) { + ximage->f.put_pixel = ximage_putpixel_32; + ximage->f.get_pixel = ximage_getpixel_32; + } else if (ximage->depth == 8) { + ximage->f.put_pixel = ximage_putpixel_8; + ximage->f.get_pixel = ximage_getpixel_8; + } else { + Assert (0, "unknown depth"); + } + return 1; +} + + +XImage * +XCreateImage (Display *dpy, Visual *visual, unsigned int depth, + int format, int offset, char *data, + unsigned int width, unsigned int height, + int bitmap_pad, int bytes_per_line) +{ + XImage *ximage = (XImage *) calloc (1, sizeof(*ximage)); + ximage->width = width; + ximage->height = height; + ximage->format = format; + ximage->data = data; + ximage->bitmap_unit = 8; + ximage->byte_order = LSBFirst; + ximage->bitmap_bit_order = ximage->byte_order; + ximage->bitmap_pad = bitmap_pad; + ximage->depth = depth; + Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy)); + ximage->red_mask = (depth == 1 ? 0 : v->red_mask); + ximage->green_mask = (depth == 1 ? 0 : v->green_mask); + ximage->blue_mask = (depth == 1 ? 0 : v->blue_mask); + ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL)); + ximage->bytes_per_line = bytes_per_line; + + XInitImage (ximage); + return ximage; +} + +XImage * +XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h) +{ + XImage *to = (XImage *) malloc (sizeof(*to)); + memcpy (to, from, sizeof(*from)); + to->width = w; + to->height = h; + to->bytes_per_line = 0; + XInitImage (to); + + to->data = (char *) malloc (h * to->bytes_per_line); + + if (x >= from->width) + w = 0; + else if (x+w > from->width) + w = from->width - x; + + if (y >= from->height) + h = 0; + else if (y+h > from->height) + h = from->height - y; + + int tx, ty; + for (ty = 0; ty < h; ty++) + for (tx = 0; tx < w; tx++) + XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty)); + return to; +} + + +XPixmapFormatValues * +XListPixmapFormats (Display *dpy, int *n_ret) +{ + XPixmapFormatValues *ret = calloc (2, sizeof(*ret)); + ret[0].depth = visual_depth (NULL, NULL); + ret[0].bits_per_pixel = 32; + ret[0].scanline_pad = 8; + ret[1].depth = 1; + ret[1].bits_per_pixel = 1; + ret[1].scanline_pad = 8; + *n_ret = 2; + return ret; +} + + +unsigned long +XGetPixel (XImage *ximage, int x, int y) +{ + return ximage->f.get_pixel (ximage, x, y); +} + + +int +XPutPixel (XImage *ximage, int x, int y, unsigned long pixel) +{ + return ximage->f.put_pixel (ximage, x, y, pixel); +} + +int +XDestroyImage (XImage *ximage) +{ + if (ximage->data) free (ximage->data); + free (ximage); + return 0; +} + + +XImage * +XGetImage (Display *dpy, Drawable d, int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, int format) +{ + unsigned depth = jwxyz_drawable_depth (d); + XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height, + 0, 0); + image->data = (char *) malloc (height * image->bytes_per_line); + + return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format, + image, 0, 0); +} + + +Pixmap +XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable, + const char *data, + unsigned int w, unsigned int h, + unsigned long fg, unsigned long bg, + unsigned int depth) +{ + Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth); + XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0, + (char *) data, w, h, 0, 0); + XGCValues gcv; + gcv.foreground = fg; + gcv.background = bg; + GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv); + XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h); + XFreeGC (dpy, gc); + image->data = 0; + XDestroyImage (image); + return p; +} + + +char * +XGetAtomName (Display *dpy, Atom atom) +{ + if (atom == XA_FONT) + return strdup ("FONT"); + + // Note that atoms (that aren't predefined) are just char *. + return strdup ((char *) atom); +} + + +// This is XQueryFont, but for the XFontStruct embedded in 'Font' +// +static void +query_font (Font fid) +{ + Assert (fid && fid->native_font, "no native font in fid"); + + int first = 32; + int last = 255; + + Display *dpy = fid->dpy; + void *native_font = fid->native_font; + + XFontStruct *f = &fid->metrics; + XCharStruct *min = &f->min_bounds; + XCharStruct *max = &f->max_bounds; + + f->fid = fid; + f->min_char_or_byte2 = first; + f->max_char_or_byte2 = last; + f->default_char = 'M'; + f->ascent = fid->ascent; + f->descent = fid->descent; + + min->width = 32767; // set to smaller values in the loop + min->ascent = 32767; + min->descent = 32767; + min->lbearing = 32767; + min->rbearing = 32767; + + f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct)); + + for (int i = first; i <= last; i++) { + XCharStruct *cs = &f->per_char[i-first]; + char s = (char) i; + jwxyz_render_text (dpy, native_font, &s, 1, False, False, cs, 0); + + max->width = MAX (max->width, cs->width); + max->ascent = MAX (max->ascent, cs->ascent); + max->descent = MAX (max->descent, cs->descent); + max->lbearing = MAX (max->lbearing, cs->lbearing); + max->rbearing = MAX (max->rbearing, cs->rbearing); + + min->width = MIN (min->width, cs->width); + min->ascent = MIN (min->ascent, cs->ascent); + min->descent = MIN (min->descent, cs->descent); + min->lbearing = MIN (min->lbearing, cs->lbearing); + min->rbearing = MIN (min->rbearing, cs->rbearing); +/* + Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d " + " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n" + i, i, cs->width, cs->lbearing, cs->rbearing, + cs->ascent, cs->descent, + bbox.size.width, bbox.size.height, + bbox.origin.x, bbox.origin.y, + advancement.width, advancement.height); + */ + } +} + + +// Since 'Font' includes the metrics, this just makes a copy of that. +// +XFontStruct * +XQueryFont (Display *dpy, Font fid) +{ + // copy XFontStruct + XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f)); + *f = fid->metrics; + f->fid = fid; + + // build XFontProps + f->n_properties = 1; + f->properties = malloc (sizeof(*f->properties) * f->n_properties); + f->properties[0].name = XA_FONT; + Assert (sizeof (f->properties[0].card32) >= sizeof (char *), + "atoms probably needs a real implementation"); + // If XInternAtom is ever implemented, use it here. + f->properties[0].card32 = (unsigned long)fid->xa_font; + + // copy XCharStruct array + int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1; + f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct)); + + memcpy (f->per_char, fid->metrics.per_char, + size * sizeof (XCharStruct)); + + return f; +} + + +static Font +copy_font (Font fid) +{ + fid->refcount++; + return fid; +} + + +/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead + of XLFD strings; also they can be comma-separated strings with multiple + font names. First one that exists wins. + */ +static void +try_native_font (Display *dpy, const char *name, Font fid) +{ + if (!name) return; + const char *spc = strrchr (name, ' '); + if (!spc) return; + + char *token = strdup (name); + char *otoken = token; + char *name2; + char *lasts; + + while ((name2 = strtok_r (token, ",", &lasts))) { + token = 0; + + while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') + name2++; + + spc = strrchr (name2, ' '); + if (!spc) continue; + + int dsize = 0; + if (1 != sscanf (spc, " %d ", &dsize)) + continue; + float size = dsize; + + if (size < 4) continue; + + name2[strlen(name2) - strlen(spc)] = 0; + + fid->native_font = jwxyz_load_native_font(XRootWindow(dpy,0), 0, 0, name2, + strlen(name2) - strlen(spc), + JWXYZ_FONT_FACE, size, NULL, + &fid->ascent, &fid->descent); + if (fid->native_font) { + fid->xa_font = strdup (name); // Maybe this should be an XLFD? + break; + } else { + // To list fonts: + // po [UIFont familyNames] + // po [UIFont fontNamesForFamilyName:@"Arial"] + Log("No native font: \"%s\" %.0f", name2, size); + } + } + + free (otoken); +} + + +static const char * +xlfd_field_end (const char *s) +{ + const char *s2 = strchr(s, '-'); + if (!s2) + s2 = s + strlen(s); + return s2; +} + + +static size_t +xlfd_next (const char **s, const char **s2) +{ + if (!**s2) { + *s = *s2; + } else { + Assert (**s2 == '-', "xlfd parse error"); + *s = *s2 + 1; + *s2 = xlfd_field_end (*s); + } + + return *s2 - *s; +} + + +static void +try_xlfd_font (Display *dpy, const char *name, Font fid) +{ + const char *family_name = NULL; /* Not NULL-terminated. */ + size_t family_name_size = 0; + int require = 0, + // Default mask is for the built-in X11 font aliases. + mask = JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD | JWXYZ_STYLE_ITALIC; + Bool rand = False; + float size = 12; /* In points (1/72 in.) */ + + const char *s = (name ? name : ""); + + size_t L = strlen (s); +# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L)) +# define UNSPEC (L == 0 || (L == 1 && *s == '*')) + if (CMP ("6x10")) size = 8, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("6x10bold")) size = 8, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD; + else if (CMP ("fixed")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("9x15")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("9x15bold")) size = 12, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD; + else if (CMP ("vga")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("console")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("gallant")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else { + + int forbid = 0; + + // Incorrect fields are ignored. + + if (*s == '-') + ++s; + const char *s2 = xlfd_field_end(s); + + // Foundry (ignore) + + L = xlfd_next (&s, &s2); // Family name + // This used to substitute Georgia for Times. Now it doesn't. + if (CMP ("random")) { + rand = True; + } else if (CMP ("fixed")) { + require |= JWXYZ_STYLE_MONOSPACE; + family_name = "Courier"; + family_name_size = strlen(family_name); + } else if (!UNSPEC) { + family_name = s; + family_name_size = L; + } + + L = xlfd_next (&s, &s2); // Weight name + if (CMP ("bold") || CMP ("demibold")) + require |= JWXYZ_STYLE_BOLD; + else if (CMP ("medium") || CMP ("regular")) + forbid |= JWXYZ_STYLE_BOLD; + + L = xlfd_next (&s, &s2); // Slant + if (CMP ("i") || CMP ("o")) + require |= JWXYZ_STYLE_ITALIC; + else if (CMP ("r")) + forbid |= JWXYZ_STYLE_ITALIC; + + xlfd_next (&s, &s2); // Set width name (ignore) + xlfd_next (&s, &s2); // Add style name (ignore) + + L = xlfd_next (&s, &s2); // Pixel size + char *s3; + uintmax_t pxsize = strtoumax(s, &s3, 10); + if (UNSPEC || s2 != s3) + pxsize = UINTMAX_MAX; // i.e. it's invalid. + + L = xlfd_next (&s, &s2); // Point size + uintmax_t ptsize = strtoumax(s, &s3, 10); + if (UNSPEC || s2 != s3) + ptsize = UINTMAX_MAX; + + xlfd_next (&s, &s2); // Resolution X (ignore) + xlfd_next (&s, &s2); // Resolution Y (ignore) + + L = xlfd_next (&s, &s2); // Spacing + if (CMP ("p")) + forbid |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("m") || CMP ("c")) + require |= JWXYZ_STYLE_MONOSPACE; + + xlfd_next (&s, &s2); // Average width (ignore) + + // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-* 14 px + // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-* 14 pt + // -*-courier-bold-r-*-*-140-* 14 pt, via wildcard + // -*-courier-bold-r-*-140-* 14 pt, not handled + // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-* error + + L = xlfd_next (&s, &s2); // Charset registry + if (ptsize != UINTMAX_MAX) { + // It was in the ptsize field, so that's definitely what it is. + size = ptsize / 10.0; + } else if (pxsize != UINTMAX_MAX) { + size = pxsize; + // If it's a fully qualified XLFD, then this really is the pxsize. + // Otherwise, this is probably point size with a multi-field wildcard. + if (L == 0) + size /= 10.0; + } + + mask = require | forbid; + } +# undef CMP +# undef UNSPEC + + if (!family_name && !rand) { + family_name = jwxyz_default_font_family (require); + family_name_size = strlen (family_name); + } + + if (size < 6 || size > 1000) + size = 12; + + char *family_name_ptr = NULL; + fid->native_font = jwxyz_load_native_font (XRootWindow(dpy,0), + require, mask, + family_name, family_name_size, + rand ? JWXYZ_FONT_RANDOM : JWXYZ_FONT_FAMILY, + size, &family_name_ptr, + &fid->ascent, &fid->descent); + + if (fid->native_font) { + unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2; + unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d); + asprintf(&fid->xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1", + family_name_ptr, + (require & JWXYZ_STYLE_BOLD) ? "bold" : "medium", + (require & JWXYZ_STYLE_ITALIC) ? 'o' : 'r', + (unsigned)(dpi * size / 72.27 + 0.5), + (unsigned)(size * 10 + 0.5), dpi, dpi, + (require & JWXYZ_STYLE_MONOSPACE) ? 'm' : 'p'); + } + + free (family_name_ptr); +} + + +Font +XLoadFont (Display *dpy, const char *name) +{ + Font fid = (Font) calloc (1, sizeof(*fid)); + + fid->refcount = 1; + fid->dpy = dpy; + try_native_font (dpy, name, fid); + + if (!fid->native_font && name && + strchr (name, ' ') && + !strchr (name, '*')) { + // If name contains a space but no stars, it is a native font spec -- + // return NULL so that we know it really didn't exist. Else, it is an + // XLFD font, so keep trying. + free (fid); + return 0; + } + + if (! fid->native_font) + try_xlfd_font (dpy, name, fid); + + if (!fid->native_font) { + free (fid); + return 0; + } + + query_font (fid); + + return fid; +} + + +XFontStruct * +XLoadQueryFont (Display *dpy, const char *name) +{ + Font fid = XLoadFont (dpy, name); + if (!fid) return 0; + return XQueryFont (dpy, fid); +} + +int +XUnloadFont (Display *dpy, Font fid) +{ + if (--fid->refcount < 0) abort(); + if (fid->refcount > 0) return 0; + + if (fid->native_font) + jwxyz_release_native_font (fid->dpy, fid->native_font); + + if (fid->metrics.per_char) + free (fid->metrics.per_char); + + free (fid); + return 0; +} + +int +XFreeFontInfo (char **names, XFontStruct *info, int n) +{ + int i; + if (names) { + for (i = 0; i < n; i++) + if (names[i]) free (names[i]); + free (names); + } + if (info) { + for (i = 0; i < n; i++) + if (info[i].per_char) { + free (info[i].per_char); + free (info[i].properties); + } + free (info); + } + return 0; +} + +int +XFreeFont (Display *dpy, XFontStruct *f) +{ + Font fid = f->fid; + XFreeFontInfo (0, f, 1); + XUnloadFont (dpy, fid); + return 0; +} + + +int +XSetFont (Display *dpy, GC gc, Font fid) +{ + XGCValues *gcv = VTBL->gc_gcv(gc); + Font font2 = copy_font (fid); + if (gcv->font) + XUnloadFont (dpy, gcv->font); + gcv->font = font2; + return 0; +} + + +XFontSet +XCreateFontSet (Display *dpy, char *name, + char ***missing_charset_list_return, + int *missing_charset_count_return, + char **def_string_return) +{ + char *name2 = strdup (name); + char *s = strchr (name, ','); + if (s) *s = 0; + XFontSet set = 0; + XFontStruct *f = XLoadQueryFont (dpy, name2); + if (f) + { + set = (XFontSet) calloc (1, sizeof(*set)); + set->font = f; + } + free (name2); + if (missing_charset_list_return) *missing_charset_list_return = 0; + if (missing_charset_count_return) *missing_charset_count_return = 0; + if (def_string_return) *def_string_return = 0; + return set; +} + + +void +XFreeFontSet (Display *dpy, XFontSet set) +{ + XFreeFont (dpy, set->font); + free (set); +} + + +void +XFreeStringList (char **list) +{ + int i; + if (!list) return; + for (i = 0; list[i]; i++) + XFree (list[i]); + XFree (list); +} + + +int +XTextExtents (XFontStruct *f, const char *s, int length, + int *dir_ret, int *ascent_ret, int *descent_ret, + XCharStruct *cs) +{ + // Unfortunately, adding XCharStructs together to get the extents for a + // string doesn't work: Cocoa uses non-integral character advancements, but + // XCharStruct.width is an integer. Plus that doesn't take into account + // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in + // Zapfino. + + Font ff = f->fid; + Display *dpy = ff->dpy; + jwxyz_render_text (dpy, ff->native_font, s, length, False, False, cs, 0); + *dir_ret = 0; + *ascent_ret = f->ascent; + *descent_ret = f->descent; + return 0; +} + +int +XTextWidth (XFontStruct *f, const char *s, int length) +{ + int ascent, descent, dir; + XCharStruct cs; + XTextExtents (f, s, length, &dir, &ascent, &descent, &cs); + return cs.width; +} + + +int +XTextExtents16 (XFontStruct *f, const XChar2b *s, int length, + int *dir_ret, int *ascent_ret, int *descent_ret, + XCharStruct *cs) +{ + // Bool latin1_p = True; + int i, utf8_len = 0; + char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized + + for (i = 0; i < length; i++) + if (s[i].byte1 > 0) { + // latin1_p = False; + break; + } + + { + Font ff = f->fid; + Display *dpy = ff->dpy; + jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8), + True, False, cs, 0); + } + + *dir_ret = 0; + *ascent_ret = f->ascent; + *descent_ret = f->descent; + free (utf8); + return 0; +} + + +/* "Returns the distance in pixels in the primary draw direction from + the drawing origin to the origin of the next character to be drawn." + + "overall_ink_return is set to the bbox of the string's character ink." + + "The overall_ink_return for a nondescending, horizontally drawn Latin + character is conventionally entirely above the baseline; that is, + overall_ink_return.height <= -overall_ink_return.y." + + [So this means that y is the top of the ink, and height grows down: + For above-the-baseline characters, y is negative.] + + "The overall_ink_return for a nonkerned character is entirely at, and to + the right of, the origin; that is, overall_ink_return.x >= 0." + + [So this means that x is the left of the ink, and width grows right. + For left-of-the-origin characters, x is negative.] + + "A character consisting of a single pixel at the origin would set + overall_ink_return fields y = 0, x = 0, width = 1, and height = 1." + */ +int +Xutf8TextExtents (XFontSet set, const char *str, int len, + XRectangle *overall_ink_return, + XRectangle *overall_logical_return) +{ + XCharStruct cs; + Font f = set->font->fid; + + jwxyz_render_text (f->dpy, f->native_font, str, len, True, False, &cs, + NULL); + + /* "The overall_logical_return is the bounding box that provides minimum + spacing to other graphical features for the string. Other graphical + features, for example, a border surrounding the text, should not + intersect this rectangle." + + So I think that means they're the same? Or maybe "ink" is the bounding + box, and "logical" is the advancement? But then why is the return value + the advancement? + */ + if (overall_ink_return) + XCharStruct_to_XmbRectangle (cs, *overall_ink_return); + if (overall_logical_return) + XCharStruct_to_XmbRectangle (cs, *overall_logical_return); + + return cs.width; +} + + +int +jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, size_t len, int utf8_p) +{ + const XGCValues *gcv = VTBL->gc_gcv (gc); + Font ff = gcv->font; + XCharStruct cs; + + char *data = 0; + jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p, + gcv->antialias_p, &cs, &data); + int w = cs.rbearing - cs.lbearing; + int h = cs.ascent + cs.descent; + + if (w < 0 || h < 0) abort(); + if (w == 0 || h == 0) { + if (data) free(data); + return 0; + } + + XImage *img = XCreateImage (dpy, VTBL->visual (dpy), 32, + ZPixmap, 0, data, w, h, 0, 0); + + /* The image of text is a 32-bit image, in white. + Take the green channel for intensity and use that as alpha. + replace RGB with the GC's foreground color. + This expects that XPutImage respects alpha and only writes + the bits that are not masked out. + */ + { +# define ROTL(x, rot) (((x) << ((rot) & 31)) | ((x) >> (32 - ((rot) & 31)))) + + Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay(dpy)); + unsigned shift = (i_log2 (v->alpha_mask) - i_log2 (v->green_mask)) & 31; + uint32_t mask = ROTL(v->green_mask, shift) & v->alpha_mask, + color = gcv->foreground & ~v->alpha_mask; + uint32_t *s = (uint32_t *)data; + uint32_t *end = s + (w * h); + while (s < end) { + + *s = (ROTL(*s, shift) & mask) | color; + ++s; + } + } + + { + Bool old_alpha = gcv->alpha_allowed_p; + jwxyz_XSetAlphaAllowed (dpy, gc, True); + XPutImage (dpy, d, gc, img, 0, 0, + x + cs.lbearing, + y - cs.ascent, + w, h); + jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha); + XDestroyImage (img); + } + + return 0; +} + + +int +XDrawString (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, int len) +{ + return VTBL->draw_string (dpy, d, gc, x, y, str, len, False); +} + + +int +XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y, + const XChar2b *str, int len) +{ + XChar2b *b2 = malloc ((len + 1) * sizeof(*b2)); + char *s2; + int ret; + memcpy (b2, str, len * sizeof(*b2)); + b2[len].byte1 = b2[len].byte2 = 0; + s2 = XChar2b_to_utf8 (b2, 0); + free (b2); + ret = VTBL->draw_string (dpy, d, gc, x, y, s2, strlen(s2), True); + free (s2); + return ret; +} + + +void +Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc, + int x, int y, const char *str, int len) +{ + VTBL->draw_string (dpy, d, gc, x, y, str, len, True); +} + + +int +XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, int len) +{ + int ascent, descent, dir; + XCharStruct cs; + XTextExtents (&VTBL->gc_gcv (gc)->font->metrics, str, len, + &dir, &ascent, &descent, &cs); + jwxyz_fill_rect (dpy, d, gc, + x + MIN (0, cs.lbearing), + y - MAX (0, ascent), + + /* The +1 here is almost certainly wrong, but BSOD + requires it; and only BSOD, fluidballs, juggle + and grabclient call XDrawImageString... */ + MAX (MAX (0, cs.rbearing) - + MIN (0, cs.lbearing), + cs.width) + 1, + MAX (0, ascent) + MAX (0, descent), + VTBL->gc_gcv(gc)->background); + return XDrawString (dpy, d, gc, x, y, str, len); +} + + +void * +jwxyz_native_font (Font f) +{ + return f->native_font; +} + + +int +XSetForeground (Display *dpy, GC gc, unsigned long fg) +{ + XGCValues *gcv = VTBL->gc_gcv (gc); + jwxyz_validate_pixel (dpy, fg, VTBL->gc_depth (gc), gcv->alpha_allowed_p); + gcv->foreground = fg; + return 0; +} + + +int +XSetBackground (Display *dpy, GC gc, unsigned long bg) +{ + XGCValues *gcv = VTBL->gc_gcv (gc); + jwxyz_validate_pixel (dpy, bg, VTBL->gc_depth (gc), gcv->alpha_allowed_p); + gcv->background = bg; + return 0; +} + +int +jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed) +{ + VTBL->gc_gcv (gc)->alpha_allowed_p = allowed; + return 0; +} + +int +jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p) +{ + VTBL->gc_gcv (gc)->antialias_p = antialias_p; + return 0; +} + + +int +XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width, + int line_style, int cap_style, int join_style) +{ + XGCValues *gcv = VTBL->gc_gcv (gc); + gcv->line_width = line_width; + Assert (line_style == LineSolid, "only LineSolid implemented"); +// gc->gcv.line_style = line_style; + gcv->cap_style = cap_style; + gcv->join_style = join_style; + return 0; +} + +int +XSetGraphicsExposures (Display *dpy, GC gc, Bool which) +{ + return 0; +} + +int +XSetFunction (Display *dpy, GC gc, int which) +{ + VTBL->gc_gcv (gc)->function = which; + return 0; +} + +int +XSetSubwindowMode (Display *dpy, GC gc, int which) +{ + VTBL->gc_gcv (gc)->subwindow_mode = which; + return 0; +} + + +Bool +XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret, + int *root_x_ret, int *root_y_ret, + int *win_x_ret, int *win_y_ret, unsigned int *mask_ret) +{ + assert_window (dpy, w); + + XPoint vpos, p; + jwxyz_get_pos (w, &vpos, &p); + + if (root_x_ret) *root_x_ret = p.x; + if (root_y_ret) *root_y_ret = p.y; + if (win_x_ret) *win_x_ret = p.x - vpos.x; + if (win_y_ret) *win_y_ret = p.y - vpos.y; + if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers? + if (root_ret) *root_ret = 0; + if (child_ret) *child_ret = 0; + return True; +} + +Bool +XTranslateCoordinates (Display *dpy, Window w, Window dest_w, + int src_x, int src_y, + int *dest_x_ret, int *dest_y_ret, + Window *child_ret) +{ + assert_window (dpy, w); + + XPoint vpos, p; + jwxyz_get_pos (w, &vpos, NULL); + + // point starts out relative to top left of view + p.x = src_x; + p.y = src_y; + + // get point relative to top left of screen + p.x += vpos.x; + p.y += vpos.y; + + *dest_x_ret = p.x; + *dest_y_ret = p.y; + if (child_ret) + *child_ret = w; + return True; +} + + +KeySym +XKeycodeToKeysym (Display *dpy, KeyCode code, int index) +{ + return code; +} + +int +XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret, + XComposeStatus *xc) +{ + KeySym ks = XKeycodeToKeysym (0, e->keycode, 0); + char c = 0; + // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string. + if ((unsigned int) ks <= 255) + c = (char) ks; + + // Put control characters in the string. Not meta. + if (e->state & ControlMask) { + if (c >= 'a' && c <= 'z') // Upcase control. + c -= 'a'-'A'; + if (c >= '@' && c <= '_') // Shift to control page. + c -= '@'; + if (c == ' ') // C-SPC is NULL. + c = 0; + } + + if (k_ret) *k_ret = ks; + if (size > 0) buf[0] = c; + if (size > 1) buf[1] = 0; + return (size > 0 ? 1 : 0); +} + + +int +XFlush (Display *dpy) +{ + // Just let the event loop take care of this on its own schedule. + return 0; +} + +int +XSync (Display *dpy, Bool flush) +{ + return XFlush (dpy); +} + + +// declared in utils/visual.h +int +has_writable_cells (Screen *s, Visual *v) +{ + return 0; +} + +int +visual_depth (Screen *s, Visual *v) +{ + return 32; +} + +int +visual_cells (Screen *s, Visual *v) +{ + return (int)(v->red_mask | v->green_mask | v->blue_mask); +} + +int +visual_class (Screen *s, Visual *v) +{ + return TrueColor; +} + +void +visual_rgb_masks (Screen *s, Visual *v, unsigned long *red_mask, + unsigned long *green_mask, unsigned long *blue_mask) +{ + *red_mask = v->red_mask; + *green_mask = v->green_mask; + *blue_mask = v->blue_mask; +} + +int +visual_pixmap_depth (Screen *s, Visual *v) +{ + return 32; +} + +int +screen_number (Screen *screen) +{ + return 0; +} + +// declared in utils/grabclient.h +Bool +use_subwindow_mode_p (Screen *screen, Window window) +{ + return False; +} + +#endif /* HAVE_JWXYZ */ |