From http://www.jwz.org/xscreensaver/xscreensaver-5.36.tar.gz
[xscreensaver] / jwxyz / jwxyz-common.c
1 /* xscreensaver, Copyright (c) 1991-2016 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 #include "config.h"
23
24 #ifdef HAVE_JWXYZ /* whole file */
25
26 #include "jwxyzI.h"
27
28 /* There's only one Window for a given jwxyz_Display. */
29 #define assert_window(dpy, w) \
30   Assert (w == RootWindow (dpy, 0), "not a window")
31
32
33 void
34 Log (const char *fmt, ...)
35 {
36   va_list args;
37   va_start (args, fmt);
38   Logv (fmt, args);
39   va_end (args);
40 }
41
42
43 int
44 XDisplayWidth (Display *dpy, int screen)
45 {
46   return jwxyz_frame (XRootWindow (dpy, 0))->width;
47 }
48
49 int
50 XDisplayHeight (Display *dpy, int screen)
51 {
52   return jwxyz_frame (XRootWindow (dpy, 0))->height;
53 }
54
55
56 /* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */
57 static const unsigned dpi = 75;
58
59 int
60 XDisplayWidthMM (Display *dpy, int screen)
61 {
62   const unsigned denom = dpi * 10 / 2;
63   return (254 * XDisplayWidth (dpy, screen) + denom) / (2 * denom);
64 }
65
66 int
67 XDisplayHeightMM (Display *dpy, int screen)
68 {
69   const unsigned denom = dpi * 10 / 2;
70   return (254 * XDisplayHeight (dpy, screen) + denom) / (2 * denom);
71 }
72
73
74 void
75 jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
76                       Bool alpha_allowed_p)
77 {
78   Assert (depth == 1 || depth == visual_depth(NULL, NULL),
79           "invalid depth: %d", depth);
80
81   if (depth == 1)
82     Assert ((pixel == 0 || pixel == 1), "bogus mono  pixel: 0x%08X", pixel);
83   else if (!alpha_allowed_p)
84     Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
85             "bogus color pixel: 0x%08X", pixel);
86 }
87
88
89 int
90 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
91 {
92   XPoint p;
93   p.x = x;
94   p.y = y;
95   return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
96 }
97
98
99 Bool
100 jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc,
101                         int x, int y, unsigned width, unsigned height)
102 {
103   XGCValues *gcv = jwxyz_gc_gcv (gc);
104
105   if (gcv->function == GXset || gcv->function == GXclear) {
106     // "set" and "clear" are dumb drawing modes that ignore the source
107     // bits and just draw solid rectangles.
108     unsigned depth = jwxyz_gc_depth (gc);
109     jwxyz_fill_rect (dpy, d, 0, x, y, width, height,
110                      (gcv->function == GXset
111                       ? (depth == 1 ? 1 : WhitePixel(dpy,0))
112                       : (depth == 1 ? 0 : BlackPixel(dpy,0))));
113     return True;
114   }
115
116   return False;
117 }
118
119
120 int
121 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
122            int src_x, int src_y, 
123            unsigned int width, unsigned int height, 
124            int dst_x, int dst_y)
125 {
126   Assert (gc, "no GC");
127   Assert ((width  < 65535), "improbably large width");
128   Assert ((height < 65535), "improbably large height");
129   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
130   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
131   Assert ((dst_x  < 65535 && dst_x  > -65535), "improbably large dst_x");
132   Assert ((dst_y  < 65535 && dst_y  > -65535), "improbably large dst_y");
133
134   if (width == 0 || height == 0)
135     return 0;
136
137   if (jwxyz_dumb_drawing_mode (dpy, dst, gc, dst_x, dst_y, width, height))
138     return 0;
139
140   XRectangle src_frame, dst_frame; // Sizes and origins of the two drawables
141   Bool clipped = False;            // Whether we did any clipping of the rects.
142
143   src_frame = *jwxyz_frame (src);
144   dst_frame = *jwxyz_frame (dst);
145   
146   // Initialize src_rect...
147   //
148   src_x += src_frame.x;
149   src_y += src_frame.y;
150   if (src_y < -65535) Assert(0, "src.origin.y went nuts");
151
152   // Initialize dst_rect...
153   //
154   dst_x += dst_frame.x;
155   dst_y += dst_frame.y;
156   if (dst_y < -65535) Assert(0, "dst.origin.y went nuts");
157
158   // Use signed width and height for this...
159   int width0 = width, height0 = height;
160
161   // Clip rects to frames...
162   //
163
164 # define CLIP(THIS,THAT,VAL,SIZE) do { \
165   int off = THIS##_##VAL; \
166   if (off < 0) { \
167     clipped = True; \
168     SIZE##0      += off; \
169     THIS##_##VAL -= off; \
170     THAT##_##VAL -= off; \
171   } \
172   off = (( THIS##_##VAL +  SIZE##0) - \
173          (THIS##_frame.VAL + THIS##_frame.SIZE)); \
174   if (off > 0) { \
175     clipped = True; \
176     SIZE##0 -= off; \
177   }} while(0)
178
179   CLIP (dst, src, x, width);
180   CLIP (dst, src, y, height);
181
182   // Not actually the original dst_rect, just the one before it's clipped to
183   // the src_frame.
184   int orig_dst_x = dst_x;
185   int orig_dst_y = dst_y;
186   int orig_width  = width0;
187   int orig_height = height0;
188
189   if (width0 <= 0 || height0 <= 0)
190     return 0;
191
192   CLIP (src, dst, x, width);
193   CLIP (src, dst, y, height);
194 # undef CLIP
195
196   // Sort-of-special case where no pixels can be grabbed from the source,
197   // and the whole destination is filled with the background color.
198   if (width0 <= 0 || height0 <= 0) {
199     width0  = 0;
200     height0 = 0;
201   } else {
202     jwxyz_copy_area (dpy, src, dst, gc,
203                      src_x, src_y, width0, height0, dst_x, dst_y);
204   }
205
206   // If either the src or dst rects did not lie within their drawables, then
207   // we have adjusted both the src and dst rects to account for the clipping;
208   // that means we need to clear to the background, so that clipped bits end
209   // up in the bg color instead of simply not being copied.
210   //
211   // This has to happen after the copy, because if it happens before, the
212   // cleared area will get grabbed if it overlaps with the source rectangle.
213   //
214   if (clipped && dst == XRootWindow (dpy,0)) {
215     int dst_x0 = dst_x;
216     int dst_y0 = dst_y;
217
218     Assert (orig_dst_x >= 0 &&
219             orig_dst_x + orig_width  <= dst_frame.width &&
220             orig_dst_y >= 0 &&
221             orig_dst_y + orig_height <= dst_frame.height,
222             "wrong dimensions");
223
224     XRectangle rects[4];
225     XRectangle *rects_end = rects;
226
227     if (orig_dst_y < dst_y0) {
228       rects_end->x = orig_dst_x;
229       rects_end->y = orig_dst_y;
230       rects_end->width = orig_width;
231       rects_end->height = dst_y0 - orig_dst_y;
232       ++rects_end;
233     }
234
235     if (orig_dst_y + orig_height > dst_y0 + height0) {
236       rects_end->x = orig_dst_x;
237       rects_end->y = dst_y0 + height0;
238       rects_end->width = orig_width;
239       rects_end->height = orig_dst_y + orig_height - dst_y0 - height0;
240       ++rects_end;
241     }
242
243     if (orig_dst_x < dst_x0) {
244       rects_end->x = orig_dst_x;
245       rects_end->y = dst_y0;
246       rects_end->width = dst_x0 - orig_dst_x;
247       rects_end->height = height0;
248       ++rects_end;
249     }
250
251     if (dst_x0 + width0 < orig_dst_x + orig_width) {
252       rects_end->x = dst_x0 + width0;
253       rects_end->y = dst_y0;
254       rects_end->width = orig_dst_x + orig_width - dst_x0 - width0;
255       rects_end->height = height0;
256       ++rects_end;
257     }
258
259     XGCValues *gcv = jwxyz_gc_gcv (gc);
260     int old_function = gcv->function;
261     gcv->function = GXcopy;
262     jwxyz_fill_rects (dpy, dst, gc, rects, rects_end - rects,
263                       jwxyz_window_background (dpy));
264     gcv->function = old_function;
265   }
266
267   return 0;
268 }
269
270
271 int
272 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
273             int src_x, int src_y,
274             unsigned width, int height,
275             int dest_x, int dest_y, unsigned long plane)
276 {
277   Assert ((jwxyz_gc_depth (gc) == 1 || plane == 1), "hairy plane mask!");
278   
279   // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
280   // not to white/black.
281   return XCopyArea (dpy, src, dest, gc,
282                     src_x, src_y, width, height, dest_x, dest_y);
283 }
284
285
286 void
287 jwxyz_fill_rect (Display *dpy, Drawable d, GC gc,
288                  int x, int y, unsigned int width, unsigned int height,
289                  unsigned long pixel)
290 {
291   XRectangle r = {x, y, width, height};
292   jwxyz_fill_rects (dpy, d, gc, &r, 1, pixel);
293 }
294
295 int
296 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
297                 unsigned int width, unsigned int height)
298 {
299   jwxyz_fill_rect (dpy, d, gc, x, y, width, height,
300                    jwxyz_gc_gcv (gc)->foreground);
301   return 0;
302 }
303
304 int
305 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
306                 unsigned int width, unsigned int height)
307 {
308   XPoint points[5] = {
309     {x, y},
310     {x, y + height},
311     {x + width, y + height},
312     {x + width, y},
313     {x, y}
314   };
315
316   XDrawLines(dpy, d, gc, points, 5, CoordModeOrigin);
317   return 0;
318 }
319
320 int
321 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
322 {
323   jwxyz_fill_rects (dpy, d, gc, rects, n, jwxyz_gc_gcv (gc)->foreground);
324   return 0;
325 }
326
327
328 int
329 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
330           unsigned int width, unsigned int height, int angle1, int angle2)
331 {
332   return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
333                          False);
334 }
335
336 int
337 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
338           unsigned int width, unsigned int height, int angle1, int angle2)
339 {
340   return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
341                          True);
342 }
343
344 int
345 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
346 {
347   int i;
348   for (i = 0; i < narcs; i++)
349     jwxyz_draw_arc (dpy, d, gc,
350                     arcs[i].x, arcs[i].y,
351                     arcs[i].width, arcs[i].height,
352                     arcs[i].angle1, arcs[i].angle2,
353                     False);
354   return 0;
355 }
356
357 int
358 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
359 {
360   int i;
361   for (i = 0; i < narcs; i++)
362     jwxyz_draw_arc (dpy, d, gc,
363                     arcs[i].x, arcs[i].y,
364                     arcs[i].width, arcs[i].height,
365                     arcs[i].angle1, arcs[i].angle2,
366                     True);
367   return 0;
368 }
369
370 void
371 jwxyz_gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
372 {
373   memset (gcv, 0, sizeof(*gcv));
374   gcv->function   = GXcopy;
375   gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
376   gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
377   gcv->line_width = 1;
378   gcv->cap_style  = CapButt;
379   gcv->join_style = JoinMiter;
380   gcv->fill_rule  = EvenOddRule;
381
382   gcv->alpha_allowed_p = False;
383   gcv->antialias_p     = True;
384 }
385
386
387 int
388 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from)
389 {
390   if (! mask) return 0;
391   Assert (gc && from, "no gc");
392   if (!gc || !from) return 0;
393
394   XGCValues *to = jwxyz_gc_gcv (gc);
395   unsigned depth = jwxyz_gc_depth (gc);
396
397   if (mask & GCFunction)        to->function            = from->function;
398   if (mask & GCForeground)      to->foreground          = from->foreground;
399   if (mask & GCBackground)      to->background          = from->background;
400   if (mask & GCLineWidth)       to->line_width          = from->line_width;
401   if (mask & GCCapStyle)        to->cap_style           = from->cap_style;
402   if (mask & GCJoinStyle)       to->join_style          = from->join_style;
403   if (mask & GCFillRule)        to->fill_rule           = from->fill_rule;
404   if (mask & GCClipXOrigin)     to->clip_x_origin       = from->clip_x_origin;
405   if (mask & GCClipYOrigin)     to->clip_y_origin       = from->clip_y_origin;
406   if (mask & GCSubwindowMode)   to->subwindow_mode      = from->subwindow_mode;
407
408   if (mask & GCClipMask)        XSetClipMask (0, gc, from->clip_mask);
409   if (mask & GCFont)            XSetFont (0, gc, from->font);
410
411   if (mask & GCForeground)
412     jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p);
413   if (mask & GCBackground)
414     jwxyz_validate_pixel (dpy, from->background, depth, to->alpha_allowed_p);
415
416   Assert ((! (mask & (GCLineStyle |
417                       GCPlaneMask |
418                       GCFillStyle |
419                       GCTile |
420                       GCStipple |
421                       GCTileStipXOrigin |
422                       GCTileStipYOrigin |
423                       GCGraphicsExposures |
424                       GCDashOffset |
425                       GCDashList |
426                       GCArcMode))),
427           "unimplemented gcvalues mask");
428
429   return 0;
430 }
431
432
433 Status
434 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
435 {
436   assert_window(dpy, w);
437   memset (xgwa, 0, sizeof(*xgwa));
438   const XRectangle *frame = jwxyz_frame (w);
439   xgwa->x      = frame->x;
440   xgwa->y      = frame->y;
441   xgwa->width  = frame->width;
442   xgwa->height = frame->height;
443   xgwa->depth  = visual_depth (NULL, NULL);
444   xgwa->screen = DefaultScreenOfDisplay (dpy);
445   xgwa->visual = XDefaultVisualOfScreen (xgwa->screen);
446   return 0;
447 }
448
449 Status
450 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
451               int *x_ret, int *y_ret,
452               unsigned int *w_ret, unsigned int *h_ret,
453               unsigned int *bw_ret, unsigned int *d_ret)
454 {
455   const XRectangle *frame = jwxyz_frame (d);
456   *x_ret    = frame->x;
457   *y_ret    = frame->y;
458   *w_ret    = frame->width;
459   *h_ret    = frame->height;
460   *d_ret    = jwxyz_drawable_depth (d);
461   *root_ret = RootWindow (dpy, 0);
462   *bw_ret   = 0;
463   return True;
464 }
465
466
467 Status
468 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
469 {
470   color->pixel = jwxyz_alloc_color (dpy,
471                                     color->red,
472                                     color->green,
473                                     color->blue,
474                                     0xFFFF);
475   return 1;
476 }
477
478 Status
479 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
480                   unsigned long *pmret, unsigned int npl,
481                   unsigned long *pxret, unsigned int npx)
482 {
483   return 0;
484 }
485
486 int
487 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
488 {
489   Assert(0, "XStoreColors called");
490   return 0;
491 }
492
493 int
494 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
495 {
496   Assert(0, "XStoreColor called");
497   return 0;
498 }
499
500 int
501 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
502              unsigned long planes)
503 {
504   return 0;
505 }
506
507 Status
508 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
509 {
510   unsigned char r=0, g=0, b=0;
511   if (*spec == '#' && strlen(spec) == 7) {
512     static unsigned const char hex[] = {   // yeah yeah, shoot me.
513       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,
514       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,
515       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,
516       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,
517       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,
518       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,
519       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,
520       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};
521     const unsigned char *uspec = (const unsigned char *)spec;
522     r = (hex[uspec[1]] << 4) | hex[uspec[2]];
523     g = (hex[uspec[3]] << 4) | hex[uspec[4]];
524     b = (hex[uspec[5]] << 4) | hex[uspec[6]];
525   } else if (!strcasecmp(spec,"black")) {
526     //  r = g = b = 0;
527   } else if (!strcasecmp(spec,"white")) {
528     r = g = b = 255;
529   } else if (!strcasecmp(spec,"red")) {
530     r = 255;
531   } else if (!strcasecmp(spec,"green")) {
532     g = 255;
533   } else if (!strcasecmp(spec,"blue")) {
534     b = 255;
535   } else if (!strcasecmp(spec,"cyan")) {
536     g = b = 255;
537   } else if (!strcasecmp(spec,"magenta")) {
538     r = b = 255;
539   } else if (!strcasecmp(spec,"yellow")) {
540     r = g = 255;
541   } else {
542     return 0;
543   }
544
545   ret->red   = (r << 8) | r;
546   ret->green = (g << 8) | g;
547   ret->blue  = (b << 8) | b;
548   ret->flags = DoRed|DoGreen|DoBlue;
549   return 1;
550 }
551
552 Status
553 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
554                   XColor *screen_ret, XColor *exact_ret)
555 {
556   if (! XParseColor (dpy, cmap, name, screen_ret))
557     return False;
558   *exact_ret = *screen_ret;
559   return XAllocColor (dpy, cmap, screen_ret);
560 }
561
562 int
563 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
564 {
565   jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False);
566   uint8_t rgba[4];
567   jwxyz_query_color (dpy, color->pixel, rgba);
568   color->red   = (rgba[0] << 8) | rgba[0];
569   color->green = (rgba[1] << 8) | rgba[1];
570   color->blue  = (rgba[2] << 8) | rgba[2];
571   color->flags = DoRed|DoGreen|DoBlue;
572   return 0;
573 }
574
575 int
576 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
577 {
578   int i;
579   for (i = 0; i < n; i++)
580     XQueryColor (dpy, cmap, &c[i]);
581   return 0;
582 }
583
584
585 static unsigned long
586 ximage_getpixel_1 (XImage *ximage, int x, int y)
587 {
588   return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
589 }
590
591 static int
592 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
593 {
594   if (pixel)
595     ximage->data [y * ximage->bytes_per_line + (x>>3)] |=  (1 << (x & 7));
596   else
597     ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
598
599   return 0;
600 }
601
602 static unsigned long
603 ximage_getpixel_32 (XImage *ximage, int x, int y)
604 {
605   return ((unsigned long)
606           *((uint32_t *) ximage->data +
607             (y * (ximage->bytes_per_line >> 2)) +
608             x));
609 }
610
611 static int
612 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
613 {
614   *((uint32_t *) ximage->data +
615     (y * (ximage->bytes_per_line >> 2)) +
616     x) = (uint32_t) pixel;
617   return 0;
618 }
619
620
621 Status
622 XInitImage (XImage *ximage)
623 {
624   if (!ximage->bytes_per_line)
625     ximage->bytes_per_line = (ximage->depth == 1
626                               ? (ximage->width + 7) / 8
627                               : ximage->width * 4);
628
629   if (ximage->depth == 1) {
630     ximage->f.put_pixel = ximage_putpixel_1;
631     ximage->f.get_pixel = ximage_getpixel_1;
632   } else if (ximage->depth == 32 || ximage->depth == 24) {
633     ximage->f.put_pixel = ximage_putpixel_32;
634     ximage->f.get_pixel = ximage_getpixel_32;
635   } else {
636     Assert (0, "unknown depth");
637   }
638   return 1;
639 }
640
641
642 XImage *
643 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
644               int format, int offset, char *data,
645               unsigned int width, unsigned int height,
646               int bitmap_pad, int bytes_per_line)
647 {
648   XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
649   ximage->width = width;
650   ximage->height = height;
651   ximage->format = format;
652   ximage->data = data;
653   ximage->bitmap_unit = 8;
654   ximage->byte_order = LSBFirst;
655   ximage->bitmap_bit_order = ximage->byte_order;
656   ximage->bitmap_pad = bitmap_pad;
657   ximage->depth = depth;
658   Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy));
659   ximage->red_mask   = (depth == 1 ? 0 : v->red_mask);
660   ximage->green_mask = (depth == 1 ? 0 : v->green_mask);
661   ximage->blue_mask  = (depth == 1 ? 0 : v->blue_mask);
662   ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL));
663   ximage->bytes_per_line = bytes_per_line;
664
665   XInitImage (ximage);
666   return ximage;
667 }
668
669 XImage *
670 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
671 {
672   XImage *to = (XImage *) malloc (sizeof(*to));
673   memcpy (to, from, sizeof(*from));
674   to->width = w;
675   to->height = h;
676   to->bytes_per_line = 0;
677   XInitImage (to);
678
679   to->data = (char *) malloc (h * to->bytes_per_line);
680
681   if (x >= from->width)
682     w = 0;
683   else if (x+w > from->width)
684     w = from->width - x;
685
686   if (y >= from->height)
687     h = 0;
688   else if (y+h > from->height)
689     h = from->height - y;
690
691   int tx, ty;
692   for (ty = 0; ty < h; ty++)
693     for (tx = 0; tx < w; tx++)
694       XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
695   return to;
696 }
697
698
699 XPixmapFormatValues *
700 XListPixmapFormats (Display *dpy, int *n_ret)
701 {
702   XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
703   ret[0].depth = visual_depth (NULL, NULL);
704   ret[0].bits_per_pixel = 32;
705   ret[0].scanline_pad = 8;
706   ret[1].depth = 1;
707   ret[1].bits_per_pixel = 1;
708   ret[1].scanline_pad = 8;
709   *n_ret = 2;
710   return ret;
711 }
712
713
714 unsigned long
715 XGetPixel (XImage *ximage, int x, int y)
716 {
717   return ximage->f.get_pixel (ximage, x, y);
718 }
719
720
721 int
722 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
723 {
724   return ximage->f.put_pixel (ximage, x, y, pixel);
725 }
726
727 int
728 XDestroyImage (XImage *ximage)
729 {
730   if (ximage->data) free (ximage->data);
731   free (ximage);
732   return 0;
733 }
734
735
736 Pixmap
737 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
738                              const char *data,
739                              unsigned int w, unsigned int h,
740                              unsigned long fg, unsigned int bg,
741                              unsigned int depth)
742 {
743   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
744   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
745                                 (char *) data, w, h, 0, 0);
746   XGCValues gcv;
747   gcv.foreground = fg;
748   gcv.background = bg;
749   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
750   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
751   XFreeGC (dpy, gc);
752   image->data = 0;
753   XDestroyImage (image);
754   return p;
755 }
756
757
758 char *
759 XGetAtomName (Display *dpy, Atom atom)
760 {
761   if (atom == XA_FONT)
762     return strdup ("FONT");
763
764   // Note that atoms (that aren't predefined) are just char *.
765   return strdup ((char *) atom);
766 }
767
768
769 int
770 XSetForeground (Display *dpy, GC gc, unsigned long fg)
771 {
772   XGCValues *gcv = jwxyz_gc_gcv (gc);
773   jwxyz_validate_pixel (dpy, fg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p);
774   gcv->foreground = fg;
775   return 0;
776 }
777
778
779 int
780 XSetBackground (Display *dpy, GC gc, unsigned long bg)
781 {
782   XGCValues *gcv = jwxyz_gc_gcv (gc);
783   jwxyz_validate_pixel (dpy, bg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p);
784   gcv->background = bg;
785   return 0;
786 }
787
788 int
789 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
790 {
791   jwxyz_gc_gcv (gc)->alpha_allowed_p = allowed;
792   return 0;
793 }
794
795 int
796 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
797 {
798   jwxyz_gc_gcv (gc)->antialias_p = antialias_p;
799   return 0;
800 }
801
802
803 int
804 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
805                     int line_style, int cap_style, int join_style)
806 {
807   XGCValues *gcv = jwxyz_gc_gcv (gc);
808   gcv->line_width = line_width;
809   Assert (line_style == LineSolid, "only LineSolid implemented");
810 //  gc->gcv.line_style = line_style;
811   gcv->cap_style = cap_style;
812   gcv->join_style = join_style;
813   return 0;
814 }
815
816 int
817 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
818 {
819   return 0;
820 }
821
822 int
823 XSetFunction (Display *dpy, GC gc, int which)
824 {
825   jwxyz_gc_gcv (gc)->function = which;
826   return 0;
827 }
828
829 int
830 XSetSubwindowMode (Display *dpy, GC gc, int which)
831 {
832   jwxyz_gc_gcv (gc)->subwindow_mode = which;
833   return 0;
834 }
835
836
837 Bool
838 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
839                int *root_x_ret, int *root_y_ret,
840                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
841 {
842   assert_window (dpy, w);
843
844   XPoint vpos, p;
845   jwxyz_get_pos (w, &vpos, &p);
846
847   if (root_x_ret) *root_x_ret = p.x;
848   if (root_y_ret) *root_y_ret = p.y;
849   if (win_x_ret)  *win_x_ret  = p.x - vpos.x;
850   if (win_y_ret)  *win_y_ret  = p.y - vpos.y;
851   if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
852   if (root_ret)   *root_ret   = 0;
853   if (child_ret)  *child_ret  = 0;
854   return True;
855 }
856
857 Bool
858 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
859                        int src_x, int src_y,
860                        int *dest_x_ret, int *dest_y_ret,
861                        Window *child_ret)
862 {
863   assert_window (dpy, w);
864
865   XPoint vpos, p;
866   jwxyz_get_pos (w, &vpos, NULL);
867
868   // point starts out relative to top left of view
869   p.x = src_x;
870   p.y = src_y;
871
872   // get point relative to top left of screen
873   p.x += vpos.x;
874   p.y += vpos.y;
875
876   *dest_x_ret = p.x;
877   *dest_y_ret = p.y;
878   if (child_ret)
879     *child_ret = w;
880   return True;
881 }
882
883
884 KeySym
885 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
886 {
887   return code;
888 }
889
890 int
891 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
892                XComposeStatus *xc)
893 {
894   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
895   char c = 0;
896   // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
897   if ((unsigned int) ks <= 255)
898     c = (char) ks;
899
900   // Put control characters in the string.  Not meta.
901   if (e->state & ControlMask) {
902     if (c >= 'a' && c <= 'z')    // Upcase control.
903       c -= 'a'-'A';
904     if (c >= '@' && c <= '_')    // Shift to control page.
905       c -= '@';
906     if (c == ' ')                // C-SPC is NULL.
907       c = 0;
908   }
909
910   if (k_ret) *k_ret = ks;
911   if (size > 0) buf[0] = c;
912   if (size > 1) buf[1] = 0;
913   return (size > 0 ? 1 : 0);
914 }
915
916
917 int
918 XFlush (Display *dpy)
919 {
920   // Just let the event loop take care of this on its own schedule.
921   return 0;
922 }
923
924 int
925 XSync (Display *dpy, Bool flush)
926 {
927   return XFlush (dpy);
928 }
929
930
931 // declared in utils/visual.h
932 int
933 has_writable_cells (Screen *s, Visual *v)
934 {
935   return 0;
936 }
937
938 int
939 visual_depth (Screen *s, Visual *v)
940 {
941   return 32;
942 }
943
944 int
945 visual_cells (Screen *s, Visual *v)
946 {
947   return (int)(v->red_mask | v->green_mask | v->blue_mask);
948 }
949
950 int
951 visual_class (Screen *s, Visual *v)
952 {
953   return TrueColor;
954 }
955
956 int
957 get_bits_per_pixel (Display *dpy, int depth)
958 {
959   Assert (depth == 32 || depth == 1, "unexpected depth");
960   return depth;
961 }
962
963 int
964 screen_number (Screen *screen)
965 {
966   Display *dpy = DisplayOfScreen (screen);
967   int i;
968   for (i = 0; i < ScreenCount (dpy); i++)
969     if (ScreenOfDisplay (dpy, i) == screen)
970       return i;
971   abort ();
972   return 0;
973 }
974
975 // declared in utils/grabclient.h
976 Bool
977 use_subwindow_mode_p (Screen *screen, Window window)
978 {
979   return False;
980 }
981
982 #endif /* HAVE_JWXYZ */