1 /* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 But it's a bunch of function definitions that bear some resemblance to
15 Xlib and that do Cocoa-ish or OpenGL-ish things that bear some resemblance
16 to the things that Xlib might have done.
18 This is the version of jwxyz for Android. The version used by MacOS
19 and iOS is in jwxyz.m.
26 #ifdef HAVE_JWXYZ /* whole file */
35 /* There's only one Window for a given jwxyz_Display. */
36 #define assert_window(dpy, w) \
37 Assert (w == RootWindow (dpy, 0), "not a window")
43 int refcount; // for deciding when to release the native font
47 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
48 // But we need the metrics on both of them, so they go here.
52 struct jwxyz_XFontSet {
58 Log (const char *fmt, ...)
68 XDisplayWidth (Display *dpy, int screen)
70 return jwxyz_frame (XRootWindow (dpy, 0))->width;
74 XDisplayHeight (Display *dpy, int screen)
76 return jwxyz_frame (XRootWindow (dpy, 0))->height;
80 /* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */
82 size_mm (Display *dpy, unsigned size)
84 /* ((mm / inch) / (points / inch)) * dots / (dots / points) */
85 return (25.4 / 72) * size / jwxyz_scale (XRootWindow (dpy,0)) + 0.5;
89 XDisplayWidthMM (Display *dpy, int screen)
91 return size_mm (dpy, XDisplayWidth (dpy, screen));
95 XDisplayHeightMM (Display *dpy, int screen)
97 return size_mm (dpy, XDisplayHeight (dpy, screen));
102 jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
103 Bool alpha_allowed_p)
105 Assert (depth == 1 || depth == visual_depth(NULL, NULL),
106 "invalid depth: %d", depth);
109 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel: 0x%08X", pixel);
110 else if (!alpha_allowed_p)
111 Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
112 "bogus color pixel: 0x%08X", pixel);
117 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
122 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
127 jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc,
128 int x, int y, unsigned width, unsigned height)
130 XGCValues *gcv = jwxyz_gc_gcv (gc);
132 if (gcv->function == GXset || gcv->function == GXclear) {
133 // "set" and "clear" are dumb drawing modes that ignore the source
134 // bits and just draw solid rectangles.
135 unsigned depth = jwxyz_gc_depth (gc);
136 jwxyz_fill_rect (dpy, d, 0, x, y, width, height,
137 (gcv->function == GXset
138 ? (depth == 1 ? 1 : WhitePixel(dpy,0))
139 : (depth == 1 ? 0 : BlackPixel(dpy,0))));
148 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
149 int src_x, int src_y,
150 unsigned int width, unsigned int height,
151 int dst_x, int dst_y)
153 Assert (gc, "no GC");
154 Assert ((width < 65535), "improbably large width");
155 Assert ((height < 65535), "improbably large height");
156 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
157 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
158 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
159 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
161 if (width == 0 || height == 0)
164 if (jwxyz_dumb_drawing_mode (dpy, dst, gc, dst_x, dst_y, width, height))
167 XRectangle src_frame, dst_frame; // Sizes and origins of the two drawables
168 Bool clipped = False; // Whether we did any clipping of the rects.
170 src_frame = *jwxyz_frame (src);
171 dst_frame = *jwxyz_frame (dst);
173 // Initialize src_rect...
175 src_x += src_frame.x;
176 src_y += src_frame.y;
177 if (src_y < -65535) Assert(0, "src.origin.y went nuts");
179 // Initialize dst_rect...
181 dst_x += dst_frame.x;
182 dst_y += dst_frame.y;
183 if (dst_y < -65535) Assert(0, "dst.origin.y went nuts");
185 // Use signed width and height for this...
186 int width0 = width, height0 = height;
188 // Clip rects to frames...
191 # define CLIP(THIS,THAT,VAL,SIZE) do { \
192 int off = THIS##_##VAL; \
196 THIS##_##VAL -= off; \
197 THAT##_##VAL -= off; \
199 off = (( THIS##_##VAL + SIZE##0) - \
200 (THIS##_frame.VAL + THIS##_frame.SIZE)); \
206 CLIP (dst, src, x, width);
207 CLIP (dst, src, y, height);
209 // Not actually the original dst_rect, just the one before it's clipped to
211 int orig_dst_x = dst_x;
212 int orig_dst_y = dst_y;
213 int orig_width = width0;
214 int orig_height = height0;
216 if (width0 <= 0 || height0 <= 0)
219 CLIP (src, dst, x, width);
220 CLIP (src, dst, y, height);
223 // Sort-of-special case where no pixels can be grabbed from the source,
224 // and the whole destination is filled with the background color.
225 if (width0 <= 0 || height0 <= 0) {
229 jwxyz_copy_area (dpy, src, dst, gc,
230 src_x, src_y, width0, height0, dst_x, dst_y);
233 // If either the src or dst rects did not lie within their drawables, then
234 // we have adjusted both the src and dst rects to account for the clipping;
235 // that means we need to clear to the background, so that clipped bits end
236 // up in the bg color instead of simply not being copied.
238 // This has to happen after the copy, because if it happens before, the
239 // cleared area will get grabbed if it overlaps with the source rectangle.
241 if (clipped && dst == XRootWindow (dpy,0)) {
245 Assert (orig_dst_x >= 0 &&
246 orig_dst_x + orig_width <= dst_frame.width &&
248 orig_dst_y + orig_height <= dst_frame.height,
252 XRectangle *rects_end = rects;
254 if (orig_dst_y < dst_y0) {
255 rects_end->x = orig_dst_x;
256 rects_end->y = orig_dst_y;
257 rects_end->width = orig_width;
258 rects_end->height = dst_y0 - orig_dst_y;
262 if (orig_dst_y + orig_height > dst_y0 + height0) {
263 rects_end->x = orig_dst_x;
264 rects_end->y = dst_y0 + height0;
265 rects_end->width = orig_width;
266 rects_end->height = orig_dst_y + orig_height - dst_y0 - height0;
270 if (orig_dst_x < dst_x0) {
271 rects_end->x = orig_dst_x;
272 rects_end->y = dst_y0;
273 rects_end->width = dst_x0 - orig_dst_x;
274 rects_end->height = height0;
278 if (dst_x0 + width0 < orig_dst_x + orig_width) {
279 rects_end->x = dst_x0 + width0;
280 rects_end->y = dst_y0;
281 rects_end->width = orig_dst_x + orig_width - dst_x0 - width0;
282 rects_end->height = height0;
286 XGCValues *gcv = jwxyz_gc_gcv (gc);
287 int old_function = gcv->function;
288 gcv->function = GXcopy;
289 jwxyz_fill_rects (dpy, dst, gc, rects, rects_end - rects,
290 jwxyz_window_background (dpy));
291 gcv->function = old_function;
299 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
300 int src_x, int src_y,
301 unsigned width, int height,
302 int dest_x, int dest_y, unsigned long plane)
304 Assert ((jwxyz_gc_depth (gc) == 1 || plane == 1), "hairy plane mask!");
306 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
307 // not to white/black.
308 return XCopyArea (dpy, src, dest, gc,
309 src_x, src_y, width, height, dest_x, dest_y);
314 jwxyz_fill_rect (Display *dpy, Drawable d, GC gc,
315 int x, int y, unsigned int width, unsigned int height,
318 XRectangle r = {x, y, width, height};
319 jwxyz_fill_rects (dpy, d, gc, &r, 1, pixel);
323 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
324 unsigned int width, unsigned int height)
326 jwxyz_fill_rect (dpy, d, gc, x, y, width, height,
327 jwxyz_gc_gcv (gc)->foreground);
332 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
333 unsigned int width, unsigned int height)
338 {x + width, y + height},
343 XDrawLines(dpy, d, gc, points, 5, CoordModeOrigin);
348 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
350 jwxyz_fill_rects (dpy, d, gc, rects, n, jwxyz_gc_gcv (gc)->foreground);
356 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
357 unsigned int width, unsigned int height, int angle1, int angle2)
359 return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
364 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
365 unsigned int width, unsigned int height, int angle1, int angle2)
367 return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
372 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
375 for (i = 0; i < narcs; i++)
376 jwxyz_draw_arc (dpy, d, gc,
377 arcs[i].x, arcs[i].y,
378 arcs[i].width, arcs[i].height,
379 arcs[i].angle1, arcs[i].angle2,
385 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
388 for (i = 0; i < narcs; i++)
389 jwxyz_draw_arc (dpy, d, gc,
390 arcs[i].x, arcs[i].y,
391 arcs[i].width, arcs[i].height,
392 arcs[i].angle1, arcs[i].angle2,
398 jwxyz_gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
400 memset (gcv, 0, sizeof(*gcv));
401 gcv->function = GXcopy;
402 gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
403 gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
405 gcv->cap_style = CapButt;
406 gcv->join_style = JoinMiter;
407 gcv->fill_rule = EvenOddRule;
409 gcv->alpha_allowed_p = False;
410 gcv->antialias_p = True;
415 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from)
417 if (! mask) return 0;
418 Assert (gc && from, "no gc");
419 if (!gc || !from) return 0;
421 XGCValues *to = jwxyz_gc_gcv (gc);
422 unsigned depth = jwxyz_gc_depth (gc);
424 if (mask & GCFunction) to->function = from->function;
425 if (mask & GCForeground) to->foreground = from->foreground;
426 if (mask & GCBackground) to->background = from->background;
427 if (mask & GCLineWidth) to->line_width = from->line_width;
428 if (mask & GCCapStyle) to->cap_style = from->cap_style;
429 if (mask & GCJoinStyle) to->join_style = from->join_style;
430 if (mask & GCFillRule) to->fill_rule = from->fill_rule;
431 if (mask & GCClipXOrigin) to->clip_x_origin = from->clip_x_origin;
432 if (mask & GCClipYOrigin) to->clip_y_origin = from->clip_y_origin;
433 if (mask & GCSubwindowMode) to->subwindow_mode = from->subwindow_mode;
435 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
436 if (mask & GCFont) XSetFont (0, gc, from->font);
438 if (mask & GCForeground)
439 jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p);
440 if (mask & GCBackground)
441 jwxyz_validate_pixel (dpy, from->background, depth, to->alpha_allowed_p);
443 Assert ((! (mask & (GCLineStyle |
450 GCGraphicsExposures |
454 "unimplemented gcvalues mask");
461 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
463 assert_window(dpy, w);
464 memset (xgwa, 0, sizeof(*xgwa));
465 const XRectangle *frame = jwxyz_frame (w);
468 xgwa->width = frame->width;
469 xgwa->height = frame->height;
470 xgwa->depth = visual_depth (NULL, NULL);
471 xgwa->screen = DefaultScreenOfDisplay (dpy);
472 xgwa->visual = XDefaultVisualOfScreen (xgwa->screen);
477 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
478 int *x_ret, int *y_ret,
479 unsigned int *w_ret, unsigned int *h_ret,
480 unsigned int *bw_ret, unsigned int *d_ret)
482 const XRectangle *frame = jwxyz_frame (d);
485 *w_ret = frame->width;
486 *h_ret = frame->height;
487 *d_ret = jwxyz_drawable_depth (d);
488 *root_ret = RootWindow (dpy, 0);
495 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
497 color->pixel = jwxyz_alloc_color (dpy,
506 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
507 unsigned long *pmret, unsigned int npl,
508 unsigned long *pxret, unsigned int npx)
514 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
516 Assert(0, "XStoreColors called");
521 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
523 Assert(0, "XStoreColor called");
528 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
529 unsigned long planes)
535 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
537 unsigned char r=0, g=0, b=0;
538 if (*spec == '#' && strlen(spec) == 7) {
539 static unsigned const char hex[] = { // yeah yeah, shoot me.
540 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,
541 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,
542 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,
543 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,
544 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,
545 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,
546 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,
547 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};
548 const unsigned char *uspec = (const unsigned char *)spec;
549 r = (hex[uspec[1]] << 4) | hex[uspec[2]];
550 g = (hex[uspec[3]] << 4) | hex[uspec[4]];
551 b = (hex[uspec[5]] << 4) | hex[uspec[6]];
552 } else if (!strcasecmp(spec,"black")) {
554 } else if (!strcasecmp(spec,"white")) {
556 } else if (!strcasecmp(spec,"red")) {
558 } else if (!strcasecmp(spec,"green")) {
560 } else if (!strcasecmp(spec,"blue")) {
562 } else if (!strcasecmp(spec,"cyan")) {
564 } else if (!strcasecmp(spec,"magenta")) {
566 } else if (!strcasecmp(spec,"yellow")) {
572 ret->red = (r << 8) | r;
573 ret->green = (g << 8) | g;
574 ret->blue = (b << 8) | b;
575 ret->flags = DoRed|DoGreen|DoBlue;
580 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
581 XColor *screen_ret, XColor *exact_ret)
583 if (! XParseColor (dpy, cmap, name, screen_ret))
585 *exact_ret = *screen_ret;
586 return XAllocColor (dpy, cmap, screen_ret);
590 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
592 jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False);
594 jwxyz_query_color (dpy, color->pixel, rgba);
595 color->red = (rgba[0] << 8) | rgba[0];
596 color->green = (rgba[1] << 8) | rgba[1];
597 color->blue = (rgba[2] << 8) | rgba[2];
598 color->flags = DoRed|DoGreen|DoBlue;
603 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
606 for (i = 0; i < n; i++)
607 XQueryColor (dpy, cmap, &c[i]);
613 ximage_getpixel_1 (XImage *ximage, int x, int y)
615 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
619 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
622 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
624 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
630 ximage_getpixel_32 (XImage *ximage, int x, int y)
632 return ((unsigned long)
633 *((uint32_t *) ximage->data +
634 (y * (ximage->bytes_per_line >> 2)) +
639 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
641 *((uint32_t *) ximage->data +
642 (y * (ximage->bytes_per_line >> 2)) +
643 x) = (uint32_t) pixel;
649 XInitImage (XImage *ximage)
651 if (!ximage->bytes_per_line)
652 ximage->bytes_per_line = (ximage->depth == 1
653 ? (ximage->width + 7) / 8
654 : ximage->width * 4);
656 if (ximage->depth == 1) {
657 ximage->f.put_pixel = ximage_putpixel_1;
658 ximage->f.get_pixel = ximage_getpixel_1;
659 } else if (ximage->depth == 32 || ximage->depth == 24) {
660 ximage->f.put_pixel = ximage_putpixel_32;
661 ximage->f.get_pixel = ximage_getpixel_32;
663 Assert (0, "unknown depth");
670 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
671 int format, int offset, char *data,
672 unsigned int width, unsigned int height,
673 int bitmap_pad, int bytes_per_line)
675 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
676 ximage->width = width;
677 ximage->height = height;
678 ximage->format = format;
680 ximage->bitmap_unit = 8;
681 ximage->byte_order = LSBFirst;
682 ximage->bitmap_bit_order = ximage->byte_order;
683 ximage->bitmap_pad = bitmap_pad;
684 ximage->depth = depth;
685 Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy));
686 ximage->red_mask = (depth == 1 ? 0 : v->red_mask);
687 ximage->green_mask = (depth == 1 ? 0 : v->green_mask);
688 ximage->blue_mask = (depth == 1 ? 0 : v->blue_mask);
689 ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL));
690 ximage->bytes_per_line = bytes_per_line;
697 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
699 XImage *to = (XImage *) malloc (sizeof(*to));
700 memcpy (to, from, sizeof(*from));
703 to->bytes_per_line = 0;
706 to->data = (char *) malloc (h * to->bytes_per_line);
708 if (x >= from->width)
710 else if (x+w > from->width)
713 if (y >= from->height)
715 else if (y+h > from->height)
716 h = from->height - y;
719 for (ty = 0; ty < h; ty++)
720 for (tx = 0; tx < w; tx++)
721 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
726 XPixmapFormatValues *
727 XListPixmapFormats (Display *dpy, int *n_ret)
729 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
730 ret[0].depth = visual_depth (NULL, NULL);
731 ret[0].bits_per_pixel = 32;
732 ret[0].scanline_pad = 8;
734 ret[1].bits_per_pixel = 1;
735 ret[1].scanline_pad = 8;
742 XGetPixel (XImage *ximage, int x, int y)
744 return ximage->f.get_pixel (ximage, x, y);
749 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
751 return ximage->f.put_pixel (ximage, x, y, pixel);
755 XDestroyImage (XImage *ximage)
757 if (ximage->data) free (ximage->data);
764 XGetImage (Display *dpy, Drawable d, int x, int y,
765 unsigned int width, unsigned int height,
766 unsigned long plane_mask, int format)
768 unsigned depth = jwxyz_drawable_depth (d);
769 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
771 image->data = (char *) malloc (height * image->bytes_per_line);
773 return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
779 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
781 unsigned int w, unsigned int h,
782 unsigned long fg, unsigned long bg,
785 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
786 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
787 (char *) data, w, h, 0, 0);
791 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
792 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
795 XDestroyImage (image);
801 XGetAtomName (Display *dpy, Atom atom)
804 return strdup ("FONT");
806 // Note that atoms (that aren't predefined) are just char *.
807 return strdup ((char *) atom);
811 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
814 query_font (Font fid)
816 Assert (fid && fid->native_font, "no native font in fid");
821 Display *dpy = fid->dpy;
822 void *native_font = fid->native_font;
824 XFontStruct *f = &fid->metrics;
825 XCharStruct *min = &f->min_bounds;
826 XCharStruct *max = &f->max_bounds;
829 f->min_char_or_byte2 = first;
830 f->max_char_or_byte2 = last;
831 f->default_char = 'M';
832 f->ascent = fid->ascent;
833 f->descent = fid->descent;
835 min->width = 32767; // set to smaller values in the loop
837 min->descent = 32767;
838 min->lbearing = 32767;
839 min->rbearing = 32767;
841 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
843 for (int i = first; i <= last; i++) {
844 XCharStruct *cs = &f->per_char[i-first];
846 jwxyz_render_text (dpy, native_font, &s, 1, False, cs, 0);
848 max->width = MAX (max->width, cs->width);
849 max->ascent = MAX (max->ascent, cs->ascent);
850 max->descent = MAX (max->descent, cs->descent);
851 max->lbearing = MAX (max->lbearing, cs->lbearing);
852 max->rbearing = MAX (max->rbearing, cs->rbearing);
854 min->width = MIN (min->width, cs->width);
855 min->ascent = MIN (min->ascent, cs->ascent);
856 min->descent = MIN (min->descent, cs->descent);
857 min->lbearing = MIN (min->lbearing, cs->lbearing);
858 min->rbearing = MIN (min->rbearing, cs->rbearing);
860 Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
861 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n"
862 i, i, cs->width, cs->lbearing, cs->rbearing,
863 cs->ascent, cs->descent,
864 bbox.size.width, bbox.size.height,
865 bbox.origin.x, bbox.origin.y,
866 advancement.width, advancement.height);
872 // Since 'Font' includes the metrics, this just makes a copy of that.
875 XQueryFont (Display *dpy, Font fid)
878 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
884 f->properties = malloc (sizeof(*f->properties) * f->n_properties);
885 f->properties[0].name = XA_FONT;
886 Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
887 "atoms probably needs a real implementation");
888 // If XInternAtom is ever implemented, use it here.
889 f->properties[0].card32 = (unsigned long)fid->xa_font;
891 // copy XCharStruct array
892 int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
893 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
895 memcpy (f->per_char, fid->metrics.per_char,
896 size * sizeof (XCharStruct));
910 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
911 of XLFD strings; also they can be comma-separated strings with multiple
912 font names. First one that exists wins.
915 try_native_font (Display *dpy, const char *name, Font fid)
918 const char *spc = strrchr (name, ' ');
921 char *token = strdup (name);
922 char *otoken = token;
926 while ((name2 = strtok_r (token, ",", &lasts))) {
929 while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
932 spc = strrchr (name2, ' ');
936 if (1 != sscanf (spc, " %d ", &dsize))
940 if (size < 4) continue;
942 name2[strlen(name2) - strlen(spc)] = 0;
944 fid->native_font = jwxyz_load_native_font(XRootWindow(dpy,0), 0, 0, name2,
945 strlen(name2) - strlen(spc),
946 JWXYZ_FONT_FACE, size, NULL,
947 &fid->ascent, &fid->descent);
948 if (fid->native_font) {
949 fid->xa_font = strdup (name); // Maybe this should be an XLFD?
952 Log("No native font: \"%s\" %.0f", name2, size);
961 xlfd_field_end (const char *s)
963 const char *s2 = strchr(s, '-');
971 xlfd_next (const char **s, const char **s2)
976 Assert (**s2 == '-', "xlfd parse error");
978 *s2 = xlfd_field_end (*s);
986 try_xlfd_font (Display *dpy, const char *name, Font fid)
988 const char *family_name = NULL; /* Not NULL-terminated. */
989 size_t family_name_size = 0;
991 // Default mask is for the built-in X11 font aliases.
992 mask = JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD | JWXYZ_STYLE_ITALIC;
994 float size = 12; /* In points (1/72 in.) */
996 const char *s = (name ? name : "");
998 size_t L = strlen (s);
999 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
1000 # define UNSPEC (L == 0 || (L == 1 && *s == '*'))
1001 if (CMP ("6x10")) size = 8, require |= JWXYZ_STYLE_MONOSPACE;
1002 else if (CMP ("6x10bold")) size = 8, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1003 else if (CMP ("fixed")) size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1004 else if (CMP ("9x15")) size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1005 else if (CMP ("9x15bold")) size = 12, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1006 else if (CMP ("vga")) size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1007 else if (CMP ("console")) size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1008 else if (CMP ("gallant")) size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1013 // Incorrect fields are ignored.
1017 const char *s2 = xlfd_field_end(s);
1021 L = xlfd_next (&s, &s2); // Family name
1022 // This used to substitute Georgia for Times. Now it doesn't.
1023 if (CMP ("random")) {
1025 } else if (CMP ("fixed")) {
1026 require |= JWXYZ_STYLE_MONOSPACE;
1027 family_name = "Courier";
1028 family_name_size = strlen(family_name);
1029 } else if (!UNSPEC) {
1031 family_name_size = L;
1034 L = xlfd_next (&s, &s2); // Weight name
1035 if (CMP ("bold") || CMP ("demibold"))
1036 require |= JWXYZ_STYLE_BOLD;
1037 else if (CMP ("medium") || CMP ("regular"))
1038 forbid |= JWXYZ_STYLE_BOLD;
1040 L = xlfd_next (&s, &s2); // Slant
1041 if (CMP ("i") || CMP ("o"))
1042 require |= JWXYZ_STYLE_ITALIC;
1044 forbid |= JWXYZ_STYLE_ITALIC;
1046 xlfd_next (&s, &s2); // Set width name (ignore)
1047 xlfd_next (&s, &s2); // Add style name (ignore)
1049 L = xlfd_next (&s, &s2); // Pixel size
1051 uintmax_t pxsize = strtoumax(s, &s3, 10);
1052 if (UNSPEC || s2 != s3)
1053 pxsize = UINTMAX_MAX; // i.e. it's invalid.
1055 L = xlfd_next (&s, &s2); // Point size
1056 uintmax_t ptsize = strtoumax(s, &s3, 10);
1057 if (UNSPEC || s2 != s3)
1058 ptsize = UINTMAX_MAX;
1060 xlfd_next (&s, &s2); // Resolution X (ignore)
1061 xlfd_next (&s, &s2); // Resolution Y (ignore)
1063 L = xlfd_next (&s, &s2); // Spacing
1065 forbid |= JWXYZ_STYLE_MONOSPACE;
1066 else if (CMP ("m") || CMP ("c"))
1067 require |= JWXYZ_STYLE_MONOSPACE;
1069 xlfd_next (&s, &s2); // Average width (ignore)
1071 // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-* 14 px
1072 // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-* 14 pt
1073 // -*-courier-bold-r-*-*-140-* 14 pt, via wildcard
1074 // -*-courier-bold-r-*-140-* 14 pt, not handled
1075 // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-* error
1077 L = xlfd_next (&s, &s2); // Charset registry
1078 if (ptsize != UINTMAX_MAX) {
1079 // It was in the ptsize field, so that's definitely what it is.
1080 size = ptsize / 10.0;
1081 } else if (pxsize != UINTMAX_MAX) {
1083 // If it's a fully qualified XLFD, then this really is the pxsize.
1084 // Otherwise, this is probably point size with a multi-field wildcard.
1089 mask = require | forbid;
1094 if (!family_name && !rand) {
1095 family_name = jwxyz_default_font_family (require);
1096 family_name_size = strlen (family_name);
1099 if (size < 6 || size > 1000)
1102 char *family_name_ptr = NULL;
1103 fid->native_font = jwxyz_load_native_font (XRootWindow(dpy,0),
1105 family_name, family_name_size,
1106 rand ? JWXYZ_FONT_RANDOM : JWXYZ_FONT_FAMILY,
1107 size, &family_name_ptr,
1108 &fid->ascent, &fid->descent);
1110 if (fid->native_font) {
1111 unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2;
1112 unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d);
1113 asprintf(&fid->xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
1115 (require & JWXYZ_STYLE_BOLD) ? "bold" : "medium",
1116 (require & JWXYZ_STYLE_ITALIC) ? 'o' : 'r',
1117 (unsigned)(dpi * size / 72.27 + 0.5),
1118 (unsigned)(size * 10 + 0.5), dpi, dpi,
1119 (require & JWXYZ_STYLE_MONOSPACE) ? 'm' : 'p');
1122 free (family_name_ptr);
1127 XLoadFont (Display *dpy, const char *name)
1129 Font fid = (Font) calloc (1, sizeof(*fid));
1133 try_native_font (dpy, name, fid);
1135 if (!fid->native_font && name &&
1136 strchr (name, ' ') &&
1137 !strchr (name, '*')) {
1138 // If name contains a space but no stars, it is a native font spec --
1139 // return NULL so that we know it really didn't exist. Else, it is an
1140 // XLFD font, so keep trying.
1145 if (! fid->native_font)
1146 try_xlfd_font (dpy, name, fid);
1148 //Log("parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
1151 fid->native_font = jwxyz_load_native_font (dpy, name,
1152 &fid->ascent, &fid->descent);
1153 if (!fid->native_font) {
1165 XLoadQueryFont (Display *dpy, const char *name)
1167 Font fid = XLoadFont (dpy, name);
1169 return XQueryFont (dpy, fid);
1173 XUnloadFont (Display *dpy, Font fid)
1175 if (--fid->refcount < 0) abort();
1176 if (fid->refcount > 0) return 0;
1178 if (fid->native_font)
1179 jwxyz_release_native_font (fid->dpy, fid->native_font);
1181 if (fid->metrics.per_char)
1182 free (fid->metrics.per_char);
1189 XFreeFontInfo (char **names, XFontStruct *info, int n)
1193 for (i = 0; i < n; i++)
1194 if (names[i]) free (names[i]);
1198 for (i = 0; i < n; i++)
1199 if (info[i].per_char) {
1200 free (info[i].per_char);
1201 free (info[i].properties);
1209 XFreeFont (Display *dpy, XFontStruct *f)
1212 XFreeFontInfo (0, f, 1);
1213 XUnloadFont (dpy, fid);
1219 XSetFont (Display *dpy, GC gc, Font fid)
1221 XGCValues *gcv = jwxyz_gc_gcv(gc);
1222 Font font2 = copy_font (fid);
1224 XUnloadFont (dpy, gcv->font);
1231 XCreateFontSet (Display *dpy, char *name,
1232 char ***missing_charset_list_return,
1233 int *missing_charset_count_return,
1234 char **def_string_return)
1236 char *name2 = strdup (name);
1237 char *s = strchr (name, ',');
1240 XFontStruct *f = XLoadQueryFont (dpy, name2);
1243 set = (XFontSet) calloc (1, sizeof(*set));
1247 if (missing_charset_list_return) *missing_charset_list_return = 0;
1248 if (missing_charset_count_return) *missing_charset_count_return = 0;
1249 if (def_string_return) *def_string_return = 0;
1255 XFreeFontSet (Display *dpy, XFontSet set)
1257 XFreeFont (dpy, set->font);
1263 XFreeStringList (char **list)
1267 for (i = 0; list[i]; i++)
1274 XTextExtents (XFontStruct *f, const char *s, int length,
1275 int *dir_ret, int *ascent_ret, int *descent_ret,
1278 // Unfortunately, adding XCharStructs together to get the extents for a
1279 // string doesn't work: Cocoa uses non-integral character advancements, but
1280 // XCharStruct.width is an integer. Plus that doesn't take into account
1281 // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
1285 Display *dpy = ff->dpy;
1286 jwxyz_render_text (dpy, ff->native_font, s, length, False, cs, 0);
1288 *ascent_ret = f->ascent;
1289 *descent_ret = f->descent;
1294 XTextWidth (XFontStruct *f, const char *s, int length)
1296 int ascent, descent, dir;
1298 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
1304 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
1305 int *dir_ret, int *ascent_ret, int *descent_ret,
1308 // Bool latin1_p = True;
1309 int i, utf8_len = 0;
1310 char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized
1312 for (i = 0; i < length; i++)
1313 if (s[i].byte1 > 0) {
1314 // latin1_p = False;
1320 Display *dpy = ff->dpy;
1321 jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
1326 *ascent_ret = f->ascent;
1327 *descent_ret = f->descent;
1333 /* "Returns the distance in pixels in the primary draw direction from
1334 the drawing origin to the origin of the next character to be drawn."
1336 "overall_ink_return is set to the bbox of the string's character ink."
1338 "The overall_ink_return for a nondescending, horizontally drawn Latin
1339 character is conventionally entirely above the baseline; that is,
1340 overall_ink_return.height <= -overall_ink_return.y."
1342 [So this means that y is the top of the ink, and height grows down:
1343 For above-the-baseline characters, y is negative.]
1345 "The overall_ink_return for a nonkerned character is entirely at, and to
1346 the right of, the origin; that is, overall_ink_return.x >= 0."
1348 [So this means that x is the left of the ink, and width grows right.
1349 For left-of-the-origin characters, x is negative.]
1351 "A character consisting of a single pixel at the origin would set
1352 overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
1355 Xutf8TextExtents (XFontSet set, const char *str, int len,
1356 XRectangle *overall_ink_return,
1357 XRectangle *overall_logical_return)
1360 Font f = set->font->fid;
1362 jwxyz_render_text (f->dpy, f->native_font, str, len, True, &cs, NULL);
1364 /* "The overall_logical_return is the bounding box that provides minimum
1365 spacing to other graphical features for the string. Other graphical
1366 features, for example, a border surrounding the text, should not
1367 intersect this rectangle."
1369 So I think that means they're the same? Or maybe "ink" is the bounding
1370 box, and "logical" is the advancement? But then why is the return value
1373 if (overall_ink_return)
1374 XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
1375 if (overall_logical_return)
1376 XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
1383 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
1384 const char *str, int len)
1386 return jwxyz_draw_string (dpy, d, gc, x, y, str, len, False);
1391 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
1392 const XChar2b *str, int len)
1394 XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
1397 memcpy (b2, str, len * sizeof(*b2));
1398 b2[len].byte1 = b2[len].byte2 = 0;
1399 s2 = XChar2b_to_utf8 (b2, 0);
1401 ret = jwxyz_draw_string (dpy, d, gc, x, y, s2, strlen(s2), True);
1408 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
1409 int x, int y, const char *str, int len)
1411 jwxyz_draw_string (dpy, d, gc, x, y, str, len, True);
1416 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
1417 const char *str, int len)
1419 int ascent, descent, dir;
1421 XTextExtents (&jwxyz_gc_gcv (gc)->font->metrics, str, len,
1422 &dir, &ascent, &descent, &cs);
1423 jwxyz_fill_rect (dpy, d, gc,
1424 x + MIN (0, cs.lbearing),
1425 y - MAX (0, ascent),
1426 MAX (MAX (0, cs.rbearing) -
1427 MIN (0, cs.lbearing),
1429 MAX (0, ascent) + MAX (0, descent),
1430 jwxyz_gc_gcv(gc)->background);
1431 return XDrawString (dpy, d, gc, x, y, str, len);
1436 jwxyz_native_font (Font f)
1438 return f->native_font;
1443 XSetForeground (Display *dpy, GC gc, unsigned long fg)
1445 XGCValues *gcv = jwxyz_gc_gcv (gc);
1446 jwxyz_validate_pixel (dpy, fg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p);
1447 gcv->foreground = fg;
1453 XSetBackground (Display *dpy, GC gc, unsigned long bg)
1455 XGCValues *gcv = jwxyz_gc_gcv (gc);
1456 jwxyz_validate_pixel (dpy, bg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p);
1457 gcv->background = bg;
1462 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
1464 jwxyz_gc_gcv (gc)->alpha_allowed_p = allowed;
1469 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
1471 jwxyz_gc_gcv (gc)->antialias_p = antialias_p;
1477 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
1478 int line_style, int cap_style, int join_style)
1480 XGCValues *gcv = jwxyz_gc_gcv (gc);
1481 gcv->line_width = line_width;
1482 Assert (line_style == LineSolid, "only LineSolid implemented");
1483 // gc->gcv.line_style = line_style;
1484 gcv->cap_style = cap_style;
1485 gcv->join_style = join_style;
1490 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
1496 XSetFunction (Display *dpy, GC gc, int which)
1498 jwxyz_gc_gcv (gc)->function = which;
1503 XSetSubwindowMode (Display *dpy, GC gc, int which)
1505 jwxyz_gc_gcv (gc)->subwindow_mode = which;
1511 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
1512 int *root_x_ret, int *root_y_ret,
1513 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
1515 assert_window (dpy, w);
1518 jwxyz_get_pos (w, &vpos, &p);
1520 if (root_x_ret) *root_x_ret = p.x;
1521 if (root_y_ret) *root_y_ret = p.y;
1522 if (win_x_ret) *win_x_ret = p.x - vpos.x;
1523 if (win_y_ret) *win_y_ret = p.y - vpos.y;
1524 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
1525 if (root_ret) *root_ret = 0;
1526 if (child_ret) *child_ret = 0;
1531 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
1532 int src_x, int src_y,
1533 int *dest_x_ret, int *dest_y_ret,
1536 assert_window (dpy, w);
1539 jwxyz_get_pos (w, &vpos, NULL);
1541 // point starts out relative to top left of view
1545 // get point relative to top left of screen
1558 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
1564 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
1567 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
1569 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
1570 if ((unsigned int) ks <= 255)
1573 // Put control characters in the string. Not meta.
1574 if (e->state & ControlMask) {
1575 if (c >= 'a' && c <= 'z') // Upcase control.
1577 if (c >= '@' && c <= '_') // Shift to control page.
1579 if (c == ' ') // C-SPC is NULL.
1583 if (k_ret) *k_ret = ks;
1584 if (size > 0) buf[0] = c;
1585 if (size > 1) buf[1] = 0;
1586 return (size > 0 ? 1 : 0);
1591 XFlush (Display *dpy)
1593 // Just let the event loop take care of this on its own schedule.
1598 XSync (Display *dpy, Bool flush)
1600 return XFlush (dpy);
1604 // declared in utils/visual.h
1606 has_writable_cells (Screen *s, Visual *v)
1612 visual_depth (Screen *s, Visual *v)
1618 visual_cells (Screen *s, Visual *v)
1620 return (int)(v->red_mask | v->green_mask | v->blue_mask);
1624 visual_class (Screen *s, Visual *v)
1630 visual_rgb_masks (Screen *s, Visual *v, unsigned long *red_mask,
1631 unsigned long *green_mask, unsigned long *blue_mask)
1633 *red_mask = v->red_mask;
1634 *green_mask = v->green_mask;
1635 *blue_mask = v->blue_mask;
1639 visual_pixmap_depth (Screen *s, Visual *v)
1645 screen_number (Screen *screen)
1650 // declared in utils/grabclient.h
1652 use_subwindow_mode_p (Screen *screen, Window window)
1657 #endif /* HAVE_JWXYZ */