1 /* xscreensaver, Copyright (c) 1991-2017 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
14 But it's a bunch of function definitions that bear some resemblance to
15 Xlib and that do Cocoa-ish or OpenGL-ish things that bear some resemblance
16 to the things that Xlib might have done.
18 This code is used by both the original jwxyz.m and the new jwxyz-gl.c.
22 #import "jwxyz-cocoa.h"
28 # import <OpenGLES/ES1/gl.h>
29 # import <OpenGLES/ES1/glext.h>
30 # define NSOpenGLContext EAGLContext
31 # define NSFont UIFont
33 # define NSFontTraitMask UIFontDescriptorSymbolicTraits
34 // The values for the flags for NSFontTraitMask and
35 // UIFontDescriptorSymbolicTraits match up, not that it really matters here.
36 # define NSBoldFontMask UIFontDescriptorTraitBold
37 # define NSFixedPitchFontMask UIFontDescriptorTraitMonoSpace
38 # define NSItalicFontMask UIFontDescriptorTraitItalic
41 #import <CoreText/CTLine.h>
42 #import <CoreText/CTRun.h>
44 /* OS X/iOS-specific JWXYZ implementation. */
47 jwxyz_logv (Bool error, const char *fmt, va_list args)
49 vfprintf (stderr, fmt, args);
53 /* Instead of calling abort(), throw a real exception, so that
54 XScreenSaverView can catch it and display a dialog.
57 jwxyz_abort (const char *fmt, ...)
66 vsprintf (s, fmt, args);
69 [[NSException exceptionWithName: NSInternalInconsistencyException
70 reason: [NSString stringWithCString: s
71 encoding:NSUTF8StringEncoding]
75 abort(); // not reached
80 jwxyz_frame (Drawable d)
87 jwxyz_drawable_depth (Drawable d)
89 return (d->type == WINDOW
90 ? visual_depth (NULL, NULL)
96 jwxyz_scale (Window main_window)
101 /* Since iOS screens are physically smaller than desktop screens, scale up
102 the fonts to make them more readable.
104 Note that X11 apps on iOS also have the backbuffer sized in points
105 instead of pixels, resulting in an effective X11 screen size of 768x1024
106 or so, even if the display has significantly higher resolution. That is
107 unrelated to this hack, which is really about DPI.
109 scale = main_window->window.view.hackedContentScaleFactor;
110 if (scale < 1) // iPad Pro magnifies the backbuffer by 3x, which makes text
111 scale = 1; // excessively blurry in BSOD.
118 /* Font metric terminology, as used by X11:
120 "lbearing" is the distance from the logical origin to the leftmost pixel.
121 If a character's ink extends to the left of the origin, it is negative.
123 "rbearing" is the distance from the logical origin to the rightmost pixel.
125 "descent" is the distance from the logical origin to the bottommost pixel.
126 For characters with descenders, it is positive. For superscripts, it
129 "ascent" is the distance from the logical origin to the topmost pixel.
130 It is the number of pixels above the baseline.
132 "width" is the distance from the logical origin to the position where
133 the logical origin of the next character should be placed.
135 If "rbearing" is greater than "width", then this character overlaps the
136 following character. If smaller, then there is trailing blank space.
139 utf8_metrics (Display *dpy, NSFont *nsfont, NSString *nsstr, XCharStruct *cs)
141 // Returns the metrics of the multi-character, single-line UTF8 string.
143 Drawable d = XRootWindow (dpy, 0);
145 CGContextRef cgc = d->cgc;
147 [NSDictionary dictionaryWithObjectsAndKeys:
148 nsfont, NSFontAttributeName,
150 NSAttributedString *astr = [[NSAttributedString alloc]
153 CTLineRef ctline = CTLineCreateWithAttributedString (
154 (__bridge CFAttributedStringRef) astr);
155 CGContextSetTextPosition (cgc, 0, 0);
156 CGContextSetShouldAntialias (cgc, True); // #### Guess?
158 memset (cs, 0, sizeof(*cs));
160 // "CTRun represents set of consecutive glyphs sharing the same
161 // attributes and direction".
163 // We also get multiple runs any time font subsitution happens:
164 // E.g., if the current font is Verdana-Bold, a ← character
165 // in the NSString will actually be rendered in LucidaGrande-Bold.
168 for (id runid in (NSArray *)CTLineGetGlyphRuns(ctline)) {
169 CTRunRef run = (CTRunRef) runid;
171 CGRect bbox = CTRunGetImageBounds (run, cgc, r);
172 CGFloat ascent, descent, leading;
173 CGFloat advancement =
174 CTRunGetTypographicBounds (run, r, &ascent, &descent, &leading);
177 // Only necessary for when LCD smoothing is enabled, which iOS doesn't do.
178 bbox.origin.x -= 2.0/3.0;
179 bbox.size.width += 4.0/3.0;
180 bbox.size.height += 1.0/2.0;
183 // Create the metrics for this run:
185 cc.ascent = ceil (bbox.origin.y + bbox.size.height);
186 cc.descent = ceil (-bbox.origin.y);
187 cc.lbearing = floor (bbox.origin.x);
188 cc.rbearing = ceil (bbox.origin.x + bbox.size.width);
189 cc.width = floor (advancement + 0.5);
191 // Add those metrics into the cumulative metrics:
196 cs->ascent = MAX (cs->ascent, cc.ascent);
197 cs->descent = MAX (cs->descent, cc.descent);
198 cs->lbearing = MIN (cs->lbearing, cs->width + cc.lbearing);
199 cs->rbearing = MAX (cs->rbearing, cs->width + cc.rbearing);
200 cs->width = MAX (cs->width, cs->width + cc.width);
203 // Why no y? What about vertical text?
204 // XCharStruct doesn't encapsulate that but XGlyphInfo does.
215 font_family_members (NSString *family_name)
218 return [[NSFontManager sharedFontManager]
219 availableMembersOfFontFamily:family_name];
221 return [UIFont fontNamesForFamilyName:family_name];
227 jwxyz_default_font_family (int require)
229 return require & JWXYZ_STYLE_MONOSPACE ? "Courier" : "Verdana";
233 static NSFontTraitMask
234 nsfonttraitmask_for (int font_style)
236 NSFontTraitMask result = 0;
237 if (font_style & JWXYZ_STYLE_BOLD)
238 result |= NSBoldFontMask;
239 if (font_style & JWXYZ_STYLE_ITALIC)
240 result |= NSItalicFontMask;
241 if (font_style & JWXYZ_STYLE_MONOSPACE)
242 result |= NSFixedPitchFontMask;
248 try_font (NSFontTraitMask traits, NSFontTraitMask mask,
249 NSString *family_name, float size)
251 NSArray *family_members = font_family_members (family_name);
252 if (!family_members.count) {
253 family_members = font_family_members (
254 [NSString stringWithUTF8String:jwxyz_default_font_family (
255 traits & NSFixedPitchFontMask ? JWXYZ_STYLE_MONOSPACE : 0)]);
259 for (unsigned k = 0; k != family_members.count; ++k) {
261 NSArray *member = [family_members objectAtIndex:k];
262 NSFontTraitMask font_mask =
263 [(NSNumber *)[member objectAtIndex:3] unsignedIntValue];
265 if ((font_mask & mask) == traits) {
267 NSString *name = [member objectAtIndex:0];
268 NSFont *f = [NSFont fontWithName:name size:size];
272 /* Don't use this font if it (probably) doesn't include ASCII characters.
274 NSStringEncoding enc = [f mostCompatibleStringEncoding];
275 if (! (enc == NSUTF8StringEncoding ||
276 enc == NSISOLatin1StringEncoding ||
277 enc == NSNonLossyASCIIStringEncoding ||
278 enc == NSISOLatin2StringEncoding ||
279 enc == NSUnicodeStringEncoding ||
280 enc == NSWindowsCP1250StringEncoding ||
281 enc == NSWindowsCP1252StringEncoding ||
282 enc == NSMacOSRomanStringEncoding)) {
283 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
286 // NSLog(@"using \"%@\": %d", name, enc);
293 // This trick needs iOS 3.1, see "Using SDK-Based Development".
294 Class has_font_descriptor = [UIFontDescriptor class];
296 for (NSString *fn in family_members) {
298 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
301 NSFontTraitMask font_mask;
302 if (has_font_descriptor) {
303 // This only works on iOS 7 and later.
304 font_mask = [[UIFontDescriptor
305 fontDescriptorWithFontAttributes:
306 @{UIFontDescriptorNameAttribute:fn}]
311 font_mask |= NSBoldFontMask;
312 if (MATCH(@"Italic") || MATCH(@"Oblique"))
313 font_mask |= NSItalicFontMask;
314 if (MATCH(@"Courier"))
315 font_mask |= NSFixedPitchFontMask;
318 if ((font_mask & mask) == traits) {
320 /* Check if it can do ASCII. No good way to accomplish this!
321 These are fonts present in iPhone Simulator as of June 2012
322 that don't include ASCII.
324 if (MATCH(@"AppleGothic") || // Korean
325 MATCH(@"Dingbats") || // Dingbats
326 MATCH(@"Emoji") || // Emoticons
327 MATCH(@"Geeza") || // Arabic
328 MATCH(@"Hebrew") || // Hebrew
329 MATCH(@"HiraKaku") || // Japanese
330 MATCH(@"HiraMin") || // Japanese
331 MATCH(@"Kailasa") || // Tibetan
332 MATCH(@"Ornaments") || // Dingbats
333 MATCH(@"STHeiti") // Chinese
337 return [UIFont fontWithName:fn size:size];
347 /* Returns a random font in the given size and face.
350 random_font (NSFontTraitMask traits, NSFontTraitMask mask, float size)
354 // Providing Unbold or Unitalic in the mask for availableFontNamesWithTraits
355 // returns an empty list, at least on a system with default fonts only.
356 NSArray *families = [[NSFontManager sharedFontManager]
357 availableFontFamilies];
358 if (!families) return 0;
360 NSArray *families = [UIFont familyNames];
362 // There are many dups in the families array -- uniquify it.
364 NSArray *sorted_families =
365 [families sortedArrayUsingSelector:@selector(compare:)];
366 NSMutableArray *new_families =
367 [NSMutableArray arrayWithCapacity:sorted_families.count];
369 NSString *prev_family = @"";
370 for (NSString *family in sorted_families) {
371 if ([family compare:prev_family])
372 [new_families addObject:family];
373 prev_family = family;
376 families = new_families;
378 # endif // USE_IPHONE
380 long n = [families count];
381 if (n <= 0) return 0;
384 for (j = 0; j < n; j++) {
385 int i = random() % n;
386 NSString *family_name = [families objectAtIndex:i];
388 NSFont *result = try_font (traits, mask, family_name, size);
393 // None of the fonts support ASCII?
398 jwxyz_load_native_font (Window main_window, int traits_jwxyz, int mask_jwxyz,
399 const char *font_name_ptr, size_t font_name_length,
400 int font_name_type, float size,
401 char **family_name_ret,
402 int *ascent_ret, int *descent_ret)
404 NSFont *nsfont = NULL;
407 traits = nsfonttraitmask_for (traits_jwxyz),
408 mask = nsfonttraitmask_for (mask_jwxyz);
410 NSString *font_name = font_name_type != JWXYZ_FONT_RANDOM ?
411 [[NSString alloc] initWithBytes:font_name_ptr
412 length:font_name_length
413 encoding:NSUTF8StringEncoding] :
416 size *= jwxyz_scale (main_window);
418 if (font_name_type == JWXYZ_FONT_RANDOM) {
420 nsfont = random_font (traits, mask, size);
423 } else if (font_name_type == JWXYZ_FONT_FACE) {
425 nsfont = [NSFont fontWithName:font_name size:size];
427 } else if (font_name_type == JWXYZ_FONT_FAMILY) {
429 Assert (size > 0, "zero font size");
432 nsfont = try_font (traits, mask, font_name, size);
434 // if that didn't work, turn off attibutes until it does
435 // (e.g., there is no "Monaco-Bold".)
437 if (!nsfont && (mask & NSItalicFontMask)) {
438 traits &= ~NSItalicFontMask;
439 mask &= ~NSItalicFontMask;
440 nsfont = try_font (traits, mask, font_name, size);
442 if (!nsfont && (mask & NSBoldFontMask)) {
443 traits &= ~NSBoldFontMask;
444 mask &= ~NSBoldFontMask;
445 nsfont = try_font (traits, mask, font_name, size);
447 if (!nsfont && (mask & NSFixedPitchFontMask)) {
448 traits &= ~NSFixedPitchFontMask;
449 mask &= ~NSFixedPitchFontMask;
450 nsfont = try_font (traits, mask, font_name, size);
457 *family_name_ret = strdup (nsfont.familyName.UTF8String);
459 CFRetain (nsfont); // needed for garbage collection?
461 *ascent_ret = ceil ([nsfont ascender]);
462 *descent_ret = -floor ([nsfont descender]);
464 Assert([nsfont fontName], "broken NSFont in fid");
472 jwxyz_release_native_font (Display *dpy, void *native_font)
474 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
475 // crashes in [NSFont ascender] <- query_font, and it seems to go away
476 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
477 // They're probably not very big...
479 // [fid->nsfont release];
480 // CFRelease (fid->nsfont);
484 // Given a UTF8 string, return an NSString. Bogus UTF8 characters are ignored.
485 // We have to do this because stringWithCString returns NULL if there are
486 // any invalid characters at all.
489 sanitize_utf8 (const char *in, size_t in_len, Bool *latin1_pP)
491 size_t out_len = in_len * 4; // length of string might increase
492 char *s2 = (char *) malloc (out_len);
494 const char *in_end = in + in_len;
495 const char *out_end = out + out_len;
496 Bool latin1_p = True;
501 long L1 = utf8_decode ((const unsigned char *) in, in_end - in, &uc);
502 long L2 = utf8_encode (uc, out, out_end - out);
505 if (uc > 255) latin1_p = False;
509 [NSString stringWithCString:s2 encoding:NSUTF8StringEncoding];
511 if (latin1_pP) *latin1_pP = latin1_p;
512 return (nsstr ? nsstr : @"");
517 nsstring_from(const char *str, size_t len, int utf8_p)
520 NSString *nsstr = utf8_p ?
521 sanitize_utf8 (str, len, &latin1_p) :
522 [[[NSString alloc] initWithBytes:str
524 encoding:NSISOLatin1StringEncoding]
530 jwxyz_render_text (Display *dpy, void *native_font,
531 const char *str, size_t len, int utf8_p,
532 XCharStruct *cs_ret, char **pixmap_ret)
534 utf8_metrics (dpy, (NSFont *)native_font, nsstring_from (str, len, utf8_p),
537 Assert (!pixmap_ret, "TODO");
542 jwxyz_get_pos (Window w, XPoint *xvpos, XPoint *xp)
550 xp->x = w->window.last_mouse_x;
551 xp->y = w->window.last_mouse_y;
554 # else // !USE_IPHONE
556 NSWindow *nsw = [w->window.view window];
558 // get bottom left of window on screen, from bottom left
560 # if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6)
561 NSRect rr1 = [w->window.view convertRect: NSMakeRect(0,0,0,0) toView:nil];
562 NSRect rr2 = [nsw convertRectToScreen: rr1];
563 NSPoint wpos = NSMakePoint (rr2.origin.x - rr1.origin.x,
564 rr2.origin.y - rr1.origin.y);
566 // deprecated as of 10.7
567 NSPoint wpos = [nsw convertBaseToScreen: NSMakePoint(0,0)];
571 // get bottom left of view on window, from bottom left
574 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
576 // get bottom left of view on screen, from bottom left
580 // get top left of view on screen, from bottom left
581 vpos.y += w->frame.height;
583 // get top left of view on screen, from top left
584 NSArray *screens = [NSScreen screens];
585 NSScreen *screen = (screens && [screens count] > 0
586 ? [screens objectAtIndex:0]
587 : [NSScreen mainScreen]);
588 NSRect srect = [screen frame];
589 vpos.y = srect.size.height - vpos.y;
595 // get the mouse position on window, from bottom left
596 NSEvent *e = [NSApp currentEvent];
597 NSPoint p = [e locationInWindow];
599 // get mouse position on screen, from bottom left
603 // get mouse position on screen, from top left
604 p.y = srect.size.height - p.y;
610 # endif // !USE_IPHONE
617 check_framebuffer_status (void)
619 int err = glCheckFramebufferStatusOES (GL_FRAMEBUFFER_OES);
621 case GL_FRAMEBUFFER_COMPLETE_OES:
623 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES:
624 Assert (0, "framebuffer incomplete attachment");
626 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES:
627 Assert (0, "framebuffer incomplete missing attachment");
629 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES:
630 Assert (0, "framebuffer incomplete dimensions");
632 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES:
633 Assert (0, "framebuffer incomplete formats");
635 case GL_FRAMEBUFFER_UNSUPPORTED_OES:
636 Assert (0, "framebuffer unsupported");
639 case GL_FRAMEBUFFER_UNDEFINED:
640 Assert (0, "framebuffer undefined");
642 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
643 Assert (0, "framebuffer incomplete draw buffer");
645 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
646 Assert (0, "framebuffer incomplete read buffer");
648 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
649 Assert (0, "framebuffer incomplete multisample");
651 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
652 Assert (0, "framebuffer incomplete layer targets");
656 NSCAssert (0, @"framebuffer incomplete, unknown error 0x%04X", err);
663 create_framebuffer (GLuint *gl_framebuffer, GLuint *gl_renderbuffer)
665 glGenFramebuffersOES (1, gl_framebuffer);
666 glBindFramebufferOES (GL_FRAMEBUFFER_OES, *gl_framebuffer);
668 glGenRenderbuffersOES (1, gl_renderbuffer);
669 glBindRenderbufferOES (GL_RENDERBUFFER_OES, *gl_renderbuffer);
675 #if defined JWXYZ_QUARTZ
677 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
680 push_bg_gc (Display *dpy, Drawable d, GC gc, Bool fill_p)
682 XGCValues *gcv = jwxyz_gc_gcv (gc);
683 push_color_gc (dpy, d, gc, gcv->background, gcv->antialias_p, fill_p);
688 jwxyz_copy_area (Display *dpy, Drawable src, Drawable dst, GC gc,
689 int src_x, int src_y,
690 unsigned int width, unsigned int height,
691 int dst_x, int dst_y)
693 XRectangle src_frame = src->frame, dst_frame = dst->frame;
694 XGCValues *gcv = jwxyz_gc_gcv (gc);
696 BOOL mask_p = src->type == PIXMAP && src->pixmap.depth == 1;
699 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
700 going on with clipping masks or depths or anything, optimize it by
701 just doing a memcpy instead of going through a CGI.
703 if (gcv->function == GXcopy &&
705 jwxyz_drawable_depth (src) == jwxyz_drawable_depth (dst)) {
707 Assert(!src_frame.x &&
711 "unexpected non-zero origin");
713 ptrdiff_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
714 ptrdiff_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
715 char *src_data = seek_xy (CGBitmapContextGetData (src->cgc), src_pitch,
717 char *dst_data = seek_xy (CGBitmapContextGetData (dst->cgc), dst_pitch,
720 size_t bytes = width * 4;
722 if (src == dst && dst_y > src_y) {
723 // Copy upwards if the areas might overlap.
724 src_data += src_pitch * (height - 1);
725 dst_data += dst_pitch * (height - 1);
726 src_pitch = -src_pitch;
727 dst_pitch = -dst_pitch;
731 // memcpy is an alias for memmove on OS X.
732 memmove(dst_data, src_data, bytes);
733 src_data += src_pitch;
734 dst_data += dst_pitch;
739 src_rect = CGRectMake(src_x, src_y, width, height),
740 dst_rect = CGRectMake(dst_x, dst_y, width, height);
742 src_rect.origin = map_point (src, src_rect.origin.x,
743 src_rect.origin.y + src_rect.size.height);
744 dst_rect.origin = map_point (dst, dst_rect.origin.x,
745 dst_rect.origin.y + src_rect.size.height);
747 NSObject *releaseme = 0;
749 BOOL free_cgi_p = NO;
751 // We must first copy the bits to an intermediary CGImage object, then
752 // copy that to the destination drawable's CGContext.
754 // First we get a CGImage out of the pixmap CGContext -- it's the whole
755 // pixmap, but it presumably shares the data pointer instead of copying
756 // it. We then cache that CGImage it inside the Pixmap object. Note:
757 // invalidate_drawable_cache() must be called to discard this any time a
758 // modification is made to the pixmap, or we'll end up re-using old bits.
761 src->cgi = CGBitmapContextCreateImage (src->cgc);
764 // if doing a sub-rect, trim it down.
765 if (src_rect.origin.x != src_frame.x ||
766 src_rect.origin.y != src_frame.y ||
767 src_rect.size.width != src_frame.width ||
768 src_rect.size.height != src_frame.height) {
769 // #### I don't understand why this is needed...
770 src_rect.origin.y = (src_frame.height -
771 src_rect.size.height - src_rect.origin.y);
772 // This does not copy image data, so it should be fast.
773 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
777 CGContextRef cgc = dst->cgc;
779 if (mask_p) { // src depth == 1
781 push_bg_gc (dpy, dst, gc, YES);
783 // fill the destination rectangle with solid background...
784 CGContextFillRect (cgc, dst_rect);
786 Assert (cgc, "no CGC with 1-bit XCopyArea");
788 // then fill in a solid rectangle of the fg color, using the image as an
789 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
790 set_color (dpy, cgc, gcv->foreground, jwxyz_gc_depth (gc),
791 gcv->alpha_allowed_p, YES);
792 CGContextClipToMask (cgc, dst_rect, cgi);
793 CGContextFillRect (cgc, dst_rect);
797 } else { // src depth > 1
801 // copy the CGImage onto the destination CGContext
802 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
803 CGContextDrawImage (cgc, dst_rect, cgi);
808 if (free_cgi_p) CGImageRelease (cgi);
810 if (releaseme) [releaseme release];
813 invalidate_drawable_cache (dst);
816 #elif defined JWXYZ_GL
818 /* Warning! The JWXYZ_GL code here is experimental and provisional and not at
819 all ready for prime time. Please be careful.
823 jwxyz_copy_area (Display *dpy, Drawable src, Drawable dst, GC gc,
824 int src_x, int src_y,
825 unsigned int width, unsigned int height,
826 int dst_x, int dst_y)
829 - glCopyPixels if src == dst
830 - Pixel buffer copying
831 - APPLE_framebuffer_multisample has ResolveMultisampleFramebufferAPPLE,
832 which is like a blit.
835 /* Strange and ugly flickering when going the glCopyTexImage2D route on
836 OS X. (Early 2009 Mac mini, OS X 10.10)
840 /* TODO: This might not still work. */
841 jwxyz_bind_drawable (dpy, dpy->main_window, src);
842 jwxyz_gl_copy_area_read_tex_image (dpy, jwxyz_frame (src)->height,
843 src_x, src_y, width, height, dst_x, dst_y);
844 jwxyz_bind_drawable (dpy, dpy->main_window, dst);
845 jwxyz_gl_copy_area_write_tex_image (dpy, gc, src_x, src_y,
846 width, height, dst_x, dst_y);
847 # else // !USE_IPHONE
848 jwxyz_gl_copy_area_read_pixels (dpy, src, dst, gc,
849 src_x, src_y, width, height, dst_x, dst_y);
850 # endif // !USE_IPHONE
858 // This is like check_gl_error, except this happens for debug builds only.
860 if([NSOpenGLContext currentContext])
862 // glFinish here drops FPS into the toilet. It might need to be on if
863 // something goes wrong.
865 GLenum error = glGetError();
866 Assert (!error, "jwxyz_assert_gl: OpenGL error");
868 #endif // !__OPTIMIZE__
872 jwxyz_assert_drawable (Window main_window, Drawable d)
874 #if !defined USE_IPHONE && !defined __OPTIMIZE__
875 XScreenSaverView *view = main_window->window.view;
876 NSOpenGLContext *ogl_ctx = [view oglContext];
878 if (d->type == WINDOW) {
879 Assert([ogl_ctx view] == view,
880 "jwxyz_assert_display: ogl_ctx view not set!");
884 /* Assert([d->ctx isKindOfClass:[NSOpenGLContext class]], "Not a context."); */
885 Class c = [ogl_ctx class];
886 Assert([c isSubclassOfClass:[NSOpenGLContext class]], "Not a context.");
887 // [d->ctx makeCurrentContext];
889 @catch (NSException *exception) {
890 perror([[exception reason] UTF8String]);
893 #endif // !USE_IPHONE && !__OPTIMIZE__
898 jwxyz_bind_drawable (Window main_window, Drawable d)
900 /* Windows and Pixmaps need to use different contexts with OpenGL
901 screenhacks, because an OpenGL screenhack sets state in an arbitrary
902 fashion, but jwxyz-gl.c has its own ideas w.r.t. OpenGL state.
904 On iOS, all pixmaps can use the same context with different FBOs. Which
908 /* OpenGL screenhacks in general won't be drawing on the Window, but they
909 can and will draw on a Pixmap -- but an OpenGL call following an Xlib
910 call won't be able to fix the fact that it's drawing offscreen.
913 /* EXT_direct_state_access might be appropriate, but it's apparently not
914 available on Mac OS X.
917 // jwxyz_assert_display (dpy);
918 jwxyz_assert_drawable (main_window, main_window);
920 jwxyz_assert_drawable (main_window, d);
922 #if defined USE_IPHONE && !defined __OPTIMIZE__
923 Drawable current_drawable = main_window->window.current_drawable;
924 Assert (!current_drawable
925 || current_drawable->ogl_ctx == [EAGLContext currentContext],
926 "bind_drawable: Wrong context.");
927 if (current_drawable) {
929 glGetIntegerv (GL_FRAMEBUFFER_BINDING_OES, &framebuffer);
930 Assert (framebuffer == current_drawable->gl_framebuffer,
931 "bind_drawable: Wrong framebuffer.");
935 if (main_window->window.current_drawable != d) {
936 main_window->window.current_drawable = d;
938 /* Doing this repeatedly is probably not OK performance-wise. Probably. */
940 [d->ogl_ctx makeCurrentContext];
942 [EAGLContext setCurrentContext:d->ogl_ctx];
943 glBindFramebufferOES(GL_FRAMEBUFFER_OES, d->gl_framebuffer);
944 if (d->type == PIXMAP) {
945 glViewport (0, 0, d->frame.width, d->frame.height);
946 jwxyz_set_matrices (d->frame.width, d->frame.height);
956 XCreatePixmap (Display *dpy, Drawable d,
957 unsigned int width, unsigned int height, unsigned int depth)
959 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
961 p->frame.width = width;
962 p->frame.height = height;
963 p->pixmap.depth = depth;
965 Assert (depth == 1 || depth == 32, "XCreatePixmap: depth must be 32");
967 /* TODO: If Pixel buffers are not supported, do something about it. */
968 Window w = XRootWindow (dpy, 0);
972 p->ogl_ctx = [[NSOpenGLContext alloc]
973 initWithFormat:w->window.pixfmt
974 shareContext:w->ogl_ctx];
975 CFRetain (p->ogl_ctx);
977 [p->ogl_ctx makeCurrentContext]; // This is indeed necessary.
979 p->pixmap.gl_pbuffer = [[NSOpenGLPixelBuffer alloc]
980 /* TODO: Only if there are rectangluar textures. */
981 initWithTextureTarget:GL_TEXTURE_RECTANGLE_EXT
982 /* TODO: Make sure GL_RGBA isn't better. */
983 textureInternalFormat:GL_RGB
984 textureMaxMipMapLevel:0
987 CFRetain (p->pixmap.gl_pbuffer);
989 [p->ogl_ctx setPixelBuffer:p->pixmap.gl_pbuffer
992 currentVirtualScreen:w->window.virtual_screen];
996 p->ogl_ctx = w->window.ogl_ctx_pixmap;
998 [EAGLContext setCurrentContext:p->ogl_ctx];
999 create_framebuffer (&p->gl_framebuffer, &p->gl_renderbuffer);
1001 glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_RGBA8_OES, width, height);
1002 glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
1003 GL_RENDERBUFFER_OES, p->gl_renderbuffer);
1005 check_framebuffer_status ();
1007 glBindFramebufferOES(GL_FRAMEBUFFER_OES, p->gl_framebuffer);
1009 # endif // USE_IPHONE
1011 w->window.current_drawable = p;
1012 glViewport (0, 0, width, height);
1013 jwxyz_set_matrices (width, height);
1015 # ifndef __OPTIMIZE__
1016 glClearColor (frand(1), frand(1), frand(1), 0);
1017 glClear (GL_COLOR_BUFFER_BIT);
1024 XFreePixmap (Display *d, Pixmap p)
1026 Assert (p && p->type == PIXMAP, "not a pixmap");
1028 Window w = RootWindow (d, 0);
1031 CFRelease (p->ogl_ctx);
1032 [p->ogl_ctx release];
1034 CFRelease (p->pixmap.gl_pbuffer);
1035 [p->pixmap.gl_pbuffer release];
1036 # else // USE_IPHONE
1037 glDeleteRenderbuffersOES (1, &p->gl_renderbuffer);
1038 glDeleteFramebuffersOES (1, &p->gl_framebuffer);
1039 # endif // USE_IPHONE
1041 if (w->window.current_drawable == p) {
1042 w->window.current_drawable = NULL;