1 /* xscreensaver, Copyright (c) 1991-2013 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 things that bear some resemblance to the
16 things that Xlib might have done.
24 # import <UIKit/UIKit.h>
25 # import <UIKit/UIScreen.h>
26 # import <QuartzCore/QuartzCore.h>
27 # import <CoreText/CTFont.h>
28 # define NSView UIView
29 # define NSRect CGRect
30 # define NSPoint CGPoint
31 # define NSSize CGSize
32 # define NSColor UIColor
33 # define NSImage UIImage
34 # define NSEvent UIEvent
35 # define NSFont UIFont
36 # define NSGlyph CGGlyph
37 # define NSWindow UIWindow
38 # define NSMakeSize CGSizeMake
39 # define NSBezierPath UIBezierPath
41 # import <Cocoa/Cocoa.h>
45 #import "jwxyz-timers.h"
48 # define USE_BACKBUFFER /* must be in sync with XScreenSaverView.h */
51 #define Assert(C,S) do { if (!(C)) jwxyz_abort ("%s",(S)); } while(0)
55 # define MAX(a,b) ((a)>(b)?(a):(b))
56 # define MIN(a,b) ((a)<(b)?(a):(b))
59 struct jwxyz_Drawable {
60 enum { WINDOW, PIXMAP } type;
67 unsigned long background;
68 int last_mouse_x, last_mouse_y;
72 void *cgc_buffer; // the bits to which CGContextRef renders
77 struct jwxyz_Display {
80 struct jwxyz_sources_data *timers_data;
83 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
84 This can change if the window is dragged to
85 a different screen. */
88 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
89 our images with this to avoid translation
101 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
107 float size; // points
109 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
110 // But we need the metrics on both of them, so they go here.
115 /* Instead of calling abort(), throw a real exception, so that
116 XScreenSaverView can catch it and display a dialog.
119 jwxyz_abort (const char *fmt, ...)
127 va_start (args, fmt);
128 vsprintf (s, fmt, args);
131 [[NSException exceptionWithName: NSInternalInconsistencyException
132 reason: [NSString stringWithCString: s
133 encoding:NSUTF8StringEncoding]
136 abort(); // not reached
141 jwxyz_make_display (void *nsview_arg, void *cgc_arg)
143 CGContextRef cgc = (CGContextRef) cgc_arg;
144 NSView *view = (NSView *) nsview_arg;
145 Assert (view, "no view");
148 Display *d = (Display *) calloc (1, sizeof(*d));
149 d->screen = (Screen *) calloc (1, sizeof(Screen));
152 Visual *v = (Visual *) calloc (1, sizeof(Visual));
153 v->class = TrueColor;
154 v->red_mask = 0x00FF0000;
155 v->green_mask = 0x0000FF00;
156 v->blue_mask = 0x000000FF;
158 d->screen->visual = v;
160 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
163 Window w = (Window) calloc (1, sizeof(*w));
165 w->window.view = view;
166 CFRetain (w->window.view); // needed for garbage collection?
167 w->window.background = BlackPixel(0,0);
174 cgc = [[[view window] graphicsContext] graphicsPort];
180 Assert (cgc, "no CGContext");
185 jwxyz_free_display (Display *dpy)
187 jwxyz_XtRemoveInput_all (dpy);
188 // #### jwxyz_XtRemoveTimeOut_all ();
190 free (dpy->screen->visual);
192 free (dpy->main_window);
198 jwxyz_window_view (Window w)
200 Assert (w && w->type == WINDOW, "not a window");
201 return w->window.view;
205 /* Call this after any modification to the bits on a Pixmap or Window.
206 Most Pixmaps are used frequently as sources and infrequently as
207 destinations, so it pays to cache the data as a CGImage as needed.
210 invalidate_drawable_cache (Drawable d)
213 CGImageRelease (d->cgi);
219 /* Call this when the View changes size or position.
222 jwxyz_window_resized (Display *dpy, Window w,
223 int new_x, int new_y, int new_width, int new_height,
226 CGContextRef cgc = (CGContextRef) cgc_arg;
227 Assert (w && w->type == WINDOW, "not a window");
228 w->frame.origin.x = new_x;
229 w->frame.origin.y = new_y;
230 w->frame.size.width = new_width;
231 w->frame.size.height = new_height;
233 if (cgc) w->cgc = cgc;
234 Assert (w->cgc, "no CGContext");
237 // Figure out which screen the window is currently on.
240 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
246 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
247 Assert (dpy->cgdpy, "unable to find CGDisplay");
249 # endif // USE_IPHONE
251 # ifndef USE_BACKBUFFER
252 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
253 // then this one's faster.
256 // Figure out this screen's colorspace, and use that for every CGImage.
258 CMProfileRef profile = 0;
259 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
260 Assert (profile, "unable to find colorspace profile");
261 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
262 Assert (dpy->colorspace, "unable to find colorspace");
264 # else // USE_BACKBUFFER
266 // WTF? It's faster if we *do not* use the screen's colorspace!
268 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
269 # endif // USE_BACKBUFFER
271 invalidate_drawable_cache (w);
277 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
279 Assert (w && w->type == WINDOW, "not a window");
280 w->window.last_mouse_x = x;
281 w->window.last_mouse_y = y;
287 jwxyz_flush_context (Display *dpy)
289 // This is only used when USE_BACKBUFFER is off.
290 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
294 display_sources_data (Display *dpy)
296 return dpy->timers_data;
301 XRootWindow (Display *dpy, int screen)
303 return dpy->main_window;
307 XDefaultScreenOfDisplay (Display *dpy)
313 XDefaultVisualOfScreen (Screen *screen)
315 return screen->visual;
319 XDisplayOfScreen (Screen *s)
325 XDisplayNumberOfScreen (Screen *s)
331 XScreenNumberOfScreen (Screen *s)
337 XDisplayWidth (Display *dpy, int screen)
339 return (int) dpy->main_window->frame.size.width;
343 XDisplayHeight (Display *dpy, int screen)
345 return (int) dpy->main_window->frame.size.height;
349 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
352 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
353 else if (!alpha_allowed_p)
354 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
355 "bogus color pixel");
360 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
361 BOOL alpha_allowed_p, BOOL fill_p)
363 validate_pixel (argb, depth, alpha_allowed_p);
366 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
368 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
370 float a = ((argb >> 24) & 0xFF) / 255.0;
371 float r = ((argb >> 16) & 0xFF) / 255.0;
372 float g = ((argb >> 8) & 0xFF) / 255.0;
373 float b = ((argb ) & 0xFF) / 255.0;
375 CGContextSetRGBFillColor (cgc, r, g, b, a);
377 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
382 set_line_mode (CGContextRef cgc, XGCValues *gcv)
384 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
385 CGContextSetLineJoin (cgc,
386 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
387 gcv->join_style == JoinRound ? kCGLineJoinRound :
389 CGContextSetLineCap (cgc,
390 gcv->cap_style == CapNotLast ? kCGLineCapButt :
391 gcv->cap_style == CapButt ? kCGLineCapButt :
392 gcv->cap_style == CapRound ? kCGLineCapRound :
397 set_clip_mask (Drawable d, GC gc)
399 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
401 Pixmap p = gc->gcv.clip_mask;
403 Assert (p->type == PIXMAP, "not a pixmap");
405 CGRect wr = d->frame;
407 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
408 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
409 - p->frame.size.height;
410 to.size.width = p->frame.size.width;
411 to.size.height = p->frame.size.height;
413 CGContextClipToMask (d->cgc, to, gc->clip_mask);
417 /* Pushes a GC context; sets BlendMode and ClipMask.
420 push_gc (Drawable d, GC gc)
422 CGContextRef cgc = d->cgc;
423 CGContextSaveGState (cgc);
425 switch (gc->gcv.function) {
428 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
429 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
430 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
431 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
432 default: Assert(0, "unknown gcv function"); break;
435 if (gc->gcv.clip_mask)
436 set_clip_mask (d, gc);
439 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
442 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
445 push_color_gc (Drawable d, GC gc, unsigned long color,
446 BOOL antialias_p, Bool fill_p)
450 int depth = gc->depth;
451 switch (gc->gcv.function) {
452 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
453 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
456 CGContextRef cgc = d->cgc;
457 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
458 CGContextSetShouldAntialias (cgc, antialias_p);
462 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
465 push_fg_gc (Drawable d, GC gc, Bool fill_p)
467 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
470 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
473 push_bg_gc (Drawable d, GC gc, Bool fill_p)
475 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
480 /* You've got to be fucking kidding me!
482 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
483 with repeated calls to CGContextDrawImage than it is to make a single
484 call to CGContextFillRects() with a list of 1x1 rectangles!
486 I still wouldn't call it *fast*, however...
488 #define XDRAWPOINTS_IMAGES
490 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
491 the bitmap data directly is faster. This only works on Pixmaps, though,
492 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
494 #define XDRAWPOINTS_CGDATA
497 XDrawPoints (Display *dpy, Drawable d, GC gc,
498 XPoint *points, int count, int mode)
501 CGRect wr = d->frame;
503 push_fg_gc (d, gc, YES);
505 # ifdef XDRAWPOINTS_CGDATA
507 # ifdef USE_BACKBUFFER
508 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
510 if (d->type == PIXMAP)
513 CGContextRef cgc = d->cgc;
514 void *data = CGBitmapContextGetData (cgc);
515 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
516 size_t w = CGBitmapContextGetWidth (cgc);
517 size_t h = CGBitmapContextGetHeight (cgc);
519 Assert (data, "no bitmap data in Drawable");
521 unsigned int argb = gc->gcv.foreground;
522 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
524 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
526 CGFloat x0 = wr.origin.x;
527 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
529 // It's uglier, but faster, to hoist the conditional out of the loop.
530 if (mode == CoordModePrevious) {
531 CGFloat x = x0, y = y0;
532 for (i = 0; i < count; i++, points++) {
536 if (x >= 0 && x < w && y >= 0 && y < h) {
537 unsigned int *p = (unsigned int *)
538 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
543 for (i = 0; i < count; i++, points++) {
544 CGFloat x = x0 + points->x;
545 CGFloat y = y0 + points->y;
547 if (x >= 0 && x < w && y >= 0 && y < h) {
548 unsigned int *p = (unsigned int *)
549 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
555 } else /* d->type == WINDOW */
557 # endif /* XDRAWPOINTS_CGDATA */
560 # ifdef XDRAWPOINTS_IMAGES
562 unsigned int argb = gc->gcv.foreground;
563 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
565 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
567 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
569 CGImageRef cgi = CGImageCreate (1, 1,
572 /* Host-ordered, since we're using the
573 address of an int as the color data. */
574 (kCGImageAlphaNoneSkipFirst |
575 kCGBitmapByteOrder32Host),
578 NO, /* interpolate */
579 kCGRenderingIntentDefault);
580 CGDataProviderRelease (prov);
582 CGContextRef cgc = d->cgc;
584 rect.size.width = rect.size.height = 1;
585 for (i = 0; i < count; i++) {
586 if (i > 0 && mode == CoordModePrevious) {
587 rect.origin.x += points->x;
588 rect.origin.x -= points->y;
590 rect.origin.x = wr.origin.x + points->x;
591 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
594 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
595 CGContextDrawImage (cgc, rect, cgi);
599 CGImageRelease (cgi);
601 # else /* ! XDRAWPOINTS_IMAGES */
603 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
606 for (i = 0; i < count; i++) {
607 r->size.width = r->size.height = 1;
608 if (i > 0 && mode == CoordModePrevious) {
609 r->origin.x = r[-1].origin.x + points->x;
610 r->origin.y = r[-1].origin.x - points->y;
612 r->origin.x = wr.origin.x + points->x;
613 r->origin.y = wr.origin.y + wr.size.height - points->y;
619 CGContextFillRects (d->cgc, rects, count);
622 # endif /* ! XDRAWPOINTS_IMAGES */
626 invalidate_drawable_cache (d);
633 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
638 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
642 static void draw_rect (Display *, Drawable, GC,
643 int x, int y, unsigned int width, unsigned int height,
644 BOOL foreground_p, BOOL fill_p);
647 bitmap_context_p (Drawable d)
649 # ifdef USE_BACKBUFFER
652 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
653 return d->type == PIXMAP;
658 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
659 size_t fill_width, size_t fill_height)
661 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
662 while (fill_height) {
663 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
664 wmemset (dst, fill_data, fill_width);
666 dst = (char *) dst + dst_pitch;
671 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
673 return (char *)dst + dst_pitch * y + x * 4;
677 drawable_depth (Drawable d)
679 return (d->type == WINDOW
680 ? visual_depth (NULL, NULL)
686 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
687 int src_x, int src_y,
688 unsigned int width, unsigned int height,
689 int dst_x, int dst_y)
691 Assert (gc, "no GC");
692 Assert ((width < 65535), "improbably large width");
693 Assert ((height < 65535), "improbably large height");
694 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
695 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
696 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
697 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
699 if (width == 0 || height == 0)
702 if (gc->gcv.function == GXset ||
703 gc->gcv.function == GXclear) {
704 // "set" and "clear" are dumb drawing modes that ignore the source
705 // bits and just draw solid rectangles.
707 (gc->gcv.function == GXset
708 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
709 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
710 gc->depth, gc->gcv.alpha_allowed_p, YES);
711 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
715 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
716 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
717 // bounds of their drawables.
718 BOOL clipped = NO; // Whether we did any clipping of the rects.
720 src_frame = src->frame;
721 dst_frame = dst->frame;
723 // Initialize src_rect...
725 src_rect.origin.x = src_frame.origin.x + src_x;
726 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
728 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
729 src_rect.size.width = width;
730 src_rect.size.height = height;
732 // Initialize dst_rect...
734 dst_rect.origin.x = dst_frame.origin.x + dst_x;
735 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
737 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
738 dst_rect.size.width = width;
739 dst_rect.size.height = height;
741 // Clip rects to frames...
744 # define CLIP(THIS,THAT,VAL,SIZE) do { \
745 float off = THIS##_rect.origin.VAL; \
748 THIS##_rect.size.SIZE += off; \
749 THAT##_rect.size.SIZE += off; \
750 THIS##_rect.origin.VAL -= off; \
751 THAT##_rect.origin.VAL -= off; \
753 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
754 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
757 THIS##_rect.size.SIZE -= off; \
758 THAT##_rect.size.SIZE -= off; \
761 CLIP (dst, src, x, width);
762 CLIP (dst, src, y, height);
764 // Not actually the original dst_rect, just the one before it's clipped to
766 CGRect orig_dst_rect = dst_rect;
768 CLIP (src, dst, x, width);
769 CLIP (src, dst, y, height);
772 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
775 // Sort-of-special case where no pixels can be grabbed from the source,
776 // and the whole destination is filled with the background color.
777 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
778 src_rect.size.width = 0;
779 src_rect.size.height = 0;
782 NSObject *releaseme = 0;
785 BOOL free_cgi_p = NO;
788 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
789 going on with clipping masks or depths or anything, optimize it by
790 just doing a memcpy instead of going through a CGI.
792 if (bitmap_context_p (src)) {
794 if (bitmap_context_p (dst) &&
795 gc->gcv.function == GXcopy &&
796 !gc->gcv.clip_mask &&
797 drawable_depth (src) == drawable_depth (dst)) {
799 Assert(!(int)src_frame.origin.x &&
800 !(int)src_frame.origin.y &&
801 !(int)dst_frame.origin.x &&
802 !(int)dst_frame.origin.y,
803 "unexpected non-zero origin");
805 char *src_data = CGBitmapContextGetData(src->cgc);
806 char *dst_data = CGBitmapContextGetData(dst->cgc);
807 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
808 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
810 // Int to float and back again. It's not very safe, but it seems to work.
811 int src_x0 = src_rect.origin.x;
812 int dst_x0 = dst_rect.origin.x;
814 // Flip the Y-axis a second time.
815 int src_y0 = (src_frame.origin.y + src_frame.size.height -
816 src_rect.size.height - src_rect.origin.y);
817 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
818 dst_rect.size.height - dst_rect.origin.y);
820 unsigned width0 = (int) src_rect.size.width;
821 unsigned height0 = (int) src_rect.size.height;
823 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
824 (int)src_rect.size.height == (int)dst_rect.size.height,
827 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
828 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
829 size_t src_pitch0 = src_pitch;
830 size_t dst_pitch0 = dst_pitch;
831 size_t bytes = width0 * 4;
833 if (src == dst && dst_y0 > src_y0) {
834 // Copy upwards if the areas might overlap.
835 src_data0 += src_pitch0 * (height0 - 1);
836 dst_data0 += dst_pitch0 * (height0 - 1);
837 src_pitch0 = -src_pitch0;
838 dst_pitch0 = -dst_pitch0;
841 size_t lines0 = height0;
843 // memcpy is an alias for memmove on OS X.
844 memmove(dst_data0, src_data0, bytes);
845 src_data0 += src_pitch0;
846 dst_data0 += dst_pitch0;
852 int orig_dst_x = orig_dst_rect.origin.x;
853 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
854 orig_dst_rect.origin.y - orig_dst_rect.size.height);
855 int orig_width = orig_dst_rect.size.width;
856 int orig_height = orig_dst_rect.size.height;
858 Assert (orig_dst_x >= 0 &&
859 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
861 orig_dst_y + orig_height <= (int) dst_frame.size.height,
864 if (orig_dst_y < dst_y0) {
865 fill_rect_memset (seek_xy (dst_data, dst_pitch,
866 orig_dst_x, orig_dst_y), dst_pitch,
867 gc->gcv.background, orig_width,
868 dst_y0 - orig_dst_y);
871 if (orig_dst_y + orig_height > dst_y0 + height0) {
872 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
875 gc->gcv.background, orig_width,
876 orig_dst_y + orig_height - dst_y0 - height0);
879 if (orig_dst_x < dst_x0) {
880 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
881 dst_pitch, gc->gcv.background,
882 dst_x0 - orig_dst_x, height0);
885 if (dst_x0 + width0 < orig_dst_x + orig_width) {
886 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
888 dst_pitch, gc->gcv.background,
889 orig_dst_x + orig_width - dst_x0 - width0,
894 invalidate_drawable_cache (dst);
899 // If we are copying from a Pixmap to a Pixmap or Window, we must first
900 // copy the bits to an intermediary CGImage object, then copy that to the
901 // destination drawable's CGContext.
903 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
904 // case of copying from a Pixmap back to itself, but I don't think that
905 // happens very often anyway.)
907 // First we get a CGImage out of the pixmap CGContext -- it's the whole
908 // pixmap, but it presumably shares the data pointer instead of copying
909 // it. We then cache that CGImage it inside the Pixmap object. Note:
910 // invalidate_drawable_cache() must be called to discard this any time a
911 // modification is made to the pixmap, or we'll end up re-using old bits.
914 src->cgi = CGBitmapContextCreateImage (src->cgc);
917 // if doing a sub-rect, trim it down.
918 if (src_rect.origin.x != src_frame.origin.x ||
919 src_rect.origin.y != src_frame.origin.y ||
920 src_rect.size.width != src_frame.size.width ||
921 src_rect.size.height != src_frame.size.height) {
922 // #### I don't understand why this is needed...
923 src_rect.origin.y = (src_frame.size.height -
924 src_rect.size.height - src_rect.origin.y);
925 // This does not copy image data, so it should be fast.
926 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
930 if (src->type == PIXMAP && src->pixmap.depth == 1)
933 # ifndef USE_BACKBUFFER
934 } else { /* (src->type == WINDOW) */
936 NSRect nsfrom; // NSRect != CGRect on 10.4
937 nsfrom.origin.x = src_rect.origin.x;
938 nsfrom.origin.y = src_rect.origin.y;
939 nsfrom.size.width = src_rect.size.width;
940 nsfrom.size.height = src_rect.size.height;
944 // If we are copying from a window to itself, we can use NSCopyBits()
945 // without first copying the rectangle to an intermediary CGImage.
946 // This is ~28% faster (but I *expected* it to be twice as fast...)
947 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
953 // If we are copying from a Window to a Pixmap, we must first copy
954 // the bits to an intermediary CGImage object, then copy that to the
955 // Pixmap's CGContext.
957 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
958 initWithFocusedViewRect:nsfrom];
959 unsigned char *data = [bm bitmapData];
960 int bps = [bm bitsPerSample];
961 int bpp = [bm bitsPerPixel];
962 int bpl = [bm bytesPerRow];
965 // create a CGImage from those bits.
966 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
967 // but that method didn't exist in 10.4.)
969 CGDataProviderRef prov =
970 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
972 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
975 /* Use whatever default bit ordering we got from
976 initWithFocusedViewRect. I would have assumed
977 that it was (kCGImageAlphaNoneSkipFirst |
978 kCGBitmapByteOrder32Host), but on Intel,
984 NO, /* interpolate */
985 kCGRenderingIntentDefault);
987 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
988 CGDataProviderRelease (prov);
991 # endif // !USE_BACKBUFFER
994 CGContextRef cgc = dst->cgc;
996 if (mask_p) { // src depth == 1
998 push_bg_gc (dst, gc, YES);
1000 // fill the destination rectangle with solid background...
1001 CGContextFillRect (cgc, orig_dst_rect);
1003 Assert (cgc, "no CGC with 1-bit XCopyArea");
1005 // then fill in a solid rectangle of the fg color, using the image as an
1006 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1007 set_color (cgc, gc->gcv.foreground, gc->depth,
1008 gc->gcv.alpha_allowed_p, YES);
1009 CGContextClipToMask (cgc, dst_rect, cgi);
1010 CGContextFillRect (cgc, dst_rect);
1014 } else { // src depth > 1
1018 // If either the src or dst rects did not lie within their drawables,
1019 // then we have adjusted both the src and dst rects to account for
1020 // the clipping; that means we need to first clear to the background,
1021 // so that clipped bits end up in the bg color instead of simply not
1025 set_color (cgc, gc->gcv.background, gc->depth,
1026 gc->gcv.alpha_allowed_p, YES);
1027 CGContextFillRect (cgc, orig_dst_rect);
1031 // copy the CGImage onto the destination CGContext
1032 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1033 CGContextDrawImage (cgc, dst_rect, cgi);
1035 // No cgi means src == dst, and both are Windows.
1037 # ifdef USE_BACKBUFFER
1038 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1040 # else // !USE_BACKBUFFER
1042 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1043 nsfrom.origin.y = src_rect.origin.y;
1044 nsfrom.size.width = src_rect.size.width;
1045 nsfrom.size.height = src_rect.size.height;
1047 nsto.x = dst_rect.origin.x;
1048 nsto.y = dst_rect.origin.y;
1049 NSCopyBits (0, nsfrom, nsto);
1050 # endif // !USE_BACKBUFFER
1056 if (free_cgi_p) CGImageRelease (cgi);
1058 if (releaseme) [releaseme release];
1059 invalidate_drawable_cache (dst);
1065 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1066 int src_x, int src_y,
1067 unsigned width, int height,
1068 int dest_x, int dest_y, unsigned long plane)
1070 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1072 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1073 // not to white/black.
1074 return XCopyArea (dpy, src, dest, gc,
1075 src_x, src_y, width, height, dest_x, dest_y);
1080 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1082 // when drawing a zero-length line, obey line-width and cap-style.
1083 if (x1 == x2 && y1 == y2) {
1084 int w = gc->gcv.line_width;
1087 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1088 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1090 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1093 CGRect wr = d->frame;
1095 p.x = wr.origin.x + x1;
1096 p.y = wr.origin.y + wr.size.height - y1;
1098 push_fg_gc (d, gc, NO);
1100 CGContextRef cgc = d->cgc;
1101 set_line_mode (cgc, &gc->gcv);
1102 CGContextBeginPath (cgc);
1103 CGContextMoveToPoint (cgc, p.x, p.y);
1104 p.x = wr.origin.x + x2;
1105 p.y = wr.origin.y + wr.size.height - y2;
1106 CGContextAddLineToPoint (cgc, p.x, p.y);
1107 CGContextStrokePath (cgc);
1109 invalidate_drawable_cache (d);
1114 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1119 CGRect wr = d->frame;
1120 push_fg_gc (d, gc, NO);
1122 CGContextRef cgc = d->cgc;
1124 set_line_mode (cgc, &gc->gcv);
1126 // if the first and last points coincide, use closepath to get
1127 // the proper line-joining.
1128 BOOL closed_p = (points[0].x == points[count-1].x &&
1129 points[0].y == points[count-1].y);
1130 if (closed_p) count--;
1132 p.x = wr.origin.x + points->x;
1133 p.y = wr.origin.y + wr.size.height - points->y;
1135 CGContextBeginPath (cgc);
1136 CGContextMoveToPoint (cgc, p.x, p.y);
1137 for (i = 1; i < count; i++) {
1138 if (mode == CoordModePrevious) {
1142 p.x = wr.origin.x + points->x;
1143 p.y = wr.origin.y + wr.size.height - points->y;
1145 CGContextAddLineToPoint (cgc, p.x, p.y);
1148 if (closed_p) CGContextClosePath (cgc);
1149 CGContextStrokePath (cgc);
1151 invalidate_drawable_cache (d);
1157 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1160 CGRect wr = d->frame;
1162 CGContextRef cgc = d->cgc;
1164 push_fg_gc (d, gc, NO);
1165 set_line_mode (cgc, &gc->gcv);
1166 CGContextBeginPath (cgc);
1167 for (i = 0; i < count; i++) {
1168 CGContextMoveToPoint (cgc,
1169 wr.origin.x + segments->x1,
1170 wr.origin.y + wr.size.height - segments->y1);
1171 CGContextAddLineToPoint (cgc,
1172 wr.origin.x + segments->x2,
1173 wr.origin.y + wr.size.height - segments->y2);
1176 CGContextStrokePath (cgc);
1178 invalidate_drawable_cache (d);
1184 XClearWindow (Display *dpy, Window win)
1186 Assert (win && win->type == WINDOW, "not a window");
1187 CGRect wr = win->frame;
1188 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1192 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1194 Assert (w && w->type == WINDOW, "not a window");
1195 validate_pixel (pixel, 32, NO);
1196 w->window.background = pixel;
1201 draw_rect (Display *dpy, Drawable d, GC gc,
1202 int x, int y, unsigned int width, unsigned int height,
1203 BOOL foreground_p, BOOL fill_p)
1205 CGRect wr = d->frame;
1207 r.origin.x = wr.origin.x + x;
1208 r.origin.y = wr.origin.y + wr.size.height - y - height;
1209 r.size.width = width;
1210 r.size.height = height;
1214 push_fg_gc (d, gc, fill_p);
1216 push_bg_gc (d, gc, fill_p);
1219 CGContextRef cgc = d->cgc;
1221 CGContextFillRect (cgc, r);
1224 set_line_mode (cgc, &gc->gcv);
1225 CGContextStrokeRect (cgc, r);
1230 invalidate_drawable_cache (d);
1235 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1236 unsigned int width, unsigned int height)
1238 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1243 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1244 unsigned int width, unsigned int height)
1246 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1251 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1253 CGRect wr = d->frame;
1255 CGContextRef cgc = d->cgc;
1256 push_fg_gc (d, gc, YES);
1257 for (i = 0; i < n; i++) {
1259 r.origin.x = wr.origin.x + rects->x;
1260 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1261 r.size.width = rects->width;
1262 r.size.height = rects->height;
1263 CGContextFillRect (cgc, r);
1267 invalidate_drawable_cache (d);
1273 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1275 Assert (win && win->type == WINDOW, "not a window");
1276 CGContextRef cgc = win->cgc;
1277 set_color (cgc, win->window.background, 32, NO, YES);
1278 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1284 XFillPolygon (Display *dpy, Drawable d, GC gc,
1285 XPoint *points, int npoints, int shape, int mode)
1287 CGRect wr = d->frame;
1289 push_fg_gc (d, gc, YES);
1290 CGContextRef cgc = d->cgc;
1291 CGContextBeginPath (cgc);
1293 for (i = 0; i < npoints; i++) {
1294 if (i > 0 && mode == CoordModePrevious) {
1298 x = wr.origin.x + points[i].x;
1299 y = wr.origin.y + wr.size.height - points[i].y;
1303 CGContextMoveToPoint (cgc, x, y);
1305 CGContextAddLineToPoint (cgc, x, y);
1307 CGContextClosePath (cgc);
1308 if (gc->gcv.fill_rule == EvenOddRule)
1309 CGContextEOFillPath (cgc);
1311 CGContextFillPath (cgc);
1313 invalidate_drawable_cache (d);
1317 #define radians(DEG) ((DEG) * M_PI / 180.0)
1318 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1321 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1322 unsigned int width, unsigned int height, int angle1, int angle2,
1325 CGRect wr = d->frame;
1327 bound.origin.x = wr.origin.x + x;
1328 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1329 bound.size.width = width;
1330 bound.size.height = height;
1333 ctr.x = bound.origin.x + bound.size.width /2;
1334 ctr.y = bound.origin.y + bound.size.height/2;
1336 float r1 = radians (angle1/64.0);
1337 float r2 = radians (angle2/64.0) + r1;
1338 BOOL clockwise = angle2 < 0;
1339 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1341 push_fg_gc (d, gc, fill_p);
1343 CGContextRef cgc = d->cgc;
1344 CGContextBeginPath (cgc);
1346 CGContextSaveGState(cgc);
1347 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1348 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1350 CGContextMoveToPoint (cgc, 0, 0);
1352 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1353 CGContextRestoreGState (cgc); // restore before stroke, for line width
1356 CGContextClosePath (cgc); // for proper line joining
1359 CGContextFillPath (cgc);
1361 set_line_mode (cgc, &gc->gcv);
1362 CGContextStrokePath (cgc);
1366 invalidate_drawable_cache (d);
1371 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1372 unsigned int width, unsigned int height, int angle1, int angle2)
1374 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1378 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1379 unsigned int width, unsigned int height, int angle1, int angle2)
1381 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1385 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1388 for (i = 0; i < narcs; i++)
1389 draw_arc (dpy, d, gc,
1390 arcs[i].x, arcs[i].y,
1391 arcs[i].width, arcs[i].height,
1392 arcs[i].angle1, arcs[i].angle2,
1398 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1401 for (i = 0; i < narcs; i++)
1402 draw_arc (dpy, d, gc,
1403 arcs[i].x, arcs[i].y,
1404 arcs[i].width, arcs[i].height,
1405 arcs[i].angle1, arcs[i].angle2,
1412 gcv_defaults (XGCValues *gcv, int depth)
1414 memset (gcv, 0, sizeof(*gcv));
1415 gcv->function = GXcopy;
1416 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1417 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1418 gcv->line_width = 1;
1419 gcv->cap_style = CapNotLast;
1420 gcv->join_style = JoinMiter;
1421 gcv->fill_rule = EvenOddRule;
1423 gcv->alpha_allowed_p = NO;
1424 gcv->antialias_p = YES;
1428 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1431 Assert (gc && from, "no gc");
1432 if (!gc || !from) return;
1434 if (mask & GCFunction) gc->gcv.function = from->function;
1435 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1436 if (mask & GCBackground) gc->gcv.background = from->background;
1437 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1438 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1439 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1440 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1441 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1442 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1443 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1445 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1446 if (mask & GCFont) XSetFont (0, gc, from->font);
1448 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1449 gc->gcv.alpha_allowed_p);
1450 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1451 gc->gcv.alpha_allowed_p);
1453 Assert ((! (mask & (GCLineStyle |
1460 GCGraphicsExposures |
1464 "unimplemented gcvalues mask");
1469 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1471 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1472 if (d->type == WINDOW) {
1474 } else { /* (d->type == PIXMAP) */
1475 gc->depth = d->pixmap.depth;
1478 gcv_defaults (&gc->gcv, gc->depth);
1479 set_gcv (gc, xgcv, mask);
1484 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1486 set_gcv (gc, gcv, mask);
1492 XFreeGC (Display *dpy, GC gc)
1495 XUnloadFont (dpy, gc->gcv.font);
1497 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1499 if (gc->gcv.clip_mask) {
1500 XFreePixmap (dpy, gc->gcv.clip_mask);
1501 CGImageRelease (gc->clip_mask);
1509 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1511 Assert (w && w->type == WINDOW, "not a window");
1512 memset (xgwa, 0, sizeof(*xgwa));
1513 xgwa->x = w->frame.origin.x;
1514 xgwa->y = w->frame.origin.y;
1515 xgwa->width = w->frame.size.width;
1516 xgwa->height = w->frame.size.height;
1518 xgwa->screen = dpy->screen;
1519 xgwa->visual = dpy->screen->visual;
1524 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1525 int *x_ret, int *y_ret,
1526 unsigned int *w_ret, unsigned int *h_ret,
1527 unsigned int *bw_ret, unsigned int *d_ret)
1529 *x_ret = d->frame.origin.x;
1530 *y_ret = d->frame.origin.y;
1531 *w_ret = d->frame.size.width;
1532 *h_ret = d->frame.size.height;
1533 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1534 *root_ret = RootWindow (dpy, 0);
1541 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1543 // store 32 bit ARGB in the pixel field.
1544 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1545 color->pixel = (uint32_t)
1547 (((color->red >> 8) & 0xFF) << 16) |
1548 (((color->green >> 8) & 0xFF) << 8) |
1549 (((color->blue >> 8) & 0xFF) ));
1554 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1555 unsigned long *pmret, unsigned int npl,
1556 unsigned long *pxret, unsigned int npx)
1562 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1564 Assert(0, "XStoreColors called");
1569 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1571 Assert(0, "XStoreColor called");
1576 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1577 unsigned long planes)
1583 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1585 unsigned char r=0, g=0, b=0;
1586 if (*spec == '#' && strlen(spec) == 7) {
1587 static unsigned const char hex[] = { // yeah yeah, shoot me.
1588 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1589 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1590 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1591 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1592 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1593 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1594 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1595 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1596 r = (hex[spec[1]] << 4) | hex[spec[2]];
1597 g = (hex[spec[3]] << 4) | hex[spec[4]];
1598 b = (hex[spec[5]] << 4) | hex[spec[6]];
1599 } else if (!strcasecmp(spec,"black")) {
1601 } else if (!strcasecmp(spec,"white")) {
1603 } else if (!strcasecmp(spec,"red")) {
1605 } else if (!strcasecmp(spec,"green")) {
1607 } else if (!strcasecmp(spec,"blue")) {
1609 } else if (!strcasecmp(spec,"cyan")) {
1611 } else if (!strcasecmp(spec,"magenta")) {
1613 } else if (!strcasecmp(spec,"yellow")) {
1619 ret->red = (r << 8) | r;
1620 ret->green = (g << 8) | g;
1621 ret->blue = (b << 8) | b;
1622 ret->flags = DoRed|DoGreen|DoBlue;
1627 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1628 XColor *screen_ret, XColor *exact_ret)
1630 if (! XParseColor (dpy, cmap, name, screen_ret))
1632 *exact_ret = *screen_ret;
1633 return XAllocColor (dpy, cmap, screen_ret);
1637 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1639 validate_pixel (color->pixel, 32, NO);
1640 unsigned char r = ((color->pixel >> 16) & 0xFF);
1641 unsigned char g = ((color->pixel >> 8) & 0xFF);
1642 unsigned char b = ((color->pixel ) & 0xFF);
1643 color->red = (r << 8) | r;
1644 color->green = (g << 8) | g;
1645 color->blue = (b << 8) | b;
1646 color->flags = DoRed|DoGreen|DoBlue;
1651 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1654 for (i = 0; i < n; i++)
1655 XQueryColor (dpy, cmap, &c[i]);
1660 static unsigned long
1661 ximage_getpixel_1 (XImage *ximage, int x, int y)
1663 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1667 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1670 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1672 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1677 static unsigned long
1678 ximage_getpixel_32 (XImage *ximage, int x, int y)
1680 return ((unsigned long)
1681 *((uint32_t *) ximage->data +
1682 (y * (ximage->bytes_per_line >> 2)) +
1687 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1689 *((uint32_t *) ximage->data +
1690 (y * (ximage->bytes_per_line >> 2)) +
1691 x) = (uint32_t) pixel;
1697 XInitImage (XImage *ximage)
1699 if (!ximage->bytes_per_line)
1700 ximage->bytes_per_line = (ximage->depth == 1
1701 ? (ximage->width + 7) / 8
1702 : ximage->width * 4);
1704 if (ximage->depth == 1) {
1705 ximage->f.put_pixel = ximage_putpixel_1;
1706 ximage->f.get_pixel = ximage_getpixel_1;
1707 } else if (ximage->depth == 32 || ximage->depth == 24) {
1708 ximage->f.put_pixel = ximage_putpixel_32;
1709 ximage->f.get_pixel = ximage_getpixel_32;
1711 Assert (0, "unknown depth");
1718 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1719 int format, int offset, char *data,
1720 unsigned int width, unsigned int height,
1721 int bitmap_pad, int bytes_per_line)
1723 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1724 ximage->width = width;
1725 ximage->height = height;
1726 ximage->format = format;
1727 ximage->data = data;
1728 ximage->bitmap_unit = 8;
1729 ximage->byte_order = MSBFirst;
1730 ximage->bitmap_bit_order = ximage->byte_order;
1731 ximage->bitmap_pad = bitmap_pad;
1732 ximage->depth = depth;
1733 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1734 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1735 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1736 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1737 ximage->bytes_per_line = bytes_per_line;
1739 XInitImage (ximage);
1744 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1746 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1747 w, h, from->bitmap_pad, 0);
1748 to->data = (char *) malloc (h * to->bytes_per_line);
1750 if (x >= from->width)
1752 else if (x+w > from->width)
1753 w = from->width - x;
1755 if (y >= from->height)
1757 else if (y+h > from->height)
1758 h = from->height - y;
1761 for (ty = 0; ty < h; ty++)
1762 for (tx = 0; tx < w; tx++)
1763 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1768 XPixmapFormatValues *
1769 XListPixmapFormats (Display *dpy, int *n_ret)
1771 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1773 ret[0].bits_per_pixel = 32;
1774 ret[0].scanline_pad = 8;
1776 ret[1].bits_per_pixel = 1;
1777 ret[1].scanline_pad = 8;
1784 XGetPixel (XImage *ximage, int x, int y)
1786 return ximage->f.get_pixel (ximage, x, y);
1791 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1793 return ximage->f.put_pixel (ximage, x, y, pixel);
1797 XDestroyImage (XImage *ximage)
1799 if (ximage->data) free (ximage->data);
1806 flipbits (unsigned const char *in, unsigned char *out, int length)
1808 static const unsigned char table[256] = {
1809 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1810 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1811 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1812 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1813 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1814 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1815 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1816 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1817 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1818 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1819 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1820 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1821 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1822 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1823 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1824 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1825 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1826 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1827 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1828 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1829 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1830 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1831 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1832 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1833 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1834 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1835 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1836 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1837 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1838 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1839 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1840 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1842 while (--length > 0)
1843 *out++ = table[*in++];
1848 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1849 int src_x, int src_y, int dest_x, int dest_y,
1850 unsigned int w, unsigned int h)
1852 CGRect wr = d->frame;
1854 Assert (gc, "no GC");
1855 Assert ((w < 65535), "improbably large width");
1856 Assert ((h < 65535), "improbably large height");
1857 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1858 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1859 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1860 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1862 // Clip width and height to the bounds of the Drawable
1864 if (dest_x + w > wr.size.width) {
1865 if (dest_x > wr.size.width)
1867 w = wr.size.width - dest_x;
1869 if (dest_y + h > wr.size.height) {
1870 if (dest_y > wr.size.height)
1872 h = wr.size.height - dest_y;
1874 if (w <= 0 || h <= 0)
1877 // Clip width and height to the bounds of the XImage
1879 if (src_x + w > ximage->width) {
1880 if (src_x > ximage->width)
1882 w = ximage->width - src_x;
1884 if (src_y + h > ximage->height) {
1885 if (src_y > ximage->height)
1887 h = ximage->height - src_y;
1889 if (w <= 0 || h <= 0)
1892 CGContextRef cgc = d->cgc;
1894 if (gc->gcv.function == GXset ||
1895 gc->gcv.function == GXclear) {
1896 // "set" and "clear" are dumb drawing modes that ignore the source
1897 // bits and just draw solid rectangles.
1898 set_color (cgc, (gc->gcv.function == GXset
1899 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1900 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1901 gc->depth, gc->gcv.alpha_allowed_p, YES);
1902 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1906 int bpl = ximage->bytes_per_line;
1907 int bpp = ximage->bits_per_pixel;
1908 int bsize = bpl * h;
1909 char *data = ximage->data;
1912 r.origin.x = wr.origin.x + dest_x;
1913 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1919 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1920 to create a CGImage from a sub-rectagle of the XImage.
1922 data += (src_y * bpl) + (src_x * 4);
1923 CGDataProviderRef prov =
1924 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1926 CGImageRef cgi = CGImageCreate (w, h,
1929 /* Need this for XPMs to have the right
1930 colors, e.g. the logo in "maze". */
1931 (kCGImageAlphaNoneSkipFirst |
1932 kCGBitmapByteOrder32Host),
1934 NULL, /* decode[] */
1935 NO, /* interpolate */
1936 kCGRenderingIntentDefault);
1937 CGDataProviderRelease (prov);
1938 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1939 CGContextDrawImage (cgc, r, cgi);
1940 CGImageRelease (cgi);
1942 } else { // (bpp == 1)
1944 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1946 #### However, the bit order within a byte in a 1bpp XImage is
1947 the wrong way around from what Quartz expects, so first we
1948 have to copy the data to reverse it. Shit! Maybe it
1949 would be worthwhile to go through the hacks and #ifdef
1950 each one that diddles 1bpp XImage->data directly...
1952 Assert ((src_x % 8) == 0,
1953 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1955 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1956 unsigned char *flipped = (unsigned char *) malloc (bsize);
1958 flipbits ((unsigned char *) data, flipped, bsize);
1960 CGDataProviderRef prov =
1961 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1962 CGImageRef mask = CGImageMaskCreate (w, h,
1965 NULL, /* decode[] */
1966 NO); /* interpolate */
1967 push_fg_gc (d, gc, YES);
1969 CGContextFillRect (cgc, r); // foreground color
1970 CGContextClipToMask (cgc, r, mask);
1971 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1972 CGContextFillRect (cgc, r); // background color
1976 CGDataProviderRelease (prov);
1977 CGImageRelease (mask);
1980 invalidate_drawable_cache (d);
1987 XGetImage (Display *dpy, Drawable d, int x, int y,
1988 unsigned int width, unsigned int height,
1989 unsigned long plane_mask, int format)
1991 const unsigned char *data = 0;
1992 int depth, ibpp, ibpl;
1993 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
1994 # ifndef USE_BACKBUFFER
1995 NSBitmapImageRep *bm = 0;
1998 Assert ((width < 65535), "improbably large width");
1999 Assert ((height < 65535), "improbably large height");
2000 Assert ((x < 65535 && x > -65535), "improbably large x");
2001 Assert ((y < 65535 && y > -65535), "improbably large y");
2003 CGContextRef cgc = d->cgc;
2005 #ifndef USE_BACKBUFFER
2006 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2007 if (d->type == PIXMAP)
2010 depth = (d->type == PIXMAP
2013 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2014 src_format = BGRA; // #### Should this be ARGB on PPC?
2015 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2016 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2017 data = CGBitmapContextGetData (cgc);
2018 Assert (data, "CGBitmapContextGetData failed");
2020 # ifndef USE_BACKBUFFER
2021 } else { /* (d->type == WINDOW) */
2023 // get the bits (desired sub-rectangle) out of the NSView
2025 nsfrom.origin.x = x;
2026 // nsfrom.origin.y = y;
2027 nsfrom.origin.y = d->frame.size.height - height - y;
2028 nsfrom.size.width = width;
2029 nsfrom.size.height = height;
2030 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2032 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2033 ibpp = [bm bitsPerPixel];
2034 ibpl = [bm bytesPerRow];
2035 data = [bm bitmapData];
2036 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2037 # endif // !USE_BACKBUFFER
2040 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2041 data += (y * ibpl) + (x * (ibpp/8));
2043 format = (depth == 1 ? XYPixmap : ZPixmap);
2044 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
2046 image->data = (char *) malloc (height * image->bytes_per_line);
2048 int obpl = image->bytes_per_line;
2050 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2051 means that on Intel it is BGRA when viewed by bytes (And BGR
2052 when using 24bpp packing).
2054 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2055 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2056 indicator of this latest kink.
2060 const unsigned char *iline = data;
2061 for (yy = 0; yy < height; yy++) {
2063 const unsigned char *iline2 = iline;
2064 for (xx = 0; xx < width; xx++) {
2066 iline2++; // ignore R or A or A or B
2067 iline2++; // ignore G or B or R or G
2068 unsigned char r = *iline2++; // use B or G or G or R
2069 if (ibpp == 32) iline2++; // ignore A or R or B or A
2071 XPutPixel (image, xx, yy, (r ? 1 : 0));
2076 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2077 const unsigned char *iline = data;
2078 unsigned char *oline = (unsigned char *) image->data;
2079 for (yy = 0; yy < height; yy++) {
2081 const unsigned char *iline2 = iline;
2082 unsigned char *oline2 = oline;
2084 switch (src_format) {
2086 for (xx = 0; xx < width; xx++) {
2087 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2088 unsigned char r = *iline2++;
2089 unsigned char g = *iline2++;
2090 unsigned char b = *iline2++;
2091 uint32_t pixel = ((a << 24) |
2095 *((uint32_t *) oline2) = pixel;
2100 for (xx = 0; xx < width; xx++) {
2101 unsigned char r = *iline2++;
2102 unsigned char g = *iline2++;
2103 unsigned char b = *iline2++;
2104 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2105 uint32_t pixel = ((a << 24) |
2109 *((uint32_t *) oline2) = pixel;
2114 for (xx = 0; xx < width; xx++) {
2115 unsigned char b = *iline2++;
2116 unsigned char g = *iline2++;
2117 unsigned char r = *iline2++;
2118 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2119 uint32_t pixel = ((a << 24) |
2123 *((uint32_t *) oline2) = pixel;
2137 # ifndef USE_BACKBUFFER
2138 if (bm) [bm release];
2146 /* Returns a transformation matrix to do rotation as per the provided
2147 EXIF "Orientation" value.
2149 static CGAffineTransform
2150 exif_rotate (int rot, CGSize rect)
2152 CGAffineTransform trans = CGAffineTransformIdentity;
2154 case 2: // flip horizontal
2155 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2156 trans = CGAffineTransformScale (trans, -1, 1);
2159 case 3: // rotate 180
2160 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2161 trans = CGAffineTransformRotate (trans, M_PI);
2164 case 4: // flip vertical
2165 trans = CGAffineTransformMakeTranslation (0, rect.height);
2166 trans = CGAffineTransformScale (trans, 1, -1);
2169 case 5: // transpose (UL-to-LR axis)
2170 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2171 trans = CGAffineTransformScale (trans, -1, 1);
2172 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2175 case 6: // rotate 90
2176 trans = CGAffineTransformMakeTranslation (0, rect.width);
2177 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2180 case 7: // transverse (UR-to-LL axis)
2181 trans = CGAffineTransformMakeScale (-1, 1);
2182 trans = CGAffineTransformRotate (trans, M_PI / 2);
2185 case 8: // rotate 270
2186 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2187 trans = CGAffineTransformRotate (trans, M_PI / 2);
2199 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2200 Bool nsimg_p, void *img_arg,
2201 XRectangle *geom_ret, int exif_rotation)
2205 CGImageSourceRef cgsrc;
2206 # endif // USE_IPHONE
2209 CGContextRef cgc = d->cgc;
2213 NSImage *nsimg = (NSImage *) img_arg;
2214 imgr = [nsimg size];
2217 // convert the NSImage to a CGImage via the toll-free-bridging
2218 // of NSData and CFData...
2220 NSData *nsdata = [NSBitmapImageRep
2221 TIFFRepresentationOfImageRepsInArray:
2222 [nsimg representations]];
2223 CFDataRef cfdata = (CFDataRef) nsdata;
2224 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2225 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2226 # else // USE_IPHONE
2227 cgi = nsimg.CGImage;
2228 # endif // USE_IPHONE
2231 cgi = (CGImageRef) img_arg;
2232 imgr.width = CGImageGetWidth (cgi);
2233 imgr.height = CGImageGetHeight (cgi);
2236 Bool rot_p = (exif_rotation >= 5);
2239 imgr = NSMakeSize (imgr.height, imgr.width);
2241 CGRect winr = d->frame;
2242 float rw = winr.size.width / imgr.width;
2243 float rh = winr.size.height / imgr.height;
2244 float r = (rw < rh ? rw : rh);
2247 dst.size.width = imgr.width * r;
2248 dst.size.height = imgr.height * r;
2249 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2250 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2252 dst2.origin.x = dst2.origin.y = 0;
2254 dst2.size.width = dst.size.height;
2255 dst2.size.height = dst.size.width;
2257 dst2.size = dst.size;
2260 // Clear the part not covered by the image to background or black.
2262 if (d->type == WINDOW)
2263 XClearWindow (dpy, d);
2265 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2266 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2269 CGAffineTransform trans =
2270 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2272 CGContextSaveGState (cgc);
2273 CGContextConcatCTM (cgc,
2274 CGAffineTransformMakeTranslation (dst.origin.x,
2276 CGContextConcatCTM (cgc, trans);
2277 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2278 CGContextDrawImage (cgc, dst2, cgi);
2279 CGContextRestoreGState (cgc);
2284 CGImageRelease (cgi);
2286 # endif // USE_IPHONE
2289 geom_ret->x = dst.origin.x;
2290 geom_ret->y = dst.origin.y;
2291 geom_ret->width = dst.size.width;
2292 geom_ret->height = dst.size.height;
2295 invalidate_drawable_cache (d);
2301 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2303 unsigned int w, unsigned int h,
2304 unsigned long fg, unsigned int bg,
2307 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2308 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2309 (char *) data, w, h, 0, 0);
2311 gcv.foreground = fg;
2312 gcv.background = bg;
2313 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2314 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2317 XDestroyImage (image);
2322 XCreatePixmap (Display *dpy, Drawable d,
2323 unsigned int width, unsigned int height, unsigned int depth)
2325 char *data = (char *) malloc (width * height * 4);
2326 if (! data) return 0;
2328 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2330 p->frame.size.width = width;
2331 p->frame.size.height = height;
2332 p->pixmap.depth = depth;
2333 p->pixmap.cgc_buffer = data;
2335 /* Quartz doesn't have a 1bpp image type.
2336 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2337 don't support that! So we always use 32bpp, regardless of depth. */
2339 p->cgc = CGBitmapContextCreate (data, width, height,
2340 8, /* bits per component */
2341 width * 4, /* bpl */
2343 // Without this, it returns 0...
2344 (kCGImageAlphaNoneSkipFirst |
2345 kCGBitmapByteOrder32Host)
2347 Assert (p->cgc, "could not create CGBitmapContext");
2353 XFreePixmap (Display *d, Pixmap p)
2355 Assert (p && p->type == PIXMAP, "not a pixmap");
2356 invalidate_drawable_cache (p);
2357 CGContextRelease (p->cgc);
2358 if (p->pixmap.cgc_buffer)
2359 free (p->pixmap.cgc_buffer);
2366 copy_pixmap (Display *dpy, Pixmap p)
2369 Assert (p->type == PIXMAP, "not a pixmap");
2371 int width = p->frame.size.width;
2372 int height = p->frame.size.height;
2373 char *data = (char *) malloc (width * height * 4);
2374 if (! data) return 0;
2376 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2378 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2381 p2->pixmap.cgc_buffer = data;
2382 p2->cgc = CGBitmapContextCreate (data, width, height,
2383 8, /* bits per component */
2384 width * 4, /* bpl */
2386 // Without this, it returns 0...
2387 (kCGImageAlphaNoneSkipFirst |
2388 kCGBitmapByteOrder32Host)
2390 Assert (p2->cgc, "could not create CGBitmapContext");
2396 /* Font metric terminology, as used by X11:
2398 "lbearing" is the distance from the logical origin to the leftmost pixel.
2399 If a character's ink extends to the left of the origin, it is negative.
2401 "rbearing" is the distance from the logical origin to the rightmost pixel.
2403 "descent" is the distance from the logical origin to the bottommost pixel.
2404 For characters with descenders, it is negative.
2406 "ascent" is the distance from the logical origin to the topmost pixel.
2407 It is the number of pixels above the baseline.
2409 "width" is the distance from the logical origin to the position where
2410 the logical origin of the next character should be placed.
2412 If "rbearing" is greater than "width", then this character overlaps the
2413 following character. If smaller, then there is trailing blank space.
2417 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2420 query_font (Font fid)
2422 if (!fid || !fid->nsfont) {
2423 Assert (0, "no NSFont in fid");
2426 if (![fid->nsfont fontName]) {
2427 Assert(0, @"broken NSFont in fid");
2434 XFontStruct *f = &fid->metrics;
2435 XCharStruct *min = &f->min_bounds;
2436 XCharStruct *max = &f->max_bounds;
2438 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2441 f->min_char_or_byte2 = first;
2442 f->max_char_or_byte2 = last;
2443 f->default_char = 'M';
2444 f->ascent = CEIL ([fid->nsfont ascender]);
2445 f->descent = -CEIL ([fid->nsfont descender]);
2447 min->width = 255; // set to smaller values in the loop
2450 min->lbearing = 255;
2451 min->rbearing = 255;
2453 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2457 NSBezierPath *bpath = [NSBezierPath bezierPath];
2458 # else // USE_IPHONE
2460 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2461 [fid->nsfont pointSize],
2463 Assert (ctfont, @"no CTFontRef for UIFont");
2464 # endif // USE_IPHONE
2466 for (i = first; i <= last; i++) {
2467 unsigned char str[2];
2471 NSString *nsstr = [NSString stringWithCString:(char *) str
2472 encoding:NSISOLatin1StringEncoding];
2473 NSPoint advancement = { 0, };
2474 NSRect bbox = {{ 0, }, };
2478 /* I can't believe we have to go through this bullshit just to
2479 convert a 'char' to an NSGlyph!!
2481 You might think that we could do
2482 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2483 but that doesn't work; my guess is that glyphWithName expects
2484 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2488 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2489 [ts setFont:fid->nsfont];
2490 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2492 /* Without this, the layout manager ends up on a queue somewhere and is
2493 referenced again after we return to the command loop. Since we don't
2494 use this layout manager again, by that time it may have been garbage
2495 collected, and we crash. Setting this seems to cause `lm' to no
2496 longer be referenced once we exit this block. */
2497 [lm setBackgroundLayoutEnabled:NO];
2499 NSTextContainer *tc = [[NSTextContainer alloc] init];
2500 [lm addTextContainer:tc];
2501 [tc release]; // lm retains tc
2502 [ts addLayoutManager:lm];
2503 [lm release]; // ts retains lm
2504 glyph = [lm glyphAtIndex:0];
2508 /* Compute the bounding box and advancement by converting the glyph
2509 to a bezier path. There appears to be *no other way* to find out
2510 the bounding box of a character: [NSFont boundingRectForGlyph] and
2511 [NSString sizeWithAttributes] both return an advancement-sized
2512 rectangle, not a rectangle completely enclosing the glyph's ink.
2514 advancement.x = advancement.y = 0;
2515 [bpath removeAllPoints];
2516 [bpath moveToPoint:advancement];
2517 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2518 advancement = [bpath currentPoint];
2519 bbox = [bpath bounds];
2521 # else // USE_IPHONE
2523 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2524 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2525 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2526 width of the character and the ascent of the font.
2528 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2529 the CoreText library, but there's no non-CoreText way to turn a
2530 unichar into a CGGlyph.
2532 UniChar uchar = [nsstr characterAtIndex: 0];
2533 CGGlyph cgglyph = 0;
2535 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2537 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2538 kCTFontDefaultOrientation,
2540 CGSize adv = { 0, };
2541 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2543 advancement.x = adv.width;
2544 advancement.y = adv.height;
2546 /* A bug that existed was that the GL FPS display was truncating
2547 characters slightly: commas looked like periods.
2549 At one point, I believed the bounding box was being rounded
2550 wrong and we needed to add padding to it here.
2552 I think what was actually going on was, I was computing rbearing
2553 wrong. Also there was an off-by-one error in texfont.c, displaying
2554 too little of the bitmap.
2556 Adding arbitrarily large padding to the bbox is fine in fontglide
2557 and FPS display, but screws up BSOD. Increasing bbox width makes
2558 inverted text print too wide; decreasing origin makes characters
2561 I think that all 3 states are correct now with the new lbearing
2562 computation plus the texfont fix.
2566 bbox.origin.x -= kludge;
2567 bbox.origin.y -= kludge;
2568 bbox.size.width += kludge;
2569 bbox.size.height += kludge;
2572 # endif // USE_IPHONE
2574 /* Now that we know the advancement and bounding box, we can compute
2575 the lbearing and rbearing.
2577 XCharStruct *cs = &f->per_char[i-first];
2579 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2580 cs->descent = CEIL(-bbox.origin.y);
2581 cs->lbearing = floor (bbox.origin.x);
2582 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2583 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
2584 cs->width = CEIL (advancement.x);
2586 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2588 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2591 max->width = MAX (max->width, cs->width);
2592 max->ascent = MAX (max->ascent, cs->ascent);
2593 max->descent = MAX (max->descent, cs->descent);
2594 max->lbearing = MAX (max->lbearing, cs->lbearing);
2595 max->rbearing = MAX (max->rbearing, cs->rbearing);
2597 min->width = MIN (min->width, cs->width);
2598 min->ascent = MIN (min->ascent, cs->ascent);
2599 min->descent = MIN (min->descent, cs->descent);
2600 min->lbearing = MIN (min->lbearing, cs->lbearing);
2601 min->rbearing = MIN (min->rbearing, cs->rbearing);
2606 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2607 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2608 i, i, cs->width, cs->lbearing, cs->rbearing,
2609 cs->ascent, cs->descent,
2610 bbox.size.width, bbox.size.height,
2611 bbox.origin.x, bbox.origin.y,
2612 advancement.x, advancement.y);
2622 // Since 'Font' includes the metrics, this just makes a copy of that.
2625 XQueryFont (Display *dpy, Font fid)
2628 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2631 // copy XCharStruct array
2632 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2633 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2634 memcpy (f->per_char, fid->metrics.per_char,
2635 size * sizeof (XCharStruct));
2642 copy_font (Font fid)
2644 // copy 'Font' struct
2645 Font fid2 = (Font) malloc (sizeof(*fid2));
2648 // copy XCharStruct array
2649 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2650 fid2->metrics.per_char = (XCharStruct *)
2651 malloc ((size + 2) * sizeof (XCharStruct));
2652 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2653 size * sizeof (XCharStruct));
2655 // copy the other pointers
2656 fid2->ps_name = strdup (fid->ps_name);
2657 // [fid2->nsfont retain];
2658 fid2->metrics.fid = fid2;
2665 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2668 Assert (size > 0, "zero font size");
2673 // "Monaco" only exists in plain.
2674 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2676 if (bold && ital) name = "Courier-BoldOblique";
2677 else if (bold) name = "Courier-Bold";
2678 else if (ital) name = "Courier-Oblique";
2679 else name = "Courier";
2683 // "Georgia" looks better than "Times".
2685 if (bold && ital) name = "Georgia-BoldItalic";
2686 else if (bold) name = "Georgia-Bold";
2687 else if (ital) name = "Georgia-Italic";
2688 else name = "Georgia";
2692 // "Geneva" only exists in plain.
2693 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2694 // "Verdana" renders smoother than "Helvetica" for some reason.
2696 if (bold && ital) name = "Verdana-BoldItalic";
2697 else if (bold) name = "Verdana-Bold";
2698 else if (ital) name = "Verdana-Italic";
2699 else name = "Verdana";
2702 NSString *nsname = [NSString stringWithCString:name
2703 encoding:NSUTF8StringEncoding];
2704 NSFont *f = [NSFont fontWithName:nsname size:size];
2706 *name_ret = strdup(name);
2711 try_native_font (const char *name, float scale,
2712 char **name_ret, float *size_ret)
2714 if (!name) return 0;
2715 const char *spc = strrchr (name, ' ');
2718 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2721 if (size <= 4) return 0;
2725 char *name2 = strdup (name);
2726 name2[strlen(name2) - strlen(spc)] = 0;
2727 NSString *nsname = [NSString stringWithCString:name2
2728 encoding:NSUTF8StringEncoding];
2729 NSFont *f = [NSFont fontWithName:nsname size:size];
2741 /* Returns a random font in the given size and face.
2744 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2747 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2748 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2749 NSArray *fonts = [[NSFontManager sharedFontManager]
2750 availableFontNamesWithTraits:mask];
2751 if (!fonts) return 0;
2753 int n = [fonts count];
2754 if (n <= 0) return 0;
2757 for (j = 0; j < n; j++) {
2758 int i = random() % n;
2759 NSString *name = [fonts objectAtIndex:i];
2760 NSFont *f = [NSFont fontWithName:name size:size];
2763 /* Don't use this font if it (probably) doesn't include ASCII characters.
2765 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2766 if (! (enc == NSUTF8StringEncoding ||
2767 enc == NSISOLatin1StringEncoding ||
2768 enc == NSNonLossyASCIIStringEncoding ||
2769 enc == NSISOLatin2StringEncoding ||
2770 enc == NSUnicodeStringEncoding ||
2771 enc == NSWindowsCP1250StringEncoding ||
2772 enc == NSWindowsCP1252StringEncoding ||
2773 enc == NSMacOSRomanStringEncoding)) {
2774 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2777 // NSLog(@"using \"%@\": %d", name, enc);
2779 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2783 // None of the fonts support ASCII?
2786 # else // USE_IPHONE
2788 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2789 NSArray *families = [UIFont familyNames];
2790 NSMutableDictionary *famdict = [NSMutableDictionary
2791 dictionaryWithCapacity:100];
2792 NSObject *y = [NSNumber numberWithBool:YES];
2793 for (NSString *name in families) {
2794 // There are many dups in the families array -- uniquify it.
2795 [famdict setValue:y forKey:name];
2798 for (NSString *name in famdict) {
2799 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2802 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2805 BOOL bb = MATCH(@"Bold");
2806 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2808 if (!bold != !bb) continue;
2809 if (!ital != !ii) continue;
2811 /* Check if it can do ASCII. No good way to accomplish this!
2812 These are fonts present in iPhone Simulator as of June 2012
2813 that don't include ASCII.
2815 if (MATCH(@"AppleGothic") || // Korean
2816 MATCH(@"Dingbats") || // Dingbats
2817 MATCH(@"Emoji") || // Emoticons
2818 MATCH(@"Geeza") || // Arabic
2819 MATCH(@"Hebrew") || // Hebrew
2820 MATCH(@"HiraKaku") || // Japanese
2821 MATCH(@"HiraMin") || // Japanese
2822 MATCH(@"Kailasa") || // Tibetan
2823 MATCH(@"Ornaments") || // Dingbats
2824 MATCH(@"STHeiti") // Chinese
2828 [fonts addObject:fn];
2833 if (! [fonts count]) return 0; // Nothing suitable?
2835 int i = random() % [fonts count];
2836 NSString *name = [fonts objectAtIndex:i];
2837 UIFont *ff = [UIFont fontWithName:name size:size];
2838 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2842 # endif // USE_IPHONE
2847 try_xlfd_font (const char *name, float scale,
2848 char **name_ret, float *size_ret)
2859 const char *s = (name ? name : "");
2861 while (*s && (*s == '*' || *s == '-'))
2864 while (*s2 && (*s2 != '*' && *s2 != '-'))
2870 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2871 else if (CMP ("random")) rand = YES;
2872 else if (CMP ("bold")) bold = YES;
2873 else if (CMP ("i")) ital = YES;
2874 else if (CMP ("o")) ital = YES;
2875 else if (CMP ("courier")) fixed = YES;
2876 else if (CMP ("fixed")) fixed = YES;
2877 else if (CMP ("m")) fixed = YES;
2878 else if (CMP ("times")) serif = YES;
2879 else if (CMP ("6x10")) fixed = YES, size = 8;
2880 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2881 else if (CMP ("9x15")) fixed = YES, size = 12;
2882 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2883 else if (CMP ("vga")) fixed = YES, size = 12;
2884 else if (CMP ("console")) fixed = YES, size = 12;
2885 else if (CMP ("gallant")) fixed = YES, size = 12;
2887 else if (size == 0) {
2889 if (1 == sscanf (s, " %d ", &n))
2896 if (size < 6 || size > 1000)
2902 nsfont = random_font (bold, ital, size, &ps_name);
2905 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2907 // if that didn't work, turn off attibutes until it does
2908 // (e.g., there is no "Monaco-Bold".)
2910 if (!nsfont && serif) {
2912 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2914 if (!nsfont && ital) {
2916 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2918 if (!nsfont && bold) {
2920 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2922 if (!nsfont && fixed) {
2924 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2928 *name_ret = ps_name;
2938 XLoadFont (Display *dpy, const char *name)
2940 Font fid = (Font) calloc (1, sizeof(*fid));
2945 // Scale up fonts on Retina displays.
2946 scale = dpy->main_window->window.view.contentScaleFactor;
2949 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2951 if (!fid->nsfont && name &&
2952 strchr (name, ' ') &&
2953 !strchr (name, '*')) {
2954 // If name contains a space but no stars, it is a native font spec --
2955 // return NULL so that we know it really didn't exist. Else, it is an
2956 // XLFD font, so keep trying.
2957 XUnloadFont (dpy, fid);
2962 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2964 // We should never return NULL for XLFD fonts.
2966 Assert (0, "no font");
2969 CFRetain (fid->nsfont); // needed for garbage collection?
2971 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2980 XLoadQueryFont (Display *dpy, const char *name)
2982 Font fid = XLoadFont (dpy, name);
2984 return XQueryFont (dpy, fid);
2988 XUnloadFont (Display *dpy, Font fid)
2991 free (fid->ps_name);
2992 if (fid->metrics.per_char)
2993 free (fid->metrics.per_char);
2995 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2996 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2997 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2998 // They're probably not very big...
3000 // [fid->nsfont release];
3001 // CFRelease (fid->nsfont);
3008 XFreeFontInfo (char **names, XFontStruct *info, int n)
3012 for (i = 0; i < n; i++)
3013 if (names[i]) free (names[i]);
3017 for (i = 0; i < n; i++)
3018 if (info[i].per_char)
3019 free (info[i].per_char);
3026 XFreeFont (Display *dpy, XFontStruct *f)
3029 XFreeFontInfo (0, f, 1);
3030 XUnloadFont (dpy, fid);
3036 XSetFont (Display *dpy, GC gc, Font fid)
3039 XUnloadFont (dpy, gc->gcv.font);
3040 gc->gcv.font = copy_font (fid);
3041 [gc->gcv.font->nsfont retain];
3042 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3047 XTextExtents (XFontStruct *f, const char *s, int length,
3048 int *dir_ret, int *ascent_ret, int *descent_ret,
3051 memset (cs, 0, sizeof(*cs));
3053 for (i = 0; i < length; i++) {
3054 unsigned char c = (unsigned char) s[i];
3055 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3056 c = f->default_char;
3057 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3061 cs->ascent = MAX (cs->ascent, cc->ascent);
3062 cs->descent = MAX (cs->descent, cc->descent);
3063 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3064 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3065 cs->width += cc->width;
3069 *ascent_ret = f->ascent;
3070 *descent_ret = f->descent;
3075 XTextWidth (XFontStruct *f, const char *s, int length)
3077 int ascent, descent, dir;
3079 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3085 set_font (Display *dpy, CGContextRef cgc, GC gc)
3087 Font font = gc->gcv.font;
3089 font = XLoadFont (dpy, 0);
3090 gc->gcv.font = font;
3091 [gc->gcv.font->nsfont retain];
3092 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3094 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
3095 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
3100 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3101 const char *str, int len, BOOL clear_background_p)
3103 if (clear_background_p) {
3104 int ascent, descent, dir;
3106 XTextExtents (&gc->gcv.font->metrics, str, len,
3107 &dir, &ascent, &descent, &cs);
3108 draw_rect (dpy, d, gc,
3109 x + MIN (0, cs.lbearing),
3110 y - MAX (0, ascent),
3111 MAX (MAX (0, cs.rbearing) -
3112 MIN (0, cs.lbearing),
3114 MAX (0, ascent) + MAX (0, descent),
3118 CGRect wr = d->frame;
3121 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
3122 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
3125 CGContextRef cgc = d->cgc;
3126 push_fg_gc (d, gc, YES);
3127 set_font (dpy, cgc, gc);
3129 CGContextSetTextDrawingMode (cgc, kCGTextFill);
3130 if (gc->gcv.antialias_p)
3131 CGContextSetShouldAntialias (cgc, YES);
3132 CGContextShowTextAtPoint (cgc,
3134 wr.origin.y + wr.size.height - y,
3143 unsigned long argb = gc->gcv.foreground;
3144 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3145 float a = ((argb >> 24) & 0xFF) / 255.0;
3146 float r = ((argb >> 16) & 0xFF) / 255.0;
3147 float g = ((argb >> 8) & 0xFF) / 255.0;
3148 float b = ((argb ) & 0xFF) / 255.0;
3149 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3150 NSDictionary *attr =
3151 [NSDictionary dictionaryWithObjectsAndKeys:
3152 gc->gcv.font->nsfont, NSFontAttributeName,
3153 fg, NSForegroundColorAttributeName,
3155 char *s2 = (char *) malloc (len + 1);
3156 strncpy (s2, str, len);
3158 NSString *nsstr = [NSString stringWithCString:s2
3159 encoding:NSISOLatin1StringEncoding];
3162 pos.x = wr.origin.x + x;
3163 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
3164 [nsstr drawAtPoint:pos withAttributes:attr];
3168 invalidate_drawable_cache (d);
3174 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3175 const char *str, int len)
3177 return draw_string (dpy, d, gc, x, y, str, len, NO);
3181 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3182 const char *str, int len)
3184 return draw_string (dpy, d, gc, x, y, str, len, YES);
3189 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3191 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3192 gc->gcv.foreground = fg;
3198 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3200 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3201 gc->gcv.background = bg;
3206 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3208 gc->gcv.alpha_allowed_p = allowed;
3213 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3215 gc->gcv.antialias_p = antialias_p;
3221 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3222 int line_style, int cap_style, int join_style)
3224 gc->gcv.line_width = line_width;
3225 Assert (line_style == LineSolid, "only LineSolid implemented");
3226 // gc->gcv.line_style = line_style;
3227 gc->gcv.cap_style = cap_style;
3228 gc->gcv.join_style = join_style;
3233 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3239 XSetFunction (Display *dpy, GC gc, int which)
3241 gc->gcv.function = which;
3246 XSetSubwindowMode (Display *dpy, GC gc, int which)
3248 gc->gcv.subwindow_mode = which;
3253 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3255 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3257 if (gc->gcv.clip_mask) {
3258 XFreePixmap (dpy, gc->gcv.clip_mask);
3259 CGImageRelease (gc->clip_mask);
3262 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3263 if (gc->gcv.clip_mask)
3265 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3273 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3275 gc->gcv.clip_x_origin = x;
3276 gc->gcv.clip_y_origin = y;
3282 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3283 int *root_x_ret, int *root_y_ret,
3284 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3286 Assert (w && w->type == WINDOW, "not a window");
3289 int x = w->window.last_mouse_x;
3290 int y = w->window.last_mouse_y;
3291 if (root_x_ret) *root_x_ret = x;
3292 if (root_y_ret) *root_y_ret = y;
3293 if (win_x_ret) *win_x_ret = x;
3294 if (win_y_ret) *win_y_ret = y;
3296 # else // !USE_IPHONE
3298 NSWindow *nsw = [w->window.view window];
3300 // get bottom left of window on screen, from bottom left
3301 wpos.x = wpos.y = 0;
3302 wpos = [nsw convertBaseToScreen:wpos];
3305 // get bottom left of view on window, from bottom left
3306 vpos.x = vpos.y = 0;
3307 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3309 // get bottom left of view on screen, from bottom left
3313 // get top left of view on screen, from bottom left
3314 vpos.y += w->frame.size.height;
3316 // get top left of view on screen, from top left
3317 NSArray *screens = [NSScreen screens];
3318 NSScreen *screen = (screens && [screens count] > 0
3319 ? [screens objectAtIndex:0]
3320 : [NSScreen mainScreen]);
3322 double s = w->window.view.contentScaleFactor;
3326 NSRect srect = [screen frame];
3327 vpos.y = (s * srect.size.height) - vpos.y;
3329 // get the mouse position on window, from bottom left
3330 NSEvent *e = [NSApp currentEvent];
3331 NSPoint p = [e locationInWindow];
3333 // get mouse position on screen, from bottom left
3337 // get mouse position on screen, from top left
3338 p.y = srect.size.height - p.y;
3340 if (root_x_ret) *root_x_ret = (int) p.x;
3341 if (root_y_ret) *root_y_ret = (int) p.y;
3342 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3343 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3344 # endif // !USE_IPHONE
3346 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3347 if (root_ret) *root_ret = 0;
3348 if (child_ret) *child_ret = 0;
3353 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3354 int src_x, int src_y,
3355 int *dest_x_ret, int *dest_y_ret,
3358 Assert (w && w->type == WINDOW, "not a window");
3366 # else // !USE_IPHONE
3368 NSWindow *nsw = [w->window.view window];
3370 // get bottom left of window on screen, from bottom left
3371 wpos.x = wpos.y = 0;
3372 wpos = [nsw convertBaseToScreen:wpos];
3375 // get bottom left of view on window, from bottom left
3376 vpos.x = vpos.y = 0;
3377 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3379 // get bottom left of view on screen, from bottom left
3383 // get top left of view on screen, from bottom left
3384 vpos.y += w->frame.size.height;
3386 // get top left of view on screen, from top left
3387 NSArray *screens = [NSScreen screens];
3388 NSScreen *screen = (screens && [screens count] > 0
3389 ? [screens objectAtIndex:0]
3390 : [NSScreen mainScreen]);
3392 double s = w->window.view.contentScaleFactor;
3396 NSRect srect = [screen frame];
3397 vpos.y = (s * srect.size.height) - vpos.y;
3399 // point starts out relative to top left of view
3404 // get point relative to top left of screen
3407 # endif // !USE_IPHONE
3418 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3424 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3427 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3429 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3430 if ((unsigned int) ks <= 255)
3433 // Put control characters in the string. Not meta.
3434 if (e->state & ControlMask) {
3435 if (c >= 'a' && c <= 'z') // Upcase control.
3437 if (c >= '@' && c <= '_') // Shift to control page.
3439 if (c == ' ') // C-SPC is NULL.
3443 if (k_ret) *k_ret = ks;
3444 if (size > 0) buf[0] = c;
3445 if (size > 1) buf[1] = 0;
3446 return (size > 0 ? 1 : 0);
3451 XFlush (Display *dpy)
3453 // Just let the event loop take care of this on its own schedule.
3458 XSync (Display *dpy, Bool flush)
3460 return XFlush (dpy);
3464 // declared in utils/visual.h
3466 has_writable_cells (Screen *s, Visual *v)
3472 visual_depth (Screen *s, Visual *v)
3478 visual_cells (Screen *s, Visual *v)
3484 visual_class (Screen *s, Visual *v)
3489 // declared in utils/grabclient.h
3491 use_subwindow_mode_p (Screen *screen, Window window)