From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / jwxyz / jwxyz-common.c
1 /* xscreensaver, Copyright (c) 1991-2018 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  */
11
12 /* JWXYZ Is Not Xlib.
13
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.
17
18    This is the version of jwxyz for Android.  The version used by MacOS
19    and iOS is in jwxyz.m.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #ifdef HAVE_JWXYZ /* whole file */
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "jwxyzI.h"
33 #include "pow2.h"
34 #include "utf8wc.h"
35 #include "xft.h"
36
37 /* There's only one Window for a given jwxyz_Display. */
38 #define assert_window(dpy, w) \
39   Assert (w == RootWindow (dpy, 0), "not a window")
40
41 #define VTBL JWXYZ_VTBL(dpy)
42
43 struct jwxyz_Font {
44   Display *dpy;
45   void *native_font;
46   int refcount; // for deciding when to release the native font
47   int ascent, descent;
48   char *xa_font;
49
50   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
51   // But we need the metrics on both of them, so they go here.
52   XFontStruct metrics;
53 };
54
55 struct jwxyz_XFontSet {
56   XFontStruct *font;
57 };
58
59
60 void
61 Log (const char *fmt, ...)
62 {
63   va_list args;
64   va_start (args, fmt);
65   Logv (fmt, args);
66   va_end (args);
67 }
68
69
70 int
71 XDisplayWidth (Display *dpy, int screen)
72 {
73   return jwxyz_frame (XRootWindow (dpy, 0))->width;
74 }
75
76 int
77 XDisplayHeight (Display *dpy, int screen)
78 {
79   return jwxyz_frame (XRootWindow (dpy, 0))->height;
80 }
81
82
83 /* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */
84 static int
85 size_mm (Display *dpy, unsigned size)
86 {
87   /* ((mm / inch) / (points / inch)) * dots / (dots / points) */
88   return (25.4 / 72) * size / jwxyz_scale (XRootWindow (dpy,0)) + 0.5;
89 }
90
91 int
92 XDisplayWidthMM (Display *dpy, int screen)
93 {
94   return size_mm (dpy, XDisplayWidth (dpy, screen));
95 }
96
97 int
98 XDisplayHeightMM (Display *dpy, int screen)
99 {
100   return size_mm (dpy, XDisplayHeight (dpy, screen));
101 }
102
103 unsigned long
104 XBlackPixelOfScreen(Screen *screen)
105 {
106   return DefaultVisualOfScreen (screen)->alpha_mask;
107 }
108
109 unsigned long
110 XWhitePixelOfScreen(Screen *screen)
111 {
112   Visual *v = DefaultVisualOfScreen (screen);
113   return (v->red_mask | v->green_mask |v->blue_mask | v->alpha_mask);
114 }
115
116 unsigned long
117 XCellsOfScreen(Screen *screen)
118 {
119   Visual *v = DefaultVisualOfScreen (screen);
120   return (v->red_mask | v->green_mask |v->blue_mask);
121 }
122
123 void
124 jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
125                       Bool alpha_allowed_p)
126 {
127   Assert (depth == 1 || depth == visual_depth(NULL, NULL),
128           "invalid depth: %d", depth);
129
130   if (depth == 1)
131     Assert ((pixel == 0 || pixel == 1), "bogus mono  pixel: 0x%08X", pixel);
132   else if (!alpha_allowed_p)
133     Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
134             "bogus color pixel: 0x%08X", pixel);
135 }
136
137
138 int
139 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
140 {
141   XPoint p;
142   p.x = x;
143   p.y = y;
144   return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
145 }
146
147
148 Bool
149 jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc,
150                         int x, int y, unsigned width, unsigned height)
151 {
152   XGCValues *gcv = VTBL->gc_gcv (gc);
153
154   if (gcv->function == GXset || gcv->function == GXclear) {
155     // "set" and "clear" are dumb drawing modes that ignore the source
156     // bits and just draw solid rectangles.
157     unsigned depth = VTBL->gc_depth (gc);
158     jwxyz_fill_rect (dpy, d, 0, x, y, width, height,
159                      (gcv->function == GXset
160                       ? (depth == 1 ? 1 : WhitePixel(dpy,0))
161                       : (depth == 1 ? 0 : BlackPixel(dpy,0))));
162     return True;
163   }
164
165   return False;
166 }
167
168
169 int
170 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
171            int src_x, int src_y, 
172            unsigned int width, unsigned int height, 
173            int dst_x, int dst_y)
174 {
175   Assert (gc, "no GC");
176   Assert ((width  < 65535), "improbably large width");
177   Assert ((height < 65535), "improbably large height");
178   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
179   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
180   Assert ((dst_x  < 65535 && dst_x  > -65535), "improbably large dst_x");
181   Assert ((dst_y  < 65535 && dst_y  > -65535), "improbably large dst_y");
182
183   if (width == 0 || height == 0)
184     return 0;
185
186   if (jwxyz_dumb_drawing_mode (dpy, dst, gc, dst_x, dst_y, width, height))
187     return 0;
188
189   XRectangle src_frame, dst_frame; // Sizes and origins of the two drawables
190   Bool clipped = False;            // Whether we did any clipping of the rects.
191
192   src_frame = *jwxyz_frame (src);
193   dst_frame = *jwxyz_frame (dst);
194   
195   // Initialize src_rect...
196   //
197   src_x += src_frame.x;
198   src_y += src_frame.y;
199   if (src_y < -65535) Assert(0, "src.origin.y went nuts");
200
201   // Initialize dst_rect...
202   //
203   dst_x += dst_frame.x;
204   dst_y += dst_frame.y;
205   if (dst_y < -65535) Assert(0, "dst.origin.y went nuts");
206
207   // Use signed width and height for this...
208   int width0 = width, height0 = height;
209
210   // Clip rects to frames...
211   //
212
213 # define CLIP(THIS,THAT,VAL,SIZE) do { \
214   int off = THIS##_##VAL; \
215   if (off < 0) { \
216     clipped = True; \
217     SIZE##0      += off; \
218     THIS##_##VAL -= off; \
219     THAT##_##VAL -= off; \
220   } \
221   off = (( THIS##_##VAL +  SIZE##0) - \
222          (THIS##_frame.VAL + THIS##_frame.SIZE)); \
223   if (off > 0) { \
224     clipped = True; \
225     SIZE##0 -= off; \
226   }} while(0)
227
228   CLIP (dst, src, x, width);
229   CLIP (dst, src, y, height);
230
231   // Not actually the original dst_rect, just the one before it's clipped to
232   // the src_frame.
233   int orig_dst_x = dst_x;
234   int orig_dst_y = dst_y;
235   int orig_width  = width0;
236   int orig_height = height0;
237
238   if (width0 <= 0 || height0 <= 0)
239     return 0;
240
241   CLIP (src, dst, x, width);
242   CLIP (src, dst, y, height);
243 # undef CLIP
244
245   // Sort-of-special case where no pixels can be grabbed from the source,
246   // and the whole destination is filled with the background color.
247   if (width0 <= 0 || height0 <= 0) {
248     width0  = 0;
249     height0 = 0;
250   } else {
251     VTBL->copy_area (dpy, src, dst, gc,
252                      src_x, src_y, width0, height0, dst_x, dst_y);
253   }
254
255   // If either the src or dst rects did not lie within their drawables, then
256   // we have adjusted both the src and dst rects to account for the clipping;
257   // that means we need to clear to the background, so that clipped bits end
258   // up in the bg color instead of simply not being copied.
259   //
260   // This has to happen after the copy, because if it happens before, the
261   // cleared area will get grabbed if it overlaps with the source rectangle.
262   //
263   if (clipped && dst == XRootWindow (dpy,0)) {
264     int dst_x0 = dst_x;
265     int dst_y0 = dst_y;
266
267     Assert (orig_dst_x >= 0 &&
268             orig_dst_x + orig_width  <= dst_frame.width &&
269             orig_dst_y >= 0 &&
270             orig_dst_y + orig_height <= dst_frame.height,
271             "wrong dimensions");
272
273     XRectangle rects[4];
274     XRectangle *rects_end = rects;
275
276     if (orig_dst_y < dst_y0) {
277       rects_end->x = orig_dst_x;
278       rects_end->y = orig_dst_y;
279       rects_end->width = orig_width;
280       rects_end->height = dst_y0 - orig_dst_y;
281       ++rects_end;
282     }
283
284     if (orig_dst_y + orig_height > dst_y0 + height0) {
285       rects_end->x = orig_dst_x;
286       rects_end->y = dst_y0 + height0;
287       rects_end->width = orig_width;
288       rects_end->height = orig_dst_y + orig_height - dst_y0 - height0;
289       ++rects_end;
290     }
291
292     if (orig_dst_x < dst_x0) {
293       rects_end->x = orig_dst_x;
294       rects_end->y = dst_y0;
295       rects_end->width = dst_x0 - orig_dst_x;
296       rects_end->height = height0;
297       ++rects_end;
298     }
299
300     if (dst_x0 + width0 < orig_dst_x + orig_width) {
301       rects_end->x = dst_x0 + width0;
302       rects_end->y = dst_y0;
303       rects_end->width = orig_dst_x + orig_width - dst_x0 - width0;
304       rects_end->height = height0;
305       ++rects_end;
306     }
307
308     XGCValues *gcv = VTBL->gc_gcv (gc);
309     int old_function = gcv->function;
310     gcv->function = GXcopy;
311     VTBL->fill_rects (dpy, dst, gc, rects, rects_end - rects,
312                       *VTBL->window_background (dpy));
313     gcv->function = old_function;
314   }
315
316   return 0;
317 }
318
319
320 void
321 jwxyz_blit (const void *src_data, ptrdiff_t src_pitch,
322             unsigned src_x, unsigned src_y,
323             void *dst_data, ptrdiff_t dst_pitch,
324             unsigned dst_x, unsigned dst_y,
325             unsigned width, unsigned height)
326 {
327   Bool same = src_data == dst_data;
328   src_data = SEEK_XY (src_data, src_pitch, src_x, src_y);
329   dst_data = SEEK_XY (dst_data, dst_pitch, dst_x, dst_y);
330
331   size_t bytes = width * 4;
332
333   if (same && dst_y > src_y) {
334     // Copy upwards if the areas might overlap.
335     src_data += src_pitch * (height - 1);
336     dst_data += dst_pitch * (height - 1);
337     src_pitch = -src_pitch;
338     dst_pitch = -dst_pitch;
339   }
340
341   while (height) {
342     // memcpy is an alias for memmove on macOS.
343     memmove (dst_data, src_data, bytes);
344     src_data += src_pitch;
345     dst_data += dst_pitch;
346     --height;
347   }
348 }
349
350
351 int
352 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
353             int src_x, int src_y,
354             unsigned width, int height,
355             int dest_x, int dest_y, unsigned long plane)
356 {
357   Assert ((VTBL->gc_depth (gc) == 1 || plane == 1), "hairy plane mask!");
358   
359   // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
360   // not to white/black.
361   return XCopyArea (dpy, src, dest, gc,
362                     src_x, src_y, width, height, dest_x, dest_y);
363 }
364
365
366 int
367 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
368 {
369   XSegment segment;
370   segment.x1 = x1;
371   segment.y1 = y1;
372   segment.x2 = x2;
373   segment.y2 = y2;
374   XDrawSegments (dpy, d, gc, &segment, 1);
375   return 0;
376 }
377
378
379 int
380 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
381 {
382   Assert (w == XRootWindow (dpy,0), "not a window");
383   jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
384   *VTBL->window_background (dpy) = pixel;
385   return 0;
386 }
387
388 void
389 jwxyz_fill_rect (Display *dpy, Drawable d, GC gc,
390                  int x, int y, unsigned int width, unsigned int height,
391                  unsigned long pixel)
392 {
393   XRectangle r = {x, y, width, height};
394   VTBL->fill_rects (dpy, d, gc, &r, 1, pixel);
395 }
396
397 int
398 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
399                 unsigned int width, unsigned int height)
400 {
401   jwxyz_fill_rect (dpy, d, gc, x, y, width, height,
402                    VTBL->gc_gcv (gc)->foreground);
403   return 0;
404 }
405
406 int
407 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
408                 unsigned int width, unsigned int height)
409 {
410   XPoint points[5] = {
411     {x, y},
412     {x, y + height},
413     {x + width, y + height},
414     {x + width, y},
415     {x, y}
416   };
417
418   XDrawLines(dpy, d, gc, points, 5, CoordModeOrigin);
419   return 0;
420 }
421
422 int
423 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
424 {
425   VTBL->fill_rects (dpy, d, gc, rects, n, VTBL->gc_gcv (gc)->foreground);
426   return 0;
427 }
428
429
430 int
431 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
432 {
433   Assert(win == XRootWindow(dpy,0), "XClearArea: not a window");
434   Assert(!exp, "XClearArea: exposures unsupported");
435   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, *VTBL->window_background (dpy));
436   return 0;
437 }
438
439
440 int
441 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
442           unsigned int width, unsigned int height, int angle1, int angle2)
443 {
444   return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
445                          False);
446 }
447
448 int
449 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
450           unsigned int width, unsigned int height, int angle1, int angle2)
451 {
452   return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
453                          True);
454 }
455
456 int
457 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
458 {
459   int i;
460   for (i = 0; i < narcs; i++)
461     VTBL->draw_arc (dpy, d, gc,
462                     arcs[i].x, arcs[i].y,
463                     arcs[i].width, arcs[i].height,
464                     arcs[i].angle1, arcs[i].angle2,
465                     False);
466   return 0;
467 }
468
469 int
470 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
471 {
472   int i;
473   for (i = 0; i < narcs; i++)
474     VTBL->draw_arc (dpy, d, gc,
475                     arcs[i].x, arcs[i].y,
476                     arcs[i].width, arcs[i].height,
477                     arcs[i].angle1, arcs[i].angle2,
478                     True);
479   return 0;
480 }
481
482 void
483 jwxyz_gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
484 {
485   memset (gcv, 0, sizeof(*gcv));
486   gcv->function   = GXcopy;
487   gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
488   gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
489   gcv->line_width = 1;
490   gcv->cap_style  = CapButt;
491   gcv->join_style = JoinMiter;
492   gcv->fill_rule  = EvenOddRule;
493
494   gcv->alpha_allowed_p = False;
495   gcv->antialias_p     = True;
496 }
497
498
499 int
500 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from)
501 {
502   if (! mask) return 0;
503   Assert (gc && from, "no gc");
504   if (!gc || !from) return 0;
505
506   XGCValues *to = VTBL->gc_gcv (gc);
507   unsigned depth = VTBL->gc_depth (gc);
508
509   if (mask & GCFunction)        to->function            = from->function;
510   if (mask & GCForeground)      to->foreground          = from->foreground;
511   if (mask & GCBackground)      to->background          = from->background;
512   if (mask & GCLineWidth)       to->line_width          = from->line_width;
513   if (mask & GCCapStyle)        to->cap_style           = from->cap_style;
514   if (mask & GCJoinStyle)       to->join_style          = from->join_style;
515   if (mask & GCFillRule)        to->fill_rule           = from->fill_rule;
516   if (mask & GCClipXOrigin)     to->clip_x_origin       = from->clip_x_origin;
517   if (mask & GCClipYOrigin)     to->clip_y_origin       = from->clip_y_origin;
518   if (mask & GCSubwindowMode)   to->subwindow_mode      = from->subwindow_mode;
519
520   if (mask & GCClipMask)        XSetClipMask (dpy, gc, from->clip_mask);
521   if (mask & GCFont)            XSetFont (dpy, gc, from->font);
522
523   if (mask & GCForeground)
524     jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p);
525   if (mask & GCBackground)
526     jwxyz_validate_pixel (dpy, from->background, depth, to->alpha_allowed_p);
527
528   Assert ((! (mask & (GCLineStyle |
529                       GCPlaneMask |
530                       GCFillStyle |
531                       GCTile |
532                       GCStipple |
533                       GCTileStipXOrigin |
534                       GCTileStipYOrigin |
535                       GCGraphicsExposures |
536                       GCDashOffset |
537                       GCDashList |
538                       GCArcMode))),
539           "unimplemented gcvalues mask");
540
541   return 0;
542 }
543
544
545 Status
546 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
547 {
548   assert_window(dpy, w);
549   memset (xgwa, 0, sizeof(*xgwa));
550   const XRectangle *frame = jwxyz_frame (w);
551   xgwa->x      = frame->x;
552   xgwa->y      = frame->y;
553   xgwa->width  = frame->width;
554   xgwa->height = frame->height;
555   xgwa->depth  = visual_depth (NULL, NULL);
556   xgwa->screen = DefaultScreenOfDisplay (dpy);
557   xgwa->visual = XDefaultVisualOfScreen (xgwa->screen);
558   return 0;
559 }
560
561 Status
562 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
563               int *x_ret, int *y_ret,
564               unsigned int *w_ret, unsigned int *h_ret,
565               unsigned int *bw_ret, unsigned int *d_ret)
566 {
567   const XRectangle *frame = jwxyz_frame (d);
568   *x_ret    = frame->x;
569   *y_ret    = frame->y;
570   *w_ret    = frame->width;
571   *h_ret    = frame->height;
572   *d_ret    = jwxyz_drawable_depth (d);
573   *root_ret = RootWindow (dpy, 0);
574   *bw_ret   = 0;
575   return True;
576 }
577
578
579 Status
580 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
581 {
582   Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy));
583   color->pixel =
584     (((color->red   << 16) >> (31 - i_log2(v->red_mask)))   & v->red_mask)   |
585     (((color->green << 16) >> (31 - i_log2(v->green_mask))) & v->green_mask) |
586     (((color->blue  << 16) >> (31 - i_log2(v->blue_mask)))  & v->blue_mask)  |
587     v->alpha_mask;
588   return 1;
589 }
590
591 Status
592 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
593                   unsigned long *pmret, unsigned int npl,
594                   unsigned long *pxret, unsigned int npx)
595 {
596   return 0;
597 }
598
599 int
600 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
601 {
602   Assert(0, "XStoreColors called");
603   return 0;
604 }
605
606 int
607 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
608 {
609   Assert(0, "XStoreColor called");
610   return 0;
611 }
612
613 int
614 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
615              unsigned long planes)
616 {
617   return 0;
618 }
619
620 Status
621 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
622 {
623   unsigned char r=0, g=0, b=0;
624   if (*spec == '#' && strlen(spec) == 7) {
625     static unsigned const char hex[] = {   // yeah yeah, shoot me.
626       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,
627       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,
628       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,
629       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,
630       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,
631       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,
632       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,
633       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};
634     const unsigned char *uspec = (const unsigned char *)spec;
635     r = (hex[uspec[1]] << 4) | hex[uspec[2]];
636     g = (hex[uspec[3]] << 4) | hex[uspec[4]];
637     b = (hex[uspec[5]] << 4) | hex[uspec[6]];
638   } else if (!strcasecmp(spec,"black")) {
639     //  r = g = b = 0;
640   } else if (!strcasecmp(spec,"white")) {
641     r = g = b = 255;
642   } else if (!strcasecmp(spec,"red")) {
643     r = 255;
644   } else if (!strcasecmp(spec,"green")) {
645     g = 255;
646   } else if (!strcasecmp(spec,"blue")) {
647     b = 255;
648   } else if (!strcasecmp(spec,"cyan")) {
649     g = b = 255;
650   } else if (!strcasecmp(spec,"magenta")) {
651     r = b = 255;
652   } else if (!strcasecmp(spec,"yellow")) {
653     r = g = 255;
654   } else {
655     return 0;
656   }
657
658   ret->red   = (r << 8) | r;
659   ret->green = (g << 8) | g;
660   ret->blue  = (b << 8) | b;
661   ret->flags = DoRed|DoGreen|DoBlue;
662   return 1;
663 }
664
665 Status
666 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
667                   XColor *screen_ret, XColor *exact_ret)
668 {
669   if (! XParseColor (dpy, cmap, name, screen_ret))
670     return False;
671   *exact_ret = *screen_ret;
672   return XAllocColor (dpy, cmap, screen_ret);
673 }
674
675 int
676 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
677 {
678   jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False);
679   uint16_t rgba[4];
680   JWXYZ_QUERY_COLOR (dpy, color->pixel, 0xffffull, rgba);
681   color->red   = rgba[0];
682   color->green = rgba[1];
683   color->blue  = rgba[2];
684   color->flags = DoRed|DoGreen|DoBlue;
685   return 0;
686 }
687
688 int
689 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
690 {
691   int i;
692   for (i = 0; i < n; i++)
693     XQueryColor (dpy, cmap, &c[i]);
694   return 0;
695 }
696
697
698 static unsigned long
699 ximage_getpixel_1 (XImage *ximage, int x, int y)
700 {
701   return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
702 }
703
704 static int
705 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
706 {
707   if (pixel)
708     ximage->data [y * ximage->bytes_per_line + (x>>3)] |=  (1 << (x & 7));
709   else
710     ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
711
712   return 0;
713 }
714
715 static unsigned long
716 ximage_getpixel_8 (XImage *ximage, int x, int y)
717 {
718   return ((unsigned long)
719           *((uint8_t *) ximage->data +
720             (y * ximage->bytes_per_line) +
721             x));
722 }
723
724 static int
725 ximage_putpixel_8 (XImage *ximage, int x, int y, unsigned long pixel)
726 {
727   *((uint8_t *) ximage->data +
728     (y * ximage->bytes_per_line) +
729     x) = (uint8_t) pixel;
730   return 0;
731 }
732
733
734 static unsigned long
735 ximage_getpixel_32 (XImage *ximage, int x, int y)
736 {
737   return ((unsigned long)
738           *((uint32_t *) ximage->data +
739             (y * (ximage->bytes_per_line >> 2)) +
740             x));
741 }
742
743 static int
744 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
745 {
746   *((uint32_t *) ximage->data +
747     (y * (ximage->bytes_per_line >> 2)) +
748     x) = (uint32_t) pixel;
749   return 0;
750 }
751
752
753 Status
754 XInitImage (XImage *ximage)
755 {
756   if (!ximage->bytes_per_line)
757     ximage->bytes_per_line = (ximage->depth == 1 ? (ximage->width + 7) / 8 :
758                               ximage->depth == 8 ? ximage->width :
759                               ximage->width * 4);
760
761   if (ximage->depth == 1) {
762     ximage->f.put_pixel = ximage_putpixel_1;
763     ximage->f.get_pixel = ximage_getpixel_1;
764   } else if (ximage->depth == 32 || ximage->depth == 24) {
765     ximage->f.put_pixel = ximage_putpixel_32;
766     ximage->f.get_pixel = ximage_getpixel_32;
767   } else if (ximage->depth == 8) {
768     ximage->f.put_pixel = ximage_putpixel_8;
769     ximage->f.get_pixel = ximage_getpixel_8;
770   } else {
771     Assert (0, "unknown depth");
772   }
773   return 1;
774 }
775
776
777 XImage *
778 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
779               int format, int offset, char *data,
780               unsigned int width, unsigned int height,
781               int bitmap_pad, int bytes_per_line)
782 {
783   XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
784   ximage->width = width;
785   ximage->height = height;
786   ximage->format = format;
787   ximage->data = data;
788   ximage->bitmap_unit = 8;
789   ximage->byte_order = LSBFirst;
790   ximage->bitmap_bit_order = ximage->byte_order;
791   ximage->bitmap_pad = bitmap_pad;
792   ximage->depth = depth;
793   Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy));
794   ximage->red_mask   = (depth == 1 ? 0 : v->red_mask);
795   ximage->green_mask = (depth == 1 ? 0 : v->green_mask);
796   ximage->blue_mask  = (depth == 1 ? 0 : v->blue_mask);
797   ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL));
798   ximage->bytes_per_line = bytes_per_line;
799
800   XInitImage (ximage);
801   return ximage;
802 }
803
804 XImage *
805 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
806 {
807   XImage *to = (XImage *) malloc (sizeof(*to));
808   memcpy (to, from, sizeof(*from));
809   to->width = w;
810   to->height = h;
811   to->bytes_per_line = 0;
812   XInitImage (to);
813
814   to->data = (char *) malloc (h * to->bytes_per_line);
815
816   if (x >= from->width)
817     w = 0;
818   else if (x+w > from->width)
819     w = from->width - x;
820
821   if (y >= from->height)
822     h = 0;
823   else if (y+h > from->height)
824     h = from->height - y;
825
826   int tx, ty;
827   for (ty = 0; ty < h; ty++)
828     for (tx = 0; tx < w; tx++)
829       XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
830   return to;
831 }
832
833
834 XPixmapFormatValues *
835 XListPixmapFormats (Display *dpy, int *n_ret)
836 {
837   XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
838   ret[0].depth = visual_depth (NULL, NULL);
839   ret[0].bits_per_pixel = 32;
840   ret[0].scanline_pad = 8;
841   ret[1].depth = 1;
842   ret[1].bits_per_pixel = 1;
843   ret[1].scanline_pad = 8;
844   *n_ret = 2;
845   return ret;
846 }
847
848
849 unsigned long
850 XGetPixel (XImage *ximage, int x, int y)
851 {
852   return ximage->f.get_pixel (ximage, x, y);
853 }
854
855
856 int
857 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
858 {
859   return ximage->f.put_pixel (ximage, x, y, pixel);
860 }
861
862 int
863 XDestroyImage (XImage *ximage)
864 {
865   if (ximage->data) free (ximage->data);
866   free (ximage);
867   return 0;
868 }
869
870
871 XImage *
872 XGetImage (Display *dpy, Drawable d, int x, int y,
873            unsigned int width, unsigned int height,
874            unsigned long plane_mask, int format)
875 {
876   unsigned depth = jwxyz_drawable_depth (d);
877   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
878                                 0, 0);
879   image->data = (char *) malloc (height * image->bytes_per_line);
880
881   return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
882                        image, 0, 0);
883 }
884
885
886 Pixmap
887 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
888                              const char *data,
889                              unsigned int w, unsigned int h,
890                              unsigned long fg, unsigned long bg,
891                              unsigned int depth)
892 {
893   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
894   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
895                                 (char *) data, w, h, 0, 0);
896   XGCValues gcv;
897   gcv.foreground = fg;
898   gcv.background = bg;
899   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
900   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
901   XFreeGC (dpy, gc);
902   image->data = 0;
903   XDestroyImage (image);
904   return p;
905 }
906
907
908 char *
909 XGetAtomName (Display *dpy, Atom atom)
910 {
911   if (atom == XA_FONT)
912     return strdup ("FONT");
913
914   // Note that atoms (that aren't predefined) are just char *.
915   return strdup ((char *) atom);
916 }
917
918
919 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
920 //
921 static void
922 query_font (Font fid)
923 {
924   Assert (fid && fid->native_font, "no native font in fid");
925
926   int first = 32;
927   int last = 255;
928
929   Display *dpy = fid->dpy;
930   void *native_font = fid->native_font;
931
932   XFontStruct *f = &fid->metrics;
933   XCharStruct *min = &f->min_bounds;
934   XCharStruct *max = &f->max_bounds;
935
936   f->fid               = fid;
937   f->min_char_or_byte2 = first;
938   f->max_char_or_byte2 = last;
939   f->default_char      = 'M';
940   f->ascent            = fid->ascent;
941   f->descent           = fid->descent;
942
943   min->width    = 32767;  // set to smaller values in the loop
944   min->ascent   = 32767;
945   min->descent  = 32767;
946   min->lbearing = 32767;
947   min->rbearing = 32767;
948
949   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
950
951   for (int i = first; i <= last; i++) {
952     XCharStruct *cs = &f->per_char[i-first];
953     char s = (char) i;
954     jwxyz_render_text (dpy, native_font, &s, 1, False, False, cs, 0);
955
956     max->width    = MAX (max->width,    cs->width);
957     max->ascent   = MAX (max->ascent,   cs->ascent);
958     max->descent  = MAX (max->descent,  cs->descent);
959     max->lbearing = MAX (max->lbearing, cs->lbearing);
960     max->rbearing = MAX (max->rbearing, cs->rbearing);
961
962     min->width    = MIN (min->width,    cs->width);
963     min->ascent   = MIN (min->ascent,   cs->ascent);
964     min->descent  = MIN (min->descent,  cs->descent);
965     min->lbearing = MIN (min->lbearing, cs->lbearing);
966     min->rbearing = MIN (min->rbearing, cs->rbearing);
967 /*
968     Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
969          " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n"
970          i, i, cs->width, cs->lbearing, cs->rbearing,
971          cs->ascent, cs->descent,
972          bbox.size.width, bbox.size.height,
973          bbox.origin.x, bbox.origin.y,
974          advancement.width, advancement.height);
975  */
976   }
977 }
978
979
980 // Since 'Font' includes the metrics, this just makes a copy of that.
981 //
982 XFontStruct *
983 XQueryFont (Display *dpy, Font fid)
984 {
985   // copy XFontStruct
986   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
987   *f = fid->metrics;
988   f->fid = fid;
989
990   // build XFontProps
991   f->n_properties = 1;
992   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
993   f->properties[0].name = XA_FONT;
994   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
995           "atoms probably needs a real implementation");
996   // If XInternAtom is ever implemented, use it here.
997   f->properties[0].card32 = (unsigned long)fid->xa_font;
998
999   // copy XCharStruct array
1000   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
1001   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
1002
1003   memcpy (f->per_char, fid->metrics.per_char,
1004           size * sizeof (XCharStruct));
1005
1006   return f;
1007 }
1008
1009
1010 static Font
1011 copy_font (Font fid)
1012 {
1013   fid->refcount++;
1014   return fid;
1015 }
1016
1017
1018 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
1019    of XLFD strings; also they can be comma-separated strings with multiple
1020    font names.  First one that exists wins.
1021  */
1022 static void
1023 try_native_font (Display *dpy, const char *name, Font fid)
1024 {
1025   if (!name) return;
1026   const char *spc = strrchr (name, ' ');
1027   if (!spc) return;
1028
1029   char *token = strdup (name);
1030   char *otoken = token;
1031   char *name2;
1032   char *lasts;
1033
1034   while ((name2 = strtok_r (token, ",", &lasts))) {
1035     token = 0;
1036
1037     while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
1038       name2++;
1039
1040     spc = strrchr (name2, ' ');
1041     if (!spc) continue;
1042
1043     int dsize = 0;
1044     if (1 != sscanf (spc, " %d ", &dsize))
1045       continue;
1046     float size = dsize;
1047
1048     if (size < 4) continue;
1049
1050     name2[strlen(name2) - strlen(spc)] = 0;
1051
1052     fid->native_font = jwxyz_load_native_font(XRootWindow(dpy,0), 0, 0, name2,
1053                                               strlen(name2) - strlen(spc),
1054                                               JWXYZ_FONT_FACE, size, NULL,
1055                                               &fid->ascent, &fid->descent);
1056     if (fid->native_font) {
1057       fid->xa_font = strdup (name); // Maybe this should be an XLFD?
1058       break;
1059     } else {
1060       // To list fonts:
1061       //  po [UIFont familyNames]
1062       //  po [UIFont fontNamesForFamilyName:@"Arial"]
1063       Log("No native font: \"%s\" %.0f", name2, size);
1064     }
1065   }
1066
1067   free (otoken);
1068 }
1069
1070
1071 static const char *
1072 xlfd_field_end (const char *s)
1073 {
1074   const char *s2 = strchr(s, '-');
1075   if (!s2)
1076     s2 = s + strlen(s);
1077   return s2;
1078 }
1079
1080
1081 static size_t
1082 xlfd_next (const char **s, const char **s2)
1083 {
1084   if (!**s2) {
1085     *s = *s2;
1086   } else {
1087     Assert (**s2 == '-', "xlfd parse error");
1088     *s = *s2 + 1;
1089     *s2 = xlfd_field_end (*s);
1090   }
1091
1092   return *s2 - *s;
1093 }
1094
1095
1096 static void
1097 try_xlfd_font (Display *dpy, const char *name, Font fid)
1098 {
1099   const char *family_name = NULL; /* Not NULL-terminated. */
1100   size_t family_name_size = 0;
1101   int require = 0,
1102    // Default mask is for the built-in X11 font aliases.
1103    mask = JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD | JWXYZ_STYLE_ITALIC;
1104   Bool rand  = False;
1105   float size = 12; /* In points (1/72 in.) */
1106
1107   const char *s = (name ? name : "");
1108
1109   size_t L = strlen (s);
1110 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
1111 # define UNSPEC   (L == 0 || (L == 1 && *s == '*'))
1112   if      (CMP ("6x10"))     size = 8,  require |= JWXYZ_STYLE_MONOSPACE;
1113   else if (CMP ("6x10bold")) size = 8,  require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1114   else if (CMP ("fixed"))    size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1115   else if (CMP ("9x15"))     size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1116   else if (CMP ("9x15bold")) size = 12, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1117   else if (CMP ("vga"))      size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1118   else if (CMP ("console"))  size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1119   else if (CMP ("gallant"))  size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1120   else {
1121
1122     int forbid = 0;
1123
1124     // Incorrect fields are ignored.
1125
1126     if (*s == '-')
1127       ++s;
1128     const char *s2 = xlfd_field_end(s);
1129
1130     // Foundry (ignore)
1131
1132     L = xlfd_next (&s, &s2); // Family name
1133     // This used to substitute Georgia for Times. Now it doesn't.
1134     if (CMP ("random")) {
1135       rand = True;
1136     } else if (CMP ("fixed")) {
1137       require |= JWXYZ_STYLE_MONOSPACE;
1138       family_name = "Courier";
1139       family_name_size = strlen(family_name);
1140     } else if (!UNSPEC) {
1141       family_name = s;
1142       family_name_size = L;
1143     }
1144
1145     L = xlfd_next (&s, &s2); // Weight name
1146     if (CMP ("bold") || CMP ("demibold"))
1147       require |= JWXYZ_STYLE_BOLD;
1148     else if (CMP ("medium") || CMP ("regular"))
1149       forbid |= JWXYZ_STYLE_BOLD;
1150
1151     L = xlfd_next (&s, &s2); // Slant
1152     if (CMP ("i") || CMP ("o"))
1153       require |= JWXYZ_STYLE_ITALIC;
1154     else if (CMP ("r"))
1155       forbid |= JWXYZ_STYLE_ITALIC;
1156
1157     xlfd_next (&s, &s2); // Set width name (ignore)
1158     xlfd_next (&s, &s2); // Add style name (ignore)
1159
1160     L = xlfd_next (&s, &s2); // Pixel size
1161     char *s3;
1162     uintmax_t pxsize = strtoumax(s, &s3, 10);
1163     if (UNSPEC || s2 != s3)
1164       pxsize = UINTMAX_MAX; // i.e. it's invalid.
1165
1166     L = xlfd_next (&s, &s2); // Point size
1167     uintmax_t ptsize = strtoumax(s, &s3, 10);
1168     if (UNSPEC || s2 != s3)
1169       ptsize = UINTMAX_MAX;
1170
1171     xlfd_next (&s, &s2); // Resolution X (ignore)
1172     xlfd_next (&s, &s2); // Resolution Y (ignore)
1173
1174     L = xlfd_next (&s, &s2); // Spacing
1175     if (CMP ("p"))
1176       forbid |= JWXYZ_STYLE_MONOSPACE;
1177     else if (CMP ("m") || CMP ("c"))
1178       require |= JWXYZ_STYLE_MONOSPACE;
1179
1180     xlfd_next (&s, &s2); // Average width (ignore)
1181
1182     // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-*         14 px
1183     // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*        14 pt
1184     // -*-courier-bold-r-*-*-140-*                    14 pt, via wildcard
1185     // -*-courier-bold-r-*-140-*                      14 pt, not handled
1186     // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-*       error
1187
1188     L = xlfd_next (&s, &s2); // Charset registry
1189     if (ptsize != UINTMAX_MAX) {
1190       // It was in the ptsize field, so that's definitely what it is.
1191       size = ptsize / 10.0;
1192     } else if (pxsize != UINTMAX_MAX) {
1193       size = pxsize;
1194       // If it's a fully qualified XLFD, then this really is the pxsize.
1195       // Otherwise, this is probably point size with a multi-field wildcard.
1196       if (L == 0)
1197         size /= 10.0;
1198     }
1199
1200     mask = require | forbid;
1201   }
1202 # undef CMP
1203 # undef UNSPEC
1204
1205   if (!family_name && !rand) {
1206     family_name = jwxyz_default_font_family (require);
1207     family_name_size = strlen (family_name);
1208   }
1209
1210   if (size < 6 || size > 1000)
1211     size = 12;
1212
1213   char *family_name_ptr = NULL;
1214   fid->native_font = jwxyz_load_native_font (XRootWindow(dpy,0),
1215                                              require, mask,
1216                                              family_name, family_name_size,
1217                                              rand ? JWXYZ_FONT_RANDOM : JWXYZ_FONT_FAMILY,
1218                                              size, &family_name_ptr,
1219                                              &fid->ascent, &fid->descent);
1220
1221   if (fid->native_font) {
1222     unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2;
1223     unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d);
1224     asprintf(&fid->xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
1225              family_name_ptr,
1226              (require & JWXYZ_STYLE_BOLD) ? "bold" : "medium",
1227              (require & JWXYZ_STYLE_ITALIC) ? 'o' : 'r',
1228              (unsigned)(dpi * size / 72.27 + 0.5),
1229              (unsigned)(size * 10 + 0.5), dpi, dpi,
1230              (require & JWXYZ_STYLE_MONOSPACE) ? 'm' : 'p');
1231   }
1232
1233   free (family_name_ptr);
1234 }
1235
1236
1237 Font
1238 XLoadFont (Display *dpy, const char *name)
1239 {
1240   Font fid = (Font) calloc (1, sizeof(*fid));
1241
1242   fid->refcount = 1;
1243   fid->dpy = dpy;
1244   try_native_font (dpy, name, fid);
1245
1246   if (!fid->native_font && name &&
1247       strchr (name, ' ') &&
1248       !strchr (name, '*')) {
1249     // If name contains a space but no stars, it is a native font spec --
1250     // return NULL so that we know it really didn't exist.  Else, it is an
1251     //  XLFD font, so keep trying.
1252     free (fid);
1253     return 0;
1254   }
1255
1256   if (! fid->native_font)
1257     try_xlfd_font (dpy, name, fid);
1258
1259   if (!fid->native_font) {
1260     free (fid);
1261     return 0;
1262   }
1263
1264   query_font (fid);
1265
1266   return fid;
1267 }
1268
1269
1270 XFontStruct *
1271 XLoadQueryFont (Display *dpy, const char *name)
1272 {
1273   Font fid = XLoadFont (dpy, name);
1274   if (!fid) return 0;
1275   return XQueryFont (dpy, fid);
1276 }
1277
1278 int
1279 XUnloadFont (Display *dpy, Font fid)
1280 {
1281   if (--fid->refcount < 0) abort();
1282   if (fid->refcount > 0) return 0;
1283
1284   if (fid->native_font)
1285     jwxyz_release_native_font (fid->dpy, fid->native_font);
1286
1287   if (fid->metrics.per_char)
1288     free (fid->metrics.per_char);
1289
1290   free (fid);
1291   return 0;
1292 }
1293
1294 int
1295 XFreeFontInfo (char **names, XFontStruct *info, int n)
1296 {
1297   int i;
1298   if (names) {
1299     for (i = 0; i < n; i++)
1300       if (names[i]) free (names[i]);
1301     free (names);
1302   }
1303   if (info) {
1304     for (i = 0; i < n; i++)
1305       if (info[i].per_char) {
1306         free (info[i].per_char);
1307         free (info[i].properties);
1308       }
1309     free (info);
1310   }
1311   return 0;
1312 }
1313
1314 int
1315 XFreeFont (Display *dpy, XFontStruct *f)
1316 {
1317   Font fid = f->fid;
1318   XFreeFontInfo (0, f, 1);
1319   XUnloadFont (dpy, fid);
1320   return 0;
1321 }
1322
1323
1324 int
1325 XSetFont (Display *dpy, GC gc, Font fid)
1326 {
1327   XGCValues *gcv = VTBL->gc_gcv(gc);
1328   Font font2 = copy_font (fid);
1329   if (gcv->font)
1330     XUnloadFont (dpy, gcv->font);
1331   gcv->font = font2;
1332   return 0;
1333 }
1334
1335
1336 XFontSet
1337 XCreateFontSet (Display *dpy, char *name,
1338                 char ***missing_charset_list_return,
1339                 int *missing_charset_count_return,
1340                 char **def_string_return)
1341 {
1342   char *name2 = strdup (name);
1343   char *s = strchr (name, ',');
1344   if (s) *s = 0;
1345   XFontSet set = 0;
1346   XFontStruct *f = XLoadQueryFont (dpy, name2);
1347   if (f)
1348   {
1349     set = (XFontSet) calloc (1, sizeof(*set));
1350     set->font = f;
1351   }
1352   free (name2);
1353   if (missing_charset_list_return)  *missing_charset_list_return = 0;
1354   if (missing_charset_count_return) *missing_charset_count_return = 0;
1355   if (def_string_return) *def_string_return = 0;
1356   return set;
1357 }
1358
1359
1360 void
1361 XFreeFontSet (Display *dpy, XFontSet set)
1362 {
1363   XFreeFont (dpy, set->font);
1364   free (set);
1365 }
1366
1367
1368 void
1369 XFreeStringList (char **list)
1370 {
1371   int i;
1372   if (!list) return;
1373   for (i = 0; list[i]; i++)
1374     XFree (list[i]);
1375   XFree (list);
1376 }
1377
1378
1379 int
1380 XTextExtents (XFontStruct *f, const char *s, int length,
1381               int *dir_ret, int *ascent_ret, int *descent_ret,
1382               XCharStruct *cs)
1383 {
1384   // Unfortunately, adding XCharStructs together to get the extents for a
1385   // string doesn't work: Cocoa uses non-integral character advancements, but
1386   // XCharStruct.width is an integer. Plus that doesn't take into account
1387   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
1388   // Zapfino.
1389
1390   Font ff = f->fid;
1391   Display *dpy = ff->dpy;
1392   jwxyz_render_text (dpy, ff->native_font, s, length, False, False, cs, 0);
1393   *dir_ret = 0;
1394   *ascent_ret  = f->ascent;
1395   *descent_ret = f->descent;
1396   return 0;
1397 }
1398
1399 int
1400 XTextWidth (XFontStruct *f, const char *s, int length)
1401 {
1402   int ascent, descent, dir;
1403   XCharStruct cs;
1404   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
1405   return cs.width;
1406 }
1407
1408
1409 int
1410 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
1411                 int *dir_ret, int *ascent_ret, int *descent_ret,
1412                 XCharStruct *cs)
1413 {
1414   // Bool latin1_p = True;
1415   int i, utf8_len = 0;
1416   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
1417
1418   for (i = 0; i < length; i++)
1419     if (s[i].byte1 > 0) {
1420       // latin1_p = False;
1421       break;
1422     }
1423
1424   {
1425     Font ff = f->fid;
1426     Display *dpy = ff->dpy;
1427     jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
1428                        True, False, cs, 0);
1429   }
1430
1431   *dir_ret = 0;
1432   *ascent_ret  = f->ascent;
1433   *descent_ret = f->descent;
1434   free (utf8);
1435   return 0;
1436 }
1437
1438
1439 /* "Returns the distance in pixels in the primary draw direction from
1440  the drawing origin to the origin of the next character to be drawn."
1441
1442  "overall_ink_return is set to the bbox of the string's character ink."
1443
1444  "The overall_ink_return for a nondescending, horizontally drawn Latin
1445  character is conventionally entirely above the baseline; that is,
1446  overall_ink_return.height <= -overall_ink_return.y."
1447
1448  [So this means that y is the top of the ink, and height grows down:
1449  For above-the-baseline characters, y is negative.]
1450
1451  "The overall_ink_return for a nonkerned character is entirely at, and to
1452  the right of, the origin; that is, overall_ink_return.x >= 0."
1453
1454  [So this means that x is the left of the ink, and width grows right.
1455  For left-of-the-origin characters, x is negative.]
1456
1457  "A character consisting of a single pixel at the origin would set
1458  overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
1459  */
1460 int
1461 Xutf8TextExtents (XFontSet set, const char *str, int len,
1462                   XRectangle *overall_ink_return,
1463                   XRectangle *overall_logical_return)
1464 {
1465   XCharStruct cs;
1466   Font f = set->font->fid;
1467
1468   jwxyz_render_text (f->dpy, f->native_font, str, len, True, False, &cs,
1469                      NULL);
1470
1471   /* "The overall_logical_return is the bounding box that provides minimum
1472    spacing to other graphical features for the string. Other graphical
1473    features, for example, a border surrounding the text, should not
1474    intersect this rectangle."
1475
1476    So I think that means they're the same?  Or maybe "ink" is the bounding
1477    box, and "logical" is the advancement?  But then why is the return value
1478    the advancement?
1479    */
1480   if (overall_ink_return)
1481     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
1482   if (overall_logical_return)
1483     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
1484
1485   return cs.width;
1486 }
1487
1488
1489 int
1490 jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
1491                    const char *str, size_t len, int utf8_p)
1492 {
1493   const XGCValues *gcv = VTBL->gc_gcv (gc);
1494   Font ff = gcv->font;
1495   XCharStruct cs;
1496
1497   char *data = 0;
1498   jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p,
1499                      gcv->antialias_p, &cs, &data);
1500   int w = cs.rbearing - cs.lbearing;
1501   int h = cs.ascent + cs.descent;
1502
1503   if (w < 0 || h < 0) abort();
1504   if (w == 0 || h == 0) {
1505     if (data) free(data);
1506     return 0;
1507   }
1508
1509   XImage *img = XCreateImage (dpy, VTBL->visual (dpy), 32,
1510                               ZPixmap, 0, data, w, h, 0, 0);
1511
1512   /* The image of text is a 32-bit image, in white.
1513      Take the green channel for intensity and use that as alpha.
1514      replace RGB with the GC's foreground color.
1515      This expects that XPutImage respects alpha and only writes
1516      the bits that are not masked out.
1517    */
1518   {
1519 # define ROTL(x, rot) (((x) << ((rot) & 31)) | ((x) >> (32 - ((rot) & 31))))
1520
1521     Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay(dpy));
1522     unsigned shift = (i_log2 (v->alpha_mask) - i_log2 (v->green_mask)) & 31;
1523     uint32_t mask = ROTL(v->green_mask, shift) & v->alpha_mask,
1524              color = gcv->foreground & ~v->alpha_mask;
1525     uint32_t *s = (uint32_t *)data;
1526     uint32_t *end = s + (w * h);
1527     while (s < end) {
1528
1529       *s = (ROTL(*s, shift) & mask) | color;
1530       ++s;
1531     }
1532   }
1533
1534   {
1535     Bool old_alpha = gcv->alpha_allowed_p;
1536     jwxyz_XSetAlphaAllowed (dpy, gc, True);
1537     XPutImage (dpy, d, gc, img, 0, 0,
1538                x + cs.lbearing,
1539                y - cs.ascent,
1540                w, h);
1541     jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha);
1542     XDestroyImage (img);
1543   }
1544
1545   return 0;
1546 }
1547
1548
1549 int
1550 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
1551              const char  *str, int len)
1552 {
1553   return VTBL->draw_string (dpy, d, gc, x, y, str, len, False);
1554 }
1555
1556
1557 int
1558 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
1559                const XChar2b *str, int len)
1560 {
1561   XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
1562   char *s2;
1563   int ret;
1564   memcpy (b2, str, len * sizeof(*b2));
1565   b2[len].byte1 = b2[len].byte2 = 0;
1566   s2 = XChar2b_to_utf8 (b2, 0);
1567   free (b2);
1568   ret = VTBL->draw_string (dpy, d, gc, x, y, s2, strlen(s2), True);
1569   free (s2);
1570   return ret;
1571 }
1572
1573
1574 void
1575 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
1576                  int x, int y, const char *str, int len)
1577 {
1578   VTBL->draw_string (dpy, d, gc, x, y, str, len, True);
1579 }
1580
1581
1582 int
1583 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
1584                   const char *str, int len)
1585 {
1586   int ascent, descent, dir;
1587   XCharStruct cs;
1588   XTextExtents (&VTBL->gc_gcv (gc)->font->metrics, str, len,
1589                 &dir, &ascent, &descent, &cs);
1590   jwxyz_fill_rect (dpy, d, gc,
1591                    x + MIN (0, cs.lbearing),
1592                    y - MAX (0, ascent),
1593
1594                    /* The +1 here is almost certainly wrong, but BSOD
1595                       requires it; and only BSOD, fluidballs, juggle
1596                       and grabclient call XDrawImageString... */
1597                    MAX (MAX (0, cs.rbearing) -
1598                         MIN (0, cs.lbearing),
1599                         cs.width) + 1,
1600                    MAX (0, ascent) + MAX (0, descent),
1601                    VTBL->gc_gcv(gc)->background);
1602   return XDrawString (dpy, d, gc, x, y, str, len);
1603 }
1604
1605
1606 void *
1607 jwxyz_native_font (Font f)
1608 {
1609   return f->native_font;
1610 }
1611
1612
1613 int
1614 XSetForeground (Display *dpy, GC gc, unsigned long fg)
1615 {
1616   XGCValues *gcv = VTBL->gc_gcv (gc);
1617   jwxyz_validate_pixel (dpy, fg, VTBL->gc_depth (gc), gcv->alpha_allowed_p);
1618   gcv->foreground = fg;
1619   return 0;
1620 }
1621
1622
1623 int
1624 XSetBackground (Display *dpy, GC gc, unsigned long bg)
1625 {
1626   XGCValues *gcv = VTBL->gc_gcv (gc);
1627   jwxyz_validate_pixel (dpy, bg, VTBL->gc_depth (gc), gcv->alpha_allowed_p);
1628   gcv->background = bg;
1629   return 0;
1630 }
1631
1632 int
1633 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
1634 {
1635   VTBL->gc_gcv (gc)->alpha_allowed_p = allowed;
1636   return 0;
1637 }
1638
1639 int
1640 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
1641 {
1642   VTBL->gc_gcv (gc)->antialias_p = antialias_p;
1643   return 0;
1644 }
1645
1646
1647 int
1648 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
1649                     int line_style, int cap_style, int join_style)
1650 {
1651   XGCValues *gcv = VTBL->gc_gcv (gc);
1652   gcv->line_width = line_width;
1653   Assert (line_style == LineSolid, "only LineSolid implemented");
1654 //  gc->gcv.line_style = line_style;
1655   gcv->cap_style = cap_style;
1656   gcv->join_style = join_style;
1657   return 0;
1658 }
1659
1660 int
1661 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
1662 {
1663   return 0;
1664 }
1665
1666 int
1667 XSetFunction (Display *dpy, GC gc, int which)
1668 {
1669   VTBL->gc_gcv (gc)->function = which;
1670   return 0;
1671 }
1672
1673 int
1674 XSetSubwindowMode (Display *dpy, GC gc, int which)
1675 {
1676   VTBL->gc_gcv (gc)->subwindow_mode = which;
1677   return 0;
1678 }
1679
1680
1681 Bool
1682 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
1683                int *root_x_ret, int *root_y_ret,
1684                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
1685 {
1686   assert_window (dpy, w);
1687
1688   XPoint vpos, p;
1689   jwxyz_get_pos (w, &vpos, &p);
1690
1691   if (root_x_ret) *root_x_ret = p.x;
1692   if (root_y_ret) *root_y_ret = p.y;
1693   if (win_x_ret)  *win_x_ret  = p.x - vpos.x;
1694   if (win_y_ret)  *win_y_ret  = p.y - vpos.y;
1695   if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
1696   if (root_ret)   *root_ret   = 0;
1697   if (child_ret)  *child_ret  = 0;
1698   return True;
1699 }
1700
1701 Bool
1702 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
1703                        int src_x, int src_y,
1704                        int *dest_x_ret, int *dest_y_ret,
1705                        Window *child_ret)
1706 {
1707   assert_window (dpy, w);
1708
1709   XPoint vpos, p;
1710   jwxyz_get_pos (w, &vpos, NULL);
1711
1712   // point starts out relative to top left of view
1713   p.x = src_x;
1714   p.y = src_y;
1715
1716   // get point relative to top left of screen
1717   p.x += vpos.x;
1718   p.y += vpos.y;
1719
1720   *dest_x_ret = p.x;
1721   *dest_y_ret = p.y;
1722   if (child_ret)
1723     *child_ret = w;
1724   return True;
1725 }
1726
1727
1728 KeySym
1729 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
1730 {
1731   return code;
1732 }
1733
1734 int
1735 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
1736                XComposeStatus *xc)
1737 {
1738   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
1739   char c = 0;
1740   // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
1741   if ((unsigned int) ks <= 255)
1742     c = (char) ks;
1743
1744   // Put control characters in the string.  Not meta.
1745   if (e->state & ControlMask) {
1746     if (c >= 'a' && c <= 'z')    // Upcase control.
1747       c -= 'a'-'A';
1748     if (c >= '@' && c <= '_')    // Shift to control page.
1749       c -= '@';
1750     if (c == ' ')                // C-SPC is NULL.
1751       c = 0;
1752   }
1753
1754   if (k_ret) *k_ret = ks;
1755   if (size > 0) buf[0] = c;
1756   if (size > 1) buf[1] = 0;
1757   return (size > 0 ? 1 : 0);
1758 }
1759
1760
1761 int
1762 XFlush (Display *dpy)
1763 {
1764   // Just let the event loop take care of this on its own schedule.
1765   return 0;
1766 }
1767
1768 int
1769 XSync (Display *dpy, Bool flush)
1770 {
1771   return XFlush (dpy);
1772 }
1773
1774
1775 // declared in utils/visual.h
1776 int
1777 has_writable_cells (Screen *s, Visual *v)
1778 {
1779   return 0;
1780 }
1781
1782 int
1783 visual_depth (Screen *s, Visual *v)
1784 {
1785   return 32;
1786 }
1787
1788 int
1789 visual_cells (Screen *s, Visual *v)
1790 {
1791   return (int)(v->red_mask | v->green_mask | v->blue_mask);
1792 }
1793
1794 int
1795 visual_class (Screen *s, Visual *v)
1796 {
1797   return TrueColor;
1798 }
1799
1800 void
1801 visual_rgb_masks (Screen *s, Visual *v, unsigned long *red_mask,
1802                   unsigned long *green_mask, unsigned long *blue_mask)
1803 {
1804   *red_mask = v->red_mask;
1805   *green_mask = v->green_mask;
1806   *blue_mask = v->blue_mask;
1807 }
1808
1809 int
1810 visual_pixmap_depth (Screen *s, Visual *v)
1811 {
1812   return 32;
1813 }
1814
1815 int
1816 screen_number (Screen *screen)
1817 {
1818   return 0;
1819 }
1820
1821 // declared in utils/grabclient.h
1822 Bool
1823 use_subwindow_mode_p (Screen *screen, Window window)
1824 {
1825   return False;
1826 }
1827
1828 #endif /* HAVE_JWXYZ */