From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / jwxyz / jwxyz-gl.c
1 /* xscreensaver, Copyright (c) 1991-2017 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 /* FWIW, at one point macOS 10.x could actually run with 256 colors. It might
25    not be able to do this anymore, though.
26  */
27
28 /* TODO:
29    - malloc error checking
30    - Check max texture sizes for XGet/PutImage, XCopyArea.
31    - Optional 5:5:5 16-bit color
32 */
33
34 /* Techniques & notes:
35    - iOS: OpenGL ES 2.0 isn't always available. Use OpenGL ES 1.1.
36    - OS X: Drivers can go back to OpenGL 1.1 (GeForce 2 MX on 10.5.8).
37    - Use stencil buffers (OpenGL 1.0+) for bitmap clipping masks.
38    - Pixmaps can be any of the following, depending on GL implementation.
39      - This requires offscreen rendering. Fortunately, this is always
40        available.
41        - OS X: Drawable objects, including: pixel buffers and offscreen
42          memory.
43          - Offscreen buffers w/ CGLSetOffScreen (10.0+)
44          - http://lists.apple.com/archives/mac-opengl/2002/Jun/msg00087.html
45            provides a very ugly solution for hardware-accelerated offscreen
46            rendering with CGLSetParameter(*, kCGLCPSurfaceTexture, *) on OS X
47            10.1+. Apple docs say it's actually for OS X 10.3+, instead.
48          - Pixel buffers w/ CGLSetPBuffer (10.3+, now deprecated)
49            - Requires APPLE_pixel_buffer.
50              - Available in software on x86 only.
51              - Always available on hardware.
52            - Need to blit? Use OpenGL pixel buffers. (GL_PIXEL_PACK_BUFFER)
53          - Framebuffer objects w/ GL_(ARB|EXT)_framebuffer_object
54            - TODO: Can EXT_framebuffers be different sizes?
55            - Preferred on 10.7+
56        - iOS: Use OES_framebuffer_object, it's always present.
57  */
58
59 /* OpenGL hacks call a number of X11 functions, including
60    XCopyArea, XDrawString, XGetImage
61    XCreatePixmap, XCreateGC, XCreateImage
62    XPutPixel
63    Check these, of course.
64  */
65
66 #ifdef JWXYZ_GL /* entire file */
67
68 #include <math.h>
69 #include <limits.h>
70 #include <stddef.h>
71 #include <stdlib.h>
72 #include <stdint.h>
73 #include <string.h>
74 #include <wchar.h>
75
76 #ifdef HAVE_COCOA
77 # ifdef USE_IPHONE
78 #  import <QuartzCore/QuartzCore.h>
79 #  include <OpenGLES/ES1/gl.h>
80 #  include <OpenGLES/ES1/glext.h>
81 # else
82 #  include <OpenGL/glu.h>
83 # endif
84 #else
85 /* TODO: Does this work on iOS? */
86 # ifndef HAVE_JWZGLES
87 #  include <gl/glu.h>
88 # else
89 #  include <GLES/gl.h>
90 #  include <GLES/glext.h>
91 # endif
92 #endif
93
94 #ifdef HAVE_JWZGLES
95 # include "jwzglesI.h"
96 #endif
97
98 #include "jwxyzI.h"
99 #include "jwxyz-timers.h"
100 #include "yarandom.h"
101 #include "utf8wc.h"
102 #include "xft.h"
103 #include "pow2.h"
104
105 union color_bytes {
106   uint32_t pixel;
107   uint8_t bytes[4];
108 };
109
110 struct jwxyz_Display {
111   const struct jwxyz_vtbl *vtbl; // Must come first.
112
113   Window main_window;
114   GLenum pixel_format, pixel_type;
115   Visual visual;
116   struct jwxyz_sources_data *timers_data;
117
118   Bool gl_texture_npot_p;
119   /* Bool opengl_core_p */;
120   GLenum gl_texture_target;
121   
122 // #if defined USE_IPHONE
123   GLuint rect_texture; // Also can work on the desktop.
124 // #endif
125
126   unsigned long window_background;
127
128   int gc_function;
129   Bool gc_alpha_allowed_p;
130
131   // Alternately, there could be one queue per pixmap.
132   size_t queue_size, queue_capacity;
133   Drawable queue_drawable;
134   GLint queue_mode;
135   GLshort *queue_vertex;
136   uint32_t *queue_color;
137   Bool queue_line_cap;
138 };
139
140 struct jwxyz_GC {
141   XGCValues gcv;
142   unsigned int depth;
143 };
144
145 struct jwxyz_linked_point {
146     short x, y;
147     linked_point *next;
148 };
149
150
151 void
152 jwxyz_assert_display(Display *dpy)
153 {
154   if(!dpy)
155     return;
156   jwxyz_assert_gl ();
157   jwxyz_assert_drawable (dpy->main_window, dpy->main_window);
158 }
159
160
161 void
162 jwxyz_set_matrices (Display *dpy, unsigned width, unsigned height,
163                     Bool window_p)
164 {
165   Assert (width, "no width");
166   Assert (height, "no height");
167
168   /* TODO: Check registration pattern from Interference with rectangles instead of points. */
169
170   // The projection matrix is always set as follows. The modelview matrix is
171   // usually identity, but for points and thin lines, it's translated by 0.5.
172   glMatrixMode(GL_PROJECTION);
173   glLoadIdentity();
174   
175 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)
176
177   if (window_p && ignore_rotation_p(dpy)) {
178     int o = (int) current_device_rotation();
179     glRotatef (-o, 0, 0, 1);
180   }
181
182   // glPointSize(1); // This is the default.
183   
184 #ifdef HAVE_JWZGLES
185   glOrthof /* TODO: Is glOrthox worth it? Signs point to no. */
186 #else
187   glOrtho
188 #endif
189     (0, width, height, 0, -1, 1);
190
191   glMatrixMode(GL_MODELVIEW);
192 # endif // HAVE_MOBILE
193 }
194
195 #ifndef HAVE_JWZGLES
196
197 struct gl_version
198 {
199   // iOS always uses OpenGL ES 1.1.
200   unsigned major;
201   unsigned minor;
202 };
203
204 static GLboolean
205 gl_check_ver (const struct gl_version *caps,
206               unsigned gl_major,
207               unsigned gl_minor)
208 {
209   return caps->major > gl_major ||
210            (caps->major == gl_major && caps->minor >= gl_minor);
211 }
212
213 #endif
214
215
216 extern const struct jwxyz_vtbl gl_vtbl;
217
218 Display *
219 jwxyz_gl_make_display (Window w)
220 {
221   Display *d = (Display *) calloc (1, sizeof(*d));
222   d->vtbl = &gl_vtbl;
223
224 # ifndef HAVE_JWZGLES
225   struct gl_version version;
226
227   {
228     const GLubyte *version_str = glGetString (GL_VERSION);
229
230     /* iPhone is always OpenGL ES 1.1. */
231     if (sscanf ((const char *) version_str, "%u.%u",
232                 &version.major, &version.minor) < 2)
233     {
234       version.major = 1;
235       version.minor = 1;
236     }
237   }
238 # endif // !HAVE_JWZGLES
239
240   const GLubyte *extensions = glGetString (GL_EXTENSIONS);
241
242   /* See:
243      - Apple TN2080: Understanding and Detecting OpenGL Functionality.
244      - OpenGL Programming Guide for the Mac - Best Practices for Working with
245        Texture Data - Optimal Data Formats and Types
246    */
247
248   // If a video adapter suports BGRA textures, then that's probably as fast as
249   // you're gonna get for getting a texture onto the screen.
250 # ifdef HAVE_JWZGLES
251   /* TODO: Make BGRA work on iOS. As it is, it breaks XPutImage. (glTexImage2D, AFAIK) */
252   d->pixel_format = GL_RGBA; /*
253     gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_format_BGRA8888",
254                        extensions) ? GL_BGRA_EXT : GL_RGBA; */
255   d->pixel_type = GL_UNSIGNED_BYTE;
256   // See also OES_read_format.
257 # else  // !HAVE_JWZGLES
258   if (gl_check_ver (&version, 1, 2) ||
259       (gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", extensions) &&
260        gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels",
261                           extensions))) {
262     // APPLE_packed_pixels is only ever available on iOS, never Android.
263     d->pixel_format = GL_BGRA_EXT;
264     // Both Intel and PowerPC-era docs say to use GL_UNSIGNED_INT_8_8_8_8_REV.
265     d->pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
266   } else {
267     d->pixel_format = GL_RGBA;
268     d->pixel_type = GL_UNSIGNED_BYTE;
269   }
270   // GL_ABGR_EXT/GL_UNSIGNED_BYTE is another possibilty that may have made more
271   // sense on PowerPC.
272 # endif // !HAVE_JWZGLES
273
274   // On really old systems, it would make sense to split textures
275   // into subsections, to work around the maximum texture size.
276 # ifndef HAVE_JWZGLES
277   d->gl_texture_npot_p = gluCheckExtension ((const GLubyte *)
278                                             "GL_ARB_texture_rectangle",
279                                             extensions);
280   d->gl_texture_target = d->gl_texture_npot_p ?
281                          GL_TEXTURE_RECTANGLE_EXT :
282                          GL_TEXTURE_2D;
283 # else
284   d->gl_texture_npot_p = jwzgles_gluCheckExtension
285       ((const GLubyte *) "GL_APPLE_texture_2D_limited_npot", extensions) ||
286     jwzgles_gluCheckExtension
287       ((const GLubyte *) "GL_OES_texture_npot", extensions) ||
288     jwzgles_gluCheckExtension // From PixelFlinger 1.4
289       ((const GLubyte *) "GL_ARB_texture_non_power_of_two", extensions);
290
291   d->gl_texture_target = GL_TEXTURE_2D;
292 # endif
293
294   Visual *v = &d->visual;
295   v->class      = TrueColor;
296   if (d->pixel_format == GL_BGRA_EXT) {
297     v->rgba_masks[0] = 0x00ff0000;
298     v->rgba_masks[1] = 0x0000ff00;
299     v->rgba_masks[2] = 0x000000ff;
300     v->rgba_masks[3] = 0xff000000;
301   } else {
302     Assert(d->pixel_format == GL_RGBA,
303            "jwxyz_gl_make_display: Unknown pixel_format");
304     for (unsigned i = 0; i != 4; ++i) {
305       union color_bytes color;
306       color.pixel = 0;
307       color.bytes[i] = 0xff;
308       v->rgba_masks[i] = color.pixel;
309     }
310   }
311
312   d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
313
314   d->window_background = BlackPixel(d,0);
315
316   d->main_window = w;
317   {
318     GLint max_texture_size;
319     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
320     Log ("GL_MAX_TEXTURE_SIZE: %d\n", max_texture_size);
321   }
322  
323   // In case a GL hack wants to use X11 to draw offscreen, the rect_texture is available.
324   Assert (d->main_window == w, "Uh-oh.");
325   glGenTextures (1, &d->rect_texture);
326   // TODO: Check for (ARB|EXT|NV)_texture_rectangle. (All three are alike.)
327   // Rectangle textures should be present on OS X with the following exceptions:
328   // - Generic renderer on PowerPC OS X 10.4 and earlier
329   // - ATI Rage 128
330   glBindTexture (d->gl_texture_target, d->rect_texture);
331   // TODO: This is all somewhere else. Refactor.
332   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
333   glTexParameteri (d->gl_texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
334
335   // This might be redundant for rectangular textures.
336 # ifndef HAVE_JWZGLES
337   const GLint wrap = GL_CLAMP;
338 # else  // HAVE_JWZGLES
339   const GLint wrap = GL_CLAMP_TO_EDGE;
340 # endif // HAVE_JWZGLES
341
342   // In OpenGL, CLAMP_TO_EDGE is OpenGL 1.2 or GL_SGIS_texture_edge_clamp.
343   // This is always present with OpenGL ES.
344   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_S, wrap);
345   glTexParameteri (d->gl_texture_target, GL_TEXTURE_WRAP_T, wrap);
346
347   d->gc_function = GXcopy;
348   d->gc_alpha_allowed_p = False;
349
350   jwxyz_assert_display(d);
351   return d;
352 }
353
354 void
355 jwxyz_gl_free_display (Display *dpy)
356 {
357   /* TODO: Go over everything. */
358
359   free (dpy->queue_vertex);
360   free (dpy->queue_color);
361
362   jwxyz_sources_free (dpy->timers_data);
363
364   free (dpy);
365 }
366
367
368 /* Call this when the View changes size or position.
369  */
370 void
371 jwxyz_window_resized (Display *dpy)
372 {
373   const XRectangle *new_frame = jwxyz_frame (dpy->main_window);
374   unsigned new_width = new_frame->width;
375   unsigned new_height = new_frame->height;
376
377   Assert (new_width, "jwxyz_window_resized: No width.");
378   Assert (new_height, "jwxyz_window_resized: No height.");
379
380 /*if (cgc) w->cgc = cgc;
381   Assert (w->cgc, "no CGContext"); */
382
383   Log("resize: %d, %d\n", new_width, new_height);
384
385   jwxyz_gl_flush (dpy);
386   jwxyz_bind_drawable (dpy, dpy->main_window, dpy->main_window);
387
388   // TODO: What does the iPhone need?
389   
390   // iOS only: If the main_window is not the current_drawable, then set_matrices
391   // was already called in bind_drawable.
392   jwxyz_set_matrices (dpy, new_width, new_height, True);
393
394 /*
395   GLint draw_buffer;
396   glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
397   
398   glDrawBuffer (GL_FRONT);
399   glClearColor (1, 0, 1, 0);
400   glClear (GL_COLOR_BUFFER_BIT);
401   glDrawBuffer (GL_BACK);
402   glClearColor (0, 1, 1, 0);
403   glClear (GL_COLOR_BUFFER_BIT);
404   
405   glDrawBuffer (draw_buffer); */
406   
407   // Stylish and attractive purple!
408   // glClearColor (1, 0, 1, 0.5);
409   // glClear (GL_COLOR_BUFFER_BIT);
410 }
411
412
413 static jwxyz_sources_data *
414 display_sources_data (Display *dpy)
415 {
416   return dpy->timers_data;
417 }
418
419
420 static Window
421 root (Display *dpy)
422 {
423   return dpy->main_window;
424 }
425
426 static Visual *
427 visual (Display *dpy)
428 {
429   return &dpy->visual;
430 }
431
432
433 /* GC attributes by usage and OpenGL implementation:
434
435    All drawing functions:
436    function                                | glLogicOp w/ GL_COLOR_LOGIC_OP
437    clip_x_origin, clip_y_origin, clip_mask | Stencil mask
438
439    Shape drawing functions:
440    foreground, background                  | glColor*
441
442    XDrawLines, XDrawRectangles, XDrawSegments:
443    line_width, cap_style, join_style       | Lotsa vertices
444
445    XFillPolygon:
446    fill_rule                               | Multiple GL_TRIANGLE_FANs
447
448    XDrawText:
449    font                                    | Cocoa, then OpenGL display lists.
450
451    alpha_allowed_p                         | GL_BLEND
452
453    antialias_p                             | Well, there's options:
454    * Multisampling would work, but that's something that would need to be set
455      per-Pixmap, not per-GC.
456    * GL_POINT, LINE, and POLYGON_SMOOTH are the old-school way of doing
457      this, but POINT_SMOOTH is unnecessary, and POLYGON_SMOOTH is missing from
458      GLES 1. All three are missing from GLES 2. Word on the street is that
459      these are deprecated anyway.
460    * Tiny textures with bilinear filtering to get the same effect as LINE_ and
461      POLYGON_SMOOTH. A bit tricky.
462    * Do nothing. Android hardware is very often high-DPI enough that
463      anti-aliasing doesn't matter all that much.
464
465    Nothing, really:
466    subwindow_mode
467  */
468
469 static GLshort *
470 enqueue (Display *dpy, Drawable d, GC gc, int mode, size_t count)
471 {
472   if (dpy->queue_size &&
473       (dpy->gc_function != gc->gcv.function ||
474        dpy->gc_alpha_allowed_p != gc->gcv.alpha_allowed_p ||
475        dpy->queue_mode != mode ||
476        dpy->queue_drawable != d)) {
477     jwxyz_gl_flush (dpy);
478   }
479
480   jwxyz_bind_drawable (dpy, dpy->main_window, d);
481   jwxyz_gl_set_gc (dpy, gc);
482
483   // TODO: Use glColor when we can get away with it.
484   size_t old_size = dpy->queue_size;
485   dpy->queue_size += count;
486   if (dpy->queue_size > dpy->queue_capacity) {
487     dpy->queue_capacity = dpy->queue_size * 2;
488
489     uint32_t *new_color = realloc (
490       dpy->queue_color, sizeof(*dpy->queue_color) * dpy->queue_capacity);
491     GLshort *new_vertex = realloc (
492       dpy->queue_vertex, sizeof(*dpy->queue_vertex) * 2 * dpy->queue_capacity);
493
494     if (!new_color || !new_vertex)
495       return NULL;
496
497     dpy->queue_color = new_color;
498     dpy->queue_vertex = new_vertex;
499   }
500
501   dpy->queue_mode = mode;
502   dpy->queue_drawable = d;
503
504   union color_bytes color;
505   // TODO: validate color
506   JWXYZ_QUERY_COLOR (dpy, gc->gcv.foreground, 0xffull, color.bytes);
507   for (size_t i = 0; i != count; ++i) // TODO: wmemset when applicable.
508     dpy->queue_color[i + old_size] = color.pixel;
509
510   return dpy->queue_vertex + old_size * 2;
511 }
512
513
514 static void
515 set_clip_mask (GC gc)
516 {
517   Assert (!gc->gcv.clip_mask, "set_clip_mask: TODO");
518 }
519
520
521 static void
522 set_color (Display *dpy, unsigned long pixel, unsigned int depth,
523            Bool alpha_allowed_p)
524 {
525   jwxyz_validate_pixel (dpy, pixel, depth, alpha_allowed_p);
526
527   if (depth == 1) {
528     GLfloat f = pixel;
529     glColor4f (f, f, f, 1);
530   } else {
531     GLfloat rgba[4];
532     JWXYZ_QUERY_COLOR (dpy, pixel, 1.0f, rgba);
533     glColor4f (rgba[0], rgba[1], rgba[2], rgba[3]);
534   }
535 }
536
537 /* Pushes a GC context; sets Function and ClipMask. */
538 void
539 jwxyz_gl_set_gc (Display *dpy, GC gc)
540 {
541   int function;
542   Bool alpha_allowed_p;
543
544   // GC is NULL for XClearArea and XClearWindow.
545   if (gc) {
546     function = gc->gcv.function;
547     alpha_allowed_p = gc->gcv.alpha_allowed_p;
548     set_clip_mask (gc);
549   } else {
550     function = GXcopy;
551     alpha_allowed_p = False;
552     // TODO: Set null clip mask for NULL GC here.
553   }
554
555   /* GL_COLOR_LOGIC_OP: OpenGL 1.1. */
556   if (function != dpy->gc_function) {
557     dpy->gc_function = function;
558     if (function != GXcopy) {
559       /* Fun fact: The glLogicOp opcode constants are the same as the X11 GX*
560          function constants | GL_CLEAR.
561        */
562       glEnable (GL_COLOR_LOGIC_OP);
563       glLogicOp (gc->gcv.function | GL_CLEAR);
564     } else {
565       glDisable (GL_COLOR_LOGIC_OP);
566     }
567   }
568
569   /* Cocoa uses add/subtract/difference blending in place of logical ops.
570      It looks nice, but implementing difference blending in OpenGL appears to
571      require GL_KHR_blend_equation_advanced, and support for this is not
572      widespread.
573    */
574
575   if (alpha_allowed_p != dpy->gc_alpha_allowed_p) {
576     dpy->gc_alpha_allowed_p = alpha_allowed_p;
577     if (gc && gc->gcv.alpha_allowed_p) {
578       // TODO: Maybe move glBlendFunc to XCreatePixmap?
579       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
580       glEnable (GL_BLEND);
581     } else {
582       glDisable (GL_BLEND);
583     }
584   }
585 }
586
587
588 static void
589 set_color_gc (Display *dpy, Drawable d, GC gc, unsigned long color)
590 {
591   jwxyz_gl_flush (dpy);
592   jwxyz_bind_drawable (dpy, dpy->main_window, d);
593   jwxyz_gl_set_gc (dpy, gc);
594
595   unsigned int depth;
596
597   if (gc) {
598     depth = gc->depth;
599
600     switch (gc->gcv.function) {
601       case GXset:   color = (depth == 1 ? 1 : WhitePixel(dpy,0)); break;
602       case GXclear: color = (depth == 1 ? 0 : BlackPixel(dpy,0)); break;
603     }
604   } else {
605     depth = visual_depth (NULL, NULL);
606   }
607
608   set_color (dpy, color, depth, gc ? gc->gcv.alpha_allowed_p : False);
609 }
610
611 /* Pushes a GC context; sets color to the foreground color.
612  */
613 static void
614 set_fg_gc (Display *dpy, Drawable d, GC gc)
615 {
616   set_color_gc (dpy, d, gc, gc->gcv.foreground);
617 }
618
619 static void
620 next_point(short *v, XPoint p, int mode)
621 {
622   switch (mode) {
623     case CoordModeOrigin:
624       v[0] = p.x;
625       v[1] = p.y;
626       break;
627     case CoordModePrevious:
628       v[0] += p.x;
629       v[1] += p.y;
630       break;
631     default:
632       Assert (False, "next_point: bad mode");
633       break;
634   }
635 }
636
637 static int
638 DrawPoints (Display *dpy, Drawable d, GC gc,
639             XPoint *points, int count, int mode)
640 {
641   short v[2] = {0, 0};
642
643   // TODO: XPoints can be fed directly to OpenGL.
644   GLshort *gl_points = enqueue (dpy, d, gc, GL_POINTS, count); // TODO: enqueue returns NULL.
645   for (unsigned i = 0; i < count; i++) {
646     next_point (v, points[i], mode);
647     gl_points[2 * i] = v[0];
648     gl_points[2 * i + 1] = v[1];
649   }
650
651   return 0;
652 }
653
654
655 static GLint
656 texture_internalformat (Display *dpy)
657 {
658 #ifdef HAVE_JWZGLES
659   return dpy->pixel_format;
660 #else
661   return GL_RGBA;
662 #endif
663 }
664
665 static GLenum
666 gl_pixel_type (const Display *dpy)
667 {
668   return dpy->pixel_type;
669 }
670
671 static void
672 clear_texture (Display *dpy)
673 {
674   glTexImage2D (dpy->gl_texture_target, 0, texture_internalformat(dpy), 0, 0,
675                 0, dpy->pixel_format, gl_pixel_type (dpy), NULL);
676 }
677
678 static void
679 set_white (void)
680 {
681 #ifdef HAVE_JWZGLES
682   glColor4f (1, 1, 1, 1);
683 #else
684   glColor3ub (0xff, 0xff, 0xff);
685 #endif
686 }
687
688
689 void
690 jwxyz_gl_flush (Display *dpy)
691 {
692   if (!dpy->queue_size)
693     return;
694
695   // jwxyz_bind_drawable() and jwxyz_gl_set_gc() is called in enqueue().
696
697   glEnableClientState (GL_COLOR_ARRAY);
698   glEnableClientState (GL_VERTEX_ARRAY);
699   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
700
701   // TODO: Use glColor instead of glColorPointer if there's just one color.
702   // TODO: Does OpenGL use both GL_COLOR_ARRAY and glColor at the same time?
703   set_white();
704
705   Bool shifted = dpy->queue_mode == GL_POINTS || dpy->queue_mode == GL_LINES;
706   if (shifted) {
707     glMatrixMode (GL_MODELVIEW);
708     glTranslatef (0.5, 0.5, 0);
709   }
710
711   glColorPointer (4, GL_UNSIGNED_BYTE, 0, dpy->queue_color);
712   glVertexPointer (2, GL_SHORT, 0, dpy->queue_vertex);
713   glDrawArrays (dpy->queue_mode, 0, dpy->queue_size);
714
715   // TODO: This is right, right?
716   if (dpy->queue_mode == GL_LINES && dpy->queue_line_cap) {
717     Assert (!(dpy->queue_size % 2), "bad count for GL_LINES");
718     glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(GLubyte) * 8,
719                     dpy->queue_color);
720     glVertexPointer (2, GL_SHORT, sizeof(GLshort) * 4, dpy->queue_vertex + 2);
721     glDrawArrays (GL_POINTS, 0, dpy->queue_size / 2);
722   }
723
724   if (shifted)
725     glLoadIdentity ();
726
727   glDisableClientState (GL_COLOR_ARRAY);
728   glDisableClientState (GL_VERTEX_ARRAY);
729
730   dpy->queue_size = 0;
731 }
732
733
734 void
735 jwxyz_gl_copy_area_read_tex_image (Display *dpy, unsigned src_height,
736                                    int src_x, int src_y,
737                                    unsigned int width, unsigned int height,
738                                    int dst_x, int dst_y)
739 {
740 #  if defined HAVE_COCOA && !defined USE_IPHONE
741   /* TODO: Does this help? */
742   /* glFinish(); */
743 #  endif
744
745   /* TODO: Fix TestX11 + mode_preserve with this one. */
746
747   unsigned tex_w = width, tex_h = height;
748   if (!dpy->gl_texture_npot_p) {
749     tex_w = to_pow2(tex_w);
750     tex_h = to_pow2(tex_h);
751   }
752
753   GLint internalformat = texture_internalformat(dpy);
754
755   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
756
757   if (tex_w == width && tex_h == height) {
758     glCopyTexImage2D (dpy->gl_texture_target, 0, internalformat,
759                       src_x, src_height - src_y - height, width, height, 0);
760   } else {
761     glTexImage2D (dpy->gl_texture_target, 0, internalformat, tex_w, tex_h,
762                   0, dpy->pixel_format, gl_pixel_type(dpy), NULL);
763     glCopyTexSubImage2D (dpy->gl_texture_target, 0, 0, 0,
764                          src_x, src_height - src_y - height, width, height);
765   }
766 }
767
768 void
769 jwxyz_gl_copy_area_write_tex_image (Display *dpy, GC gc, int src_x, int src_y,
770                                     unsigned int width, unsigned int height,
771                                     int dst_x, int dst_y)
772 {
773   jwxyz_gl_set_gc (dpy, gc);
774
775   /* TODO: Copy-pasted from read_tex_image. */
776   unsigned tex_w = width, tex_h = height;
777   if (!dpy->gl_texture_npot_p) {
778     tex_w = to_pow2(tex_w);
779     tex_h = to_pow2(tex_h);
780   }
781
782   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
783
784   jwxyz_gl_draw_image (dpy->gl_texture_target, tex_w, tex_h, 0, 0,
785                        width, height, dst_x, dst_y);
786
787   clear_texture (dpy);
788 }
789
790
791 void
792 jwxyz_gl_draw_image (GLenum gl_texture_target,
793                      unsigned int tex_w, unsigned int tex_h,
794                      int src_x, int src_y,
795                      unsigned int width, unsigned int height,
796                      int dst_x, int dst_y)
797 {
798   set_white ();
799   glEnable (gl_texture_target);
800
801   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
802   glEnableClientState (GL_VERTEX_ARRAY);
803     
804   /* TODO: Copied from XPutImage. Refactor. */
805   /* TODO: EXT_draw_texture or whatever it's called. */
806   GLfloat vertices[4][2] =
807   {
808     {dst_x, dst_y},
809     {dst_x, dst_y + height},
810     {dst_x + width, dst_y + height},
811     {dst_x + width, dst_y}
812   };
813
814   GLfloat
815     tex_x0 = src_x, tex_y0 = src_y + height,
816     tex_x1 = src_x + width, tex_y1 = src_y;
817
818 # ifndef HAVE_JWZGLES
819   if (gl_texture_target != GL_TEXTURE_RECTANGLE_EXT)
820 # endif
821   {
822     GLfloat mx = 1.0f / tex_w, my = 1.0f / tex_h;
823     tex_x0 *= mx;
824     tex_y0 *= my;
825     tex_x1 *= mx;
826     tex_y1 *= my;
827   }
828
829   GLfloat tex_coords[4][2] =
830   {
831     {tex_x0, tex_y0},
832     {tex_x0, tex_y1},
833     {tex_x1, tex_y1},
834     {tex_x1, tex_y0}
835   };
836
837   glVertexPointer (2, GL_FLOAT, 0, vertices);
838   glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
839   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
840
841   glDisable (gl_texture_target);
842 }
843
844 void
845 jwxyz_gl_copy_area_read_pixels (Display *dpy, Drawable src, Drawable dst,
846                                 GC gc, int src_x, int src_y,
847                                 unsigned int width, unsigned int height,
848                                 int dst_x, int dst_y)
849 {
850   XImage *img = XGetImage (dpy, src, src_x, src_y, width, height, ~0, ZPixmap);
851   XPutImage (dpy, dst, gc, img, 0, 0, dst_x, dst_y, width, height);
852   XDestroyImage (img);
853 }
854
855
856 static int
857 DrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
858            int mode)
859 {
860   set_fg_gc (dpy, d, gc);
861
862   /* TODO: Thick lines
863    * Zero-length line segments
864    * Paths with zero length total (Remember line width, cap style.)
865    * Closed loops
866    */
867   
868   if (!count)
869     return 0;
870
871   GLshort *vertices = malloc(2 * sizeof(GLshort) * count); // TODO malloc NULL sigh
872   
873   glMatrixMode (GL_MODELVIEW);
874   glTranslatef (0.5f, 0.5f, 0);
875   
876   short p[2] = {0, 0};
877   for (unsigned i = 0; i < count; i++) {
878     next_point (p, points[i], mode);
879     vertices[2 * i] = p[0];
880     vertices[2 * i + 1] = p[1];
881   }
882   
883   glEnableClientState (GL_VERTEX_ARRAY);
884   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
885   glVertexPointer (2, GL_SHORT, 0, vertices);
886   glDrawArrays (GL_LINE_STRIP, 0, count);
887   
888   free (vertices);
889
890   if (gc->gcv.cap_style != CapNotLast) {
891     // TODO: How does this look with multisampling?
892     // TODO: Disable me for closed loops.
893     glVertexPointer (2, GL_SHORT, 0, p);
894     glDrawArrays (GL_POINTS, 0, 1);
895   }
896
897   glLoadIdentity ();
898   
899   return 0;
900 }
901
902
903 // Turn line segment into parallelogram based on line_width
904 //
905 // TODO: Fix epicycle hack with large thickness, and truchet line segment ends
906 //
907 static void drawThickLine(int line_width, XSegment *segments)
908 {
909     double dx, dy, di, m, angle;
910     int sx1, sx2, sy1, sy2;
911
912     sx1 = segments->x1;
913     sy1 = segments->y1;
914     sx2 = segments->x2;
915     sy2 = segments->y2;
916
917     dx = sx1 - sx2;
918     dy = sy1 - sy2;
919     di = sqrt(dx * dx + dy * dy);
920     dx = dx / di;
921     dy = dy / di;
922     m = dy / dx;
923
924     angle = atan(m); 
925
926     float sn = sin(angle);
927     float cs = cos(angle);
928     float line_width_f = (float) line_width;
929
930     float wsn = line_width_f * (sn/2);
931     float csn = line_width_f * (cs/2);
932
933     float x3 = sx1 - wsn;
934     float y3 = sy1 + csn;
935     float x4 = sx1 + wsn;
936     float y4 = sy1 - csn;
937
938     float x5 = sx2 - wsn;
939     float y5 = sy2 + csn;
940     float x6 = sx2 + wsn;
941     float y6 = sy2 - csn;
942
943     GLfloat coords[4][2] =
944     {
945       {x3, y3},
946       {x4, y4},
947       {x6, y6},
948       {x5, y5}
949     };
950
951     glEnableClientState (GL_VERTEX_ARRAY);
952     glVertexPointer (2, GL_FLOAT, 0, coords);
953     jwxyz_assert_gl ();
954     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
955     jwxyz_assert_gl ();
956 }
957
958
959 static int
960 DrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
961 {
962   /* TODO: Caps on thick lines. */
963   /* Thin lines <= 1px are offset by +0.5; thick lines are not. */
964
965   if (count == 1 && gc->gcv.line_width > 1) {
966     set_fg_gc (dpy, d, gc);
967     drawThickLine(gc->gcv.line_width,segments);
968   }
969   else {
970     if (dpy->queue_line_cap != (gc->gcv.cap_style != CapNotLast))
971       jwxyz_gl_flush (dpy);
972     dpy->queue_line_cap = gc->gcv.cap_style != CapNotLast;
973
974     // TODO: Static assert here.
975     Assert (sizeof(XSegment) == sizeof(short) * 4 &&
976             sizeof(GLshort) == sizeof(short) &&
977             offsetof(XSegment, x1) == 0 &&
978             offsetof(XSegment, x2) == 4,
979             "XDrawSegments: Data alignment mix-up.");
980
981     memcpy (enqueue(dpy, d, gc, GL_LINES, count * 2), segments,
982             count * sizeof(XSegment));
983   }
984
985   return 0;
986 }
987
988
989 static int
990 ClearWindow (Display *dpy, Window win)
991 {
992   Assert (win == dpy->main_window, "not a window");
993
994   jwxyz_gl_flush (dpy);
995   jwxyz_bind_drawable (dpy, win, win);
996
997   GLfloat color[4];
998   JWXYZ_QUERY_COLOR (dpy, dpy->window_background, 1.0f, color);
999
1000   glClearColor (color[0], color[1], color[2], 1);
1001   glClear (GL_COLOR_BUFFER_BIT);
1002   return True;
1003 }
1004
1005 static unsigned long *
1006 window_background (Display *dpy)
1007 {
1008   return &dpy->window_background;
1009 }
1010
1011 static void
1012 fill_rects (Display *dpy, Drawable d, GC gc,
1013             const XRectangle *rectangles, unsigned long nrectangles,
1014             unsigned long pixel)
1015 {
1016   set_color_gc (dpy, d, gc, pixel);
1017
1018 /*
1019   glBegin(GL_QUADS);
1020   for (unsigned i = 0; i != nrectangles; ++i) {
1021     const XRectangle *r = &rectangles[i];
1022     glVertex2i(r->x, r->y);
1023     glVertex2i(r->x, r->y + r->height);
1024     glVertex2i(r->x + r->width, r->y + r->height);
1025     glVertex2i(r->x + r->width, r->y);
1026   }
1027   glEnd();
1028 */
1029   
1030   glEnableClientState (GL_VERTEX_ARRAY);
1031   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1032     
1033   for (unsigned long i = 0; i != nrectangles; ++i)
1034   {
1035     const XRectangle *r = &rectangles[i];
1036     
1037     GLfloat coords[4][2] =
1038     {
1039       {r->x, r->y},
1040       {r->x, r->y + r->height},
1041       {r->x + r->width, r->y + r->height},
1042       {r->x + r->width, r->y}
1043     };
1044     
1045     // TODO: Several rects at once. Maybe even tune for XScreenSaver workloads.
1046     glVertexPointer (2, GL_FLOAT, 0, coords);
1047     jwxyz_assert_gl ();
1048     glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1049     jwxyz_assert_gl ();
1050   }
1051 }
1052
1053
1054 static int
1055 FillPolygon (Display *dpy, Drawable d, GC gc,
1056              XPoint *points, int npoints, int shape, int mode)
1057 {
1058   set_fg_gc(dpy, d, gc);
1059   
1060   // TODO: Re-implement the GLU tesselation functions.
1061
1062   /* Complex: Pedal, and for some reason Attraction, Mountain, Qix, SpeedMine, Starfish
1063    * Nonconvex: Goop, Pacman, Rocks, Speedmine
1064    *
1065    * We currently do Nonconvex with the simple-to-implement ear clipping
1066    * algorithm, but in the future we can replace that with an algorithm
1067    * with slower big-O growth
1068    *
1069    */
1070   
1071   
1072   // TODO: Feed vertices straight to OpenGL for CoordModeOrigin.
1073
1074   if (shape == Convex) {
1075
1076     GLshort *vertices = malloc(npoints * sizeof(GLshort) * 2); // TODO: Oh look, another unchecked malloc.
1077     short v[2] = {0, 0};
1078   
1079     for (unsigned i = 0; i < npoints; i++) {
1080       next_point(v, points[i], mode);
1081       vertices[2 * i] = v[0];
1082       vertices[2 * i + 1] = v[1];
1083     }
1084
1085     glEnableClientState (GL_VERTEX_ARRAY);
1086     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1087
1088     glVertexPointer (2, GL_SHORT, 0, vertices);
1089     glDrawArrays (GL_TRIANGLE_FAN, 0, npoints);
1090
1091     free(vertices);
1092
1093   } else if (shape == Nonconvex) {
1094
1095     // TODO: assert that x,y of first and last point match, as that is assumed
1096
1097     linked_point *root;
1098     root = (linked_point *) malloc( sizeof(linked_point) );
1099     set_points_list(points,npoints,root);
1100     traverse_points_list(root);
1101
1102   } else {
1103     Assert((shape == Convex || shape == Nonconvex),
1104            "XFillPolygon: (TODO) Unimplemented shape");
1105   }
1106
1107   return 0;
1108 }
1109
1110 #define radians(DEG) ((DEG) * M_PI / 180.0)
1111 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1112
1113 static void
1114 arc_xy(GLfloat *p, double cx, double cy, double w2, double h2, double theta)
1115 {
1116   p[0] = cos(theta) * w2 + cx;
1117   p[1] = -sin(theta) * h2 + cy;
1118 }
1119
1120 static unsigned
1121 mod_neg(int a, unsigned b)
1122 {
1123   /* Normal modulus is implementation defined for negative numbers. This is 
1124    * well-defined such that the repeating pattern for a >= 0 is maintained for 
1125    * a < 0. */
1126   return a < 0 ? (b - 1) - (-(a + 1) % b) : a % b;
1127 }
1128
1129 /* TODO: Fill in arcs with line width > 1 */
1130 static int
1131 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1132           unsigned int width, unsigned int height,
1133           int angle1, int angle2, Bool fill_p)
1134 {
1135     int gglw = gc->gcv.line_width;
1136
1137     if (fill_p || gglw <= 1) {
1138         draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1139     }
1140     else {
1141         int w1, w2, h1, h2, gglwh;
1142         w1 = width + gglw;
1143         h1 = height + gglw;
1144         h2 = height - gglw;
1145         w2 = width - gglw;
1146         gglwh = gglw / 2;
1147         int x1 = x - gglwh;
1148         int x2 = x + gglwh;
1149         int y1 = y - gglwh;
1150         int y2 = y + gglwh;
1151         //draw_arc_gl (dpy, d, gc, x, y, width, height, angle1, angle2, fill_p);
1152         draw_arc_gl (dpy, d, gc, x1, y1, w1, h1, angle1, angle2, fill_p);
1153         draw_arc_gl (dpy, d, gc, x2, y2, w2, h2, angle1, angle2, fill_p);
1154     }
1155     return 0;
1156 }
1157
1158
1159 int
1160 draw_arc_gl (Display *dpy, Drawable d, GC gc, int x, int y,
1161           unsigned int width, unsigned int height,
1162           int angle1, int angle2, Bool fill_p)
1163 {
1164   set_fg_gc(dpy, d, gc);
1165
1166   /* Let's say the number of line segments needed to make a convincing circle is
1167      4*sqrt(radius). (But these arcs aren't necessarily circular arcs...) */
1168
1169   double w2 = width * 0.5f, h2 = height * 0.5f;
1170   double a, b; /* Semi-major/minor axes. */
1171   if(w2 > h2) {
1172     a = w2;
1173     b = h2;
1174   } else {
1175     a = h2;
1176     b = w2;
1177   }
1178   
1179   const double two_pi = 2 * M_PI;
1180
1181   double amb = a - b, apb = a + b;
1182   double h = (amb * amb) / (apb * apb);
1183   // TODO: Math cleanup.
1184   double C_approx = M_PI * apb * (1 + 3 * h / (10 + sqrtf(4 - 3 * h)));
1185   double segments_f = 4 * sqrtf(C_approx / (2 * M_PI));
1186
1187   // TODO: Explain how drawing works what with the points of overlapping arcs
1188   // matching up.
1189  
1190 #if 1
1191   unsigned segments_360 = segments_f;
1192   
1193   /* TODO: angle2 == 0. This is a tilted square with CapSquare. */
1194   /* TODO: color, thick lines, CapNotLast for thin lines */
1195   /* TODO: Transformations. */
1196
1197   double segment_angle = two_pi / segments_360;
1198
1199   const unsigned deg64 = 360 * 64;
1200   const double rad_from_deg64 = two_pi / deg64;
1201   
1202   if (angle2 < 0) {
1203     angle1 += angle2;
1204     angle2 = -angle2;
1205   }
1206
1207   angle1 = mod_neg(angle1, deg64); // TODO: Is this OK? Consider negative numbers.
1208
1209   if (angle2 > deg64)
1210     angle2 = deg64; // TODO: Handle circles special.
1211   
1212   double
1213     angle1_f = angle1 * rad_from_deg64,
1214     angle2_f = angle2 * rad_from_deg64;
1215   
1216   if (angle2_f > two_pi) // TODO: Move this up.
1217     angle2_f = two_pi;
1218   
1219   double segment1_angle_part = fmodf(angle1_f, segment_angle);
1220   
1221   unsigned segment1 = ((angle1_f - segment1_angle_part) / segment_angle) + 1.5;
1222
1223   double angle_2r = angle2_f - segment1_angle_part;
1224   unsigned segments = angle_2r / segment_angle;
1225   
1226   GLfloat cx = x + w2, cy = y + h2;
1227
1228   GLfloat *data = malloc((segments + 3) * sizeof(GLfloat) * 2); // TODO: Check result.
1229   
1230   GLfloat *data_ptr = data;
1231   if (fill_p) {
1232     data_ptr[0] = cx;
1233     data_ptr[1] = cy;
1234     data_ptr += 2;
1235   }
1236   
1237   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f);
1238   data_ptr += 2;
1239   
1240   for (unsigned s = 0; s != segments; ++s) {
1241     // TODO: Make sure values of theta for the following arc_xy call are between
1242     // angle1_f and angle1_f + angle2_f.
1243     arc_xy (data_ptr, cx, cy, w2, h2, (segment1 + s) * segment_angle);
1244     data_ptr += 2;
1245   }
1246   
1247   arc_xy (data_ptr, cx, cy, w2, h2, angle1_f + angle2_f);
1248   data_ptr += 2;
1249
1250   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1251   glEnableClientState (GL_VERTEX_ARRAY);
1252   
1253   glVertexPointer (2, GL_FLOAT, 0, data);
1254   glDrawArrays (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP,
1255                 0,
1256                 (GLsizei)((data_ptr - data) / 2));
1257
1258   free(data);
1259   
1260 #endif
1261   
1262 #if 0
1263   unsigned segments = segments_f * (fabs(angle2) / (360 * 64));
1264  
1265   glBegin (fill_p ? GL_TRIANGLE_FAN : GL_LINE_STRIP);
1266   
1267   if (fill_p /* && gc->gcv.arc_mode == ArcPieSlice */)
1268     glVertex2f (cx, cy);
1269   
1270   /* TODO: This should fix the middle points of the arc so that the starting and ending points are OK. */
1271   
1272   float to_radians = 2 * M_PI / (360 * 64);
1273   float theta = angle1 * to_radians, d_theta = angle2 * to_radians / segments;
1274   
1275   for (unsigned s = 0; s != segments + 1; ++s) /* TODO: This is the right number of segments, yes? */
1276   {
1277     glVertex2f(cos(theta) * w2 + cx, -sin(theta) * h2 + cy);
1278     theta += d_theta;
1279   }
1280   
1281   glEnd ();
1282 #endif
1283   
1284   return 0;
1285 }
1286
1287
1288 static XGCValues *
1289 gc_gcv (GC gc)
1290 {
1291   return &gc->gcv;
1292 }
1293
1294
1295 static unsigned int
1296 gc_depth (GC gc)
1297 {
1298   return gc->depth;
1299 }
1300
1301
1302 static GC
1303 CreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1304 {
1305   struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1306   gc->depth = jwxyz_drawable_depth (d);
1307
1308   jwxyz_gcv_defaults (dpy, &gc->gcv, gc->depth);
1309   XChangeGC (dpy, gc, mask, xgcv);
1310   return gc;
1311 }
1312
1313
1314 static int
1315 FreeGC (Display *dpy, GC gc)
1316 {
1317   if (gc->gcv.font)
1318     XUnloadFont (dpy, gc->gcv.font);
1319
1320   // TODO
1321 /*
1322   Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1323
1324   if (gc->gcv.clip_mask) {
1325     XFreePixmap (dpy, gc->gcv.clip_mask);
1326     CGImageRelease (gc->clip_mask);
1327   }
1328 */
1329   free (gc);
1330   return 0;
1331 }
1332
1333
1334 static int
1335 PutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1336           int src_x, int src_y, int dest_x, int dest_y,
1337           unsigned int w, unsigned int h)
1338 {
1339   jwxyz_assert_display (dpy);
1340  
1341   const XRectangle *wr = jwxyz_frame (d);
1342
1343   Assert (gc, "no GC");
1344   Assert ((w < 65535), "improbably large width");
1345   Assert ((h < 65535), "improbably large height");
1346   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
1347   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
1348   Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1349   Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1350
1351   // Clip width and height to the bounds of the Drawable
1352   //
1353   if (dest_x + w > wr->width) {
1354     if (dest_x > wr->width)
1355       return 0;
1356     w = wr->width - dest_x;
1357   }
1358   if (dest_y + h > wr->height) {
1359     if (dest_y > wr->height)
1360       return 0;
1361     h = wr->height - dest_y;
1362   }
1363   if (w <= 0 || h <= 0)
1364     return 0;
1365
1366   // Clip width and height to the bounds of the XImage
1367   //
1368   if (src_x + w > ximage->width) {
1369     if (src_x > ximage->width)
1370       return 0;
1371     w = ximage->width - src_x;
1372   }
1373   if (src_y + h > ximage->height) {
1374     if (src_y > ximage->height)
1375       return 0;
1376     h = ximage->height - src_y;
1377   }
1378   if (w <= 0 || h <= 0)
1379     return 0;
1380
1381   /* Assert (d->win */
1382
1383   if (jwxyz_dumb_drawing_mode(dpy, d, gc, dest_x, dest_y, w, h))
1384     return 0;
1385
1386   jwxyz_gl_flush (dpy);
1387   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1388   jwxyz_gl_set_gc (dpy, gc);
1389
1390   int bpl = ximage->bytes_per_line;
1391   int bpp = ximage->bits_per_pixel;
1392
1393   char *tex_data;
1394   unsigned src_w;
1395   GLint tex_internalformat;
1396   GLenum tex_format, tex_type;
1397
1398   if (bpp == 32) {
1399     tex_data = ximage->data + src_y * bpl + (src_x * 4);
1400
1401     jwxyz_assert_display(dpy);
1402     
1403     /* There probably won't be any hacks that do this, but... */
1404     Assert (!(bpl % 4), "XPutImage: bytes_per_line not divisible by four.");
1405     
1406     tex_internalformat = texture_internalformat(dpy);
1407     tex_format = dpy->pixel_format;
1408     tex_type = gl_pixel_type(dpy);
1409
1410     /* GL_UNPACK_ROW_LENGTH is not allowed to be negative. (sigh) */
1411 # ifndef HAVE_JWZGLES
1412     src_w = w;
1413     glPixelStorei (GL_UNPACK_ROW_LENGTH, src_w);
1414 # else
1415     src_w = bpl / 4;
1416 # endif
1417
1418     // glPixelStorei (GL_UNPACK_ALIGNMENT, 4); // Probably unnecessary.
1419
1420     set_white ();
1421   } else {
1422     Assert (bpp == 1, "expected 1 or 32 bpp");
1423     Assert ((src_x % 8) == 0,
1424             "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1425
1426     const char *src_data = ximage->data + src_y * bpl + (src_x / 8);
1427     unsigned w8 = (w + 7) / 8;
1428
1429     src_w = w8 * 8;
1430
1431     tex_data = malloc(src_w * h);
1432
1433     uint32_t *data_out = (uint32_t *)tex_data;
1434     for(unsigned y = h; y; --y) {
1435       for(unsigned x = 0; x != w8; ++x) {
1436         // TODO: Does big endian work here?
1437         uint8_t byte = src_data[x];
1438         uint32_t word = byte;
1439         word = (word & 0x3) | ((word & 0xc) << 14);
1440         word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1441         data_out[x << 1] = (word << 8) - word;
1442
1443         word = byte >> 4;
1444         word = (word & 0x3) | ((word & 0xc) << 14);
1445         word = (word & 0x00010001) | ((word & 0x00020002) << 7);
1446         data_out[(x << 1) | 1] = (word << 8) - word;
1447       }
1448       src_data += bpl;
1449       data_out += src_w / 4;
1450     }
1451
1452     tex_internalformat = GL_LUMINANCE;
1453     tex_format = GL_LUMINANCE;
1454     tex_type = GL_UNSIGNED_BYTE;
1455
1456     // glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
1457
1458     set_color (dpy, gc->gcv.foreground, gc->depth, gc->gcv.alpha_allowed_p);
1459     // TODO: Deal with the background color.
1460   }
1461
1462 # if 1 // defined HAVE_JWZGLES
1463   // Regular OpenGL uses GL_TEXTURE_RECTANGLE_EXT in place of GL_TEXTURE_2D.
1464   // TODO: Make use of OES_draw_texture.
1465
1466   unsigned tex_w = src_w, tex_h = h;
1467   if (!dpy->gl_texture_npot_p) {
1468     tex_w = to_pow2(tex_w);
1469     tex_h = to_pow2(tex_h);
1470   }
1471
1472   glBindTexture (dpy->gl_texture_target, dpy->rect_texture);
1473
1474   // A fun project: reimplement xshm.c by means of a PBO using
1475   // GL_MAP_UNSYNCHRONIZED_BIT.
1476
1477   // TODO: Would using glTexSubImage2D exclusively be faster?
1478   if (tex_w == src_w && tex_h == h) {
1479     glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1480                   0, tex_format, tex_type, tex_data);
1481   } else {
1482     // TODO: Sampling the last row might be a problem if src_x != 0.
1483     glTexImage2D (dpy->gl_texture_target, 0, tex_internalformat, tex_w, tex_h,
1484                   0, tex_format, tex_type, NULL);
1485     glTexSubImage2D (dpy->gl_texture_target, 0, 0, 0, src_w, h,
1486                      tex_format, tex_type, tex_data);
1487   }
1488
1489   if (bpp == 1)
1490     free(tex_data);
1491
1492   // TODO: This looks a lot like jwxyz_gl_draw_image. Refactor.
1493
1494   // glEnable (dpy->gl_texture_target);
1495   // glColor4f (0.5, 0, 1, 1);
1496   glEnable (dpy->gl_texture_target);
1497   glEnableClientState (GL_VERTEX_ARRAY);
1498   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1499
1500   // TODO: Why are these ever turned on in the first place?
1501   glDisableClientState (GL_COLOR_ARRAY);
1502   glDisableClientState (GL_NORMAL_ARRAY);
1503   // glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1504
1505   GLfloat vertices[4][2] =
1506   {
1507     {dest_x, dest_y},
1508     {dest_x, dest_y + h},
1509     {dest_x + w, dest_y + h},
1510     {dest_x + w, dest_y}
1511   };
1512
1513   GLfloat texcoord_w, texcoord_h;
1514 #  ifndef HAVE_JWZGLES
1515   if (dpy->gl_texture_target == GL_TEXTURE_RECTANGLE_EXT) {
1516     texcoord_w = w;
1517     texcoord_h = h;
1518   } else
1519 #  endif /* HAVE_JWZGLES */
1520   {
1521     texcoord_w = (double)w / tex_w;
1522     texcoord_h = (double)h / tex_h;
1523   }
1524
1525   GLfloat tex_coords[4][2];
1526   tex_coords[0][0] = 0;
1527   tex_coords[0][1] = 0;
1528   tex_coords[1][0] = 0;
1529   tex_coords[1][1] = texcoord_h;
1530   tex_coords[2][0] = texcoord_w;
1531   tex_coords[2][1] = texcoord_h;
1532   tex_coords[3][0] = texcoord_w;
1533   tex_coords[3][1] = 0;
1534
1535   glVertexPointer (2, GL_FLOAT, 0, vertices);
1536   glTexCoordPointer (2, GL_FLOAT, 0, tex_coords);
1537   glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1538
1539 //clear_texture();
1540   glDisable (dpy->gl_texture_target);
1541 # else
1542   glRasterPos2i (dest_x, dest_y);
1543   glPixelZoom (1, -1);
1544   jwxyz_assert_display (dpy);
1545   glDrawPixels (w, h, dpy->pixel_format, gl_pixel_type(dpy), data);
1546 # endif
1547
1548   jwxyz_assert_gl ();
1549
1550   return 0;
1551 }
1552
1553 /* At the moment only XGetImage and get_xshm_image use XGetSubImage. */
1554 /* #### Twang calls XGetImage on the window intending to get a
1555    buffer full of black.  This is returning a buffer full of white
1556    instead of black for some reason. */
1557 static XImage *
1558 GetSubImage (Display *dpy, Drawable d, int x, int y,
1559              unsigned int width, unsigned int height,
1560              unsigned long plane_mask, int format,
1561              XImage *dest_image, int dest_x, int dest_y)
1562 {
1563   Assert ((width  < 65535), "improbably large width");
1564   Assert ((height < 65535), "improbably large height");
1565   Assert ((x < 65535 && x > -65535), "improbably large x");
1566   Assert ((y < 65535 && y > -65535), "improbably large y");
1567
1568   jwxyz_gl_flush (dpy);
1569   jwxyz_bind_drawable (dpy, dpy->main_window, d);
1570   
1571   // TODO: What if this reads off the edge? What is supposed to happen?
1572
1573   {
1574     // In case the caller tries to write off the edge.
1575     int
1576       max_width = dest_image->width - dest_x,
1577       max_height = dest_image->height - dest_y;
1578
1579     if (width > max_width) {
1580       width = max_width;
1581     }
1582     
1583     if (height > max_height) {
1584       height = max_height;
1585     }
1586   }
1587   
1588   Assert (jwxyz_drawable_depth (d) == dest_image->depth, "XGetSubImage: depth mismatch");
1589   
1590   if (dest_image->depth == visual_depth (NULL, NULL)) {
1591     Assert (!(dest_image->bytes_per_line % 4), "XGetSubImage: bytes_per_line not divisible by 4");
1592     unsigned pixels_per_line = dest_image->bytes_per_line / 4;
1593 #ifdef HAVE_JWZGLES
1594     Assert (pixels_per_line == width, "XGetSubImage: (TODO) pixels_per_line != width");
1595 #else
1596     glPixelStorei (GL_PACK_ROW_LENGTH, pixels_per_line);
1597 #endif
1598     glPixelStorei (GL_PACK_ALIGNMENT, 4);
1599     
1600     uint32_t *dest_data = (uint32_t *)dest_image->data + pixels_per_line * dest_y + dest_x;
1601     
1602     glReadPixels (x, jwxyz_frame (d)->height - (y + height), width, height,
1603                   dpy->pixel_format, gl_pixel_type(dpy), dest_data);
1604
1605     /* Flip this upside down. :( */
1606     uint32_t *top = dest_data;
1607     uint32_t *bottom = dest_data + pixels_per_line * (height - 1);
1608     for (unsigned y = height / 2; y; --y) {
1609       for (unsigned x = 0; x != width; ++x) {
1610         uint32_t px = top[x];
1611         top[x] = bottom[x];
1612         bottom[x] = px;
1613       }
1614       top += pixels_per_line;
1615       bottom -= pixels_per_line;
1616     }
1617   } else {
1618
1619     /* TODO: Actually get pixels. */
1620
1621     Assert (!(dest_x % 8), "XGetSubImage: dest_x not byte-aligned");
1622     uint8_t *dest_data =
1623       (uint8_t *)dest_image->data + dest_image->bytes_per_line * dest_y
1624       + dest_x / 8;
1625     for (unsigned y = height / 2; y; --y) {
1626       memset (dest_data, y & 1 ? 0x55 : 0xAA, width / 8);
1627       dest_data += dest_image->bytes_per_line;
1628     }
1629   }
1630
1631   return dest_image;
1632 }
1633
1634
1635 #if 0
1636 static Pixmap
1637 copy_pixmap (Display *dpy, Pixmap p)
1638 {
1639   if (!p) return 0;
1640   Assert (p->type == PIXMAP, "not a pixmap");
1641
1642   Pixmap p2 = 0;
1643
1644   Window root;
1645   int x, y;
1646   unsigned int width, height, border_width, depth;
1647   if (XGetGeometry (dpy, p, &root,
1648                     &x, &y, &width, &height, &border_width, &depth)) {
1649     XGCValues gcv;
1650     gcv.function = GXcopy;
1651     GC gc = XCreateGC (dpy, p, GCFunction, &gcv);
1652     if (gc) {
1653       p2 = XCreatePixmap (dpy, p, width, height, depth);
1654       if (p2)
1655         XCopyArea (dpy, p, p2, gc, 0, 0, width, height, 0, 0);
1656       XFreeGC (dpy, gc);
1657     }
1658   }
1659
1660   Assert (p2, "could not copy pixmap");
1661
1662   return p2;
1663 }
1664 #endif
1665
1666
1667 static int
1668 SetClipMask (Display *dpy, GC gc, Pixmap m)
1669 {
1670   Log ("TODO: No clip masks yet");
1671   /* Protip: Do glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1672      clearing just the stencil buffer in a packed depth/stencil arrangement is
1673      slower than the above. Adreno recommends this, but other GPUs probably
1674      benefit as well.
1675    */
1676   return 0;
1677 }
1678
1679 static int
1680 SetClipOrigin (Display *dpy, GC gc, int x, int y)
1681 {
1682   gc->gcv.clip_x_origin = x;
1683   gc->gcv.clip_y_origin = y;
1684   return 0;
1685 }
1686
1687 void set_points_list(XPoint *points, int npoints, linked_point *root)
1688 {
1689     linked_point *current;  
1690
1691     current = root;
1692     for (int i = 0; i < npoints - 2 ; i++) {
1693         current->x = points[i].x;
1694         current->y = points[i].y;
1695         current->next = (linked_point *) malloc(sizeof(linked_point)); 
1696         current = current->next;
1697     }
1698     current->x = points[npoints-2].x;
1699     current->y = points[npoints-2].y;
1700     current->next = root;
1701 }
1702
1703
1704 double compute_edge_length(linked_point * a, linked_point * b)
1705 {
1706
1707     int xdiff, ydiff, xsq, ysq, added;
1708     double xy_add, edge_length;
1709
1710     xdiff = a->x - b->x;
1711     ydiff = a->y - b->y;
1712     xsq = xdiff * xdiff;
1713     ysq = ydiff * ydiff;
1714     added = xsq + ysq;
1715     xy_add = (double) added;
1716     edge_length = sqrt(xy_add);
1717     return edge_length;
1718 }
1719
1720 double get_angle(double a, double b, double c)
1721 {
1722     double cos_a, i_cos_a;
1723     cos_a = (((b * b) + (c * c)) - (a * a)) / (double) (2.0 * b * c);
1724     i_cos_a = acos(cos_a);
1725     return i_cos_a;
1726 }
1727
1728
1729 Bool is_same_slope(linked_point * a)
1730 {
1731
1732     int abx, bcx, aby, bcy, aa, bb;
1733     linked_point *b;
1734     linked_point *c;
1735
1736     b = a->next;
1737     c = b->next;
1738
1739     // test if slopes are indefinite for both line segments
1740     if (a->x == b->x) {
1741         return b->x == c->x;
1742     } else if (b->x == c->x) {
1743         return False;   // false, as ax/bx is not indefinite
1744     }
1745
1746     abx = a->x - b->x;
1747     bcx = b->x - c->x;
1748     aby = a->y - b->y;
1749     bcy = b->y - c->y;
1750     aa = abx * bcy;
1751     bb = bcx * aby;
1752
1753     return aa == bb;
1754 }
1755
1756 void draw_three_vertices(linked_point * a, Bool triangle)
1757 {
1758
1759     linked_point *b;
1760     linked_point *c;
1761     GLenum drawType;
1762
1763     b = a->next;
1764     c = b->next;
1765
1766     GLfloat vertices[3][2] = {
1767         {a->x, a->y},
1768         {b->x, b->y},
1769         {c->x, c->y}
1770     };
1771
1772     if (triangle) {
1773         drawType = GL_TRIANGLES;
1774     } else {
1775         drawType = GL_LINES;
1776     }
1777
1778     glEnableClientState(GL_VERTEX_ARRAY);
1779     glVertexPointer(2, GL_FLOAT, 0, vertices);
1780     glDrawArrays(drawType, 0, 3);
1781
1782     free(b);  // cut midpoint off from remaining polygon vertex list
1783     a->next = c;
1784 }
1785
1786
1787 Bool is_an_ear(linked_point * a)
1788 {
1789     double edge_ab, edge_bc, edge_ac;
1790     double angle_a, angle_b, angle_c;
1791     double my_pi;
1792     linked_point *b, *c;
1793
1794     b = a->next;
1795     c = b->next;
1796     my_pi = (double) M_PI;
1797
1798     edge_ab = compute_edge_length(a, b);
1799     edge_bc = compute_edge_length(b, c);
1800     edge_ac = compute_edge_length(a, c);
1801     angle_a = get_angle(edge_bc, edge_ab, edge_ac);
1802     angle_b = get_angle(edge_ac, edge_ab, edge_bc);
1803     angle_c = get_angle(edge_ab, edge_ac, edge_bc);
1804
1805     return angle_a < my_pi && angle_b < my_pi && angle_c < my_pi;
1806 }
1807
1808
1809 Bool is_three_point_loop(linked_point * head)
1810 {
1811     return head->x == head->next->next->next->x
1812         && head->y == head->next->next->next->y;
1813 }
1814
1815
1816 void traverse_points_list(linked_point * root)
1817 {
1818     linked_point *head;
1819     head = root;
1820
1821     while (!is_three_point_loop(head)) {
1822         if (is_an_ear(head)) {
1823             draw_three_vertices(head, True);
1824         } else if (is_same_slope(head)) {
1825             draw_three_vertices(head, False);
1826         } else {
1827             head = head->next;
1828         }
1829     }
1830
1831     // handle final three vertices in polygon
1832     if (is_an_ear(head)) {
1833         draw_three_vertices(head, True);
1834     } else if (is_same_slope(head)) {
1835         draw_three_vertices(head, False);
1836     } else {
1837         free(head->next->next);
1838         free(head->next);
1839         free(head);
1840         Assert (False, "traverse_points_list: unknown configuration");
1841     }
1842
1843     free(head->next);
1844     free(head);
1845 }
1846
1847
1848 const struct jwxyz_vtbl gl_vtbl = {
1849   root,
1850   visual,
1851   display_sources_data,
1852
1853   window_background,
1854   draw_arc,
1855   fill_rects,
1856   gc_gcv,
1857   gc_depth,
1858   jwxyz_draw_string,
1859
1860   jwxyz_gl_copy_area,
1861
1862   DrawPoints,
1863   DrawSegments,
1864   CreateGC,
1865   FreeGC,
1866   ClearWindow,
1867   SetClipMask,
1868   SetClipOrigin,
1869   FillPolygon,
1870   DrawLines,
1871   PutImage,
1872   GetSubImage
1873 };
1874
1875 #endif /* JWXYZ_GL -- entire file */