From http://www.jwz.org/xscreensaver/xscreensaver-5.36.tar.gz
[xscreensaver] / jwxyz / jwxyz-gl.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 OpenGL-ish things that bear some resemblance to the
16    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 /* Be advised, this is all very much a work in progress.
23
24    TODO: The following should be implemented before OpenGL can be considered
25    practical here:
26    - Above all, pick the smallest not-yet working hack that utilizes the
27      needed functionality.
28      - Half-ass the drawing functions.
29    - [OK] What Interference needs
30    - Fast Pixmaps
31    - Whatever clipping is used in XScreenSaver (shape and/or bitmap clipping)
32    - Delayed context creation to support anti-aliasing/multisampling
33    - Everything these hacks need:
34      - FuzzyFlakes (needs wide lines)
35      - Greynetic
36      - [OK] Deluxe
37    - [OK] Get DangerBall going.
38    - [OK] iOS.
39      - [Interference, so far...] And fast, too.
40    - And text really needs to work for the FPS display. */
41
42 /* Also, Take note that OS X can actually run with 256 colors. */
43
44 /* TODO:
45    - malloc error checking
46    - Check max texture sizes for XGet/PutImage, XCopyArea.
47    - Optional 5:5:5 16-bit color
48 */
49
50 /* Techniques & notes:
51    - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
52    - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
53    - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
54    - glLogicOp is an actual thing that should work for GCs.
55    - Pixmaps can be any of the following, depending on GL implementation.
56      - This requires offscreen rendering. Fortunately, this is always
57        available.
58        - OS X: Drawable objects, including: pixel buffers and offscreen
59          memory.
60          - Offscreen buffers w/ CGLSetOffScreen (10.0+)
61          - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
62            provides a very ugly solution for hardware-accelerated offscreen
63            rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
64            10.1+. Apple docs say it's actually for OS X 10.3+, instead.
65          - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
66            - Requires APPLE_pixel_buffer.
67              - Available in software on x86 only.
68              - Always available on hardware.
69            - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
70          - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
71            - TODO: Can EXT_framebuffers be different sizes?
72            - Preferred on 10.7+
73        - iOS: Use OES_framebuffer_object, it's always present.
74  */
75
76 /* OpenGL hacks call a number of X11 functions, including
77  * XCopyArea, XDrawString, XGetImage
78  * XCreatePixmap, XCreateGC, XCreateImage
79  * XPutPixel
80  * Check these, of course. */
81
82 #ifdef JWXYZ_GL /* entire file */
83
84 #include <math.h>
85 #include <limits.h>
86 #include <stddef.h>
87 #include <stdlib.h>
88 #include <stdint.h>
89 #include <string.h>
90 #include <wchar.h>
91
92 #ifdef HAVE_COCOA
93 # ifdef USE_IPHONE
94 #  import <QuartzCore/QuartzCore.h>
95 #  include <OpenGLES/ES1/gl.h>
96 #  include <OpenGLES/ES1/glext.h>
97
98 #  define NSView  UIView
99 #  define NSRect  CGRect
100 #  define NSPoint CGPoint
101 #  define NSSize  CGSize
102 #  define NSColor UIColor
103 #  define NSImage UIImage
104 #  define NSEvent UIEvent
105 #  define NSFont  UIFont
106 #  define NSGlyph CGGlyph
107 #  define NSWindow UIWindow
108 #  define NSMakeSize   CGSizeMake
109 #  define NSBezierPath UIBezierPath
110 #  define colorWithDeviceRed colorWithRed
111
112 #  define NSFontTraitMask      UIFontDescriptorSymbolicTraits
113 // The values for the flags for NSFontTraitMask and
114 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
115 #  define NSBoldFontMask       UIFontDescriptorTraitBold
116 #  define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
117 #  define NSItalicFontMask     UIFontDescriptorTraitItalic
118
119 #  define NSOpenGLContext EAGLContext
120
121 # else
122 #  include <OpenGL/glu.h>
123 # endif
124 #else
125 /* TODO: Does this work on iOS? */
126 # ifndef HAVE_JWZGLES
127 #  include <gl/glu.h>
128 # else
129 #  include <GLES/gl.h>
130 #  include <GLES/glext.h>
131 # endif
132 #endif
133
134 #ifdef HAVE_JWZGLES
135 # include "jwzglesI.h"
136 #endif
137
138 #ifdef HAVE_ANDROID
139 # include <android/log.h>
140 #endif
141
142 #include "jwxyzI.h"
143 #include "jwxyz-timers.h"
144 #include "yarandom.h"
145 #include "utf8wc.h"
146 #include "xft.h"
147
148 #if defined HAVE_COCOA
149 # include <CoreGraphics/CGGeometry.h>
150 #else
151
152 struct CGPoint {
153     float x;
154     float y;
155 };
156 typedef struct CGPoint CGPoint;
157
158 struct CGSize {
159     float width;
160     float height;
161 };
162 typedef struct CGSize CGSize;
163
164 struct CGRect {
165     CGPoint origin;
166     CGSize size;
167 };
168 typedef struct CGRect CGRect;
169
170 #endif
171
172 # undef MAX
173 # undef MIN
174 # define MAX(a,b) ((a)>(b)?(a):(b))
175 # define MIN(a,b) ((a)<(b)?(a):(b))
176
177 union color_bytes
178 {
179   /* On 64-bit systems, high bits of the 32-bit pixel are available as scratch
180      space. I doubt if any screen savers need it, but just in case... */
181   unsigned long pixel;
182   uint8_t bytes[4];
183 };
184
185 struct jwxyz_Display {
186   Window main_window;
187   Screen *screen;
188   int screen_count;
189   struct jwxyz_sources_data *timers_data;
190
191   Bool gl_texture_npot_p;
192   /* Bool opengl_core_p */;
193   GLenum gl_texture_target;
194   
195 // #if defined USE_IPHONE
196   GLuint rect_texture; // Also can work on the desktop.
197 // #endif
198
199   unsigned long window_background;
200 };
201
202 struct jwxyz_Screen {
203   Display *dpy;
204   GLenum pixel_format, pixel_type;
205   unsigned long black, white;
206   Visual *visual;
207   int screen_number;
208 };
209
210 struct jwxyz_GC {
211   XGCValues gcv;
212   unsigned int depth;
213   // CGImageRef clip_mask;  // CGImage copy of the Pixmap in gcv.clip_mask
214 };
215
216 struct jwxyz_Font {
217   Display *dpy;
218   char *ps_name;
219   void *native_font;
220   int refcount; // for deciding when to release the native font
221   float size;   // points
222   int ascent, descent;
223   char *xa_font;
224
225   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
226   // But we need the metrics on both of them, so they go here.
227   XFontStruct metrics;
228 };
229
230 struct jwxyz_XFontSet {
231   XFontStruct *font;
232 };
233
234
235 /* XGetImage in CoreGraphics JWXYZ has to deal with funky pixel formats
236    necessitating fast & flexible pixel conversion. OpenGL does image format
237    conversion itself, so alloc_color and query_color are mercifully simple.
238  */
239 uint32_t
240 jwxyz_alloc_color (Display *dpy,
241                    uint16_t r, uint16_t g, uint16_t b, uint16_t a)
242 {
243   union color_bytes color;
244
245   /* Instead of (int)(c / 256.0), another possibility is
246      (int)(c * 255.0 / 65535.0 + 0.5). This can be calculated using only
247      uint8_t integer_math(uint16_t c) {
248        unsigned c0 = c + 128;
249        return (c0 - (c0 >> 8)) >> 8;
250      }
251    */
252
253   color.bytes[0] = r >> 8;
254   color.bytes[1] = g >> 8;
255   color.bytes[2] = b >> 8;
256   color.bytes[3] = a >> 8;
257
258   if (dpy->screen->pixel_format == GL_BGRA_EXT) {
259     color.pixel = color.bytes[2] |
260                   (color.bytes[1] << 8) |
261                   (color.bytes[0] << 16) |
262                   (color.bytes[3] << 24);
263   } else {
264     Assert(dpy->screen->pixel_format == GL_RGBA,
265            "jwxyz_alloc_color: Unknown pixel_format");
266   }
267
268   return (uint32_t)color.pixel;
269 }
270
271 // Converts an array of pixels ('src') from one format to another, placing the
272 // result in 'dest', according to the pixel conversion mode 'mode'.
273 void
274 jwxyz_query_color (Display *dpy, unsigned long pixel, uint8_t *rgba)
275 {
276   union color_bytes color;
277
278   if(dpy->screen->pixel_format == GL_RGBA)
279   {
280     color.pixel = pixel;
281     for (unsigned i = 0; i != 4; ++i)
282       rgba[i] = color.bytes[i];
283     return;
284   }
285
286   Assert (dpy->screen->pixel_format == GL_BGRA_EXT,
287           "jwxyz_query_color: Unknown pixel format");
288   /* TODO: Cross-check with XAllocColor. */
289   rgba[0] = (pixel >> 16) & 0xFF;
290   rgba[1] = (pixel >>  8) & 0xFF;
291   rgba[2] = (pixel >>  0) & 0xFF;
292   rgba[3] = (pixel >> 24) & 0xFF;
293 }
294
295
296 void
297 jwxyz_assert_display(Display *dpy)
298 {
299   if(!dpy)
300     return;
301   jwxyz_assert_gl ();
302   jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
303 }
304
305
306 void
307 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
308                     Bool window_p)
309 {
310   /* TODO: Check registration pattern from Interference with rectangles instead of points. */
311
312   // The projection matrix is always set as follows. The modelview matrix is
313   // usually identity, but for points and thin lines, it's translated by 0.5.
314   glMatrixMode(GL_PROJECTION);
315   glLoadIdentity();
316   
317 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
318
319   if (window_p && ignore_rotation_p(dpy)) {
320     int o = (int) current_device_rotation();
321     glRotatef (-o, 0, 0, 1);
322   }
323
324   // glPointSize(1); // This is the default.
325   
326 #ifdef HAVE_JWZGLES
327   glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
328 #else
329   glOrtho
330 #endif
331     (0, width, height, 0, -1, 1);
332
333   glMatrixMode(GL_MODELVIEW);
334 # endif // HAVE_MOBILE
335 }
336
337 #ifndef HAVE_JWZGLES
338
339 struct gl_version
340 {
341   // iOS always uses OpenGL ES 1.1.
342   unsigned major;
343   unsigned minor;
344 };
345
346 static GLboolean
347 gl_check_ver (const struct gl_version *caps,
348               unsigned gl_major,
349               unsigned gl_minor)
350 {
351   return caps->major > gl_major ||
352            (caps->major == gl_major && caps->minor >= gl_minor);
353 }
354
355 /*
356 static GLboolean gl_check_ext(const struct gl_caps *caps,
357                               unsigned gl_major,
358                               unsigned gl_minor,
359                               const char *extension)
360 {
361   return
362     gl_check_ver(caps, gl_major, gl_minor) ||
363     gluCheckExtension(extension, caps->extensions);
364 }
365 */
366
367 #endif
368
369
370 // NSOpenGLContext *jwxyz_debug_context;
371
372 /* We keep a list of all of the Displays that have been created and not
373    yet freed so that they can have sensible display numbers.  If three
374    displays are created (0, 1, 2) and then #1 is closed, then the fourth
375    display will be given the now-unused display number 1. (Everything in
376    here assumes a 1:1 Display/Screen mapping.)
377
378    The size of this array is the most number of live displays at one time.
379    So if it's 20, then we'll blow up if the system has 19 monitors and also
380    has System Preferences open (the small preview window).
381
382    Note that xlockmore-style savers tend to allocate big structures, so
383    setting this to 1000 will waste a few megabytes.  Also some of them assume
384    that the number of screens never changes, so dynamically expanding this
385    array won't work.
386  */
387 # ifndef USE_IPHONE
388 static Display *jwxyz_live_displays[20] = { 0, };
389 # endif
390
391
392 Display *
393 jwxyz_make_display (Window w)
394 {
395   Display *d = (Display *) calloc (1, sizeof(*d));
396   d->screen = (Screen *) calloc (1, sizeof(Screen));
397   d->screen->dpy = d;
398   
399   d->screen_count = 1;
400   d->screen->screen_number = 0;
401 # ifndef USE_IPHONE
402   {
403     // Find the first empty slot in live_displays and plug us in.
404     int size = sizeof(jwxyz_live_displays) / sizeof(*jwxyz_live_displays);
405     int i;
406     for (i = 0; i < size; i++) {
407       if (! jwxyz_live_displays[i])
408         break;
409     }
410     if (i >= size) abort();
411     jwxyz_live_displays[i] = d;
412     d->screen_count = size;
413     d->screen->screen_number = i;
414   }
415 # endif // !USE_IPHONE
416
417 # ifndef HAVE_JWZGLES
418   struct gl_version version;
419
420   {
421     const GLubyte *version_str = glGetString (GL_VERSION);
422
423     /* iPhone is always OpenGL ES 1.1. */
424     if (sscanf ((const char *) version_str, "%u.%u",
425                 &version.major, &version.minor) < 2)
426     {
427       version.major = 1;
428       version.minor = 1;
429     }
430   }
431 # endif // !HAVE_JWZGLES
432
433   const GLubyte *extensions = glGetString (GL_EXTENSIONS);
434
435   /* See:
436      - Apple TN2080: Understanding and Detecting OpenGL Functionality.
437      - OpenGL Programming Guide for the Mac - Best Practices for Working with
438        Texture Data - Optimal Data Formats and Types
439    */
440
441   // If a video adapter suports BGRA textures, then that's probably as fast as
442   // you're gonna get for getting a texture onto the screen.
443 # ifdef HAVE_JWZGLES
444   /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
445   d->screen->pixel_format = GL_RGBA; /*
446     gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
447                        extensions) ? GL_BGRA_EXT : GL_RGBA; */
448   d->screen->pixel_type = GL_UNSIGNED_BYTE;
449   // See also OES_read_format.
450 # else  // !HAVE_JWZGLES
451   if (gl_check_ver (&version, 1, 2) ||
452       (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
453        gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
454                           extensions))) {
455     d->screen->pixel_format = GL_BGRA_EXT;
456     // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
457     d->screen->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
458   } else {
459     d->screen->pixel_format = GL_RGBA;
460     d->screen->pixel_type = GL_UNSIGNED_BYTE;
461   }
462   // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
463   // sense on PowerPC.
464 # endif // !HAVE_JWZGLES
465
466   // On really old systems, it would make sense to split the texture
467   // into subsections.
468 # ifndef HAVE_JWZGLES
469   d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
470                                             "GL_ARB_texture_rectangle",
471                                             extensions);
472   d->gl_texture_target = d->gl_texture_npot_p ?
473                          GL_TEXTURE_RECTANGLE_EXT :
474                          GL_TEXTURE_2D;
475 # else
476   d->gl_texture_npot_p = jwzgles_gluCheckExtension
477       ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
478     jwzgles_gluCheckExtension
479       ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
480     jwzgles_gluCheckExtension // From PixelFlinger 1.4
481       ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
482
483   d->gl_texture_target = GL_TEXTURE_2D;
484 # endif
485
486   d->screen->black = jwxyz_alloc_color (d, 0x0000, 0x0000, 0x0000, 0xFFFF);
487   d->screen->white = jwxyz_alloc_color (d, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
488
489   Visual *v = (Visual *) calloc (1, sizeof(Visual));
490   v->class      = TrueColor;
491   v->red_mask   = jwxyz_alloc_color (d, 0xFFFF, 0x0000, 0x0000, 0x0000);
492   v->green_mask = jwxyz_alloc_color (d, 0x0000, 0xFFFF, 0x0000, 0x0000);
493   v->blue_mask  = jwxyz_alloc_color (d, 0x0000, 0x0000, 0xFFFF, 0x0000);
494   v->bits_per_rgb = 8;
495   d->screen->visual = v;
496
497   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
498
499   d->window_background = BlackPixel(d,0);
500
501   d->main_window = w;
502   {
503     fputs((char *)glGetString(GL_VENDOR), stderr);
504     fputc(' ', stderr);
505     fputs((char *)glGetString(GL_RENDERER), stderr);
506     fputc(' ', stderr);
507     fputs((char *)glGetString(GL_VERSION), stderr);
508     fputc('\n', stderr);
509 //  puts(caps.extensions);
510     GLint max_texture_size;
511     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
512     printf ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
513   }
514  
515   // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
516   Assert (d->main_window == w, "Uh-oh.");
517   glGenTextures (1, &d->rect_texture);
518   // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
519   // Rectangle textures should be present on OS X with the following exceptions:
520   // - Generic renderer on PowerPC OS X 10.4 and earlier
521   // - ATI Rage 128
522   glBindTexture (d->gl_texture_target, d->rect_texture);
523   // TODO: This is all somewhere else. Refactor.
524   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
525   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
526
527   // This might be redundant for rectangular textures.
528 # ifndef HAVE_JWZGLES
529   const GLint wrap = GL_CLAMP;
530 # else  // HAVE_JWZGLES
531   const GLint wrap = GL_CLAMP_TO_EDGE;
532 # endif // HAVE_JWZGLES
533
534   // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
535   // This is always present with OpenGL ES.
536   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
537   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
538
539   jwxyz_assert_display(d);
540   return d;
541 }
542
543 void
544 jwxyz_free_display (Display *dpy)
545 {
546   /* TODO: Go over everything. */
547
548   jwxyz_sources_free (dpy->timers_data);
549
550 # ifndef USE_IPHONE
551   {
552     // Find us in live_displays and clear that slot.
553     int size = ScreenCount(dpy);
554     int i;
555     for (i = 0; i < size; i++) {
556       if (dpy == jwxyz_live_displays[i]) {
557         jwxyz_live_displays[i] = 0;
558         break;
559       }
560     }
561     if (i >= size) abort();
562   }
563 # endif // !USE_IPHONE
564
565   free (dpy->screen->visual);
566   free (dpy->screen);
567   free (dpy);
568 }
569
570
571 /* Call this after any modification to the bits on a Pixmap or Window.
572    Most Pixmaps are used frequently as sources and infrequently as
573    destinations, so it pays to cache the data as a CGImage as needed.
574  */
575 static void
576 invalidate_drawable_cache (Drawable d)
577 {
578   /* TODO: Kill this outright. jwxyz_bind_drawable handles any potential 
579      invalidation.
580    */
581
582   /*
583   if (d && d->cgi) {
584     abort();
585     CGImageRelease (d->cgi);
586     d->cgi = 0;
587   }
588  */
589 }
590
591
592 /* Call this when the View changes size or position.
593  */
594 void
595 jwxyz_window_resized (Display *dpy)
596 {
597   const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
598   unsigned new_width = new_frame->width;
599   unsigned new_height = new_frame->height;
600
601   Assert (new_width, "jwxyz_window_resized: No width.");
602   Assert (new_height, "jwxyz_window_resized: No height.");
603
604 /*if (cgc) w->cgc = cgc;
605   Assert (w->cgc, "no CGContext"); */
606
607   Log("resize: %d, %d\n", new_width, new_height);
608
609   jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
610
611   // TODO: What does the iPhone need?
612   
613   // iOS only: If the main_window is not the current_drawable, then set_matrices
614   // was already called in bind_drawable.
615   jwxyz_set_matrices (dpy, new_width, new_height, True);
616
617 /*
618   GLint draw_buffer;
619   glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
620   
621   glDrawBuffer (GL_FRONT);
622   glClearColor (1, 0, 1, 0);
623   glClear (GL_COLOR_BUFFER_BIT);
624   glDrawBuffer (GL_BACK);
625   glClearColor (0, 1, 1, 0);
626   glClear (GL_COLOR_BUFFER_BIT);
627   
628   glDrawBuffer (draw_buffer); */
629   
630   // Stylish and attractive purple!
631   // glClearColor (1, 0, 1, 0.5);
632   // glClear (GL_COLOR_BUFFER_BIT);
633   
634   invalidate_drawable_cache (dpy->main_window);
635 }
636
637
638 jwxyz_sources_data *
639 display_sources_data (Display *dpy)
640 {
641   return dpy->timers_data;
642 }
643
644
645 Window
646 XRootWindow (Display *dpy, int screen)
647 {
648   return dpy ? dpy->main_window : 0;
649 }
650
651 Screen *
652 XDefaultScreenOfDisplay (Display *dpy)
653 {
654   return dpy ? dpy->screen : 0;
655 }
656
657 Visual *
658 XDefaultVisualOfScreen (Screen *screen)
659 {
660   return screen ? screen->visual : 0;
661 }
662
663 Display *
664 XDisplayOfScreen (Screen *s)
665 {
666   return s ? s->dpy : 0;
667 }
668
669 int
670 XDisplayNumberOfScreen (Screen *s)
671 {
672   return 0;
673 }
674
675 int
676 XScreenNumberOfScreen (Screen *s)
677 {
678   return s? s->screen_number : 0;
679 }
680
681 int
682 jwxyz_ScreenCount (Display *dpy)
683 {
684   return dpy ? dpy->screen_count : 0;
685 }
686
687 unsigned long
688 XBlackPixelOfScreen(Screen *screen)
689 {
690   return screen->black;
691 }
692
693 unsigned long
694 XWhitePixelOfScreen(Screen *screen)
695 {
696   return screen->white;
697 }
698
699 unsigned long
700 XCellsOfScreen(Screen *screen)
701 {
702   Visual *v = screen->visual;
703   return v->red_mask | v->green_mask | v->blue_mask;
704 }
705
706
707 /* GC attributes by usage and OpenGL implementation:
708  *
709  * All drawing functions:
710  * function                                | glLogicOp w/ GL_COLOR_LOGIC_OP
711  * clip_x_origin, clip_y_origin, clip_mask | Stencil mask
712  *
713  * Shape drawing functions:
714  * foreground, background                  | glColor*
715  *
716  * XDrawLines, XDrawRectangles, XDrawSegments:
717  * line_width, cap_style, join_style       | Lotsa vertices
718  *
719  * XFillPolygon:
720  * fill_rule                               | Multiple GL_TRIANGLE_FANs
721  *
722  * XDrawText:
723  * font                                    | Cocoa, then OpenGL display lists.
724  *
725  * alpha_allowed_p                         | TODO
726  * antialias_p                             | TODO
727  *
728  * Nothing, really:
729  * subwindow_mode
730 */
731
732 static void
733 set_clip_mask (GC gc)
734 {
735   Assert (!gc->gcv.clip_mask, "set_gc: TODO");
736 }
737
738 static void
739 set_function (int function)
740 {
741   Assert (function == GXcopy, "set_gc: (TODO) Stubbed gcv function");
742   
743   /* TODO: The GL_COLOR_LOGIC_OP extension is exactly what is needed here. (OpenGL 1.1)
744    Fun fact: The glLogicOp opcode constants are the same as the X11 GX* function constants | GL_CLEAR.
745    */
746   
747 #if 0
748   switch (gc->gcv.function) {
749     case GXset:
750     case GXclear:
751     case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/   break;
752     case GXxor:   CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
753     case GXor:    CGContextSetBlendMode (cgc, kCGBlendModeLighten);    break;
754     case GXand:   CGContextSetBlendMode (cgc, kCGBlendModeDarken);     break;
755     default: Assert(0, "unknown gcv function"); break;
756   }
757 #endif
758 }
759
760
761 static void
762 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
763            Bool alpha_allowed_p)
764 {
765   jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
766
767   if (depth == 1) {
768     GLfloat f = pixel;
769     glColor4f (f, f, f, 1);
770   } else {
771     /* TODO: alpha_allowed_p */
772     uint8_t rgba[4];
773     jwxyz_query_color (dpy, pixel, rgba);
774 #ifdef HAVE_JWZGLES
775     glColor4f (rgba[0] / 255.0f, rgba[1] / 255.0f,
776                rgba[2] / 255.0f, rgba[3] / 255.0f);
777 #else
778     glColor4ubv (rgba);
779 #endif
780   }
781 }
782
783 /* Pushes a GC context; sets Function, ClipMask, and color.
784  */
785 static void
786 set_color_gc (Display *dpy, GC gc, unsigned long color)
787 {
788   // GC is NULL for XClearArea and XClearWindow.
789   unsigned int depth;
790   int function;
791   if (gc) {
792     function = gc->gcv.function;
793     depth = gc->depth;
794     set_clip_mask (gc);
795   } else {
796     function = GXcopy;
797     depth = visual_depth (NULL, NULL);
798     // TODO: Set null clip mask here.
799   }
800
801   set_function (function);
802
803   switch (function) {
804     case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
805     case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
806   }
807
808   set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
809   
810   /* TODO: Antialiasing. */
811   /* CGContextSetShouldAntialias (cgc, antialias_p); */
812 }
813
814 /* Pushes a GC context; sets color to the foreground color.
815  */
816 static void
817 set_fg_gc (Display *dpy, GC gc)
818 {
819   set_color_gc (dpy, gc, gc->gcv.foreground);
820 }
821
822 static void
823 next_point(short *v, XPoint p, int mode)
824 {
825   switch (mode) {
826     case CoordModeOrigin:
827       v[0] = p.x;
828       v[1] = p.y;
829       break;
830     case CoordModePrevious:
831       v[0] += p.x;
832       v[1] += p.y;
833       break;
834     default:
835       Assert (False, "next_point: bad mode");
836       break;
837   }
838 }
839
840 int
841 XDrawPoints (Display *dpy, Drawable d, GC gc, 
842              XPoint *points, int count, int mode)
843 {
844   jwxyz_bind_drawable (dpy, dpy->main_window, d);
845   set_fg_gc (dpy, gc);
846
847 /*
848   
849   glBegin(GL_POINTS);
850   for (unsigned i = 0; i < count; i++) {
851     next_point(v, points[i], mode);
852     glVertex2f(v[0] + 0.5f, v[1] + 0.5f);
853   }
854   glEnd();
855  */
856
857   short v[2] = {0, 0};
858
859   // TODO: XPoints can be fed directly to OpenGL.
860   GLshort *gl_points = malloc (count * 2 * sizeof(GLshort)); // TODO: malloc returns NULL.
861   for (unsigned i = 0; i < count; i++) {
862     next_point (v, points[i], mode);
863     gl_points[2 * i] = v[0];
864     gl_points[2 * i + 1] = v[1];
865   }
866   
867   glMatrixMode (GL_MODELVIEW);
868   glTranslatef (0.5, 0.5, 0);
869   
870   glEnableClientState (GL_VERTEX_ARRAY);
871   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
872   glVertexPointer (2, GL_SHORT, 0, gl_points);
873   glDrawArrays (GL_POINTS, 0, count);
874   
875   free (gl_points);
876   
877   glLoadIdentity ();
878   
879   return 0;
880 }
881
882
883 static GLint
884 texture_internalformat(Display *dpy)
885 {
886 #ifdef HAVE_JWZGLES
887   return dpy->screen->pixel_format;
888 #else
889   return GL_RGBA;
890 #endif
891 }
892
893 static GLenum gl_pixel_type(const Display *dpy)
894 {
895   return dpy->screen->pixel_type;
896 }
897
898 static void
899 clear_texture (Display *dpy)
900 {
901   glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
902                 0, dpy->screen->pixel_format, gl_pixel_type (dpy), NULL);
903 }
904
905 static void set_white (void)
906 {
907 #ifdef HAVE_JWZGLES
908   glColor4f (1, 1, 1, 1);
909 #else
910   glColor3ub (0xff, 0xff, 0xff);
911 #endif
912 }
913
914
915 static GLsizei to_pow2 (size_t x);
916
917
918 void
919 jwxyz_gl_copy_area_copy_tex_image (Display *dpy, Drawable src, Drawable dst,
920                                    GC gc, int src_x, int src_y,
921                                    unsigned int width, unsigned int height,
922                                    int dst_x, int dst_y)
923 {
924   const XRectangle *src_frame = jwxyz_frame (src);
925
926   Assert(gc->gcv.function == GXcopy, "XCopyArea: Unknown function");
927
928   jwxyz_bind_drawable (dpy, dpy->main_window, src);
929
930 #  if defined HAVE_COCOA && !defined USE_IPHONE
931   /* TODO: Does this help? */
932   /* glFinish(); */
933 #  endif
934
935   unsigned tex_w = width, tex_h = height;
936   if (!dpy->gl_texture_npot_p) {
937     tex_w = to_pow2(tex_w);
938     tex_h = to_pow2(tex_h);
939   }
940
941   GLint internalformat = texture_internalformat(dpy);
942
943   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
944
945   if (tex_w == width && tex_h == height) {
946     glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
947                       src_x, src_frame->height - src_y - height,
948                       width, height, 0);
949   } else {
950     glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
951                   0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
952     glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
953                          src_x, src_frame->height - src_y - height,
954                          width, height);
955   }
956
957   jwxyz_bind_drawable (dpy, dpy->main_window, dst);
958   set_white ();
959   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
960   glEnable (dpy->gl_texture_target);
961
962   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
963   glEnableClientState (GL_VERTEX_ARRAY);
964     
965   /* TODO: Copied from XPutImage. Refactor. */
966   /* TODO: EXT_draw_texture or whatever it's called. */
967   GLfloat vertices[4][2] =
968   {
969     {dst_x, dst_y},
970     {dst_x, dst_y + height},
971     {dst_x + width, dst_y + height},
972     {dst_x + width, dst_y}
973   };
974   
975 #ifdef HAVE_JWZGLES
976   static const GLshort tex_coords[4][2] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
977 #else
978   GLshort tex_coords[4][2] = {{0, height}, {0, 0}, {width, 0}, {width, height}};
979 #endif
980
981   glVertexPointer (2, GL_FLOAT, 0, vertices);
982   glTexCoordPointer (2, GL_SHORT, 0, tex_coords);
983   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
984   
985   clear_texture (dpy);
986   
987   glDisable (dpy->gl_texture_target);
988 }
989
990 void
991 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
992                                 GC gc, int src_x, int src_y,
993                                 unsigned int width, unsigned int height,
994                                 int dst_x, int dst_y)
995 {
996 # if 1
997   XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
998   XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
999   XDestroyImage (img);
1000 # endif
1001
1002 # if 0
1003   /* Something may or may not be broken in here. (shrug) */
1004   bind_drawable(dpy, src);
1005
1006   /* Error checking would be nice. */
1007   void *pixels = malloc (src_rect.size.width * 4 * src_rect.size.height);
1008
1009   glPixelStorei (GL_PACK_ROW_LENGTH, 0);
1010   glPixelStorei (GL_PACK_ALIGNMENT, 4);
1011
1012   glReadPixels (src_rect.origin.x, dst_frame.size.height - (src_rect.origin.y + src_rect.size.height),
1013                 src_rect.size.width, src_rect.size.height,
1014                 GL_RGBA, GL_UNSIGNED_BYTE, // TODO: Pick better formats.
1015                 pixels);
1016
1017   bind_drawable (dpy, dst);
1018
1019   glPixelZoom (1.0f, 1.0f);
1020
1021   glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
1022   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1023
1024   glRasterPos2i (dst_rect.origin.x, dst_rect.origin.y + dst_rect.size.height);
1025   glDrawPixels (dst_rect.size.width, dst_rect.size.height,
1026                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
1027
1028   free(pixels);
1029 # endif
1030 }
1031
1032
1033 #if 0
1034 // TODO: Make sure offset works in super-sampled mode.
1035 static void
1036 adjust_point_for_line (GC gc, CGPoint *p)
1037 {
1038   // Here's the authoritative discussion on how X draws lines:
1039   // http://www.x.org/releases/current/doc/xproto/x11protocol.html#requests:CreateGC:line-width
1040   if (gc->gcv.line_width <= 1) {
1041     /* Thin lines are "drawn using an unspecified, device-dependent
1042        algorithm", but seriously though, Bresenham's algorithm. Bresenham's
1043        algorithm runs to and from pixel centers.
1044
1045        There's a few screenhacks (Maze, at the very least) that set line_width
1046        to 1 when it probably should be set to 0, so it's line_width <= 1
1047        instead of < 1.
1048      */
1049     p->x += 0.5;
1050     p->y -= 0.5;
1051   } else {
1052     /* Thick lines OTOH run from the upper-left corners of pixels. This means
1053        that a horizontal thick line of width 1 straddles two scan lines.
1054        Aliasing requires one of these scan lines be chosen; the following
1055        nudges the point so that the right choice is made. */
1056     p->y -= 1e-3;
1057   }
1058 }
1059 #endif
1060
1061
1062 int
1063 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1064 {
1065   // TODO: XDrawLine == XDrawSegments(nlines == 1), also in jwxyz.m
1066   XSegment segment;
1067   segment.x1 = x1;
1068   segment.y1 = y1;
1069   segment.x2 = x2;
1070   segment.y2 = y2;
1071   XDrawSegments (dpy, d, gc, &segment, 1);
1072
1073   // when drawing a zero-length line, obey line-width and cap-style.
1074 /* if (x1 == x2 && y1 == y2) {
1075     int w = gc->gcv.line_width;
1076     x1 -= w/2;
1077     y1 -= w/2;
1078     if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1079       return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1080     else {
1081       if (!w)
1082         w = 1; // Actually show zero-length lines.
1083       return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1084     }
1085   }
1086
1087   CGPoint p = point_for_line (d, gc, x1, y1);
1088
1089   push_fg_gc (dpy, d, gc, NO);
1090
1091   CGContextRef cgc = d->cgc;
1092   set_line_mode (cgc, &gc->gcv);
1093   CGContextBeginPath (cgc);
1094   CGContextMoveToPoint (cgc, p.x, p.y);
1095   p = point_for_line(d, gc, x2, y2);
1096   CGContextAddLineToPoint (cgc, p.x, p.y);
1097   CGContextStrokePath (cgc);
1098   pop_gc (d, gc);
1099   invalidate_drawable_cache (d); */
1100   return 0;
1101 }
1102
1103 int
1104 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1105             int mode)
1106 {
1107   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1108   set_fg_gc (dpy, gc);
1109
1110   /* TODO: Thick lines
1111    * Zero-length line segments
1112    * Paths with zero length total (Remember line width, cap style.)
1113    * Closed loops
1114    */
1115   
1116   if (!count)
1117     return 0;
1118
1119   GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
1120   
1121   glMatrixMode (GL_MODELVIEW);
1122   glTranslatef (0.5f, 0.5f, 0);
1123   
1124   short p[2] = {0, 0};
1125   for (unsigned i = 0; i < count; i++) {
1126     next_point (p, points[i], mode);
1127     vertices[2 * i] = p[0];
1128     vertices[2 * i + 1] = p[1];
1129   }
1130   
1131   glEnableClientState (GL_VERTEX_ARRAY);
1132   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1133   glVertexPointer (2, GL_SHORT, 0, vertices);
1134   glDrawArrays (GL_LINE_STRIP, 0, count);
1135   
1136   free (vertices);
1137
1138   if (gc->gcv.cap_style != CapNotLast) {
1139     // TODO: How does this look with multisampling?
1140     // TODO: Disable me for closed loops.
1141     glVertexPointer (2, GL_SHORT, 0, p);
1142     glDrawArrays (GL_POINTS, 0, 1);
1143   }
1144
1145   glLoadIdentity ();
1146   
1147   return 0;
1148 }
1149
1150
1151 int
1152 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1153 {
1154   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1155   set_fg_gc (dpy, gc);
1156   
1157   /* TODO: Thick lines. */
1158   /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
1159   
1160   glMatrixMode (GL_MODELVIEW);
1161   glTranslatef (0.5, 0.5, 0);
1162
1163   glEnableClientState (GL_VERTEX_ARRAY);
1164   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1165   
1166   Assert (sizeof(XSegment) == sizeof(short) * 4, "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1167   Assert (sizeof(GLshort) == sizeof(short), "XDrawSegments: Data alignment mix-up."); // TODO: Static assert here.
1168   Assert (offsetof(XSegment, x1) == 0, "XDrawSegments: Data alignment mix-up.");
1169   Assert (offsetof(XSegment, x2) == 4, "XDrawSegments: Data alignment mix-up.");
1170   glVertexPointer (2, GL_SHORT, 0, segments);
1171   glDrawArrays (GL_LINES, 0, count * 2);
1172   
1173   if (gc->gcv.cap_style != CapNotLast) {
1174     glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, (const GLshort *)segments + 2);
1175     glDrawArrays (GL_POINTS, 0, count);
1176   }
1177   
1178   glLoadIdentity ();
1179   
1180 /* CGRect wr = d->frame;
1181   push_fg_gc (dpy, d, gc, NO);
1182   set_line_mode (cgc, &gc->gcv);
1183   CGContextBeginPath (cgc);
1184   for (i = 0; i < count; i++) {
1185     CGPoint p = point_for_line (d, gc, segments->x1, segments->y1);
1186     CGContextMoveToPoint (cgc, p.x, p.y);
1187     p = point_for_line (d, gc, segments->x2, segments->y2);
1188     CGContextAddLineToPoint (cgc, p.x, p.y);
1189     segments++;
1190   }
1191   CGContextStrokePath (cgc);
1192   pop_gc (d, gc);
1193   invalidate_drawable_cache (d); */
1194   return 0;
1195 }
1196
1197
1198 int
1199 XClearWindow (Display *dpy, Window win)
1200 {
1201   Assert (win == dpy->main_window, "not a window");
1202   const XRectangle *wr = jwxyz_frame (win);
1203   /* TODO: Use glClear if there's no background pixmap. */
1204   return XClearArea (dpy, win, 0, 0, wr->width, wr->height, 0);
1205 }
1206
1207 unsigned long
1208 jwxyz_window_background (Display *dpy)
1209 {
1210   return dpy->window_background;
1211 }
1212
1213 int
1214 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1215 {
1216   Assert (w == dpy->main_window, "not a window");
1217   jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
1218   dpy->window_background = pixel;
1219   return 0;
1220 }
1221
1222 void
1223 jwxyz_fill_rects (Display *dpy, Drawable d, GC gc,
1224                   const XRectangle *rectangles, unsigned long nrectangles,
1225                   unsigned long pixel)
1226 {
1227   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1228   set_color_gc (dpy, gc, pixel);
1229 /*
1230   glBegin(GL_QUADS);
1231   for (unsigned i = 0; i != nrectangles; ++i) {
1232     const XRectangle *r = &rectangles[i];
1233     glVertex2i(r->x, r->y);
1234     glVertex2i(r->x, r->y + r->height);
1235     glVertex2i(r->x + r->width, r->y + r->height);
1236     glVertex2i(r->x + r->width, r->y);
1237   }
1238   glEnd(); */
1239   
1240   glEnableClientState (GL_VERTEX_ARRAY);
1241   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1242     
1243   for (unsigned long i = 0; i != nrectangles; ++i)
1244   {
1245     const XRectangle *r = &rectangles[i];
1246     
1247     GLfloat coords[4][2] =
1248     {
1249       {r->x, r->y},
1250       {r->x, r->y + r->height},
1251       {r->x + r->width, r->y + r->height},
1252       {r->x + r->width, r->y}
1253     };
1254     
1255     // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1256     glVertexPointer (2, GL_FLOAT, 0, coords);
1257     jwxyz_assert_gl ();
1258     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1259     jwxyz_assert_gl ();
1260   }
1261 }
1262
1263
1264 int
1265 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1266 {
1267   Assert(win == dpy->main_window, "XClearArea: not a window");
1268   Assert(!exp, "XClearArea: exposures unsupported");
1269
1270   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, dpy->window_background);
1271   return 0;
1272 }
1273
1274
1275 int
1276 XFillPolygon (Display *dpy, Drawable d, GC gc, 
1277               XPoint *points, int npoints, int shape, int mode)
1278 {
1279   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1280   set_fg_gc(dpy, gc);
1281   
1282   // TODO: Re-implement the GLU tesselation functions.
1283
1284   /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1285    * Nonconvex: Goop, Pacman, Rocks, Speedmine
1286    */
1287   
1288   Assert(shape == Convex, "XFillPolygon: (TODO) Unimplemented shape");
1289   
1290   // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1291   GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1292   short v[2] = {0, 0};
1293   
1294   for (unsigned i = 0; i < npoints; i++) {
1295     next_point(v, points[i], mode);
1296     vertices[2 * i] = v[0];
1297     vertices[2 * i + 1] = v[1];
1298   }
1299
1300   glEnableClientState (GL_VERTEX_ARRAY);
1301   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1302   
1303   glVertexPointer (2, GL_SHORT, 0, vertices);
1304   glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1305   
1306   free(vertices);
1307
1308   /*
1309   CGRect wr = d->frame;
1310   int i;
1311   push_fg_gc (dpy, d, gc, YES);
1312   CGContextRef cgc = d->cgc;
1313   CGContextBeginPath (cgc);
1314   float x = 0, y = 0;
1315   for (i = 0; i < npoints; i++) {
1316     if (i > 0 && mode == CoordModePrevious) {
1317       x += points[i].x;
1318       y -= points[i].y;
1319     } else {
1320       x = wr.origin.x + points[i].x;
1321       y = wr.origin.y + wr.size.height - points[i].y;
1322     }
1323         
1324     if (i == 0)
1325       CGContextMoveToPoint (cgc, x, y);
1326     else
1327       CGContextAddLineToPoint (cgc, x, y);
1328   }
1329   CGContextClosePath (cgc);
1330   if (gc->gcv.fill_rule == EvenOddRule)
1331     CGContextEOFillPath (cgc);
1332   else
1333     CGContextFillPath (cgc);
1334   pop_gc (d, gc);
1335   invalidate_drawable_cache (d);
1336   */
1337   return 0;
1338 }
1339
1340 #define radians(DEG) ((DEG) * M_PI / 180.0)
1341 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1342
1343 static void
1344 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1345 {
1346   p[0] = cos(theta) * w2 + cx;
1347   p[1] = -sin(theta) * h2 + cy;
1348 }
1349
1350 static unsigned
1351 mod_neg(int a, unsigned b)
1352 {
1353   /* Normal modulus is implementation defined for negative numbers. This is 
1354    * well-defined such that the repeating pattern for a >= 0 is maintained for 
1355    * a < 0. */
1356   return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1357 }
1358
1359 int
1360 jwxyz_draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1361                 unsigned int width, unsigned int height,
1362                 int angle1, int angle2, Bool fill_p)
1363 {
1364   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1365   set_fg_gc(dpy, gc);
1366
1367   /* Let's say the number of line segments needed to make a convincing circle is
1368      4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1369
1370   double w2 = width * 0.5f, h2 = height * 0.5f;
1371   double a, b; /* Semi-major/minor axes. */
1372   if(w2 > h2) {
1373     a = w2;
1374     b = h2;
1375   } else {
1376     a = h2;
1377     b = w2;
1378   }
1379   
1380   const double two_pi = 2 * M_PI;
1381
1382   double amb = a - b, apb = a + b;
1383   double h = (amb * amb) / (apb * apb);
1384   // TODO: Math cleanup.
1385   double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1386   double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1387
1388   // TODO: Explain how drawing works what with the points of overlapping arcs
1389   // matching up.
1390  
1391 #if 1
1392   unsigned segments_360 = segments_f;
1393   
1394   /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1395   /* TODO: color, thick lines, CapNotLast for thin lines */
1396   /* TODO: Transformations. */
1397
1398   double segment_angle = two_pi / segments_360;
1399
1400   const unsigned deg64 = 360 * 64;
1401   const double rad_from_deg64 = two_pi / deg64;
1402   
1403   if (angle2 < 0) {
1404     angle1 += angle2;
1405     angle2 = -angle2;
1406   }
1407
1408   angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1409
1410   if (angle2 > deg64)
1411     angle2 = deg64; // TODO: Handle circles special.
1412   
1413   double
1414     angle1_f = angle1 * rad_from_deg64,
1415     angle2_f = angle2 * rad_from_deg64;
1416   
1417   if (angle2_f > two_pi) // TODO: Move this up.
1418     angle2_f = two_pi;
1419   
1420   double segment1_angle_part = fmodf(angle1_f, segment_angle);
1421   
1422   unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1423
1424   double angle_2r = angle2_f - segment1_angle_part;
1425   unsigned segments = angle_2r / segment_angle;
1426   
1427   GLfloat cx = x + w2, cy = y + h2;
1428
1429   GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1430   
1431   GLfloat *data_ptr = data;
1432   if (fill_p) {
1433     data_ptr[0] = cx;
1434     data_ptr[1] = cy;
1435     data_ptr += 2;
1436   }
1437   
1438   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1439   data_ptr += 2;
1440   
1441   for (unsigned s = 0; s != segments; ++s) {
1442     // TODO: Make sure values of theta for the following arc_xy call are between
1443     // angle1_f and angle1_f + angle2_f.
1444     arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1445     data_ptr += 2;
1446   }
1447   
1448   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1449   data_ptr += 2;
1450
1451   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1452   glEnableClientState (GL_VERTEX_ARRAY);
1453   
1454   glVertexPointer (2, GL_FLOAT, 0, data);
1455   glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1456                 0,
1457                 (GLsizei)((data_ptr - data) / 2));
1458
1459   free(data);
1460   
1461 #endif
1462   
1463 #if 0
1464   unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1465  
1466   glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1467   
1468   if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1469     glVertex2f (cx, cy);
1470   
1471   /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1472   
1473   float to_radians = 2 * M_PI / (360 * 64);
1474   float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1475   
1476   for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1477   {
1478     glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1479     theta += d_theta;
1480   }
1481   
1482   glEnd ();
1483 #endif
1484   
1485   return 0;
1486 }
1487
1488
1489 XGCValues *
1490 jwxyz_gc_gcv (GC gc)
1491 {
1492   return &gc->gcv;
1493 }
1494
1495
1496 unsigned int
1497 jwxyz_gc_depth (GC gc)
1498 {
1499   return gc->depth;
1500 }
1501
1502
1503 GC
1504 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1505 {
1506   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1507   gc->depth = jwxyz_drawable_depth (d);
1508
1509   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1510   XChangeGC (dpy, gc, mask, xgcv);
1511   return gc;
1512 }
1513
1514
1515 int
1516 XFreeGC (Display *dpy, GC gc)
1517 {
1518   if (gc->gcv.font)
1519     XUnloadFont (dpy, gc->gcv.font);
1520
1521   // TODO
1522 /*
1523   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1524
1525   if (gc->gcv.clip_mask) {
1526     XFreePixmap (dpy, gc->gcv.clip_mask);
1527     CGImageRelease (gc->clip_mask);
1528   }
1529 */
1530   free (gc);
1531   return 0;
1532 }
1533
1534
1535 /*
1536 static void
1537 flipbits (unsigned const char *in, unsigned char *out, int length)
1538 {
1539   static const unsigned char table[256] = {
1540     0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 
1541     0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
1542     0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 
1543     0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
1544     0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 
1545     0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
1546     0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 
1547     0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
1548     0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 
1549     0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
1550     0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 
1551     0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
1552     0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 
1553     0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
1554     0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 
1555     0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
1556     0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 
1557     0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
1558     0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 
1559     0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
1560     0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 
1561     0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
1562     0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 
1563     0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
1564     0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 
1565     0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
1566     0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1567     0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
1568     0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 
1569     0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
1570     0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 
1571     0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1572   };
1573   while (length-- > 0)
1574     *out++ = table[*in++];
1575 }
1576 */
1577
1578 // Copied and pasted from OSX/XScreenSaverView.m
1579 static GLsizei
1580 to_pow2 (size_t x)
1581 {
1582   if (x <= 1)
1583     return 1;
1584
1585   size_t mask = (size_t)-1;
1586   unsigned bits = sizeof(x) * CHAR_BIT;
1587   unsigned log2 = bits;
1588
1589   --x;
1590   while (bits) {
1591     if (!(x & mask)) {
1592       log2 -= bits;
1593       x <<= bits;
1594     }
1595
1596     bits >>= 1;
1597     mask <<= bits;
1598   }
1599
1600   return 1 << log2;
1601 }
1602
1603
1604 int
1605 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1606            int src_x, int src_y, int dest_x, int dest_y,
1607            unsigned int w, unsigned int h)
1608 {
1609   jwxyz_assert_display (dpy);
1610  
1611   const XRectangle *wr = jwxyz_frame (d);
1612
1613   Assert (gc, "no GC");
1614   Assert ((w < 65535), "improbably large width");
1615   Assert ((h < 65535), "improbably large height");
1616   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1617   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1618   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1619   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1620
1621   // Clip width and height to the bounds of the Drawable
1622   //
1623   if (dest_x + w > wr->width) {
1624     if (dest_x > wr->width)
1625       return 0;
1626     w = wr->width - dest_x;
1627   }
1628   if (dest_y + h > wr->height) {
1629     if (dest_y > wr->height)
1630       return 0;
1631     h = wr->height - dest_y;
1632   }
1633   if (w <= 0 || h <= 0)
1634     return 0;
1635
1636   // Clip width and height to the bounds of the XImage
1637   //
1638   if (src_x + w > ximage->width) {
1639     if (src_x > ximage->width)
1640       return 0;
1641     w = ximage->width - src_x;
1642   }
1643   if (src_y + h > ximage->height) {
1644     if (src_y > ximage->height)
1645       return 0;
1646     h = ximage->height - src_y;
1647   }
1648   if (w <= 0 || h <= 0)
1649     return 0;
1650
1651   /* Assert (d->win */
1652
1653   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1654     return 0;
1655
1656   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1657   int bpl = ximage->bytes_per_line;
1658   int bpp = ximage->bits_per_pixel;
1659   /* int bsize = bpl * h; */
1660   char *data = ximage->data;
1661
1662 /*
1663   CGRect r;
1664   r.origin.x = wr->x + dest_x;
1665   r.origin.y = wr->y + wr->height - dest_y - h;
1666   r.size.width = w;
1667   r.size.height = h;
1668 */
1669
1670   Assert (gc->gcv.function == GXcopy, "XPutImage: (TODO) GC function not supported");
1671   Assert (!gc->gcv.clip_mask, "XPutImage: (TODO) GC clip mask not supported");
1672   
1673   if (bpp == 32) {
1674
1675     /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1676        to create a CGImage from a sub-rectagle of the XImage.
1677      */
1678     data += (src_y * bpl) + (src_x * 4);
1679
1680     jwxyz_assert_display(dpy);
1681     
1682     /* There probably won't be any hacks that do this, but... */
1683     Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1684     
1685     unsigned src_w = bpl / 4;
1686
1687     /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1688 # ifndef HAVE_JWZGLES
1689     glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1690     src_w = w;
1691 # endif
1692
1693     glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
1694
1695 # if 1 // defined HAVE_JWZGLES
1696     // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1697     // TODO: Make use of OES_draw_texture.
1698     // TODO: Coords might be wrong; things might be upside-down or backwards
1699     //       or whatever.
1700
1701     unsigned tex_w = src_w, tex_h = h;
1702     if (!dpy->gl_texture_npot_p) {
1703       tex_w = to_pow2(tex_w);
1704       tex_h = to_pow2(tex_h);
1705     }
1706
1707     GLint internalformat = texture_internalformat(dpy);
1708
1709     glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
1710
1711     if (tex_w == src_w && tex_h == h) {
1712       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1713                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1714     } else {
1715       // TODO: Sampling the last row might be a problem if src_x != 0.
1716       glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
1717                     0, dpy->screen->pixel_format, gl_pixel_type(dpy), NULL);
1718       glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1719                        dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1720     }
1721     
1722     set_white ();
1723     // glEnable (dpy->gl_texture_target);
1724     // glColor4f (0.5, 0, 1, 1);
1725     glEnable (dpy->gl_texture_target);
1726     glEnableClientState (GL_VERTEX_ARRAY);
1727     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1728
1729     // TODO: Why are these ever turned on in the first place?
1730     glDisableClientState (GL_COLOR_ARRAY);
1731     glDisableClientState (GL_NORMAL_ARRAY);
1732     // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1733
1734     GLfloat vertices[4][2] =
1735     {
1736       {dest_x, dest_y},
1737       {dest_x, dest_y + h},
1738       {dest_x + w, dest_y + h},
1739       {dest_x + w, dest_y}
1740     };
1741
1742     GLfloat texcoord_w, texcoord_h;
1743 #  ifndef HAVE_JWZGLES
1744     if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1745       texcoord_w = w;
1746       texcoord_h = h;
1747     } else
1748 #  endif /* HAVE_JWZGLES */
1749     {
1750       texcoord_w = (double)w / tex_w;
1751       texcoord_h = (double)h / tex_h;
1752     }
1753
1754     GLfloat tex_coords[4][2];
1755     tex_coords[0][0] = 0;
1756     tex_coords[0][1] = 0;
1757     tex_coords[1][0] = 0;
1758     tex_coords[1][1] = texcoord_h;
1759     tex_coords[2][0] = texcoord_w;
1760     tex_coords[2][1] = texcoord_h;
1761     tex_coords[3][0] = texcoord_w;
1762     tex_coords[3][1] = 0;
1763
1764     glVertexPointer (2, GL_FLOAT, 0, vertices);
1765     glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1766
1767     // Respect the alpha channel in the XImage if we're using alpha.
1768     if (gc->gcv.alpha_allowed_p) {
1769       glEnable (GL_BLEND);
1770       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1771     }
1772
1773     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1774
1775     if (gc->gcv.alpha_allowed_p)
1776       glDisable (GL_BLEND);
1777
1778 //  clear_texture();
1779     glDisable (dpy->gl_texture_target);
1780 # else
1781     glRasterPos2i (dest_x, dest_y);
1782     glPixelZoom (1, -1);
1783     jwxyz_assert_display (dpy);
1784     glDrawPixels (w, h, dpy->screen->pixel_format, gl_pixel_type(dpy), data);
1785 # endif
1786   } else {   // (bpp == 1)
1787
1788     // Assert(FALSE, "XPutImage: TODO");
1789     // Check out ximage_(get|put)pixel_1
1790     
1791 #if 0
1792     /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1793
1794        #### However, the bit order within a byte in a 1bpp XImage is
1795             the wrong way around from what Quartz expects, so first we
1796             have to copy the data to reverse it.  Shit!  Maybe it
1797             would be worthwhile to go through the hacks and #ifdef
1798             each one that diddles 1bpp XImage->data directly...
1799      */
1800     Assert ((src_x % 8) == 0,
1801             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1802
1803     data += (src_y * bpl) + (src_x / 8);   // move to x,y within the data
1804     unsigned char *flipped = (unsigned char *) malloc (bsize);
1805
1806     flipbits ((unsigned char *) data, flipped, bsize);
1807
1808     CGDataProviderRef prov = 
1809       CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1810     CGImageRef mask = CGImageMaskCreate (w, h, 
1811                                          1, bpp, bpl,
1812                                          prov,
1813                                          NULL,  /* decode[] */
1814                                          GL_FALSE); /* interpolate */
1815     push_fg_gc (dpy, d, gc, GL_TRUE);
1816
1817     CGContextFillRect (cgc, r);                         // foreground color
1818     CGContextClipToMask (cgc, r, mask);
1819     set_color (dpy, cgc, gc->gcv.background, gc->depth, GL_FALSE, GL_TRUE);
1820     CGContextFillRect (cgc, r);                         // background color
1821     pop_gc (d, gc);
1822
1823     free (flipped);
1824     CGDataProviderRelease (prov);
1825     CGImageRelease (mask);
1826 #endif
1827   }
1828  
1829   jwxyz_assert_gl ();
1830   invalidate_drawable_cache (d);
1831
1832   return 0;
1833 }
1834
1835 /* At the moment nothing actually uses XGetSubImage. */
1836 /* #### Actually lots of things call XGetImage, which calls XGetSubImage.
1837    E.g., Twang calls XGetImage on the window intending to get a
1838    buffer full of black.  This is returning a buffer full of white
1839    instead of black for some reason. */
1840 static XImage *
1841 XGetSubImage (Display *dpy, Drawable d, int x, int y,
1842               unsigned int width, unsigned int height,
1843               unsigned long plane_mask, int format,
1844               XImage *dest_image, int dest_x, int dest_y)
1845 {
1846   Assert ((width  < 65535), "improbably large width");
1847   Assert ((height < 65535), "improbably large height");
1848   Assert ((x < 65535 && x > -65535), "improbably large x");
1849   Assert ((y < 65535 && y > -65535), "improbably large y");
1850
1851   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1852   
1853   // TODO: What if this reads off the edge? What is supposed to happen?
1854
1855   {
1856     // In case the caller tries to write off the edge.
1857     int
1858       max_width = dest_image->width - dest_x,
1859       max_height = dest_image->height - dest_y;
1860
1861     if (width > max_width) {
1862       width = max_width;
1863     }
1864     
1865     if (height > max_height) {
1866       height = max_height;
1867     }
1868   }
1869   
1870   Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1871   
1872   if (dest_image->depth == visual_depth (NULL, NULL)) {
1873     Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1874     unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1875 #ifdef HAVE_JWZGLES
1876     Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1877 #else
1878     glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1879 #endif
1880     glPixelStorei (GL_PACK_ALIGNMENT, 4);
1881     
1882     uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1883     
1884     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1885                   dpy->screen->pixel_format, gl_pixel_type(dpy), dest_data);
1886
1887     /* Flip this upside down. :( */
1888     uint32_t *top = dest_data;
1889     uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1890     for (unsigned y = height / 2; y; --y) {
1891       for (unsigned x = 0; x != width; ++x) {
1892         uint32_t px = top[x];
1893         top[x] = bottom[x];
1894         bottom[x] = px;
1895       }
1896       top += pixels_per_line;
1897       bottom -= pixels_per_line;
1898     }
1899   } else {
1900
1901     /* TODO: Actually get pixels. */
1902
1903     Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1904     uint8_t *dest_data =
1905       (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1906       + dest_x / 8;
1907     for (unsigned y = height / 2; y; --y) {
1908       memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
1909       dest_data += dest_image->bytes_per_line;
1910     }
1911   }
1912
1913   return dest_image;
1914 }
1915
1916 XImage *
1917 XGetImage (Display *dpy, Drawable d, int x, int y,
1918            unsigned int width, unsigned int height,
1919            unsigned long plane_mask, int format)
1920 {
1921   unsigned depth = jwxyz_drawable_depth (d);
1922   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1923                                 0, 0);
1924   image->data = (char *) malloc (height * image->bytes_per_line);
1925
1926   return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
1927                        image, 0, 0);
1928 }
1929
1930 /* Returns a transformation matrix to do rotation as per the provided
1931    EXIF "Orientation" value.
1932  */
1933 /*
1934 static CGAffineTransform
1935 exif_rotate (int rot, CGSize rect)
1936 {
1937   CGAffineTransform trans = CGAffineTransformIdentity;
1938   switch (rot) {
1939   case 2:               // flip horizontal
1940     trans = CGAffineTransformMakeTranslation (rect.width, 0);
1941     trans = CGAffineTransformScale (trans, -1, 1);
1942     break;
1943
1944   case 3:               // rotate 180
1945     trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1946     trans = CGAffineTransformRotate (trans, M_PI);
1947     break;
1948
1949   case 4:               // flip vertical
1950     trans = CGAffineTransformMakeTranslation (0, rect.height);
1951     trans = CGAffineTransformScale (trans, 1, -1);
1952     break;
1953
1954   case 5:               // transpose (UL-to-LR axis)
1955     trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1956     trans = CGAffineTransformScale (trans, -1, 1);
1957     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1958     break;
1959
1960   case 6:               // rotate 90
1961     trans = CGAffineTransformMakeTranslation (0, rect.width);
1962     trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1963     break;
1964
1965   case 7:               // transverse (UR-to-LL axis)
1966     trans = CGAffineTransformMakeScale (-1, 1);
1967     trans = CGAffineTransformRotate (trans, M_PI / 2);
1968     break;
1969
1970   case 8:               // rotate 270
1971     trans = CGAffineTransformMakeTranslation (rect.height, 0);
1972     trans = CGAffineTransformRotate (trans, M_PI / 2);
1973     break;
1974
1975   default: 
1976     break;
1977   }
1978
1979   return trans;
1980 }
1981 */
1982
1983 void
1984 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
1985                                 Bool nsimg_p, void *img_arg,
1986                                XRectangle *geom_ret, int exif_rotation)
1987 {
1988   Assert (False, "jwxyz_draw_NSImage_or_CGImage: TODO stub");
1989 #if 0
1990   CGImageRef cgi;
1991 # ifndef USE_IPHONE
1992   CGImageSourceRef cgsrc;
1993 # endif // USE_IPHONE
1994   NSSize imgr;
1995
1996   CGContextRef cgc = d->cgc;
1997
1998   if (nsimg_p) {
1999
2000     NSImage *nsimg = (NSImage *) img_arg;
2001     imgr = [nsimg size];
2002
2003 # ifndef USE_IPHONE
2004     // convert the NSImage to a CGImage via the toll-free-bridging 
2005     // of NSData and CFData...
2006     //
2007     NSData *nsdata = [NSBitmapImageRep
2008                        TIFFRepresentationOfImageRepsInArray:
2009                          [nsimg representations]];
2010     CFDataRef cfdata = (CFDataRef) nsdata;
2011     cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2012     cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2013 # else  // USE_IPHONE
2014     cgi = nsimg.CGImage;
2015 # endif // USE_IPHONE
2016
2017   } else {
2018     cgi = (CGImageRef) img_arg;
2019     imgr.width  = CGImageGetWidth (cgi);
2020     imgr.height = CGImageGetHeight (cgi);
2021   }
2022
2023   Bool rot_p = (exif_rotation >= 5);
2024
2025   if (rot_p)
2026     imgr = NSMakeSize (imgr.height, imgr.width);
2027
2028   CGRect winr = d->frame;
2029   float rw = winr.size.width  / imgr.width;
2030   float rh = winr.size.height / imgr.height;
2031   float r = (rw < rh ? rw : rh);
2032
2033   CGRect dst, dst2;
2034   dst.size.width  = imgr.width  * r;
2035   dst.size.height = imgr.height * r;
2036   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
2037   dst.origin.y = (winr.size.height - dst.size.height) / 2;
2038
2039   dst2.origin.x = dst2.origin.y = 0;
2040   if (rot_p) {
2041     dst2.size.width = dst.size.height; 
2042     dst2.size.height = dst.size.width;
2043   } else {
2044     dst2.size = dst.size;
2045   }
2046
2047   // Clear the part not covered by the image to background or black.
2048   //
2049   if (d->type == WINDOW)
2050     XClearWindow (dpy, d);
2051   else {
2052     jwxyz_fill_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height,
2053                      drawable_depth (d) == 1 ? 0 : BlackPixel(dpy,0));
2054   }
2055
2056   CGAffineTransform trans = 
2057     exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2058
2059   CGContextSaveGState (cgc);
2060   CGContextConcatCTM (cgc, 
2061                       CGAffineTransformMakeTranslation (dst.origin.x,
2062                                                         dst.origin.y));
2063   CGContextConcatCTM (cgc, trans);
2064   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2065   CGContextDrawImage (cgc, dst2, cgi);
2066   CGContextRestoreGState (cgc);
2067
2068 # ifndef USE_IPHONE
2069   if (nsimg_p) {
2070     CFRelease (cgsrc);
2071     CGImageRelease (cgi);
2072   }
2073 # endif // USE_IPHONE
2074
2075   if (geom_ret) {
2076     geom_ret->x = dst.origin.x;
2077     geom_ret->y = dst.origin.y;
2078     geom_ret->width  = dst.size.width;
2079     geom_ret->height = dst.size.height;
2080   }
2081
2082   invalidate_drawable_cache (d);
2083 #endif
2084 }
2085
2086 #ifndef HAVE_JWZGLES
2087
2088 /*
2089 static void
2090 create_rectangle_texture (GLuint *texture)
2091 {
2092   glGenTextures(1, texture);
2093   glBindTexture(GL_TEXTURE_RECTANGLE_EXT, *texture);
2094   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2095   glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2096 }
2097 */
2098
2099 #endif
2100
2101
2102 #if 0
2103 static Pixmap
2104 copy_pixmap (Display *dpy, Pixmap p)
2105 {
2106   if (!p) return 0;
2107   Assert (p->type == PIXMAP, "not a pixmap");
2108
2109   Pixmap p2 = 0;
2110
2111   Window root;
2112   int x, y;
2113   unsigned int width, height, border_width, depth;
2114   if (XGetGeometry (dpy, p, &root,
2115                     &x, &y, &width, &height, &border_width, &depth)) {
2116     XGCValues gcv;
2117     gcv.function = GXcopy;
2118     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
2119     if (gc) {
2120       p2 = XCreatePixmap (dpy, p, width, height, depth);
2121       if (p2)
2122         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
2123       XFreeGC (dpy, gc);
2124     }
2125   }
2126
2127   Assert (p2, "could not copy pixmap");
2128
2129   return p2;
2130 }
2131 #endif
2132
2133
2134 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2135 //
2136 static void
2137 query_font (Font fid)
2138 {
2139   if (!fid || !fid->native_font) {
2140     Assert (0, "no native font in fid");
2141     return;
2142   }
2143
2144   int first = 32;
2145   int last = 255;
2146
2147   Display *dpy = fid->dpy;
2148   void *native_font = fid->native_font;
2149
2150   XFontStruct *f = &fid->metrics;
2151   XCharStruct *min = &f->min_bounds;
2152   XCharStruct *max = &f->max_bounds;
2153
2154   f->fid               = fid;
2155   f->min_char_or_byte2 = first;
2156   f->max_char_or_byte2 = last;
2157   f->default_char      = 'M';
2158   f->ascent            = fid->ascent;
2159   f->descent           = fid->descent;
2160
2161   min->width    = 32767;  // set to smaller values in the loop
2162   min->ascent   = 32767;
2163   min->descent  = 32767;
2164   min->lbearing = 32767;
2165   min->rbearing = 32767;
2166
2167   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2168
2169   for (int i = first; i <= last; i++) {
2170     XCharStruct *cs = &f->per_char[i-first];
2171     char s = (char) i;
2172     jwxyz_render_text (dpy, native_font, &s, 1, GL_FALSE, cs, 0);
2173
2174     max->width    = MAX (max->width,    cs->width);
2175     max->ascent   = MAX (max->ascent,   cs->ascent);
2176     max->descent  = MAX (max->descent,  cs->descent);
2177     max->lbearing = MAX (max->lbearing, cs->lbearing);
2178     max->rbearing = MAX (max->rbearing, cs->rbearing);
2179
2180     min->width    = MIN (min->width,    cs->width);
2181     min->ascent   = MIN (min->ascent,   cs->ascent);
2182     min->descent  = MIN (min->descent,  cs->descent);
2183     min->lbearing = MIN (min->lbearing, cs->lbearing);
2184     min->rbearing = MIN (min->rbearing, cs->rbearing);
2185 /*
2186     Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d\n",
2187          i, i, cs->width, cs->lbearing, cs->rbearing, 
2188          cs->ascent, cs->descent);
2189  */
2190   }
2191 }
2192
2193
2194 // Since 'Font' includes the metrics, this just makes a copy of that.
2195 //
2196 XFontStruct *
2197 XQueryFont (Display *dpy, Font fid)
2198 {
2199   // copy XFontStruct
2200   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2201   *f = fid->metrics;
2202   f->fid = fid;
2203
2204   // build XFontProps
2205   f->n_properties = 1;
2206   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
2207   f->properties[0].name = XA_FONT;
2208   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
2209           "atoms probably needs a real implementation");
2210   // If XInternAtom is ever implemented, use it here.
2211   f->properties[0].card32 = (unsigned long)(char *)fid->xa_font;
2212
2213   // copy XCharStruct array
2214   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
2215   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2216
2217   memcpy (f->per_char, fid->metrics.per_char,
2218           size * sizeof (XCharStruct));
2219
2220   return f;
2221 }
2222
2223
2224 static Font
2225 copy_font (Font fid)
2226 {
2227   fid->refcount++;
2228   return fid;
2229 }
2230
2231
2232 #if 0
2233
2234
2235 static NSArray *
2236 font_family_members (NSString *family_name)
2237 {
2238 # ifndef USE_IPHONE
2239   return [[NSFontManager sharedFontManager]
2240           availableMembersOfFontFamily:family_name];
2241 # else
2242   return [UIFont fontNamesForFamilyName:family_name];
2243 # endif
2244 }
2245
2246
2247 static NSString *
2248 default_font_family (NSFontTraitMask require)
2249 {
2250   return require & NSFixedPitchFontMask ? @"Courier" : @"Verdana";
2251 }
2252
2253
2254 static NSFont *
2255 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
2256           NSString *family_name, float size,
2257           char **name_ret)
2258 {
2259   Assert (size > 0, "zero font size");
2260
2261   NSArray *family_members = font_family_members (family_name);
2262   if (!family_members.count)
2263     family_members = font_family_members (default_font_family (traits));
2264
2265 # ifndef USE_IPHONE
2266   for (unsigned k = 0; k != family_members.count; ++k) {
2267
2268     NSArray *member = [family_members objectAtIndex:k];
2269     NSFontTraitMask font_mask =
2270     [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
2271
2272     if ((font_mask & mask) == traits) {
2273
2274       NSString *name = [member objectAtIndex:0];
2275       NSFont *f = [NSFont fontWithName:name size:size];
2276       if (!f)
2277         break;
2278
2279       /* Don't use this font if it (probably) doesn't include ASCII characters.
2280        */
2281       NSStringEncoding enc = [f mostCompatibleStringEncoding];
2282       if (! (enc == NSUTF8StringEncoding ||
2283              enc == NSISOLatin1StringEncoding ||
2284              enc == NSNonLossyASCIIStringEncoding ||
2285              enc == NSISOLatin2StringEncoding ||
2286              enc == NSUnicodeStringEncoding ||
2287              enc == NSWindowsCP1250StringEncoding ||
2288              enc == NSWindowsCP1252StringEncoding ||
2289              enc == NSMacOSRomanStringEncoding)) {
2290         // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2291         break;
2292       }
2293       // NSLog(@"using \"%@\": %d", name, enc);
2294
2295       // *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2296       *name_ret = strdup (name.UTF8String);
2297       return f;
2298     }
2299   }
2300 # else // USE_IPHONE
2301
2302   for (NSString *fn in family_members) {
2303 # define MATCH(X) \
2304          ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2305          != NSNotFound)
2306
2307     // The magic invocation for getting font names is
2308     // [[UIFontDescriptor
2309     //   fontDescriptorWithFontAttributes:@{UIFontDescriptorNameAttribute: name}]
2310     //  symbolicTraits]
2311     // ...but this only works on iOS 7 and later.
2312     NSFontTraitMask font_mask = 0;
2313     if (MATCH(@"Bold"))
2314       font_mask |= NSBoldFontMask;
2315     if (MATCH(@"Italic") || MATCH(@"Oblique"))
2316       font_mask |= NSItalicFontMask;
2317
2318     if ((font_mask & mask) == traits) {
2319
2320       /* Check if it can do ASCII.  No good way to accomplish this!
2321          These are fonts present in iPhone Simulator as of June 2012
2322          that don't include ASCII.
2323        */
2324       if (MATCH(@"AppleGothic") ||      // Korean
2325           MATCH(@"Dingbats") ||         // Dingbats
2326           MATCH(@"Emoji") ||            // Emoticons
2327           MATCH(@"Geeza") ||            // Arabic
2328           MATCH(@"Hebrew") ||           // Hebrew
2329           MATCH(@"HiraKaku") ||         // Japanese
2330           MATCH(@"HiraMin") ||          // Japanese
2331           MATCH(@"Kailasa") ||          // Tibetan
2332           MATCH(@"Ornaments") ||        // Dingbats
2333           MATCH(@"STHeiti")             // Chinese
2334        )
2335          break;
2336
2337       *name_ret = strdup (fn.UTF8String);
2338       return [UIFont fontWithName:fn size:size];
2339     }
2340 # undef MATCH
2341   }
2342
2343 # endif
2344
2345   return NULL;
2346 }
2347
2348
2349
2350 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
2351    of XLFD strings; also they can be comma-separated strings with multiple
2352    font names.  First one that exists wins.
2353  */
2354 static NSFont *
2355 try_native_font (const char *name, float scale,
2356                  char **name_ret, float *size_ret, char **xa_font)
2357 {
2358   if (!name) return 0;
2359   const char *spc = strrchr (name, ' ');
2360   if (!spc) return 0;
2361
2362   NSFont *f = 0;
2363   char *token = strdup (name);
2364   char *name2;
2365
2366   while ((name2 = strtok (token, ","))) {
2367     token = 0;
2368
2369     while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
2370       name2++;
2371
2372     spc = strrchr (name2, ' ');
2373     if (!spc) continue;
2374
2375     int dsize = 0;
2376     if (1 != sscanf (spc, " %d ", &dsize))
2377       continue;
2378     float size = dsize;
2379
2380     if (size <= 4) continue;
2381
2382     size *= scale;
2383
2384     name2[strlen(name2) - strlen(spc)] = 0;
2385
2386     NSString *nsname = [NSString stringWithCString:name2
2387                                           encoding:NSUTF8StringEncoding];
2388     f = [NSFont fontWithName:nsname size:size];
2389     if (f) {
2390       *name_ret = name2;
2391       *size_ret = size;
2392       *xa_font = strdup (name); // Maybe this should be an XLFD?
2393       break;
2394     } else {
2395       NSLog(@"No native font: \"%@\" %.0f", nsname, size);
2396     }
2397   }
2398
2399   free (token);
2400   return f;
2401 }
2402
2403
2404 /* Returns a random font in the given size and face.
2405  */
2406 static NSFont *
2407 random_font (NSFontTraitMask traits, NSFontTraitMask mask,
2408              float size, NSString **family_ret, char **name_ret)
2409 {
2410
2411 # ifndef USE_IPHONE
2412   // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
2413   // returns an empty list, at least on a system with default fonts only.
2414   NSArray *families = [[NSFontManager sharedFontManager]
2415                        availableFontFamilies];
2416   if (!families) return 0;
2417 # else
2418   NSArray *families = [UIFont familyNames];
2419
2420   // There are many dups in the families array -- uniquify it.
2421   {
2422     NSArray *sorted_families =
2423     [families sortedArrayUsingSelector:@selector(compare:)];
2424     NSMutableArray *new_families =
2425     [NSMutableArray arrayWithCapacity:sorted_families.count];
2426
2427     NSString *prev_family = nil;
2428     for (NSString *family in sorted_families) {
2429       if ([family compare:prev_family])
2430         [new_families addObject:family];
2431     }
2432
2433     families = new_families;
2434   }
2435 # endif // USE_IPHONE
2436
2437   long n = [families count];
2438   if (n <= 0) return 0;
2439
2440   int j;
2441   for (j = 0; j < n; j++) {
2442     int i = random() % n;
2443     NSString *family_name = [families objectAtIndex:i];
2444
2445     NSFont *result = try_font (traits, mask, family_name, size, name_ret);
2446     if (result) {
2447       [*family_ret release];
2448       *family_ret = family_name;
2449       [*family_ret retain];
2450       return result;
2451     }
2452   }
2453
2454   // None of the fonts support ASCII?
2455   return 0;
2456 }
2457
2458
2459 // Fonts need this. XDisplayHeightMM and friends should probably be consistent
2460 // with this as well if they're ever implemented.
2461 static const unsigned dpi = 75;
2462
2463
2464 static const char *
2465 xlfd_field_end (const char *s)
2466 {
2467   const char *s2 = strchr(s, '-');
2468   if (!s2)
2469     s2 = s + strlen(s);
2470   return s2;
2471 }
2472
2473
2474 static size_t
2475 xlfd_next (const char **s, const char **s2)
2476 {
2477   if (!**s2) {
2478     *s = *s2;
2479   } else {
2480     Assert (**s2 == '-', "xlfd parse error");
2481     *s = *s2 + 1;
2482     *s2 = xlfd_field_end (*s);
2483   }
2484
2485   return *s2 - *s;
2486 }
2487
2488 static NSFont *
2489 try_xlfd_font (const char *name, float scale,
2490                char **name_ret, float *size_ret, char **xa_font)
2491 {
2492   NSFont *nsfont = 0;
2493   NSString *family_name = nil;
2494   NSFontTraitMask require = 0, forbid = 0;
2495   GLboolean rand  = GL_FALSE;
2496   float size = 0;
2497   char *ps_name = 0;
2498
2499   const char *s = (name ? name : "");
2500
2501   size_t L = strlen (s);
2502 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2503 # define UNSPEC   (L == 0 || L == 1 && *s == '*')
2504   if      (CMP ("6x10"))     size = 8,  require |= NSFixedPitchFontMask;
2505   else if (CMP ("6x10bold")) size = 8,  require |= NSFixedPitchFontMask | NSBoldFontMask;
2506   else if (CMP ("fixed"))    size = 12, require |= NSFixedPitchFontMask;
2507   else if (CMP ("9x15"))     size = 12, require |= NSFixedPitchFontMask;
2508   else if (CMP ("9x15bold")) size = 12, require |= NSFixedPitchFontMask | NSBoldFontMask;
2509   else if (CMP ("vga"))      size = 12, require |= NSFixedPitchFontMask;
2510   else if (CMP ("console"))  size = 12, require |= NSFixedPitchFontMask;
2511   else if (CMP ("gallant"))  size = 12, require |= NSFixedPitchFontMask;
2512   else {
2513
2514     // Incorrect fields are ignored.
2515
2516     if (*s == '-')
2517       ++s;
2518     const char *s2 = xlfd_field_end(s);
2519
2520     // Foundry (ignore)
2521
2522     L = xlfd_next (&s, &s2); // Family name
2523     // This used to substitute Georgia for Times. Now it doesn't.
2524     if (CMP ("random")) {
2525       rand = GL_TRUE;
2526     } else if (CMP ("fixed")) {
2527       require |= NSFixedPitchFontMask;
2528       family_name = @"Courier";
2529     } else if (!UNSPEC) {
2530       family_name = [[[NSString alloc] initWithBytes:s
2531                                               length:L
2532                                             encoding:NSUTF8StringEncoding]
2533                      autorelease];
2534     }
2535
2536     L = xlfd_next (&s, &s2); // Weight name
2537     if (CMP ("bold") || CMP ("demibold"))
2538       require |= NSBoldFontMask;
2539     else if (CMP ("medium") || CMP ("regular"))
2540       forbid |= NSBoldFontMask;
2541
2542     L = xlfd_next (&s, &s2); // Slant
2543     if (CMP ("i") || CMP ("o"))
2544       require |= NSItalicFontMask;
2545     else if (CMP ("r"))
2546       forbid |= NSItalicFontMask;
2547
2548     xlfd_next (&s, &s2); // Set width name (ignore)
2549     xlfd_next (&s, &s2); // Add style name (ignore)
2550
2551     xlfd_next (&s, &s2); // Pixel size (ignore)
2552
2553     xlfd_next (&s, &s2); // Point size
2554     char *s3;
2555     uintmax_t n = strtoumax(s, &s3, 10);
2556     if (s2 == s3)
2557       size = n / 10.0;
2558
2559     xlfd_next (&s, &s2); // Resolution X (ignore)
2560     xlfd_next (&s, &s2); // Resolution Y (ignore)
2561
2562     xlfd_next (&s, &s2); // Spacing
2563     if (CMP ("p"))
2564       forbid |= NSFixedPitchFontMask;
2565     else if (CMP ("m") || CMP ("c"))
2566       require |= NSFixedPitchFontMask;
2567
2568     // Don't care about average_width or charset registry.
2569   }
2570 # undef CMP
2571 # undef UNSPEC
2572
2573   if (!family_name && !rand)
2574     family_name = default_font_family (require);
2575
2576   if (size < 6 || size > 1000)
2577     size = 12;
2578
2579   size *= scale;
2580
2581   NSFontTraitMask mask = require | forbid;
2582
2583   if (rand) {
2584     nsfont   = random_font (require, mask, size, &family_name, &ps_name);
2585     [family_name autorelease];
2586   }
2587
2588   if (!nsfont)
2589     nsfont   = try_font (require, mask, family_name, size, &ps_name);
2590
2591   // if that didn't work, turn off attibutes until it does
2592   // (e.g., there is no "Monaco-Bold".)
2593   //
2594   if (!nsfont && (mask & NSItalicFontMask)) {
2595     require &= ~NSItalicFontMask;
2596     mask &= ~NSItalicFontMask;
2597     nsfont = try_font (require, mask, family_name, size, &ps_name);
2598   }
2599   if (!nsfont && (mask & NSBoldFontMask)) {
2600     require &= ~NSBoldFontMask;
2601     mask &= ~NSBoldFontMask;
2602     nsfont = try_font (require, mask, family_name, size, &ps_name);
2603   }
2604   if (!nsfont && (mask & NSFixedPitchFontMask)) {
2605     require &= ~NSFixedPitchFontMask;
2606     mask &= ~NSFixedPitchFontMask;
2607     nsfont = try_font (require, mask, family_name, size, &ps_name);
2608   }
2609
2610   if (nsfont) {
2611     *name_ret = ps_name;
2612     *size_ret = size;
2613     float actual_size = size / scale;
2614     asprintf(xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
2615              family_name.UTF8String,
2616              (require & NSBoldFontMask) ? "bold" : "medium",
2617              (require & NSItalicFontMask) ? 'o' : 'r',
2618              (unsigned)(dpi * actual_size / 72.27 + 0.5),
2619              (unsigned)(actual_size * 10 + 0.5), dpi, dpi,
2620              (require & NSFixedPitchFontMask) ? 'm' : 'p');
2621     return nsfont;
2622   } else {
2623     return 0;
2624   }
2625 }
2626
2627 #endif
2628
2629 Font
2630 XLoadFont (Display *dpy, const char *name)
2631 {
2632   Font fid = (Font) calloc (1, sizeof(*fid));
2633   fid->refcount = 1;
2634   fid->dpy = dpy;
2635
2636   // (TODO) float scale = 1;
2637
2638 # ifdef USE_IPHONE
2639   /* Since iOS screens are physically smaller than desktop screens, scale up
2640      the fonts to make them more readable.
2641
2642      Note that X11 apps on iOS also have the backbuffer sized in points
2643      instead of pixels, resulting in an effective X11 screen size of 768x1024
2644      or so, even if the display has significantly higher resolution.  That is
2645      unrelated to this hack, which is really about DPI.
2646    */
2647   /* scale = 2; */
2648 # endif
2649
2650   fid->dpy = dpy;
2651   fid->native_font = jwxyz_load_native_font (dpy, name,
2652                                              &fid->ps_name, &fid->size,
2653                                              &fid->ascent, &fid->descent);
2654   if (!fid->native_font) {
2655     free (fid);
2656     return 0;
2657   }
2658   query_font (fid);
2659
2660   return fid;
2661 }
2662
2663
2664 XFontStruct *
2665 XLoadQueryFont (Display *dpy, const char *name)
2666 {
2667   Font fid = XLoadFont (dpy, name);
2668   if (!fid) return 0;
2669   return XQueryFont (dpy, fid);
2670 }
2671
2672 int
2673 XUnloadFont (Display *dpy, Font fid)
2674 {
2675   if (--fid->refcount < 0) abort();
2676   if (fid->refcount > 0) return 0;
2677
2678   if (fid->native_font)
2679     jwxyz_release_native_font (fid->dpy, fid->native_font);
2680
2681   if (fid->ps_name)
2682     free (fid->ps_name);
2683   if (fid->metrics.per_char)
2684     free (fid->metrics.per_char);
2685
2686   // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
2687   //      crashes in [NSFont ascender] <- query_font, and it seems to go away
2688   //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
2689   //      They're probably not very big...
2690   //
2691   //  [fid->nsfont release];
2692   //  CFRelease (fid->nsfont);
2693
2694   free (fid);
2695   return 0;
2696 }
2697
2698 int
2699 XFreeFontInfo (char **names, XFontStruct *info, int n)
2700 {
2701   int i;
2702   if (names) {
2703     for (i = 0; i < n; i++)
2704       if (names[i]) free (names[i]);
2705     free (names);
2706   }
2707   if (info) {
2708     for (i = 0; i < n; i++)
2709       if (info[i].per_char) {
2710         free (info[i].per_char);
2711         free (info[i].properties);
2712       }
2713     free (info);
2714   }
2715   return 0;
2716 }
2717
2718 int
2719 XFreeFont (Display *dpy, XFontStruct *f)
2720 {
2721   Font fid = f->fid;
2722   XFreeFontInfo (0, f, 1);
2723   XUnloadFont (dpy, fid);
2724   return 0;
2725 }
2726
2727
2728 int
2729 XSetFont (Display *dpy, GC gc, Font fid)
2730 {
2731   Font font2 = copy_font (fid);
2732   if (gc->gcv.font)
2733     XUnloadFont (dpy, gc->gcv.font);
2734   gc->gcv.font = font2;
2735   return 0;
2736 }
2737
2738
2739 XFontSet
2740 XCreateFontSet (Display *dpy, char *name, 
2741                 char ***missing_charset_list_return,
2742                 int *missing_charset_count_return,
2743                 char **def_string_return)
2744 {
2745   char *name2 = strdup (name);
2746   char *s = strchr (name, ',');
2747   if (s) *s = 0;
2748   XFontSet set = 0;
2749   XFontStruct *f = XLoadQueryFont (dpy, name2);
2750   if (f)
2751     {
2752       set = (XFontSet) calloc (1, sizeof(*set));
2753       set->font = f;
2754     }
2755   free (name2);
2756   if (missing_charset_list_return)  *missing_charset_list_return = 0;
2757   if (missing_charset_count_return) *missing_charset_count_return = 0;
2758   if (def_string_return) *def_string_return = 0;
2759   return set;
2760 }
2761
2762
2763 void
2764 XFreeFontSet (Display *dpy, XFontSet set)
2765 {
2766   XFreeFont (dpy, set->font);
2767   free (set);
2768 }
2769
2770
2771 const char *
2772 jwxyz_nativeFontName (Font f, float *size)
2773 {
2774   if (size) *size = f->size;
2775   return f->ps_name;
2776 }
2777
2778
2779 void
2780 XFreeStringList (char **list)
2781 {
2782   int i;
2783   if (!list) return;
2784   for (i = 0; list[i]; i++)
2785     XFree (list[i]);
2786   XFree (list);
2787 }
2788
2789
2790 // Given a UTF8 string, return an NSString.  Bogus UTF8 characters are ignored.
2791 // We have to do this because stringWithCString returns NULL if there are
2792 // any invalid characters at all.
2793 //
2794 /* TODO
2795 static NSString *
2796 sanitize_utf8 (const char *in, int in_len, Bool *latin1_pP)
2797 {
2798   int out_len = in_len * 4;   // length of string might increase
2799   char *s2 = (char *) malloc (out_len);
2800   char *out = s2;
2801   const char *in_end  = in  + in_len;
2802   const char *out_end = out + out_len;
2803   Bool latin1_p = True;
2804
2805   while (in < in_end)
2806     {
2807       unsigned long uc;
2808       long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
2809       long L2 = utf8_encode (uc, out, out_end - out);
2810       in  += L1;
2811       out += L2;
2812       if (uc > 255) latin1_p = False;
2813     }
2814   *out = 0;
2815   NSString *nsstr =
2816     [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
2817   free (s2);
2818   if (latin1_pP) *latin1_pP = latin1_p;
2819   return (nsstr ? nsstr : @"");
2820 }
2821 */
2822
2823 int
2824 XTextExtents (XFontStruct *f, const char *s, int length,
2825               int *dir_ret, int *ascent_ret, int *descent_ret,
2826               XCharStruct *cs)
2827 {
2828   // Unfortunately, adding XCharStructs together to get the extents for a
2829   // string doesn't work: Cocoa uses non-integral character advancements, but
2830   // XCharStruct.width is an integer. Plus that doesn't take into account
2831   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
2832   // Zapfino.
2833
2834   Font ff = f->fid;
2835   Display *dpy = ff->dpy;
2836   jwxyz_render_text (dpy, ff->native_font, s, length, GL_FALSE, cs, 0);
2837   *dir_ret = 0;
2838   *ascent_ret  = f->ascent;
2839   *descent_ret = f->descent;
2840   return 0;
2841 }
2842
2843 int
2844 XTextWidth (XFontStruct *f, const char *s, int length)
2845 {
2846   int ascent, descent, dir;
2847   XCharStruct cs;
2848   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2849   return cs.width;
2850 }
2851
2852
2853 int
2854 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
2855                 int *dir_ret, int *ascent_ret, int *descent_ret,
2856                 XCharStruct *cs)
2857 {
2858   // Bool latin1_p = True;
2859   int i, utf8_len = 0;
2860   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
2861
2862   for (i = 0; i < length; i++)
2863     if (s[i].byte1 > 0) {
2864       // latin1_p = False;
2865       break;
2866     }
2867
2868   {
2869     Font ff = f->fid;
2870     Display *dpy = ff->dpy;
2871     jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
2872                        GL_TRUE, cs, 0);
2873   }
2874
2875   *dir_ret = 0;
2876   *ascent_ret  = f->ascent;
2877   *descent_ret = f->descent;
2878   free (utf8);
2879   return 0;
2880 }
2881
2882
2883 /* "Returns the distance in pixels in the primary draw direction from
2884    the drawing origin to the origin of the next character to be drawn."
2885
2886    "overall_ink_return is set to the bbox of the string's character ink."
2887
2888    "The overall_ink_return for a nondescending, horizontally drawn Latin
2889    character is conventionally entirely above the baseline; that is,
2890    overall_ink_return.height <= -overall_ink_return.y."
2891
2892      [So this means that y is the top of the ink, and height grows down:
2893       For above-the-baseline characters, y is negative.]
2894
2895    "The overall_ink_return for a nonkerned character is entirely at, and to
2896    the right of, the origin; that is, overall_ink_return.x >= 0."
2897
2898      [So this means that x is the left of the ink, and width grows right.
2899       For left-of-the-origin characters, x is negative.]
2900
2901    "A character consisting of a single pixel at the origin would set
2902    overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
2903  */
2904 int
2905 Xutf8TextExtents (XFontSet set, const char *str, int len,
2906                   XRectangle *overall_ink_return,
2907                   XRectangle *overall_logical_return)
2908 {
2909 #if 0
2910   Bool latin1_p;
2911   NSString *nsstr = sanitize_utf8 (str, len, &latin1_p);
2912   XCharStruct cs;
2913
2914   utf8_metrics (set->font->fid, nsstr, &cs);
2915
2916   /* "The overall_logical_return is the bounding box that provides minimum
2917      spacing to other graphical features for the string. Other graphical
2918      features, for example, a border surrounding the text, should not
2919      intersect this rectangle."
2920
2921      So I think that means they're the same?  Or maybe "ink" is the bounding
2922      box, and "logical" is the advancement?  But then why is the return value
2923      the advancement?
2924    */
2925   if (overall_ink_return)
2926     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
2927   if (overall_logical_return)
2928     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
2929
2930   return cs.width;
2931 #endif
2932   abort();
2933 }
2934
2935
2936 static int
2937 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2938              const char *str, size_t len, GLboolean utf8)
2939 {
2940   Font ff = gc->gcv.font;
2941   XCharStruct cs;
2942
2943   char *data = 0;
2944   jwxyz_render_text (dpy, ff->native_font, str, len, utf8, &cs, &data);
2945   int w = cs.rbearing - cs.lbearing;
2946   int h = cs.ascent + cs.descent;
2947
2948   if (w < 0 || h < 0) abort();
2949   if (w == 0 || h == 0) {
2950     if (data) free(data);
2951     return 0;
2952   }
2953
2954   XImage *img = XCreateImage (dpy, dpy->screen->visual, 32,
2955                               ZPixmap, 0, data, w, h, 0, 0);
2956
2957   /* The image of text is a 32-bit image, in white.
2958      Take the red channel for intensity and use that as alpha.
2959      replace RGB with the GC's foreground color.
2960      This expects that XPutImage respects alpha and only writes
2961      the bits that are not masked out.
2962      This also assumes that XPutImage expects ARGB.
2963    */
2964   {
2965     char *s = data;
2966     char *end = s + (w * h * 4);
2967     uint8_t rgba[4];
2968     jwxyz_query_color (dpy, gc->gcv.foreground, rgba);
2969     while (s < end) {
2970
2971       s[3] = s[1];
2972       s[0] = rgba[0];
2973       s[1] = rgba[1];
2974       s[2] = rgba[2];
2975       s += 4;
2976     }
2977   }
2978
2979   XPutImage (dpy, d, gc, img, 0, 0, 
2980              x + cs.lbearing,
2981              y - cs.ascent,
2982              w, h);
2983   XDestroyImage (img);
2984
2985   return 0;
2986 }
2987
2988 int
2989 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2990              const char  *str, int len)
2991 {
2992   return draw_string (dpy, d, gc, x, y, str, len, GL_FALSE);
2993 }
2994
2995
2996 int
2997 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
2998              const XChar2b *str, int len)
2999 {
3000   XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
3001   char *s2;
3002   int ret;
3003   memcpy (b2, str, len * sizeof(*b2));
3004   b2[len].byte1 = b2[len].byte2 = 0;
3005   s2 = XChar2b_to_utf8 (b2, 0);
3006   free (b2);
3007   ret = draw_string (dpy, d, gc, x, y, s2, strlen(s2), GL_TRUE);
3008   free (s2);
3009   return ret;
3010 }
3011
3012
3013 void
3014 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
3015                  int x, int y, const char *str, int len)
3016 {
3017   draw_string (dpy, d, gc, x, y, str, len, GL_TRUE);
3018 }
3019
3020
3021 int
3022 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3023                   const char *str, int len)
3024 {
3025   int ascent, descent, dir;
3026   XCharStruct cs;
3027   XTextExtents (&gc->gcv.font->metrics, str, len,
3028                 &dir, &ascent, &descent, &cs);
3029   jwxyz_fill_rect (dpy, d, gc,
3030                    x + MIN (0, cs.lbearing),
3031                    y - MAX (0, ascent),
3032                    MAX (MAX (0, cs.rbearing) -
3033                         MIN (0, cs.lbearing),
3034                         cs.width),
3035                    MAX (0, ascent) + MAX (0, descent),
3036                    gc->gcv.background);
3037   return XDrawString (dpy, d, gc, x, y, str, len);
3038 }
3039
3040
3041 int
3042 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3043 {
3044 //####  abort();
3045 /*
3046   TODO
3047
3048   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3049
3050   if (gc->gcv.clip_mask) {
3051     XFreePixmap (dpy, gc->gcv.clip_mask);
3052     CGImageRelease (gc->clip_mask);
3053   }
3054
3055   gc->gcv.clip_mask = copy_pixmap (dpy, m);
3056   if (gc->gcv.clip_mask)
3057     gc->clip_mask =
3058       CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3059   else
3060     gc->clip_mask = 0;
3061 */
3062   
3063   return 0;
3064 }
3065
3066 int
3067 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3068 {
3069   gc->gcv.clip_x_origin = x;
3070   gc->gcv.clip_y_origin = y;
3071   return 0;
3072 }
3073
3074 #endif /* JWXYZ_GL -- entire file */