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);
250 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
252 Assert (dpy->cgdpy, "unable to find CGDisplay");
254 # endif // USE_IPHONE
256 # ifndef USE_BACKBUFFER
257 // Funny thing: As of OS X 10.9, if USE_BACKBUFFER is turned off,
258 // then this one's faster.
261 // Figure out this screen's colorspace, and use that for every CGImage.
263 CMProfileRef profile = 0;
264 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
265 Assert (profile, "unable to find colorspace profile");
266 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
267 Assert (dpy->colorspace, "unable to find colorspace");
269 # else // USE_BACKBUFFER
271 // WTF? It's faster if we *do not* use the screen's colorspace!
273 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
274 # endif // USE_BACKBUFFER
276 invalidate_drawable_cache (w);
282 jwxyz_mouse_moved (Display *dpy, Window w, int x, int y)
284 Assert (w && w->type == WINDOW, "not a window");
285 w->window.last_mouse_x = x;
286 w->window.last_mouse_y = y;
292 jwxyz_flush_context (Display *dpy)
294 // This is only used when USE_BACKBUFFER is off.
295 CGContextFlush(dpy->main_window->cgc); // CGContextSynchronize is another possibility.
299 display_sources_data (Display *dpy)
301 return dpy->timers_data;
306 XRootWindow (Display *dpy, int screen)
308 return dpy->main_window;
312 XDefaultScreenOfDisplay (Display *dpy)
318 XDefaultVisualOfScreen (Screen *screen)
320 return screen->visual;
324 XDisplayOfScreen (Screen *s)
330 XDisplayNumberOfScreen (Screen *s)
336 XScreenNumberOfScreen (Screen *s)
342 XDisplayWidth (Display *dpy, int screen)
344 return (int) dpy->main_window->frame.size.width;
348 XDisplayHeight (Display *dpy, int screen)
350 return (int) dpy->main_window->frame.size.height;
354 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
357 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
358 else if (!alpha_allowed_p)
359 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
360 "bogus color pixel");
365 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
366 BOOL alpha_allowed_p, BOOL fill_p)
368 validate_pixel (argb, depth, alpha_allowed_p);
371 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
373 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
375 float a = ((argb >> 24) & 0xFF) / 255.0;
376 float r = ((argb >> 16) & 0xFF) / 255.0;
377 float g = ((argb >> 8) & 0xFF) / 255.0;
378 float b = ((argb ) & 0xFF) / 255.0;
380 CGContextSetRGBFillColor (cgc, r, g, b, a);
382 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
387 set_line_mode (CGContextRef cgc, XGCValues *gcv)
389 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
390 CGContextSetLineJoin (cgc,
391 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
392 gcv->join_style == JoinRound ? kCGLineJoinRound :
394 CGContextSetLineCap (cgc,
395 gcv->cap_style == CapNotLast ? kCGLineCapButt :
396 gcv->cap_style == CapButt ? kCGLineCapButt :
397 gcv->cap_style == CapRound ? kCGLineCapRound :
402 set_clip_mask (Drawable d, GC gc)
404 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
406 Pixmap p = gc->gcv.clip_mask;
408 Assert (p->type == PIXMAP, "not a pixmap");
410 CGRect wr = d->frame;
412 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
413 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
414 - p->frame.size.height;
415 to.size.width = p->frame.size.width;
416 to.size.height = p->frame.size.height;
418 CGContextClipToMask (d->cgc, to, gc->clip_mask);
422 /* Pushes a GC context; sets BlendMode and ClipMask.
425 push_gc (Drawable d, GC gc)
427 CGContextRef cgc = d->cgc;
428 CGContextSaveGState (cgc);
430 switch (gc->gcv.function) {
433 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
434 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
435 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
436 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
437 default: Assert(0, "unknown gcv function"); break;
440 if (gc->gcv.clip_mask)
441 set_clip_mask (d, gc);
444 #define pop_gc(d,gc) CGContextRestoreGState (d->cgc)
447 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
450 push_color_gc (Drawable d, GC gc, unsigned long color,
451 BOOL antialias_p, Bool fill_p)
455 int depth = gc->depth;
456 switch (gc->gcv.function) {
457 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
458 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
461 CGContextRef cgc = d->cgc;
462 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
463 CGContextSetShouldAntialias (cgc, antialias_p);
467 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
470 push_fg_gc (Drawable d, GC gc, Bool fill_p)
472 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
475 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
478 push_bg_gc (Drawable d, GC gc, Bool fill_p)
480 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
485 /* You've got to be fucking kidding me!
487 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
488 with repeated calls to CGContextDrawImage than it is to make a single
489 call to CGContextFillRects() with a list of 1x1 rectangles!
491 I still wouldn't call it *fast*, however...
493 #define XDRAWPOINTS_IMAGES
495 /* Update, 2012: Kurt Revis <krevis@snoize.com> points out that diddling
496 the bitmap data directly is faster. This only works on Pixmaps, though,
497 not Windows. (Fortunately, on iOS, the Window is really a Pixmap.)
499 #define XDRAWPOINTS_CGDATA
502 XDrawPoints (Display *dpy, Drawable d, GC gc,
503 XPoint *points, int count, int mode)
506 CGRect wr = d->frame;
508 push_fg_gc (d, gc, YES);
510 # ifdef XDRAWPOINTS_CGDATA
512 # ifdef USE_BACKBUFFER
513 if (1) // Because of the backbuffer, all iPhone Windows work like Pixmaps.
515 if (d->type == PIXMAP)
518 CGContextRef cgc = d->cgc;
519 void *data = CGBitmapContextGetData (cgc);
520 size_t bpr = CGBitmapContextGetBytesPerRow (cgc);
521 size_t w = CGBitmapContextGetWidth (cgc);
522 size_t h = CGBitmapContextGetHeight (cgc);
524 Assert (data, "no bitmap data in Drawable");
526 unsigned int argb = gc->gcv.foreground;
527 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
529 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
531 CGFloat x0 = wr.origin.x;
532 CGFloat y0 = wr.origin.y; // Y axis is refreshingly not flipped.
534 // It's uglier, but faster, to hoist the conditional out of the loop.
535 if (mode == CoordModePrevious) {
536 CGFloat x = x0, y = y0;
537 for (i = 0; i < count; i++, points++) {
541 if (x >= 0 && x < w && y >= 0 && y < h) {
542 unsigned int *p = (unsigned int *)
543 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
548 for (i = 0; i < count; i++, points++) {
549 CGFloat x = x0 + points->x;
550 CGFloat y = y0 + points->y;
552 if (x >= 0 && x < w && y >= 0 && y < h) {
553 unsigned int *p = (unsigned int *)
554 ((char *) data + (size_t) y * bpr + (size_t) x * 4);
560 } else /* d->type == WINDOW */
562 # endif /* XDRAWPOINTS_CGDATA */
565 # ifdef XDRAWPOINTS_IMAGES
567 unsigned int argb = gc->gcv.foreground;
568 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
570 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
572 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4,
574 CGImageRef cgi = CGImageCreate (1, 1,
577 /* Host-ordered, since we're using the
578 address of an int as the color data. */
579 (kCGImageAlphaNoneSkipFirst |
580 kCGBitmapByteOrder32Host),
583 NO, /* interpolate */
584 kCGRenderingIntentDefault);
585 CGDataProviderRelease (prov);
587 CGContextRef cgc = d->cgc;
589 rect.size.width = rect.size.height = 1;
590 for (i = 0; i < count; i++) {
591 if (i > 0 && mode == CoordModePrevious) {
592 rect.origin.x += points->x;
593 rect.origin.x -= points->y;
595 rect.origin.x = wr.origin.x + points->x;
596 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
599 //Assert(CGImageGetColorSpace (cgi) == dpy->colorspace,"bad colorspace");
600 CGContextDrawImage (cgc, rect, cgi);
604 CGImageRelease (cgi);
606 # else /* ! XDRAWPOINTS_IMAGES */
608 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
611 for (i = 0; i < count; i++) {
612 r->size.width = r->size.height = 1;
613 if (i > 0 && mode == CoordModePrevious) {
614 r->origin.x = r[-1].origin.x + points->x;
615 r->origin.y = r[-1].origin.x - points->y;
617 r->origin.x = wr.origin.x + points->x;
618 r->origin.y = wr.origin.y + wr.size.height - points->y;
624 CGContextFillRects (d->cgc, rects, count);
627 # endif /* ! XDRAWPOINTS_IMAGES */
631 invalidate_drawable_cache (d);
638 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
643 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
647 static void draw_rect (Display *, Drawable, GC,
648 int x, int y, unsigned int width, unsigned int height,
649 BOOL foreground_p, BOOL fill_p);
652 bitmap_context_p (Drawable d)
654 # ifdef USE_BACKBUFFER
657 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
658 return d->type == PIXMAP;
663 fill_rect_memset (void *dst, size_t dst_pitch, uint32_t fill_data,
664 size_t fill_width, size_t fill_height)
666 Assert(sizeof(wchar_t) == 4, "somebody changed the ABI");
667 while (fill_height) {
668 // Would be nice if Apple used SSE/NEON in wmemset. Maybe someday.
669 wmemset (dst, fill_data, fill_width);
671 dst = (char *) dst + dst_pitch;
676 seek_xy (void *dst, size_t dst_pitch, unsigned x, unsigned y)
678 return (char *)dst + dst_pitch * y + x * 4;
682 drawable_depth (Drawable d)
684 return (d->type == WINDOW
685 ? visual_depth (NULL, NULL)
691 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
692 int src_x, int src_y,
693 unsigned int width, unsigned int height,
694 int dst_x, int dst_y)
696 Assert (gc, "no GC");
697 Assert ((width < 65535), "improbably large width");
698 Assert ((height < 65535), "improbably large height");
699 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
700 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
701 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
702 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
704 if (width == 0 || height == 0)
707 if (gc->gcv.function == GXset ||
708 gc->gcv.function == GXclear) {
709 // "set" and "clear" are dumb drawing modes that ignore the source
710 // bits and just draw solid rectangles.
712 (gc->gcv.function == GXset
713 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
714 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
715 gc->depth, gc->gcv.alpha_allowed_p, YES);
716 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
720 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
721 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
722 // bounds of their drawables.
723 BOOL clipped = NO; // Whether we did any clipping of the rects.
725 src_frame = src->frame;
726 dst_frame = dst->frame;
728 // Initialize src_rect...
730 src_rect.origin.x = src_frame.origin.x + src_x;
731 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
733 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
734 src_rect.size.width = width;
735 src_rect.size.height = height;
737 // Initialize dst_rect...
739 dst_rect.origin.x = dst_frame.origin.x + dst_x;
740 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
742 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
743 dst_rect.size.width = width;
744 dst_rect.size.height = height;
746 // Clip rects to frames...
749 # define CLIP(THIS,THAT,VAL,SIZE) do { \
750 float off = THIS##_rect.origin.VAL; \
753 THIS##_rect.size.SIZE += off; \
754 THAT##_rect.size.SIZE += off; \
755 THIS##_rect.origin.VAL -= off; \
756 THAT##_rect.origin.VAL -= off; \
758 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
759 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
762 THIS##_rect.size.SIZE -= off; \
763 THAT##_rect.size.SIZE -= off; \
766 CLIP (dst, src, x, width);
767 CLIP (dst, src, y, height);
769 // Not actually the original dst_rect, just the one before it's clipped to
771 CGRect orig_dst_rect = dst_rect;
773 CLIP (src, dst, x, width);
774 CLIP (src, dst, y, height);
777 if (orig_dst_rect.size.width <= 0 || orig_dst_rect.size.height <= 0)
780 // Sort-of-special case where no pixels can be grabbed from the source,
781 // and the whole destination is filled with the background color.
782 if (src_rect.size.width < 0 || src_rect.size.height < 0) {
784 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
785 (int)src_rect.size.height == (int)dst_rect.size.height,
788 src_rect.size.width = 0;
789 src_rect.size.height = 0;
790 dst_rect.size.width = 0;
791 dst_rect.size.height = 0;
794 NSObject *releaseme = 0;
797 BOOL free_cgi_p = NO;
800 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
801 going on with clipping masks or depths or anything, optimize it by
802 just doing a memcpy instead of going through a CGI.
804 if (bitmap_context_p (src)) {
806 if (bitmap_context_p (dst) &&
807 gc->gcv.function == GXcopy &&
808 !gc->gcv.clip_mask &&
809 drawable_depth (src) == drawable_depth (dst)) {
811 Assert(!(int)src_frame.origin.x &&
812 !(int)src_frame.origin.y &&
813 !(int)dst_frame.origin.x &&
814 !(int)dst_frame.origin.y,
815 "unexpected non-zero origin");
817 char *src_data = CGBitmapContextGetData(src->cgc);
818 char *dst_data = CGBitmapContextGetData(dst->cgc);
819 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
820 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
822 // Int to float and back again. It's not very safe, but it seems to work.
823 int src_x0 = src_rect.origin.x;
824 int dst_x0 = dst_rect.origin.x;
826 // Flip the Y-axis a second time.
827 int src_y0 = (src_frame.origin.y + src_frame.size.height -
828 src_rect.size.height - src_rect.origin.y);
829 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
830 dst_rect.size.height - dst_rect.origin.y);
832 unsigned width0 = (int) src_rect.size.width;
833 unsigned height0 = (int) src_rect.size.height;
835 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
836 (int)src_rect.size.height == (int)dst_rect.size.height,
839 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
840 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
841 size_t src_pitch0 = src_pitch;
842 size_t dst_pitch0 = dst_pitch;
843 size_t bytes = width0 * 4;
845 if (src == dst && dst_y0 > src_y0) {
846 // Copy upwards if the areas might overlap.
847 src_data0 += src_pitch0 * (height0 - 1);
848 dst_data0 += dst_pitch0 * (height0 - 1);
849 src_pitch0 = -src_pitch0;
850 dst_pitch0 = -dst_pitch0;
853 size_t lines0 = height0;
855 // memcpy is an alias for memmove on OS X.
856 memmove(dst_data0, src_data0, bytes);
857 src_data0 += src_pitch0;
858 dst_data0 += dst_pitch0;
864 int orig_dst_x = orig_dst_rect.origin.x;
865 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
866 orig_dst_rect.origin.y - orig_dst_rect.size.height);
867 int orig_width = orig_dst_rect.size.width;
868 int orig_height = orig_dst_rect.size.height;
870 Assert (orig_dst_x >= 0 &&
871 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
873 orig_dst_y + orig_height <= (int) dst_frame.size.height,
876 if (orig_dst_y < dst_y0) {
877 fill_rect_memset (seek_xy (dst_data, dst_pitch,
878 orig_dst_x, orig_dst_y), dst_pitch,
879 gc->gcv.background, orig_width,
880 dst_y0 - orig_dst_y);
883 if (orig_dst_y + orig_height > dst_y0 + height0) {
884 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
887 gc->gcv.background, orig_width,
888 orig_dst_y + orig_height - dst_y0 - height0);
891 if (orig_dst_x < dst_x0) {
892 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
893 dst_pitch, gc->gcv.background,
894 dst_x0 - orig_dst_x, height0);
897 if (dst_x0 + width0 < orig_dst_x + orig_width) {
898 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
900 dst_pitch, gc->gcv.background,
901 orig_dst_x + orig_width - dst_x0 - width0,
906 invalidate_drawable_cache (dst);
911 // If we are copying from a Pixmap to a Pixmap or Window, we must first
912 // copy the bits to an intermediary CGImage object, then copy that to the
913 // destination drawable's CGContext.
915 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
916 // case of copying from a Pixmap back to itself, but I don't think that
917 // happens very often anyway.)
919 // First we get a CGImage out of the pixmap CGContext -- it's the whole
920 // pixmap, but it presumably shares the data pointer instead of copying
921 // it. We then cache that CGImage it inside the Pixmap object. Note:
922 // invalidate_drawable_cache() must be called to discard this any time a
923 // modification is made to the pixmap, or we'll end up re-using old bits.
926 src->cgi = CGBitmapContextCreateImage (src->cgc);
929 // if doing a sub-rect, trim it down.
930 if (src_rect.origin.x != src_frame.origin.x ||
931 src_rect.origin.y != src_frame.origin.y ||
932 src_rect.size.width != src_frame.size.width ||
933 src_rect.size.height != src_frame.size.height) {
934 // #### I don't understand why this is needed...
935 src_rect.origin.y = (src_frame.size.height -
936 src_rect.size.height - src_rect.origin.y);
937 // This does not copy image data, so it should be fast.
938 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
942 if (src->type == PIXMAP && src->pixmap.depth == 1)
945 # ifndef USE_BACKBUFFER
946 } else { /* (src->type == WINDOW) */
948 NSRect nsfrom; // NSRect != CGRect on 10.4
949 nsfrom.origin.x = src_rect.origin.x;
950 nsfrom.origin.y = src_rect.origin.y;
951 nsfrom.size.width = src_rect.size.width;
952 nsfrom.size.height = src_rect.size.height;
956 // If we are copying from a window to itself, we can use NSCopyBits()
957 // without first copying the rectangle to an intermediary CGImage.
958 // This is ~28% faster (but I *expected* it to be twice as fast...)
959 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
965 // If we are copying from a Window to a Pixmap, we must first copy
966 // the bits to an intermediary CGImage object, then copy that to the
967 // Pixmap's CGContext.
969 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
970 initWithFocusedViewRect:nsfrom];
971 unsigned char *data = [bm bitmapData];
972 int bps = [bm bitsPerSample];
973 int bpp = [bm bitsPerPixel];
974 int bpl = [bm bytesPerRow];
977 // create a CGImage from those bits.
978 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
979 // but that method didn't exist in 10.4.)
981 CGDataProviderRef prov =
982 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
984 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
987 /* Use whatever default bit ordering we got from
988 initWithFocusedViewRect. I would have assumed
989 that it was (kCGImageAlphaNoneSkipFirst |
990 kCGBitmapByteOrder32Host), but on Intel,
996 NO, /* interpolate */
997 kCGRenderingIntentDefault);
999 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
1000 CGDataProviderRelease (prov);
1003 # endif // !USE_BACKBUFFER
1006 CGContextRef cgc = dst->cgc;
1008 if (mask_p) { // src depth == 1
1010 push_bg_gc (dst, gc, YES);
1012 // fill the destination rectangle with solid background...
1013 CGContextFillRect (cgc, orig_dst_rect);
1015 Assert (cgc, "no CGC with 1-bit XCopyArea");
1017 // then fill in a solid rectangle of the fg color, using the image as an
1018 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1019 set_color (cgc, gc->gcv.foreground, gc->depth,
1020 gc->gcv.alpha_allowed_p, YES);
1021 CGContextClipToMask (cgc, dst_rect, cgi);
1022 CGContextFillRect (cgc, dst_rect);
1026 } else { // src depth > 1
1030 // If either the src or dst rects did not lie within their drawables,
1031 // then we have adjusted both the src and dst rects to account for
1032 // the clipping; that means we need to first clear to the background,
1033 // so that clipped bits end up in the bg color instead of simply not
1037 set_color (cgc, gc->gcv.background, gc->depth,
1038 gc->gcv.alpha_allowed_p, YES);
1039 CGContextFillRect (cgc, orig_dst_rect);
1043 // copy the CGImage onto the destination CGContext
1044 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1045 CGContextDrawImage (cgc, dst_rect, cgi);
1047 // No cgi means src == dst, and both are Windows.
1049 # ifdef USE_BACKBUFFER
1050 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1052 # else // !USE_BACKBUFFER
1054 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1055 nsfrom.origin.y = src_rect.origin.y;
1056 nsfrom.size.width = src_rect.size.width;
1057 nsfrom.size.height = src_rect.size.height;
1059 nsto.x = dst_rect.origin.x;
1060 nsto.y = dst_rect.origin.y;
1061 NSCopyBits (0, nsfrom, nsto);
1062 # endif // !USE_BACKBUFFER
1068 if (free_cgi_p) CGImageRelease (cgi);
1070 if (releaseme) [releaseme release];
1071 invalidate_drawable_cache (dst);
1077 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1078 int src_x, int src_y,
1079 unsigned width, int height,
1080 int dest_x, int dest_y, unsigned long plane)
1082 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1084 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1085 // not to white/black.
1086 return XCopyArea (dpy, src, dest, gc,
1087 src_x, src_y, width, height, dest_x, dest_y);
1092 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1094 // when drawing a zero-length line, obey line-width and cap-style.
1095 if (x1 == x2 && y1 == y2) {
1096 int w = gc->gcv.line_width;
1099 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1100 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1102 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1105 CGRect wr = d->frame;
1107 p.x = wr.origin.x + x1;
1108 p.y = wr.origin.y + wr.size.height - y1;
1110 push_fg_gc (d, gc, NO);
1112 CGContextRef cgc = d->cgc;
1113 set_line_mode (cgc, &gc->gcv);
1114 CGContextBeginPath (cgc);
1115 CGContextMoveToPoint (cgc, p.x, p.y);
1116 p.x = wr.origin.x + x2;
1117 p.y = wr.origin.y + wr.size.height - y2;
1118 CGContextAddLineToPoint (cgc, p.x, p.y);
1119 CGContextStrokePath (cgc);
1121 invalidate_drawable_cache (d);
1126 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1131 CGRect wr = d->frame;
1132 push_fg_gc (d, gc, NO);
1134 CGContextRef cgc = d->cgc;
1136 set_line_mode (cgc, &gc->gcv);
1138 // if the first and last points coincide, use closepath to get
1139 // the proper line-joining.
1140 BOOL closed_p = (points[0].x == points[count-1].x &&
1141 points[0].y == points[count-1].y);
1142 if (closed_p) count--;
1144 p.x = wr.origin.x + points->x;
1145 p.y = wr.origin.y + wr.size.height - points->y;
1147 CGContextBeginPath (cgc);
1148 CGContextMoveToPoint (cgc, p.x, p.y);
1149 for (i = 1; i < count; i++) {
1150 if (mode == CoordModePrevious) {
1154 p.x = wr.origin.x + points->x;
1155 p.y = wr.origin.y + wr.size.height - points->y;
1157 CGContextAddLineToPoint (cgc, p.x, p.y);
1160 if (closed_p) CGContextClosePath (cgc);
1161 CGContextStrokePath (cgc);
1163 invalidate_drawable_cache (d);
1169 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1172 CGRect wr = d->frame;
1174 CGContextRef cgc = d->cgc;
1176 push_fg_gc (d, gc, NO);
1177 set_line_mode (cgc, &gc->gcv);
1178 CGContextBeginPath (cgc);
1179 for (i = 0; i < count; i++) {
1180 CGContextMoveToPoint (cgc,
1181 wr.origin.x + segments->x1,
1182 wr.origin.y + wr.size.height - segments->y1);
1183 CGContextAddLineToPoint (cgc,
1184 wr.origin.x + segments->x2,
1185 wr.origin.y + wr.size.height - segments->y2);
1188 CGContextStrokePath (cgc);
1190 invalidate_drawable_cache (d);
1196 XClearWindow (Display *dpy, Window win)
1198 Assert (win && win->type == WINDOW, "not a window");
1199 CGRect wr = win->frame;
1200 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1204 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1206 Assert (w && w->type == WINDOW, "not a window");
1207 validate_pixel (pixel, 32, NO);
1208 w->window.background = pixel;
1213 draw_rect (Display *dpy, Drawable d, GC gc,
1214 int x, int y, unsigned int width, unsigned int height,
1215 BOOL foreground_p, BOOL fill_p)
1217 CGRect wr = d->frame;
1219 r.origin.x = wr.origin.x + x;
1220 r.origin.y = wr.origin.y + wr.size.height - y - height;
1221 r.size.width = width;
1222 r.size.height = height;
1226 push_fg_gc (d, gc, fill_p);
1228 push_bg_gc (d, gc, fill_p);
1231 CGContextRef cgc = d->cgc;
1233 CGContextFillRect (cgc, r);
1236 set_line_mode (cgc, &gc->gcv);
1237 CGContextStrokeRect (cgc, r);
1242 invalidate_drawable_cache (d);
1247 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1248 unsigned int width, unsigned int height)
1250 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1255 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1256 unsigned int width, unsigned int height)
1258 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1263 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1265 CGRect wr = d->frame;
1267 CGContextRef cgc = d->cgc;
1268 push_fg_gc (d, gc, YES);
1269 for (i = 0; i < n; i++) {
1271 r.origin.x = wr.origin.x + rects->x;
1272 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1273 r.size.width = rects->width;
1274 r.size.height = rects->height;
1275 CGContextFillRect (cgc, r);
1279 invalidate_drawable_cache (d);
1285 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1287 Assert (win && win->type == WINDOW, "not a window");
1288 CGContextRef cgc = win->cgc;
1289 set_color (cgc, win->window.background, 32, NO, YES);
1290 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1296 XFillPolygon (Display *dpy, Drawable d, GC gc,
1297 XPoint *points, int npoints, int shape, int mode)
1299 CGRect wr = d->frame;
1301 push_fg_gc (d, gc, YES);
1302 CGContextRef cgc = d->cgc;
1303 CGContextBeginPath (cgc);
1305 for (i = 0; i < npoints; i++) {
1306 if (i > 0 && mode == CoordModePrevious) {
1310 x = wr.origin.x + points[i].x;
1311 y = wr.origin.y + wr.size.height - points[i].y;
1315 CGContextMoveToPoint (cgc, x, y);
1317 CGContextAddLineToPoint (cgc, x, y);
1319 CGContextClosePath (cgc);
1320 if (gc->gcv.fill_rule == EvenOddRule)
1321 CGContextEOFillPath (cgc);
1323 CGContextFillPath (cgc);
1325 invalidate_drawable_cache (d);
1329 #define radians(DEG) ((DEG) * M_PI / 180.0)
1330 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1333 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1334 unsigned int width, unsigned int height, int angle1, int angle2,
1337 CGRect wr = d->frame;
1339 bound.origin.x = wr.origin.x + x;
1340 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1341 bound.size.width = width;
1342 bound.size.height = height;
1345 ctr.x = bound.origin.x + bound.size.width /2;
1346 ctr.y = bound.origin.y + bound.size.height/2;
1348 float r1 = radians (angle1/64.0);
1349 float r2 = radians (angle2/64.0) + r1;
1350 BOOL clockwise = angle2 < 0;
1351 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1353 push_fg_gc (d, gc, fill_p);
1355 CGContextRef cgc = d->cgc;
1356 CGContextBeginPath (cgc);
1358 CGContextSaveGState(cgc);
1359 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1360 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1362 CGContextMoveToPoint (cgc, 0, 0);
1364 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1365 CGContextRestoreGState (cgc); // restore before stroke, for line width
1368 CGContextClosePath (cgc); // for proper line joining
1371 CGContextFillPath (cgc);
1373 set_line_mode (cgc, &gc->gcv);
1374 CGContextStrokePath (cgc);
1378 invalidate_drawable_cache (d);
1383 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1384 unsigned int width, unsigned int height, int angle1, int angle2)
1386 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1390 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1391 unsigned int width, unsigned int height, int angle1, int angle2)
1393 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1397 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1400 for (i = 0; i < narcs; i++)
1401 draw_arc (dpy, d, gc,
1402 arcs[i].x, arcs[i].y,
1403 arcs[i].width, arcs[i].height,
1404 arcs[i].angle1, arcs[i].angle2,
1410 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1413 for (i = 0; i < narcs; i++)
1414 draw_arc (dpy, d, gc,
1415 arcs[i].x, arcs[i].y,
1416 arcs[i].width, arcs[i].height,
1417 arcs[i].angle1, arcs[i].angle2,
1424 gcv_defaults (XGCValues *gcv, int depth)
1426 memset (gcv, 0, sizeof(*gcv));
1427 gcv->function = GXcopy;
1428 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1429 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1430 gcv->line_width = 1;
1431 gcv->cap_style = CapNotLast;
1432 gcv->join_style = JoinMiter;
1433 gcv->fill_rule = EvenOddRule;
1435 gcv->alpha_allowed_p = NO;
1436 gcv->antialias_p = YES;
1440 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1443 Assert (gc && from, "no gc");
1444 if (!gc || !from) return;
1446 if (mask & GCFunction) gc->gcv.function = from->function;
1447 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1448 if (mask & GCBackground) gc->gcv.background = from->background;
1449 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1450 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1451 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1452 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1453 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1454 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1455 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1457 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1458 if (mask & GCFont) XSetFont (0, gc, from->font);
1460 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1461 gc->gcv.alpha_allowed_p);
1462 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1463 gc->gcv.alpha_allowed_p);
1465 Assert ((! (mask & (GCLineStyle |
1472 GCGraphicsExposures |
1476 "unimplemented gcvalues mask");
1481 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1483 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1484 if (d->type == WINDOW) {
1486 } else { /* (d->type == PIXMAP) */
1487 gc->depth = d->pixmap.depth;
1490 gcv_defaults (&gc->gcv, gc->depth);
1491 set_gcv (gc, xgcv, mask);
1496 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1498 set_gcv (gc, gcv, mask);
1504 XFreeGC (Display *dpy, GC gc)
1507 XUnloadFont (dpy, gc->gcv.font);
1509 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1511 if (gc->gcv.clip_mask) {
1512 XFreePixmap (dpy, gc->gcv.clip_mask);
1513 CGImageRelease (gc->clip_mask);
1521 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1523 Assert (w && w->type == WINDOW, "not a window");
1524 memset (xgwa, 0, sizeof(*xgwa));
1525 xgwa->x = w->frame.origin.x;
1526 xgwa->y = w->frame.origin.y;
1527 xgwa->width = w->frame.size.width;
1528 xgwa->height = w->frame.size.height;
1530 xgwa->screen = dpy->screen;
1531 xgwa->visual = dpy->screen->visual;
1536 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1537 int *x_ret, int *y_ret,
1538 unsigned int *w_ret, unsigned int *h_ret,
1539 unsigned int *bw_ret, unsigned int *d_ret)
1541 *x_ret = d->frame.origin.x;
1542 *y_ret = d->frame.origin.y;
1543 *w_ret = d->frame.size.width;
1544 *h_ret = d->frame.size.height;
1545 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1546 *root_ret = RootWindow (dpy, 0);
1553 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1555 // store 32 bit ARGB in the pixel field.
1556 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1557 color->pixel = (uint32_t)
1559 (((color->red >> 8) & 0xFF) << 16) |
1560 (((color->green >> 8) & 0xFF) << 8) |
1561 (((color->blue >> 8) & 0xFF) ));
1566 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1567 unsigned long *pmret, unsigned int npl,
1568 unsigned long *pxret, unsigned int npx)
1574 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1576 Assert(0, "XStoreColors called");
1581 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1583 Assert(0, "XStoreColor called");
1588 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1589 unsigned long planes)
1595 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1597 unsigned char r=0, g=0, b=0;
1598 if (*spec == '#' && strlen(spec) == 7) {
1599 static unsigned const char hex[] = { // yeah yeah, shoot me.
1600 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,
1601 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,
1602 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,
1603 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,
1604 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,
1605 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,
1606 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,
1607 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};
1608 r = (hex[spec[1]] << 4) | hex[spec[2]];
1609 g = (hex[spec[3]] << 4) | hex[spec[4]];
1610 b = (hex[spec[5]] << 4) | hex[spec[6]];
1611 } else if (!strcasecmp(spec,"black")) {
1613 } else if (!strcasecmp(spec,"white")) {
1615 } else if (!strcasecmp(spec,"red")) {
1617 } else if (!strcasecmp(spec,"green")) {
1619 } else if (!strcasecmp(spec,"blue")) {
1621 } else if (!strcasecmp(spec,"cyan")) {
1623 } else if (!strcasecmp(spec,"magenta")) {
1625 } else if (!strcasecmp(spec,"yellow")) {
1631 ret->red = (r << 8) | r;
1632 ret->green = (g << 8) | g;
1633 ret->blue = (b << 8) | b;
1634 ret->flags = DoRed|DoGreen|DoBlue;
1639 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1640 XColor *screen_ret, XColor *exact_ret)
1642 if (! XParseColor (dpy, cmap, name, screen_ret))
1644 *exact_ret = *screen_ret;
1645 return XAllocColor (dpy, cmap, screen_ret);
1649 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1651 validate_pixel (color->pixel, 32, NO);
1652 unsigned char r = ((color->pixel >> 16) & 0xFF);
1653 unsigned char g = ((color->pixel >> 8) & 0xFF);
1654 unsigned char b = ((color->pixel ) & 0xFF);
1655 color->red = (r << 8) | r;
1656 color->green = (g << 8) | g;
1657 color->blue = (b << 8) | b;
1658 color->flags = DoRed|DoGreen|DoBlue;
1663 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1666 for (i = 0; i < n; i++)
1667 XQueryColor (dpy, cmap, &c[i]);
1672 static unsigned long
1673 ximage_getpixel_1 (XImage *ximage, int x, int y)
1675 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1679 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1682 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1684 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1689 static unsigned long
1690 ximage_getpixel_32 (XImage *ximage, int x, int y)
1692 return ((unsigned long)
1693 *((uint32_t *) ximage->data +
1694 (y * (ximage->bytes_per_line >> 2)) +
1699 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1701 *((uint32_t *) ximage->data +
1702 (y * (ximage->bytes_per_line >> 2)) +
1703 x) = (uint32_t) pixel;
1709 XInitImage (XImage *ximage)
1711 if (!ximage->bytes_per_line)
1712 ximage->bytes_per_line = (ximage->depth == 1
1713 ? (ximage->width + 7) / 8
1714 : ximage->width * 4);
1716 if (ximage->depth == 1) {
1717 ximage->f.put_pixel = ximage_putpixel_1;
1718 ximage->f.get_pixel = ximage_getpixel_1;
1719 } else if (ximage->depth == 32 || ximage->depth == 24) {
1720 ximage->f.put_pixel = ximage_putpixel_32;
1721 ximage->f.get_pixel = ximage_getpixel_32;
1723 Assert (0, "unknown depth");
1730 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1731 int format, int offset, char *data,
1732 unsigned int width, unsigned int height,
1733 int bitmap_pad, int bytes_per_line)
1735 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1736 ximage->width = width;
1737 ximage->height = height;
1738 ximage->format = format;
1739 ximage->data = data;
1740 ximage->bitmap_unit = 8;
1741 ximage->byte_order = MSBFirst;
1742 ximage->bitmap_bit_order = ximage->byte_order;
1743 ximage->bitmap_pad = bitmap_pad;
1744 ximage->depth = depth;
1745 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1746 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1747 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1748 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1749 ximage->bytes_per_line = bytes_per_line;
1751 XInitImage (ximage);
1756 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1758 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1759 w, h, from->bitmap_pad, 0);
1760 to->data = (char *) malloc (h * to->bytes_per_line);
1762 if (x >= from->width)
1764 else if (x+w > from->width)
1765 w = from->width - x;
1767 if (y >= from->height)
1769 else if (y+h > from->height)
1770 h = from->height - y;
1773 for (ty = 0; ty < h; ty++)
1774 for (tx = 0; tx < w; tx++)
1775 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1780 XPixmapFormatValues *
1781 XListPixmapFormats (Display *dpy, int *n_ret)
1783 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1785 ret[0].bits_per_pixel = 32;
1786 ret[0].scanline_pad = 8;
1788 ret[1].bits_per_pixel = 1;
1789 ret[1].scanline_pad = 8;
1796 XGetPixel (XImage *ximage, int x, int y)
1798 return ximage->f.get_pixel (ximage, x, y);
1803 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1805 return ximage->f.put_pixel (ximage, x, y, pixel);
1809 XDestroyImage (XImage *ximage)
1811 if (ximage->data) free (ximage->data);
1818 flipbits (unsigned const char *in, unsigned char *out, int length)
1820 static const unsigned char table[256] = {
1821 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1822 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1823 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1824 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1825 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1826 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1827 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1828 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1829 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1830 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1831 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1832 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1833 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1834 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1835 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1836 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1837 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1838 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1839 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1840 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1841 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1842 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1843 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1844 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1845 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1846 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1847 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1848 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1849 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1850 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1851 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1852 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1854 while (--length > 0)
1855 *out++ = table[*in++];
1860 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1861 int src_x, int src_y, int dest_x, int dest_y,
1862 unsigned int w, unsigned int h)
1864 CGRect wr = d->frame;
1866 Assert (gc, "no GC");
1867 Assert ((w < 65535), "improbably large width");
1868 Assert ((h < 65535), "improbably large height");
1869 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1870 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1871 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1872 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1874 // Clip width and height to the bounds of the Drawable
1876 if (dest_x + w > wr.size.width) {
1877 if (dest_x > wr.size.width)
1879 w = wr.size.width - dest_x;
1881 if (dest_y + h > wr.size.height) {
1882 if (dest_y > wr.size.height)
1884 h = wr.size.height - dest_y;
1886 if (w <= 0 || h <= 0)
1889 // Clip width and height to the bounds of the XImage
1891 if (src_x + w > ximage->width) {
1892 if (src_x > ximage->width)
1894 w = ximage->width - src_x;
1896 if (src_y + h > ximage->height) {
1897 if (src_y > ximage->height)
1899 h = ximage->height - src_y;
1901 if (w <= 0 || h <= 0)
1904 CGContextRef cgc = d->cgc;
1906 if (gc->gcv.function == GXset ||
1907 gc->gcv.function == GXclear) {
1908 // "set" and "clear" are dumb drawing modes that ignore the source
1909 // bits and just draw solid rectangles.
1910 set_color (cgc, (gc->gcv.function == GXset
1911 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1912 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1913 gc->depth, gc->gcv.alpha_allowed_p, YES);
1914 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1918 int bpl = ximage->bytes_per_line;
1919 int bpp = ximage->bits_per_pixel;
1920 int bsize = bpl * h;
1921 char *data = ximage->data;
1924 r.origin.x = wr.origin.x + dest_x;
1925 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1931 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1932 to create a CGImage from a sub-rectagle of the XImage.
1934 data += (src_y * bpl) + (src_x * 4);
1935 CGDataProviderRef prov =
1936 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1938 CGImageRef cgi = CGImageCreate (w, h,
1941 /* Need this for XPMs to have the right
1942 colors, e.g. the logo in "maze". */
1943 (kCGImageAlphaNoneSkipFirst |
1944 kCGBitmapByteOrder32Host),
1946 NULL, /* decode[] */
1947 NO, /* interpolate */
1948 kCGRenderingIntentDefault);
1949 CGDataProviderRelease (prov);
1950 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1951 CGContextDrawImage (cgc, r, cgi);
1952 CGImageRelease (cgi);
1954 } else { // (bpp == 1)
1956 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1958 #### However, the bit order within a byte in a 1bpp XImage is
1959 the wrong way around from what Quartz expects, so first we
1960 have to copy the data to reverse it. Shit! Maybe it
1961 would be worthwhile to go through the hacks and #ifdef
1962 each one that diddles 1bpp XImage->data directly...
1964 Assert ((src_x % 8) == 0,
1965 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1967 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1968 unsigned char *flipped = (unsigned char *) malloc (bsize);
1970 flipbits ((unsigned char *) data, flipped, bsize);
1972 CGDataProviderRef prov =
1973 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1974 CGImageRef mask = CGImageMaskCreate (w, h,
1977 NULL, /* decode[] */
1978 NO); /* interpolate */
1979 push_fg_gc (d, gc, YES);
1981 CGContextFillRect (cgc, r); // foreground color
1982 CGContextClipToMask (cgc, r, mask);
1983 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1984 CGContextFillRect (cgc, r); // background color
1988 CGDataProviderRelease (prov);
1989 CGImageRelease (mask);
1992 invalidate_drawable_cache (d);
1999 XGetImage (Display *dpy, Drawable d, int x, int y,
2000 unsigned int width, unsigned int height,
2001 unsigned long plane_mask, int format)
2003 const unsigned char *data = 0;
2004 int depth, ibpp, ibpl;
2005 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
2006 # ifndef USE_BACKBUFFER
2007 NSBitmapImageRep *bm = 0;
2010 Assert ((width < 65535), "improbably large width");
2011 Assert ((height < 65535), "improbably large height");
2012 Assert ((x < 65535 && x > -65535), "improbably large x");
2013 Assert ((y < 65535 && y > -65535), "improbably large y");
2015 CGContextRef cgc = d->cgc;
2017 #ifndef USE_BACKBUFFER
2018 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2019 if (d->type == PIXMAP)
2022 depth = (d->type == PIXMAP
2025 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2026 src_format = BGRA; // #### Should this be ARGB on PPC?
2027 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2028 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2029 data = CGBitmapContextGetData (cgc);
2030 Assert (data, "CGBitmapContextGetData failed");
2032 # ifndef USE_BACKBUFFER
2033 } else { /* (d->type == WINDOW) */
2035 // get the bits (desired sub-rectangle) out of the NSView
2037 nsfrom.origin.x = x;
2038 // nsfrom.origin.y = y;
2039 nsfrom.origin.y = d->frame.size.height - height - y;
2040 nsfrom.size.width = width;
2041 nsfrom.size.height = height;
2042 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2044 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2045 ibpp = [bm bitsPerPixel];
2046 ibpl = [bm bytesPerRow];
2047 data = [bm bitmapData];
2048 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2049 # endif // !USE_BACKBUFFER
2052 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2053 data += (y * ibpl) + (x * (ibpp/8));
2055 format = (depth == 1 ? XYPixmap : ZPixmap);
2056 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
2058 image->data = (char *) malloc (height * image->bytes_per_line);
2060 int obpl = image->bytes_per_line;
2062 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2063 means that on Intel it is BGRA when viewed by bytes (And BGR
2064 when using 24bpp packing).
2066 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2067 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2068 indicator of this latest kink.
2072 const unsigned char *iline = data;
2073 for (yy = 0; yy < height; yy++) {
2075 const unsigned char *iline2 = iline;
2076 for (xx = 0; xx < width; xx++) {
2078 iline2++; // ignore R or A or A or B
2079 iline2++; // ignore G or B or R or G
2080 unsigned char r = *iline2++; // use B or G or G or R
2081 if (ibpp == 32) iline2++; // ignore A or R or B or A
2083 XPutPixel (image, xx, yy, (r ? 1 : 0));
2088 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2089 const unsigned char *iline = data;
2090 unsigned char *oline = (unsigned char *) image->data;
2091 for (yy = 0; yy < height; yy++) {
2093 const unsigned char *iline2 = iline;
2094 unsigned char *oline2 = oline;
2096 switch (src_format) {
2098 for (xx = 0; xx < width; xx++) {
2099 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2100 unsigned char r = *iline2++;
2101 unsigned char g = *iline2++;
2102 unsigned char b = *iline2++;
2103 uint32_t pixel = ((a << 24) |
2107 *((uint32_t *) oline2) = pixel;
2112 for (xx = 0; xx < width; xx++) {
2113 unsigned char r = *iline2++;
2114 unsigned char g = *iline2++;
2115 unsigned char b = *iline2++;
2116 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2117 uint32_t pixel = ((a << 24) |
2121 *((uint32_t *) oline2) = pixel;
2126 for (xx = 0; xx < width; xx++) {
2127 unsigned char b = *iline2++;
2128 unsigned char g = *iline2++;
2129 unsigned char r = *iline2++;
2130 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2131 uint32_t pixel = ((a << 24) |
2135 *((uint32_t *) oline2) = pixel;
2149 # ifndef USE_BACKBUFFER
2150 if (bm) [bm release];
2158 /* Returns a transformation matrix to do rotation as per the provided
2159 EXIF "Orientation" value.
2161 static CGAffineTransform
2162 exif_rotate (int rot, CGSize rect)
2164 CGAffineTransform trans = CGAffineTransformIdentity;
2166 case 2: // flip horizontal
2167 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2168 trans = CGAffineTransformScale (trans, -1, 1);
2171 case 3: // rotate 180
2172 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2173 trans = CGAffineTransformRotate (trans, M_PI);
2176 case 4: // flip vertical
2177 trans = CGAffineTransformMakeTranslation (0, rect.height);
2178 trans = CGAffineTransformScale (trans, 1, -1);
2181 case 5: // transpose (UL-to-LR axis)
2182 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2183 trans = CGAffineTransformScale (trans, -1, 1);
2184 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2187 case 6: // rotate 90
2188 trans = CGAffineTransformMakeTranslation (0, rect.width);
2189 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2192 case 7: // transverse (UR-to-LL axis)
2193 trans = CGAffineTransformMakeScale (-1, 1);
2194 trans = CGAffineTransformRotate (trans, M_PI / 2);
2197 case 8: // rotate 270
2198 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2199 trans = CGAffineTransformRotate (trans, M_PI / 2);
2211 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2212 Bool nsimg_p, void *img_arg,
2213 XRectangle *geom_ret, int exif_rotation)
2217 CGImageSourceRef cgsrc;
2218 # endif // USE_IPHONE
2221 CGContextRef cgc = d->cgc;
2225 NSImage *nsimg = (NSImage *) img_arg;
2226 imgr = [nsimg size];
2229 // convert the NSImage to a CGImage via the toll-free-bridging
2230 // of NSData and CFData...
2232 NSData *nsdata = [NSBitmapImageRep
2233 TIFFRepresentationOfImageRepsInArray:
2234 [nsimg representations]];
2235 CFDataRef cfdata = (CFDataRef) nsdata;
2236 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2237 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2238 # else // USE_IPHONE
2239 cgi = nsimg.CGImage;
2240 # endif // USE_IPHONE
2243 cgi = (CGImageRef) img_arg;
2244 imgr.width = CGImageGetWidth (cgi);
2245 imgr.height = CGImageGetHeight (cgi);
2248 Bool rot_p = (exif_rotation >= 5);
2251 imgr = NSMakeSize (imgr.height, imgr.width);
2253 CGRect winr = d->frame;
2254 float rw = winr.size.width / imgr.width;
2255 float rh = winr.size.height / imgr.height;
2256 float r = (rw < rh ? rw : rh);
2259 dst.size.width = imgr.width * r;
2260 dst.size.height = imgr.height * r;
2261 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2262 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2264 dst2.origin.x = dst2.origin.y = 0;
2266 dst2.size.width = dst.size.height;
2267 dst2.size.height = dst.size.width;
2269 dst2.size = dst.size;
2272 // Clear the part not covered by the image to background or black.
2274 if (d->type == WINDOW)
2275 XClearWindow (dpy, d);
2277 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2278 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2281 CGAffineTransform trans =
2282 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2284 CGContextSaveGState (cgc);
2285 CGContextConcatCTM (cgc,
2286 CGAffineTransformMakeTranslation (dst.origin.x,
2288 CGContextConcatCTM (cgc, trans);
2289 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2290 CGContextDrawImage (cgc, dst2, cgi);
2291 CGContextRestoreGState (cgc);
2296 CGImageRelease (cgi);
2298 # endif // USE_IPHONE
2301 geom_ret->x = dst.origin.x;
2302 geom_ret->y = dst.origin.y;
2303 geom_ret->width = dst.size.width;
2304 geom_ret->height = dst.size.height;
2307 invalidate_drawable_cache (d);
2313 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2315 unsigned int w, unsigned int h,
2316 unsigned long fg, unsigned int bg,
2319 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2320 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2321 (char *) data, w, h, 0, 0);
2323 gcv.foreground = fg;
2324 gcv.background = bg;
2325 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2326 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2329 XDestroyImage (image);
2334 XCreatePixmap (Display *dpy, Drawable d,
2335 unsigned int width, unsigned int height, unsigned int depth)
2337 char *data = (char *) malloc (width * height * 4);
2338 if (! data) return 0;
2340 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2342 p->frame.size.width = width;
2343 p->frame.size.height = height;
2344 p->pixmap.depth = depth;
2345 p->pixmap.cgc_buffer = data;
2347 /* Quartz doesn't have a 1bpp image type.
2348 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2349 don't support that! So we always use 32bpp, regardless of depth. */
2351 p->cgc = CGBitmapContextCreate (data, width, height,
2352 8, /* bits per component */
2353 width * 4, /* bpl */
2355 // Without this, it returns 0...
2356 (kCGImageAlphaNoneSkipFirst |
2357 kCGBitmapByteOrder32Host)
2359 Assert (p->cgc, "could not create CGBitmapContext");
2365 XFreePixmap (Display *d, Pixmap p)
2367 Assert (p && p->type == PIXMAP, "not a pixmap");
2368 invalidate_drawable_cache (p);
2369 CGContextRelease (p->cgc);
2370 if (p->pixmap.cgc_buffer)
2371 free (p->pixmap.cgc_buffer);
2378 copy_pixmap (Display *dpy, Pixmap p)
2381 Assert (p->type == PIXMAP, "not a pixmap");
2383 int width = p->frame.size.width;
2384 int height = p->frame.size.height;
2385 char *data = (char *) malloc (width * height * 4);
2386 if (! data) return 0;
2388 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2390 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2393 p2->pixmap.cgc_buffer = data;
2394 p2->cgc = CGBitmapContextCreate (data, width, height,
2395 8, /* bits per component */
2396 width * 4, /* bpl */
2398 // Without this, it returns 0...
2399 (kCGImageAlphaNoneSkipFirst |
2400 kCGBitmapByteOrder32Host)
2402 Assert (p2->cgc, "could not create CGBitmapContext");
2408 /* Font metric terminology, as used by X11:
2410 "lbearing" is the distance from the logical origin to the leftmost pixel.
2411 If a character's ink extends to the left of the origin, it is negative.
2413 "rbearing" is the distance from the logical origin to the rightmost pixel.
2415 "descent" is the distance from the logical origin to the bottommost pixel.
2416 For characters with descenders, it is negative.
2418 "ascent" is the distance from the logical origin to the topmost pixel.
2419 It is the number of pixels above the baseline.
2421 "width" is the distance from the logical origin to the position where
2422 the logical origin of the next character should be placed.
2424 If "rbearing" is greater than "width", then this character overlaps the
2425 following character. If smaller, then there is trailing blank space.
2429 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2432 query_font (Font fid)
2434 if (!fid || !fid->nsfont) {
2435 Assert (0, "no NSFont in fid");
2438 if (![fid->nsfont fontName]) {
2439 Assert(0, @"broken NSFont in fid");
2446 XFontStruct *f = &fid->metrics;
2447 XCharStruct *min = &f->min_bounds;
2448 XCharStruct *max = &f->max_bounds;
2450 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2453 f->min_char_or_byte2 = first;
2454 f->max_char_or_byte2 = last;
2455 f->default_char = 'M';
2456 f->ascent = CEIL ([fid->nsfont ascender]);
2457 f->descent = -CEIL ([fid->nsfont descender]);
2459 min->width = 255; // set to smaller values in the loop
2462 min->lbearing = 255;
2463 min->rbearing = 255;
2465 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2469 NSBezierPath *bpath = [NSBezierPath bezierPath];
2470 # else // USE_IPHONE
2472 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2473 [fid->nsfont pointSize],
2475 Assert (ctfont, @"no CTFontRef for UIFont");
2476 # endif // USE_IPHONE
2478 for (i = first; i <= last; i++) {
2479 unsigned char str[2];
2483 NSString *nsstr = [NSString stringWithCString:(char *) str
2484 encoding:NSISOLatin1StringEncoding];
2485 NSPoint advancement = { 0, };
2486 NSRect bbox = {{ 0, }, };
2490 /* I can't believe we have to go through this bullshit just to
2491 convert a 'char' to an NSGlyph!!
2493 You might think that we could do
2494 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2495 but that doesn't work; my guess is that glyphWithName expects
2496 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2500 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2501 [ts setFont:fid->nsfont];
2502 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2504 /* Without this, the layout manager ends up on a queue somewhere and is
2505 referenced again after we return to the command loop. Since we don't
2506 use this layout manager again, by that time it may have been garbage
2507 collected, and we crash. Setting this seems to cause `lm' to no
2508 longer be referenced once we exit this block. */
2509 [lm setBackgroundLayoutEnabled:NO];
2511 NSTextContainer *tc = [[NSTextContainer alloc] init];
2512 [lm addTextContainer:tc];
2513 [tc release]; // lm retains tc
2514 [ts addLayoutManager:lm];
2515 [lm release]; // ts retains lm
2516 glyph = [lm glyphAtIndex:0];
2520 /* Compute the bounding box and advancement by converting the glyph
2521 to a bezier path. There appears to be *no other way* to find out
2522 the bounding box of a character: [NSFont boundingRectForGlyph] and
2523 [NSString sizeWithAttributes] both return an advancement-sized
2524 rectangle, not a rectangle completely enclosing the glyph's ink.
2526 advancement.x = advancement.y = 0;
2527 [bpath removeAllPoints];
2528 [bpath moveToPoint:advancement];
2529 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2530 advancement = [bpath currentPoint];
2531 bbox = [bpath bounds];
2533 # else // USE_IPHONE
2535 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2536 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2537 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2538 width of the character and the ascent of the font.
2540 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2541 the CoreText library, but there's no non-CoreText way to turn a
2542 unichar into a CGGlyph.
2544 UniChar uchar = [nsstr characterAtIndex: 0];
2545 CGGlyph cgglyph = 0;
2547 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2549 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2550 kCTFontDefaultOrientation,
2552 CGSize adv = { 0, };
2553 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2555 advancement.x = adv.width;
2556 advancement.y = adv.height;
2558 /* A bug that existed was that the GL FPS display was truncating
2559 characters slightly: commas looked like periods.
2561 At one point, I believed the bounding box was being rounded
2562 wrong and we needed to add padding to it here.
2564 I think what was actually going on was, I was computing rbearing
2565 wrong. Also there was an off-by-one error in texfont.c, displaying
2566 too little of the bitmap.
2568 Adding arbitrarily large padding to the bbox is fine in fontglide
2569 and FPS display, but screws up BSOD. Increasing bbox width makes
2570 inverted text print too wide; decreasing origin makes characters
2573 I think that all 3 states are correct now with the new lbearing
2574 computation plus the texfont fix.
2578 bbox.origin.x -= kludge;
2579 bbox.origin.y -= kludge;
2580 bbox.size.width += kludge;
2581 bbox.size.height += kludge;
2584 # endif // USE_IPHONE
2586 /* Now that we know the advancement and bounding box, we can compute
2587 the lbearing and rbearing.
2589 XCharStruct *cs = &f->per_char[i-first];
2591 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2592 cs->descent = CEIL(-bbox.origin.y);
2593 cs->lbearing = floor (bbox.origin.x);
2594 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2595 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
2596 cs->width = CEIL (advancement.x);
2598 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2600 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2603 max->width = MAX (max->width, cs->width);
2604 max->ascent = MAX (max->ascent, cs->ascent);
2605 max->descent = MAX (max->descent, cs->descent);
2606 max->lbearing = MAX (max->lbearing, cs->lbearing);
2607 max->rbearing = MAX (max->rbearing, cs->rbearing);
2609 min->width = MIN (min->width, cs->width);
2610 min->ascent = MIN (min->ascent, cs->ascent);
2611 min->descent = MIN (min->descent, cs->descent);
2612 min->lbearing = MIN (min->lbearing, cs->lbearing);
2613 min->rbearing = MIN (min->rbearing, cs->rbearing);
2618 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2619 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2620 i, i, cs->width, cs->lbearing, cs->rbearing,
2621 cs->ascent, cs->descent,
2622 bbox.size.width, bbox.size.height,
2623 bbox.origin.x, bbox.origin.y,
2624 advancement.x, advancement.y);
2634 // Since 'Font' includes the metrics, this just makes a copy of that.
2637 XQueryFont (Display *dpy, Font fid)
2640 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2643 // copy XCharStruct array
2644 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2645 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2646 memcpy (f->per_char, fid->metrics.per_char,
2647 size * sizeof (XCharStruct));
2654 copy_font (Font fid)
2656 // copy 'Font' struct
2657 Font fid2 = (Font) malloc (sizeof(*fid2));
2660 // copy XCharStruct array
2661 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2662 fid2->metrics.per_char = (XCharStruct *)
2663 malloc ((size + 2) * sizeof (XCharStruct));
2664 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2665 size * sizeof (XCharStruct));
2667 // copy the other pointers
2668 fid2->ps_name = strdup (fid->ps_name);
2669 // [fid2->nsfont retain];
2670 fid2->metrics.fid = fid2;
2677 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2680 Assert (size > 0, "zero font size");
2685 // "Monaco" only exists in plain.
2686 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2688 if (bold && ital) name = "Courier-BoldOblique";
2689 else if (bold) name = "Courier-Bold";
2690 else if (ital) name = "Courier-Oblique";
2691 else name = "Courier";
2695 // "Georgia" looks better than "Times".
2697 if (bold && ital) name = "Georgia-BoldItalic";
2698 else if (bold) name = "Georgia-Bold";
2699 else if (ital) name = "Georgia-Italic";
2700 else name = "Georgia";
2704 // "Geneva" only exists in plain.
2705 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2706 // "Verdana" renders smoother than "Helvetica" for some reason.
2708 if (bold && ital) name = "Verdana-BoldItalic";
2709 else if (bold) name = "Verdana-Bold";
2710 else if (ital) name = "Verdana-Italic";
2711 else name = "Verdana";
2714 NSString *nsname = [NSString stringWithCString:name
2715 encoding:NSUTF8StringEncoding];
2716 NSFont *f = [NSFont fontWithName:nsname size:size];
2718 *name_ret = strdup(name);
2723 try_native_font (const char *name, float scale,
2724 char **name_ret, float *size_ret)
2726 if (!name) return 0;
2727 const char *spc = strrchr (name, ' ');
2730 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2733 if (size <= 4) return 0;
2737 char *name2 = strdup (name);
2738 name2[strlen(name2) - strlen(spc)] = 0;
2739 NSString *nsname = [NSString stringWithCString:name2
2740 encoding:NSUTF8StringEncoding];
2741 NSFont *f = [NSFont fontWithName:nsname size:size];
2753 /* Returns a random font in the given size and face.
2756 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2759 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2760 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2761 NSArray *fonts = [[NSFontManager sharedFontManager]
2762 availableFontNamesWithTraits:mask];
2763 if (!fonts) return 0;
2765 int n = [fonts count];
2766 if (n <= 0) return 0;
2769 for (j = 0; j < n; j++) {
2770 int i = random() % n;
2771 NSString *name = [fonts objectAtIndex:i];
2772 NSFont *f = [NSFont fontWithName:name size:size];
2775 /* Don't use this font if it (probably) doesn't include ASCII characters.
2777 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2778 if (! (enc == NSUTF8StringEncoding ||
2779 enc == NSISOLatin1StringEncoding ||
2780 enc == NSNonLossyASCIIStringEncoding ||
2781 enc == NSISOLatin2StringEncoding ||
2782 enc == NSUnicodeStringEncoding ||
2783 enc == NSWindowsCP1250StringEncoding ||
2784 enc == NSWindowsCP1252StringEncoding ||
2785 enc == NSMacOSRomanStringEncoding)) {
2786 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2789 // NSLog(@"using \"%@\": %d", name, enc);
2791 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2795 // None of the fonts support ASCII?
2798 # else // USE_IPHONE
2800 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2801 NSArray *families = [UIFont familyNames];
2802 NSMutableDictionary *famdict = [NSMutableDictionary
2803 dictionaryWithCapacity:100];
2804 NSObject *y = [NSNumber numberWithBool:YES];
2805 for (NSString *name in families) {
2806 // There are many dups in the families array -- uniquify it.
2807 [famdict setValue:y forKey:name];
2810 for (NSString *name in famdict) {
2811 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2814 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2817 BOOL bb = MATCH(@"Bold");
2818 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2820 if (!bold != !bb) continue;
2821 if (!ital != !ii) continue;
2823 /* Check if it can do ASCII. No good way to accomplish this!
2824 These are fonts present in iPhone Simulator as of June 2012
2825 that don't include ASCII.
2827 if (MATCH(@"AppleGothic") || // Korean
2828 MATCH(@"Dingbats") || // Dingbats
2829 MATCH(@"Emoji") || // Emoticons
2830 MATCH(@"Geeza") || // Arabic
2831 MATCH(@"Hebrew") || // Hebrew
2832 MATCH(@"HiraKaku") || // Japanese
2833 MATCH(@"HiraMin") || // Japanese
2834 MATCH(@"Kailasa") || // Tibetan
2835 MATCH(@"Ornaments") || // Dingbats
2836 MATCH(@"STHeiti") // Chinese
2840 [fonts addObject:fn];
2845 if (! [fonts count]) return 0; // Nothing suitable?
2847 int i = random() % [fonts count];
2848 NSString *name = [fonts objectAtIndex:i];
2849 UIFont *ff = [UIFont fontWithName:name size:size];
2850 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2854 # endif // USE_IPHONE
2859 try_xlfd_font (const char *name, float scale,
2860 char **name_ret, float *size_ret)
2871 const char *s = (name ? name : "");
2873 while (*s && (*s == '*' || *s == '-'))
2876 while (*s2 && (*s2 != '*' && *s2 != '-'))
2882 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2883 else if (CMP ("random")) rand = YES;
2884 else if (CMP ("bold")) bold = YES;
2885 else if (CMP ("i")) ital = YES;
2886 else if (CMP ("o")) ital = YES;
2887 else if (CMP ("courier")) fixed = YES;
2888 else if (CMP ("fixed")) fixed = YES;
2889 else if (CMP ("m")) fixed = YES;
2890 else if (CMP ("times")) serif = YES;
2891 else if (CMP ("6x10")) fixed = YES, size = 8;
2892 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2893 else if (CMP ("9x15")) fixed = YES, size = 12;
2894 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2895 else if (CMP ("vga")) fixed = YES, size = 12;
2896 else if (CMP ("console")) fixed = YES, size = 12;
2897 else if (CMP ("gallant")) fixed = YES, size = 12;
2899 else if (size == 0) {
2901 if (1 == sscanf (s, " %d ", &n))
2908 if (size < 6 || size > 1000)
2914 nsfont = random_font (bold, ital, size, &ps_name);
2917 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2919 // if that didn't work, turn off attibutes until it does
2920 // (e.g., there is no "Monaco-Bold".)
2922 if (!nsfont && serif) {
2924 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2926 if (!nsfont && ital) {
2928 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2930 if (!nsfont && bold) {
2932 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2934 if (!nsfont && fixed) {
2936 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2940 *name_ret = ps_name;
2950 XLoadFont (Display *dpy, const char *name)
2952 Font fid = (Font) calloc (1, sizeof(*fid));
2957 // Scale up fonts on Retina displays.
2958 scale = dpy->main_window->window.view.contentScaleFactor;
2961 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2963 if (!fid->nsfont && name &&
2964 strchr (name, ' ') &&
2965 !strchr (name, '*')) {
2966 // If name contains a space but no stars, it is a native font spec --
2967 // return NULL so that we know it really didn't exist. Else, it is an
2968 // XLFD font, so keep trying.
2969 XUnloadFont (dpy, fid);
2974 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2976 // We should never return NULL for XLFD fonts.
2978 Assert (0, "no font");
2981 CFRetain (fid->nsfont); // needed for garbage collection?
2983 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2992 XLoadQueryFont (Display *dpy, const char *name)
2994 Font fid = XLoadFont (dpy, name);
2996 return XQueryFont (dpy, fid);
3000 XUnloadFont (Display *dpy, Font fid)
3003 free (fid->ps_name);
3004 if (fid->metrics.per_char)
3005 free (fid->metrics.per_char);
3007 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3008 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3009 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3010 // They're probably not very big...
3012 // [fid->nsfont release];
3013 // CFRelease (fid->nsfont);
3020 XFreeFontInfo (char **names, XFontStruct *info, int n)
3024 for (i = 0; i < n; i++)
3025 if (names[i]) free (names[i]);
3029 for (i = 0; i < n; i++)
3030 if (info[i].per_char)
3031 free (info[i].per_char);
3038 XFreeFont (Display *dpy, XFontStruct *f)
3041 XFreeFontInfo (0, f, 1);
3042 XUnloadFont (dpy, fid);
3048 XSetFont (Display *dpy, GC gc, Font fid)
3051 XUnloadFont (dpy, gc->gcv.font);
3052 gc->gcv.font = copy_font (fid);
3053 [gc->gcv.font->nsfont retain];
3054 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3059 XTextExtents (XFontStruct *f, const char *s, int length,
3060 int *dir_ret, int *ascent_ret, int *descent_ret,
3063 memset (cs, 0, sizeof(*cs));
3065 for (i = 0; i < length; i++) {
3066 unsigned char c = (unsigned char) s[i];
3067 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3068 c = f->default_char;
3069 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3073 cs->ascent = MAX (cs->ascent, cc->ascent);
3074 cs->descent = MAX (cs->descent, cc->descent);
3075 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3076 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3077 cs->width += cc->width;
3081 *ascent_ret = f->ascent;
3082 *descent_ret = f->descent;
3087 XTextWidth (XFontStruct *f, const char *s, int length)
3089 int ascent, descent, dir;
3091 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3097 set_font (Display *dpy, CGContextRef cgc, GC gc)
3099 Font font = gc->gcv.font;
3101 font = XLoadFont (dpy, 0);
3102 gc->gcv.font = font;
3103 [gc->gcv.font->nsfont retain];
3104 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3106 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
3107 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
3112 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3113 const char *str, int len, BOOL clear_background_p)
3115 if (clear_background_p) {
3116 int ascent, descent, dir;
3118 XTextExtents (&gc->gcv.font->metrics, str, len,
3119 &dir, &ascent, &descent, &cs);
3120 draw_rect (dpy, d, gc,
3121 x + MIN (0, cs.lbearing),
3122 y - MAX (0, ascent),
3123 MAX (MAX (0, cs.rbearing) -
3124 MIN (0, cs.lbearing),
3126 MAX (0, ascent) + MAX (0, descent),
3130 CGRect wr = d->frame;
3133 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
3134 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
3137 CGContextRef cgc = d->cgc;
3138 push_fg_gc (d, gc, YES);
3139 set_font (dpy, cgc, gc);
3141 CGContextSetTextDrawingMode (cgc, kCGTextFill);
3142 if (gc->gcv.antialias_p)
3143 CGContextSetShouldAntialias (cgc, YES);
3144 CGContextShowTextAtPoint (cgc,
3146 wr.origin.y + wr.size.height - y,
3155 unsigned long argb = gc->gcv.foreground;
3156 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3157 float a = ((argb >> 24) & 0xFF) / 255.0;
3158 float r = ((argb >> 16) & 0xFF) / 255.0;
3159 float g = ((argb >> 8) & 0xFF) / 255.0;
3160 float b = ((argb ) & 0xFF) / 255.0;
3161 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3162 NSDictionary *attr =
3163 [NSDictionary dictionaryWithObjectsAndKeys:
3164 gc->gcv.font->nsfont, NSFontAttributeName,
3165 fg, NSForegroundColorAttributeName,
3167 char *s2 = (char *) malloc (len + 1);
3168 strncpy (s2, str, len);
3170 NSString *nsstr = [NSString stringWithCString:s2
3171 encoding:NSISOLatin1StringEncoding];
3174 pos.x = wr.origin.x + x;
3175 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
3176 [nsstr drawAtPoint:pos withAttributes:attr];
3180 invalidate_drawable_cache (d);
3186 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3187 const char *str, int len)
3189 return draw_string (dpy, d, gc, x, y, str, len, NO);
3193 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
3194 const char *str, int len)
3196 return draw_string (dpy, d, gc, x, y, str, len, YES);
3201 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3203 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3204 gc->gcv.foreground = fg;
3210 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3212 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3213 gc->gcv.background = bg;
3218 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3220 gc->gcv.alpha_allowed_p = allowed;
3225 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3227 gc->gcv.antialias_p = antialias_p;
3233 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3234 int line_style, int cap_style, int join_style)
3236 gc->gcv.line_width = line_width;
3237 Assert (line_style == LineSolid, "only LineSolid implemented");
3238 // gc->gcv.line_style = line_style;
3239 gc->gcv.cap_style = cap_style;
3240 gc->gcv.join_style = join_style;
3245 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3251 XSetFunction (Display *dpy, GC gc, int which)
3253 gc->gcv.function = which;
3258 XSetSubwindowMode (Display *dpy, GC gc, int which)
3260 gc->gcv.subwindow_mode = which;
3265 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3267 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3269 if (gc->gcv.clip_mask) {
3270 XFreePixmap (dpy, gc->gcv.clip_mask);
3271 CGImageRelease (gc->clip_mask);
3274 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3275 if (gc->gcv.clip_mask)
3277 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3285 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3287 gc->gcv.clip_x_origin = x;
3288 gc->gcv.clip_y_origin = y;
3294 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3295 int *root_x_ret, int *root_y_ret,
3296 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3298 Assert (w && w->type == WINDOW, "not a window");
3301 int x = w->window.last_mouse_x;
3302 int y = w->window.last_mouse_y;
3303 if (root_x_ret) *root_x_ret = x;
3304 if (root_y_ret) *root_y_ret = y;
3305 if (win_x_ret) *win_x_ret = x;
3306 if (win_y_ret) *win_y_ret = y;
3308 # else // !USE_IPHONE
3310 NSWindow *nsw = [w->window.view window];
3312 // get bottom left of window on screen, from bottom left
3313 wpos.x = wpos.y = 0;
3314 wpos = [nsw convertBaseToScreen:wpos];
3317 // get bottom left of view on window, from bottom left
3318 vpos.x = vpos.y = 0;
3319 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3321 // get bottom left of view on screen, from bottom left
3325 // get top left of view on screen, from bottom left
3326 vpos.y += w->frame.size.height;
3328 // get top left of view on screen, from top left
3329 NSArray *screens = [NSScreen screens];
3330 NSScreen *screen = (screens && [screens count] > 0
3331 ? [screens objectAtIndex:0]
3332 : [NSScreen mainScreen]);
3334 double s = w->window.view.contentScaleFactor;
3338 NSRect srect = [screen frame];
3339 vpos.y = (s * srect.size.height) - vpos.y;
3341 // get the mouse position on window, from bottom left
3342 NSEvent *e = [NSApp currentEvent];
3343 NSPoint p = [e locationInWindow];
3345 // get mouse position on screen, from bottom left
3349 // get mouse position on screen, from top left
3350 p.y = srect.size.height - p.y;
3352 if (root_x_ret) *root_x_ret = (int) p.x;
3353 if (root_y_ret) *root_y_ret = (int) p.y;
3354 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3355 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3356 # endif // !USE_IPHONE
3358 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3359 if (root_ret) *root_ret = 0;
3360 if (child_ret) *child_ret = 0;
3365 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3366 int src_x, int src_y,
3367 int *dest_x_ret, int *dest_y_ret,
3370 Assert (w && w->type == WINDOW, "not a window");
3378 # else // !USE_IPHONE
3380 NSWindow *nsw = [w->window.view window];
3382 // get bottom left of window on screen, from bottom left
3383 wpos.x = wpos.y = 0;
3384 wpos = [nsw convertBaseToScreen:wpos];
3387 // get bottom left of view on window, from bottom left
3388 vpos.x = vpos.y = 0;
3389 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3391 // get bottom left of view on screen, from bottom left
3395 // get top left of view on screen, from bottom left
3396 vpos.y += w->frame.size.height;
3398 // get top left of view on screen, from top left
3399 NSArray *screens = [NSScreen screens];
3400 NSScreen *screen = (screens && [screens count] > 0
3401 ? [screens objectAtIndex:0]
3402 : [NSScreen mainScreen]);
3404 double s = w->window.view.contentScaleFactor;
3408 NSRect srect = [screen frame];
3409 vpos.y = (s * srect.size.height) - vpos.y;
3411 // point starts out relative to top left of view
3416 // get point relative to top left of screen
3419 # endif // !USE_IPHONE
3430 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3436 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3439 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3441 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3442 if ((unsigned int) ks <= 255)
3445 // Put control characters in the string. Not meta.
3446 if (e->state & ControlMask) {
3447 if (c >= 'a' && c <= 'z') // Upcase control.
3449 if (c >= '@' && c <= '_') // Shift to control page.
3451 if (c == ' ') // C-SPC is NULL.
3455 if (k_ret) *k_ret = ks;
3456 if (size > 0) buf[0] = c;
3457 if (size > 1) buf[1] = 0;
3458 return (size > 0 ? 1 : 0);
3463 XFlush (Display *dpy)
3465 // Just let the event loop take care of this on its own schedule.
3470 XSync (Display *dpy, Bool flush)
3472 return XFlush (dpy);
3476 // declared in utils/visual.h
3478 has_writable_cells (Screen *s, Visual *v)
3484 visual_depth (Screen *s, Visual *v)
3490 visual_cells (Screen *s, Visual *v)
3496 visual_class (Screen *s, Visual *v)
3501 // declared in utils/grabclient.h
3503 use_subwindow_mode_p (Screen *screen, Window window)