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) {
783 src_rect.size.width = 0;
784 src_rect.size.height = 0;
787 NSObject *releaseme = 0;
790 BOOL free_cgi_p = NO;
793 /* If we're copying from a bitmap to a bitmap, and there's nothing funny
794 going on with clipping masks or depths or anything, optimize it by
795 just doing a memcpy instead of going through a CGI.
797 if (bitmap_context_p (src)) {
799 if (bitmap_context_p (dst) &&
800 gc->gcv.function == GXcopy &&
801 !gc->gcv.clip_mask &&
802 drawable_depth (src) == drawable_depth (dst)) {
804 Assert(!(int)src_frame.origin.x &&
805 !(int)src_frame.origin.y &&
806 !(int)dst_frame.origin.x &&
807 !(int)dst_frame.origin.y,
808 "unexpected non-zero origin");
810 char *src_data = CGBitmapContextGetData(src->cgc);
811 char *dst_data = CGBitmapContextGetData(dst->cgc);
812 size_t src_pitch = CGBitmapContextGetBytesPerRow(src->cgc);
813 size_t dst_pitch = CGBitmapContextGetBytesPerRow(dst->cgc);
815 // Int to float and back again. It's not very safe, but it seems to work.
816 int src_x0 = src_rect.origin.x;
817 int dst_x0 = dst_rect.origin.x;
819 // Flip the Y-axis a second time.
820 int src_y0 = (src_frame.origin.y + src_frame.size.height -
821 src_rect.size.height - src_rect.origin.y);
822 int dst_y0 = (dst_frame.origin.y + dst_frame.size.height -
823 dst_rect.size.height - dst_rect.origin.y);
825 unsigned width0 = (int) src_rect.size.width;
826 unsigned height0 = (int) src_rect.size.height;
828 Assert((int)src_rect.size.width == (int)dst_rect.size.width ||
829 (int)src_rect.size.height == (int)dst_rect.size.height,
832 char *src_data0 = seek_xy(src_data, src_pitch, src_x0, src_y0);
833 char *dst_data0 = seek_xy(dst_data, dst_pitch, dst_x0, dst_y0);
834 size_t src_pitch0 = src_pitch;
835 size_t dst_pitch0 = dst_pitch;
836 size_t bytes = width0 * 4;
838 if (src == dst && dst_y0 > src_y0) {
839 // Copy upwards if the areas might overlap.
840 src_data0 += src_pitch0 * (height0 - 1);
841 dst_data0 += dst_pitch0 * (height0 - 1);
842 src_pitch0 = -src_pitch0;
843 dst_pitch0 = -dst_pitch0;
846 size_t lines0 = height0;
848 // memcpy is an alias for memmove on OS X.
849 memmove(dst_data0, src_data0, bytes);
850 src_data0 += src_pitch0;
851 dst_data0 += dst_pitch0;
857 int orig_dst_x = orig_dst_rect.origin.x;
858 int orig_dst_y = (dst_frame.origin.y + dst_frame.size.height -
859 orig_dst_rect.origin.y - orig_dst_rect.size.height);
860 int orig_width = orig_dst_rect.size.width;
861 int orig_height = orig_dst_rect.size.height;
863 Assert (orig_dst_x >= 0 &&
864 orig_dst_x + orig_width <= (int) dst_frame.size.width &&
866 orig_dst_y + orig_height <= (int) dst_frame.size.height,
869 if (orig_dst_y < dst_y0) {
870 fill_rect_memset (seek_xy (dst_data, dst_pitch,
871 orig_dst_x, orig_dst_y), dst_pitch,
872 gc->gcv.background, orig_width,
873 dst_y0 - orig_dst_y);
876 if (orig_dst_y + orig_height > dst_y0 + height0) {
877 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x,
880 gc->gcv.background, orig_width,
881 orig_dst_y + orig_height - dst_y0 - height0);
884 if (orig_dst_x < dst_x0) {
885 fill_rect_memset (seek_xy (dst_data, dst_pitch, orig_dst_x, dst_y0),
886 dst_pitch, gc->gcv.background,
887 dst_x0 - orig_dst_x, height0);
890 if (dst_x0 + width0 < orig_dst_x + orig_width) {
891 fill_rect_memset (seek_xy (dst_data, dst_pitch, dst_x0 + width0,
893 dst_pitch, gc->gcv.background,
894 orig_dst_x + orig_width - dst_x0 - width0,
899 invalidate_drawable_cache (dst);
904 // If we are copying from a Pixmap to a Pixmap or Window, we must first
905 // copy the bits to an intermediary CGImage object, then copy that to the
906 // destination drawable's CGContext.
908 // (It doesn't seem to be possible to use NSCopyBits() to optimize the
909 // case of copying from a Pixmap back to itself, but I don't think that
910 // happens very often anyway.)
912 // First we get a CGImage out of the pixmap CGContext -- it's the whole
913 // pixmap, but it presumably shares the data pointer instead of copying
914 // it. We then cache that CGImage it inside the Pixmap object. Note:
915 // invalidate_drawable_cache() must be called to discard this any time a
916 // modification is made to the pixmap, or we'll end up re-using old bits.
919 src->cgi = CGBitmapContextCreateImage (src->cgc);
922 // if doing a sub-rect, trim it down.
923 if (src_rect.origin.x != src_frame.origin.x ||
924 src_rect.origin.y != src_frame.origin.y ||
925 src_rect.size.width != src_frame.size.width ||
926 src_rect.size.height != src_frame.size.height) {
927 // #### I don't understand why this is needed...
928 src_rect.origin.y = (src_frame.size.height -
929 src_rect.size.height - src_rect.origin.y);
930 // This does not copy image data, so it should be fast.
931 cgi = CGImageCreateWithImageInRect (cgi, src_rect);
935 if (src->type == PIXMAP && src->pixmap.depth == 1)
938 # ifndef USE_BACKBUFFER
939 } else { /* (src->type == WINDOW) */
941 NSRect nsfrom; // NSRect != CGRect on 10.4
942 nsfrom.origin.x = src_rect.origin.x;
943 nsfrom.origin.y = src_rect.origin.y;
944 nsfrom.size.width = src_rect.size.width;
945 nsfrom.size.height = src_rect.size.height;
949 // If we are copying from a window to itself, we can use NSCopyBits()
950 // without first copying the rectangle to an intermediary CGImage.
951 // This is ~28% faster (but I *expected* it to be twice as fast...)
952 // (kumppa, bsod, decayscreen, memscroller, slidescreen, slip, xjack)
958 // If we are copying from a Window to a Pixmap, we must first copy
959 // the bits to an intermediary CGImage object, then copy that to the
960 // Pixmap's CGContext.
962 NSBitmapImageRep *bm = [[NSBitmapImageRep alloc]
963 initWithFocusedViewRect:nsfrom];
964 unsigned char *data = [bm bitmapData];
965 int bps = [bm bitsPerSample];
966 int bpp = [bm bitsPerPixel];
967 int bpl = [bm bytesPerRow];
970 // create a CGImage from those bits.
971 // (As of 10.5, we could just do cgi = [bm CGImage] (it is autoreleased)
972 // but that method didn't exist in 10.4.)
974 CGDataProviderRef prov =
975 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
977 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
980 /* Use whatever default bit ordering we got from
981 initWithFocusedViewRect. I would have assumed
982 that it was (kCGImageAlphaNoneSkipFirst |
983 kCGBitmapByteOrder32Host), but on Intel,
989 NO, /* interpolate */
990 kCGRenderingIntentDefault);
992 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace,"bad colorspace");
993 CGDataProviderRelease (prov);
996 # endif // !USE_BACKBUFFER
999 CGContextRef cgc = dst->cgc;
1001 if (mask_p) { // src depth == 1
1003 push_bg_gc (dst, gc, YES);
1005 // fill the destination rectangle with solid background...
1006 CGContextFillRect (cgc, orig_dst_rect);
1008 Assert (cgc, "no CGC with 1-bit XCopyArea");
1010 // then fill in a solid rectangle of the fg color, using the image as an
1011 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
1012 set_color (cgc, gc->gcv.foreground, gc->depth,
1013 gc->gcv.alpha_allowed_p, YES);
1014 CGContextClipToMask (cgc, dst_rect, cgi);
1015 CGContextFillRect (cgc, dst_rect);
1019 } else { // src depth > 1
1023 // If either the src or dst rects did not lie within their drawables,
1024 // then we have adjusted both the src and dst rects to account for
1025 // the clipping; that means we need to first clear to the background,
1026 // so that clipped bits end up in the bg color instead of simply not
1030 set_color (cgc, gc->gcv.background, gc->depth,
1031 gc->gcv.alpha_allowed_p, YES);
1032 CGContextFillRect (cgc, orig_dst_rect);
1036 // copy the CGImage onto the destination CGContext
1037 //Assert(CGImageGetColorSpace(cgi) == dpy->colorspace, "bad colorspace");
1038 CGContextDrawImage (cgc, dst_rect, cgi);
1040 // No cgi means src == dst, and both are Windows.
1042 # ifdef USE_BACKBUFFER
1043 Assert (0, "NSCopyBits unimplemented"); // shouldn't be reached anyway
1045 # else // !USE_BACKBUFFER
1047 nsfrom.origin.x = src_rect.origin.x; // NSRect != CGRect on 10.4
1048 nsfrom.origin.y = src_rect.origin.y;
1049 nsfrom.size.width = src_rect.size.width;
1050 nsfrom.size.height = src_rect.size.height;
1052 nsto.x = dst_rect.origin.x;
1053 nsto.y = dst_rect.origin.y;
1054 NSCopyBits (0, nsfrom, nsto);
1055 # endif // !USE_BACKBUFFER
1061 if (free_cgi_p) CGImageRelease (cgi);
1063 if (releaseme) [releaseme release];
1064 invalidate_drawable_cache (dst);
1070 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
1071 int src_x, int src_y,
1072 unsigned width, int height,
1073 int dest_x, int dest_y, unsigned long plane)
1075 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
1077 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
1078 // not to white/black.
1079 return XCopyArea (dpy, src, dest, gc,
1080 src_x, src_y, width, height, dest_x, dest_y);
1085 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
1087 // when drawing a zero-length line, obey line-width and cap-style.
1088 if (x1 == x2 && y1 == y2) {
1089 int w = gc->gcv.line_width;
1092 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
1093 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
1095 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
1098 CGRect wr = d->frame;
1100 p.x = wr.origin.x + x1;
1101 p.y = wr.origin.y + wr.size.height - y1;
1103 push_fg_gc (d, gc, NO);
1105 CGContextRef cgc = d->cgc;
1106 set_line_mode (cgc, &gc->gcv);
1107 CGContextBeginPath (cgc);
1108 CGContextMoveToPoint (cgc, p.x, p.y);
1109 p.x = wr.origin.x + x2;
1110 p.y = wr.origin.y + wr.size.height - y2;
1111 CGContextAddLineToPoint (cgc, p.x, p.y);
1112 CGContextStrokePath (cgc);
1114 invalidate_drawable_cache (d);
1119 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
1124 CGRect wr = d->frame;
1125 push_fg_gc (d, gc, NO);
1127 CGContextRef cgc = d->cgc;
1129 set_line_mode (cgc, &gc->gcv);
1131 // if the first and last points coincide, use closepath to get
1132 // the proper line-joining.
1133 BOOL closed_p = (points[0].x == points[count-1].x &&
1134 points[0].y == points[count-1].y);
1135 if (closed_p) count--;
1137 p.x = wr.origin.x + points->x;
1138 p.y = wr.origin.y + wr.size.height - points->y;
1140 CGContextBeginPath (cgc);
1141 CGContextMoveToPoint (cgc, p.x, p.y);
1142 for (i = 1; i < count; i++) {
1143 if (mode == CoordModePrevious) {
1147 p.x = wr.origin.x + points->x;
1148 p.y = wr.origin.y + wr.size.height - points->y;
1150 CGContextAddLineToPoint (cgc, p.x, p.y);
1153 if (closed_p) CGContextClosePath (cgc);
1154 CGContextStrokePath (cgc);
1156 invalidate_drawable_cache (d);
1162 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
1165 CGRect wr = d->frame;
1167 CGContextRef cgc = d->cgc;
1169 push_fg_gc (d, gc, NO);
1170 set_line_mode (cgc, &gc->gcv);
1171 CGContextBeginPath (cgc);
1172 for (i = 0; i < count; i++) {
1173 CGContextMoveToPoint (cgc,
1174 wr.origin.x + segments->x1,
1175 wr.origin.y + wr.size.height - segments->y1);
1176 CGContextAddLineToPoint (cgc,
1177 wr.origin.x + segments->x2,
1178 wr.origin.y + wr.size.height - segments->y2);
1181 CGContextStrokePath (cgc);
1183 invalidate_drawable_cache (d);
1189 XClearWindow (Display *dpy, Window win)
1191 Assert (win && win->type == WINDOW, "not a window");
1192 CGRect wr = win->frame;
1193 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
1197 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
1199 Assert (w && w->type == WINDOW, "not a window");
1200 validate_pixel (pixel, 32, NO);
1201 w->window.background = pixel;
1206 draw_rect (Display *dpy, Drawable d, GC gc,
1207 int x, int y, unsigned int width, unsigned int height,
1208 BOOL foreground_p, BOOL fill_p)
1210 CGRect wr = d->frame;
1212 r.origin.x = wr.origin.x + x;
1213 r.origin.y = wr.origin.y + wr.size.height - y - height;
1214 r.size.width = width;
1215 r.size.height = height;
1219 push_fg_gc (d, gc, fill_p);
1221 push_bg_gc (d, gc, fill_p);
1224 CGContextRef cgc = d->cgc;
1226 CGContextFillRect (cgc, r);
1229 set_line_mode (cgc, &gc->gcv);
1230 CGContextStrokeRect (cgc, r);
1235 invalidate_drawable_cache (d);
1240 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1241 unsigned int width, unsigned int height)
1243 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
1248 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
1249 unsigned int width, unsigned int height)
1251 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
1256 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
1258 CGRect wr = d->frame;
1260 CGContextRef cgc = d->cgc;
1261 push_fg_gc (d, gc, YES);
1262 for (i = 0; i < n; i++) {
1264 r.origin.x = wr.origin.x + rects->x;
1265 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
1266 r.size.width = rects->width;
1267 r.size.height = rects->height;
1268 CGContextFillRect (cgc, r);
1272 invalidate_drawable_cache (d);
1278 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
1280 Assert (win && win->type == WINDOW, "not a window");
1281 CGContextRef cgc = win->cgc;
1282 set_color (cgc, win->window.background, 32, NO, YES);
1283 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
1289 XFillPolygon (Display *dpy, Drawable d, GC gc,
1290 XPoint *points, int npoints, int shape, int mode)
1292 CGRect wr = d->frame;
1294 push_fg_gc (d, gc, YES);
1295 CGContextRef cgc = d->cgc;
1296 CGContextBeginPath (cgc);
1298 for (i = 0; i < npoints; i++) {
1299 if (i > 0 && mode == CoordModePrevious) {
1303 x = wr.origin.x + points[i].x;
1304 y = wr.origin.y + wr.size.height - points[i].y;
1308 CGContextMoveToPoint (cgc, x, y);
1310 CGContextAddLineToPoint (cgc, x, y);
1312 CGContextClosePath (cgc);
1313 if (gc->gcv.fill_rule == EvenOddRule)
1314 CGContextEOFillPath (cgc);
1316 CGContextFillPath (cgc);
1318 invalidate_drawable_cache (d);
1322 #define radians(DEG) ((DEG) * M_PI / 180.0)
1323 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
1326 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
1327 unsigned int width, unsigned int height, int angle1, int angle2,
1330 CGRect wr = d->frame;
1332 bound.origin.x = wr.origin.x + x;
1333 bound.origin.y = wr.origin.y + wr.size.height - y - height;
1334 bound.size.width = width;
1335 bound.size.height = height;
1338 ctr.x = bound.origin.x + bound.size.width /2;
1339 ctr.y = bound.origin.y + bound.size.height/2;
1341 float r1 = radians (angle1/64.0);
1342 float r2 = radians (angle2/64.0) + r1;
1343 BOOL clockwise = angle2 < 0;
1344 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
1346 push_fg_gc (d, gc, fill_p);
1348 CGContextRef cgc = d->cgc;
1349 CGContextBeginPath (cgc);
1351 CGContextSaveGState(cgc);
1352 CGContextTranslateCTM (cgc, ctr.x, ctr.y);
1353 CGContextScaleCTM (cgc, width/2.0, height/2.0);
1355 CGContextMoveToPoint (cgc, 0, 0);
1357 CGContextAddArc (cgc, 0.0, 0.0, 1, r1, r2, clockwise);
1358 CGContextRestoreGState (cgc); // restore before stroke, for line width
1361 CGContextClosePath (cgc); // for proper line joining
1364 CGContextFillPath (cgc);
1366 set_line_mode (cgc, &gc->gcv);
1367 CGContextStrokePath (cgc);
1371 invalidate_drawable_cache (d);
1376 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1377 unsigned int width, unsigned int height, int angle1, int angle2)
1379 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1383 XFillArc (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, YES);
1390 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1393 for (i = 0; i < narcs; i++)
1394 draw_arc (dpy, d, gc,
1395 arcs[i].x, arcs[i].y,
1396 arcs[i].width, arcs[i].height,
1397 arcs[i].angle1, arcs[i].angle2,
1403 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1406 for (i = 0; i < narcs; i++)
1407 draw_arc (dpy, d, gc,
1408 arcs[i].x, arcs[i].y,
1409 arcs[i].width, arcs[i].height,
1410 arcs[i].angle1, arcs[i].angle2,
1417 gcv_defaults (XGCValues *gcv, int depth)
1419 memset (gcv, 0, sizeof(*gcv));
1420 gcv->function = GXcopy;
1421 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1422 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1423 gcv->line_width = 1;
1424 gcv->cap_style = CapNotLast;
1425 gcv->join_style = JoinMiter;
1426 gcv->fill_rule = EvenOddRule;
1428 gcv->alpha_allowed_p = NO;
1429 gcv->antialias_p = YES;
1433 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1436 Assert (gc && from, "no gc");
1437 if (!gc || !from) return;
1439 if (mask & GCFunction) gc->gcv.function = from->function;
1440 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1441 if (mask & GCBackground) gc->gcv.background = from->background;
1442 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1443 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1444 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1445 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1446 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1447 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1448 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1450 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1451 if (mask & GCFont) XSetFont (0, gc, from->font);
1453 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1454 gc->gcv.alpha_allowed_p);
1455 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1456 gc->gcv.alpha_allowed_p);
1458 Assert ((! (mask & (GCLineStyle |
1465 GCGraphicsExposures |
1469 "unimplemented gcvalues mask");
1474 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1476 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1477 if (d->type == WINDOW) {
1479 } else { /* (d->type == PIXMAP) */
1480 gc->depth = d->pixmap.depth;
1483 gcv_defaults (&gc->gcv, gc->depth);
1484 set_gcv (gc, xgcv, mask);
1489 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1491 set_gcv (gc, gcv, mask);
1497 XFreeGC (Display *dpy, GC gc)
1500 XUnloadFont (dpy, gc->gcv.font);
1502 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1504 if (gc->gcv.clip_mask) {
1505 XFreePixmap (dpy, gc->gcv.clip_mask);
1506 CGImageRelease (gc->clip_mask);
1514 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1516 Assert (w && w->type == WINDOW, "not a window");
1517 memset (xgwa, 0, sizeof(*xgwa));
1518 xgwa->x = w->frame.origin.x;
1519 xgwa->y = w->frame.origin.y;
1520 xgwa->width = w->frame.size.width;
1521 xgwa->height = w->frame.size.height;
1523 xgwa->screen = dpy->screen;
1524 xgwa->visual = dpy->screen->visual;
1529 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1530 int *x_ret, int *y_ret,
1531 unsigned int *w_ret, unsigned int *h_ret,
1532 unsigned int *bw_ret, unsigned int *d_ret)
1534 *x_ret = d->frame.origin.x;
1535 *y_ret = d->frame.origin.y;
1536 *w_ret = d->frame.size.width;
1537 *h_ret = d->frame.size.height;
1538 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1539 *root_ret = RootWindow (dpy, 0);
1546 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1548 // store 32 bit ARGB in the pixel field.
1549 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1550 color->pixel = (uint32_t)
1552 (((color->red >> 8) & 0xFF) << 16) |
1553 (((color->green >> 8) & 0xFF) << 8) |
1554 (((color->blue >> 8) & 0xFF) ));
1559 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1560 unsigned long *pmret, unsigned int npl,
1561 unsigned long *pxret, unsigned int npx)
1567 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1569 Assert(0, "XStoreColors called");
1574 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1576 Assert(0, "XStoreColor called");
1581 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1582 unsigned long planes)
1588 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1590 unsigned char r=0, g=0, b=0;
1591 if (*spec == '#' && strlen(spec) == 7) {
1592 static unsigned const char hex[] = { // yeah yeah, shoot me.
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,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
1595 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,
1596 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,
1597 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,
1598 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,
1599 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,
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 r = (hex[spec[1]] << 4) | hex[spec[2]];
1602 g = (hex[spec[3]] << 4) | hex[spec[4]];
1603 b = (hex[spec[5]] << 4) | hex[spec[6]];
1604 } else if (!strcasecmp(spec,"black")) {
1606 } else if (!strcasecmp(spec,"white")) {
1608 } else if (!strcasecmp(spec,"red")) {
1610 } else if (!strcasecmp(spec,"green")) {
1612 } else if (!strcasecmp(spec,"blue")) {
1614 } else if (!strcasecmp(spec,"cyan")) {
1616 } else if (!strcasecmp(spec,"magenta")) {
1618 } else if (!strcasecmp(spec,"yellow")) {
1624 ret->red = (r << 8) | r;
1625 ret->green = (g << 8) | g;
1626 ret->blue = (b << 8) | b;
1627 ret->flags = DoRed|DoGreen|DoBlue;
1632 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1633 XColor *screen_ret, XColor *exact_ret)
1635 if (! XParseColor (dpy, cmap, name, screen_ret))
1637 *exact_ret = *screen_ret;
1638 return XAllocColor (dpy, cmap, screen_ret);
1642 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1644 validate_pixel (color->pixel, 32, NO);
1645 unsigned char r = ((color->pixel >> 16) & 0xFF);
1646 unsigned char g = ((color->pixel >> 8) & 0xFF);
1647 unsigned char b = ((color->pixel ) & 0xFF);
1648 color->red = (r << 8) | r;
1649 color->green = (g << 8) | g;
1650 color->blue = (b << 8) | b;
1651 color->flags = DoRed|DoGreen|DoBlue;
1656 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1659 for (i = 0; i < n; i++)
1660 XQueryColor (dpy, cmap, &c[i]);
1665 static unsigned long
1666 ximage_getpixel_1 (XImage *ximage, int x, int y)
1668 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1672 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1675 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1677 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1682 static unsigned long
1683 ximage_getpixel_32 (XImage *ximage, int x, int y)
1685 return ((unsigned long)
1686 *((uint32_t *) ximage->data +
1687 (y * (ximage->bytes_per_line >> 2)) +
1692 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1694 *((uint32_t *) ximage->data +
1695 (y * (ximage->bytes_per_line >> 2)) +
1696 x) = (uint32_t) pixel;
1702 XInitImage (XImage *ximage)
1704 if (!ximage->bytes_per_line)
1705 ximage->bytes_per_line = (ximage->depth == 1
1706 ? (ximage->width + 7) / 8
1707 : ximage->width * 4);
1709 if (ximage->depth == 1) {
1710 ximage->f.put_pixel = ximage_putpixel_1;
1711 ximage->f.get_pixel = ximage_getpixel_1;
1712 } else if (ximage->depth == 32 || ximage->depth == 24) {
1713 ximage->f.put_pixel = ximage_putpixel_32;
1714 ximage->f.get_pixel = ximage_getpixel_32;
1716 Assert (0, "unknown depth");
1723 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1724 int format, int offset, char *data,
1725 unsigned int width, unsigned int height,
1726 int bitmap_pad, int bytes_per_line)
1728 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1729 ximage->width = width;
1730 ximage->height = height;
1731 ximage->format = format;
1732 ximage->data = data;
1733 ximage->bitmap_unit = 8;
1734 ximage->byte_order = MSBFirst;
1735 ximage->bitmap_bit_order = ximage->byte_order;
1736 ximage->bitmap_pad = bitmap_pad;
1737 ximage->depth = depth;
1738 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1739 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1740 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1741 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1742 ximage->bytes_per_line = bytes_per_line;
1744 XInitImage (ximage);
1749 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1751 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1752 w, h, from->bitmap_pad, 0);
1753 to->data = (char *) malloc (h * to->bytes_per_line);
1755 if (x >= from->width)
1757 else if (x+w > from->width)
1758 w = from->width - x;
1760 if (y >= from->height)
1762 else if (y+h > from->height)
1763 h = from->height - y;
1766 for (ty = 0; ty < h; ty++)
1767 for (tx = 0; tx < w; tx++)
1768 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1773 XPixmapFormatValues *
1774 XListPixmapFormats (Display *dpy, int *n_ret)
1776 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1778 ret[0].bits_per_pixel = 32;
1779 ret[0].scanline_pad = 8;
1781 ret[1].bits_per_pixel = 1;
1782 ret[1].scanline_pad = 8;
1789 XGetPixel (XImage *ximage, int x, int y)
1791 return ximage->f.get_pixel (ximage, x, y);
1796 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1798 return ximage->f.put_pixel (ximage, x, y, pixel);
1802 XDestroyImage (XImage *ximage)
1804 if (ximage->data) free (ximage->data);
1811 flipbits (unsigned const char *in, unsigned char *out, int length)
1813 static const unsigned char table[256] = {
1814 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1815 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1816 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1817 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1818 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1819 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1820 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1821 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1822 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1823 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1824 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1825 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1826 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1827 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1828 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1829 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1830 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1831 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1832 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1833 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1834 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1835 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1836 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1837 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1838 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1839 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1840 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1841 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1842 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1843 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1844 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1845 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1847 while (--length > 0)
1848 *out++ = table[*in++];
1853 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1854 int src_x, int src_y, int dest_x, int dest_y,
1855 unsigned int w, unsigned int h)
1857 CGRect wr = d->frame;
1859 Assert (gc, "no GC");
1860 Assert ((w < 65535), "improbably large width");
1861 Assert ((h < 65535), "improbably large height");
1862 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1863 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1864 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1865 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1867 // Clip width and height to the bounds of the Drawable
1869 if (dest_x + w > wr.size.width) {
1870 if (dest_x > wr.size.width)
1872 w = wr.size.width - dest_x;
1874 if (dest_y + h > wr.size.height) {
1875 if (dest_y > wr.size.height)
1877 h = wr.size.height - dest_y;
1879 if (w <= 0 || h <= 0)
1882 // Clip width and height to the bounds of the XImage
1884 if (src_x + w > ximage->width) {
1885 if (src_x > ximage->width)
1887 w = ximage->width - src_x;
1889 if (src_y + h > ximage->height) {
1890 if (src_y > ximage->height)
1892 h = ximage->height - src_y;
1894 if (w <= 0 || h <= 0)
1897 CGContextRef cgc = d->cgc;
1899 if (gc->gcv.function == GXset ||
1900 gc->gcv.function == GXclear) {
1901 // "set" and "clear" are dumb drawing modes that ignore the source
1902 // bits and just draw solid rectangles.
1903 set_color (cgc, (gc->gcv.function == GXset
1904 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1905 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1906 gc->depth, gc->gcv.alpha_allowed_p, YES);
1907 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1911 int bpl = ximage->bytes_per_line;
1912 int bpp = ximage->bits_per_pixel;
1913 int bsize = bpl * h;
1914 char *data = ximage->data;
1917 r.origin.x = wr.origin.x + dest_x;
1918 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1924 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1925 to create a CGImage from a sub-rectagle of the XImage.
1927 data += (src_y * bpl) + (src_x * 4);
1928 CGDataProviderRef prov =
1929 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1931 CGImageRef cgi = CGImageCreate (w, h,
1934 /* Need this for XPMs to have the right
1935 colors, e.g. the logo in "maze". */
1936 (kCGImageAlphaNoneSkipFirst |
1937 kCGBitmapByteOrder32Host),
1939 NULL, /* decode[] */
1940 NO, /* interpolate */
1941 kCGRenderingIntentDefault);
1942 CGDataProviderRelease (prov);
1943 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1944 CGContextDrawImage (cgc, r, cgi);
1945 CGImageRelease (cgi);
1947 } else { // (bpp == 1)
1949 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1951 #### However, the bit order within a byte in a 1bpp XImage is
1952 the wrong way around from what Quartz expects, so first we
1953 have to copy the data to reverse it. Shit! Maybe it
1954 would be worthwhile to go through the hacks and #ifdef
1955 each one that diddles 1bpp XImage->data directly...
1957 Assert ((src_x % 8) == 0,
1958 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1960 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1961 unsigned char *flipped = (unsigned char *) malloc (bsize);
1963 flipbits ((unsigned char *) data, flipped, bsize);
1965 CGDataProviderRef prov =
1966 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1967 CGImageRef mask = CGImageMaskCreate (w, h,
1970 NULL, /* decode[] */
1971 NO); /* interpolate */
1972 push_fg_gc (d, gc, YES);
1974 CGContextFillRect (cgc, r); // foreground color
1975 CGContextClipToMask (cgc, r, mask);
1976 set_color (cgc, gc->gcv.background, gc->depth, NO, YES);
1977 CGContextFillRect (cgc, r); // background color
1981 CGDataProviderRelease (prov);
1982 CGImageRelease (mask);
1985 invalidate_drawable_cache (d);
1992 XGetImage (Display *dpy, Drawable d, int x, int y,
1993 unsigned int width, unsigned int height,
1994 unsigned long plane_mask, int format)
1996 const unsigned char *data = 0;
1997 int depth, ibpp, ibpl;
1998 enum { RGBA, ARGB, BGRA } src_format; // As bytes.
1999 # ifndef USE_BACKBUFFER
2000 NSBitmapImageRep *bm = 0;
2003 Assert ((width < 65535), "improbably large width");
2004 Assert ((height < 65535), "improbably large height");
2005 Assert ((x < 65535 && x > -65535), "improbably large x");
2006 Assert ((y < 65535 && y > -65535), "improbably large y");
2008 CGContextRef cgc = d->cgc;
2010 #ifndef USE_BACKBUFFER
2011 // Because of the backbuffer, all iPhone Windows work like Pixmaps.
2012 if (d->type == PIXMAP)
2015 depth = (d->type == PIXMAP
2018 // We create pixmaps and iPhone backbuffers with kCGImageAlphaNoneSkipFirst.
2019 src_format = BGRA; // #### Should this be ARGB on PPC?
2020 ibpp = CGBitmapContextGetBitsPerPixel (cgc);
2021 ibpl = CGBitmapContextGetBytesPerRow (cgc);
2022 data = CGBitmapContextGetData (cgc);
2023 Assert (data, "CGBitmapContextGetData failed");
2025 # ifndef USE_BACKBUFFER
2026 } else { /* (d->type == WINDOW) */
2028 // get the bits (desired sub-rectangle) out of the NSView
2030 nsfrom.origin.x = x;
2031 // nsfrom.origin.y = y;
2032 nsfrom.origin.y = d->frame.size.height - height - y;
2033 nsfrom.size.width = width;
2034 nsfrom.size.height = height;
2035 bm = [[NSBitmapImageRep alloc] initWithFocusedViewRect:nsfrom];
2037 src_format = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat) ? ARGB : RGBA;
2038 ibpp = [bm bitsPerPixel];
2039 ibpl = [bm bytesPerRow];
2040 data = [bm bitmapData];
2041 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
2042 # endif // !USE_BACKBUFFER
2045 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
2046 data += (y * ibpl) + (x * (ibpp/8));
2048 format = (depth == 1 ? XYPixmap : ZPixmap);
2049 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
2051 image->data = (char *) malloc (height * image->bytes_per_line);
2053 int obpl = image->bytes_per_line;
2055 /* both PPC and Intel use word-ordered ARGB frame buffers, which
2056 means that on Intel it is BGRA when viewed by bytes (And BGR
2057 when using 24bpp packing).
2059 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
2060 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
2061 indicator of this latest kink.
2065 const unsigned char *iline = data;
2066 for (yy = 0; yy < height; yy++) {
2068 const unsigned char *iline2 = iline;
2069 for (xx = 0; xx < width; xx++) {
2071 iline2++; // ignore R or A or A or B
2072 iline2++; // ignore G or B or R or G
2073 unsigned char r = *iline2++; // use B or G or G or R
2074 if (ibpp == 32) iline2++; // ignore A or R or B or A
2076 XPutPixel (image, xx, yy, (r ? 1 : 0));
2081 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
2082 const unsigned char *iline = data;
2083 unsigned char *oline = (unsigned char *) image->data;
2084 for (yy = 0; yy < height; yy++) {
2086 const unsigned char *iline2 = iline;
2087 unsigned char *oline2 = oline;
2089 switch (src_format) {
2091 for (xx = 0; xx < width; xx++) {
2092 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2093 unsigned char r = *iline2++;
2094 unsigned char g = *iline2++;
2095 unsigned char b = *iline2++;
2096 uint32_t pixel = ((a << 24) |
2100 *((uint32_t *) oline2) = pixel;
2105 for (xx = 0; xx < width; xx++) {
2106 unsigned char r = *iline2++;
2107 unsigned char g = *iline2++;
2108 unsigned char b = *iline2++;
2109 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2110 uint32_t pixel = ((a << 24) |
2114 *((uint32_t *) oline2) = pixel;
2119 for (xx = 0; xx < width; xx++) {
2120 unsigned char b = *iline2++;
2121 unsigned char g = *iline2++;
2122 unsigned char r = *iline2++;
2123 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
2124 uint32_t pixel = ((a << 24) |
2128 *((uint32_t *) oline2) = pixel;
2142 # ifndef USE_BACKBUFFER
2143 if (bm) [bm release];
2151 /* Returns a transformation matrix to do rotation as per the provided
2152 EXIF "Orientation" value.
2154 static CGAffineTransform
2155 exif_rotate (int rot, CGSize rect)
2157 CGAffineTransform trans = CGAffineTransformIdentity;
2159 case 2: // flip horizontal
2160 trans = CGAffineTransformMakeTranslation (rect.width, 0);
2161 trans = CGAffineTransformScale (trans, -1, 1);
2164 case 3: // rotate 180
2165 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
2166 trans = CGAffineTransformRotate (trans, M_PI);
2169 case 4: // flip vertical
2170 trans = CGAffineTransformMakeTranslation (0, rect.height);
2171 trans = CGAffineTransformScale (trans, 1, -1);
2174 case 5: // transpose (UL-to-LR axis)
2175 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
2176 trans = CGAffineTransformScale (trans, -1, 1);
2177 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2180 case 6: // rotate 90
2181 trans = CGAffineTransformMakeTranslation (0, rect.width);
2182 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
2185 case 7: // transverse (UR-to-LL axis)
2186 trans = CGAffineTransformMakeScale (-1, 1);
2187 trans = CGAffineTransformRotate (trans, M_PI / 2);
2190 case 8: // rotate 270
2191 trans = CGAffineTransformMakeTranslation (rect.height, 0);
2192 trans = CGAffineTransformRotate (trans, M_PI / 2);
2204 jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d,
2205 Bool nsimg_p, void *img_arg,
2206 XRectangle *geom_ret, int exif_rotation)
2210 CGImageSourceRef cgsrc;
2211 # endif // USE_IPHONE
2214 CGContextRef cgc = d->cgc;
2218 NSImage *nsimg = (NSImage *) img_arg;
2219 imgr = [nsimg size];
2222 // convert the NSImage to a CGImage via the toll-free-bridging
2223 // of NSData and CFData...
2225 NSData *nsdata = [NSBitmapImageRep
2226 TIFFRepresentationOfImageRepsInArray:
2227 [nsimg representations]];
2228 CFDataRef cfdata = (CFDataRef) nsdata;
2229 cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
2230 cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
2231 # else // USE_IPHONE
2232 cgi = nsimg.CGImage;
2233 # endif // USE_IPHONE
2236 cgi = (CGImageRef) img_arg;
2237 imgr.width = CGImageGetWidth (cgi);
2238 imgr.height = CGImageGetHeight (cgi);
2241 Bool rot_p = (exif_rotation >= 5);
2244 imgr = NSMakeSize (imgr.height, imgr.width);
2246 CGRect winr = d->frame;
2247 float rw = winr.size.width / imgr.width;
2248 float rh = winr.size.height / imgr.height;
2249 float r = (rw < rh ? rw : rh);
2252 dst.size.width = imgr.width * r;
2253 dst.size.height = imgr.height * r;
2254 dst.origin.x = (winr.size.width - dst.size.width) / 2;
2255 dst.origin.y = (winr.size.height - dst.size.height) / 2;
2257 dst2.origin.x = dst2.origin.y = 0;
2259 dst2.size.width = dst.size.height;
2260 dst2.size.height = dst.size.width;
2262 dst2.size = dst.size;
2265 // Clear the part not covered by the image to background or black.
2267 if (d->type == WINDOW)
2268 XClearWindow (dpy, d);
2270 set_color (cgc, BlackPixel(dpy,0), 32, NO, YES);
2271 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
2274 CGAffineTransform trans =
2275 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
2277 CGContextSaveGState (cgc);
2278 CGContextConcatCTM (cgc,
2279 CGAffineTransformMakeTranslation (dst.origin.x,
2281 CGContextConcatCTM (cgc, trans);
2282 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
2283 CGContextDrawImage (cgc, dst2, cgi);
2284 CGContextRestoreGState (cgc);
2289 CGImageRelease (cgi);
2291 # endif // USE_IPHONE
2294 geom_ret->x = dst.origin.x;
2295 geom_ret->y = dst.origin.y;
2296 geom_ret->width = dst.size.width;
2297 geom_ret->height = dst.size.height;
2300 invalidate_drawable_cache (d);
2306 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
2308 unsigned int w, unsigned int h,
2309 unsigned long fg, unsigned int bg,
2312 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
2313 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
2314 (char *) data, w, h, 0, 0);
2316 gcv.foreground = fg;
2317 gcv.background = bg;
2318 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
2319 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
2322 XDestroyImage (image);
2327 XCreatePixmap (Display *dpy, Drawable d,
2328 unsigned int width, unsigned int height, unsigned int depth)
2330 char *data = (char *) malloc (width * height * 4);
2331 if (! data) return 0;
2333 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
2335 p->frame.size.width = width;
2336 p->frame.size.height = height;
2337 p->pixmap.depth = depth;
2338 p->pixmap.cgc_buffer = data;
2340 /* Quartz doesn't have a 1bpp image type.
2341 Used to use 8bpp gray images instead of 1bpp, but some Mac video cards
2342 don't support that! So we always use 32bpp, regardless of depth. */
2344 p->cgc = CGBitmapContextCreate (data, width, height,
2345 8, /* bits per component */
2346 width * 4, /* bpl */
2348 // Without this, it returns 0...
2349 (kCGImageAlphaNoneSkipFirst |
2350 kCGBitmapByteOrder32Host)
2352 Assert (p->cgc, "could not create CGBitmapContext");
2358 XFreePixmap (Display *d, Pixmap p)
2360 Assert (p && p->type == PIXMAP, "not a pixmap");
2361 invalidate_drawable_cache (p);
2362 CGContextRelease (p->cgc);
2363 if (p->pixmap.cgc_buffer)
2364 free (p->pixmap.cgc_buffer);
2371 copy_pixmap (Display *dpy, Pixmap p)
2374 Assert (p->type == PIXMAP, "not a pixmap");
2376 int width = p->frame.size.width;
2377 int height = p->frame.size.height;
2378 char *data = (char *) malloc (width * height * 4);
2379 if (! data) return 0;
2381 memcpy (data, p->pixmap.cgc_buffer, width * height * 4);
2383 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
2386 p2->pixmap.cgc_buffer = data;
2387 p2->cgc = CGBitmapContextCreate (data, width, height,
2388 8, /* bits per component */
2389 width * 4, /* bpl */
2391 // Without this, it returns 0...
2392 (kCGImageAlphaNoneSkipFirst |
2393 kCGBitmapByteOrder32Host)
2395 Assert (p2->cgc, "could not create CGBitmapContext");
2401 /* Font metric terminology, as used by X11:
2403 "lbearing" is the distance from the logical origin to the leftmost pixel.
2404 If a character's ink extends to the left of the origin, it is negative.
2406 "rbearing" is the distance from the logical origin to the rightmost pixel.
2408 "descent" is the distance from the logical origin to the bottommost pixel.
2409 For characters with descenders, it is negative.
2411 "ascent" is the distance from the logical origin to the topmost pixel.
2412 It is the number of pixels above the baseline.
2414 "width" is the distance from the logical origin to the position where
2415 the logical origin of the next character should be placed.
2417 If "rbearing" is greater than "width", then this character overlaps the
2418 following character. If smaller, then there is trailing blank space.
2422 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
2425 query_font (Font fid)
2427 if (!fid || !fid->nsfont) {
2428 Assert (0, "no NSFont in fid");
2431 if (![fid->nsfont fontName]) {
2432 Assert(0, @"broken NSFont in fid");
2439 XFontStruct *f = &fid->metrics;
2440 XCharStruct *min = &f->min_bounds;
2441 XCharStruct *max = &f->max_bounds;
2443 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
2446 f->min_char_or_byte2 = first;
2447 f->max_char_or_byte2 = last;
2448 f->default_char = 'M';
2449 f->ascent = CEIL ([fid->nsfont ascender]);
2450 f->descent = -CEIL ([fid->nsfont descender]);
2452 min->width = 255; // set to smaller values in the loop
2455 min->lbearing = 255;
2456 min->rbearing = 255;
2458 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
2462 NSBezierPath *bpath = [NSBezierPath bezierPath];
2463 # else // USE_IPHONE
2465 CTFontCreateWithName ((CFStringRef) [fid->nsfont fontName],
2466 [fid->nsfont pointSize],
2468 Assert (ctfont, @"no CTFontRef for UIFont");
2469 # endif // USE_IPHONE
2471 for (i = first; i <= last; i++) {
2472 unsigned char str[2];
2476 NSString *nsstr = [NSString stringWithCString:(char *) str
2477 encoding:NSISOLatin1StringEncoding];
2478 NSPoint advancement = { 0, };
2479 NSRect bbox = {{ 0, }, };
2483 /* I can't believe we have to go through this bullshit just to
2484 convert a 'char' to an NSGlyph!!
2486 You might think that we could do
2487 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2488 but that doesn't work; my guess is that glyphWithName expects
2489 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2493 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2494 [ts setFont:fid->nsfont];
2495 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2497 /* Without this, the layout manager ends up on a queue somewhere and is
2498 referenced again after we return to the command loop. Since we don't
2499 use this layout manager again, by that time it may have been garbage
2500 collected, and we crash. Setting this seems to cause `lm' to no
2501 longer be referenced once we exit this block. */
2502 [lm setBackgroundLayoutEnabled:NO];
2504 NSTextContainer *tc = [[NSTextContainer alloc] init];
2505 [lm addTextContainer:tc];
2506 [tc release]; // lm retains tc
2507 [ts addLayoutManager:lm];
2508 [lm release]; // ts retains lm
2509 glyph = [lm glyphAtIndex:0];
2513 /* Compute the bounding box and advancement by converting the glyph
2514 to a bezier path. There appears to be *no other way* to find out
2515 the bounding box of a character: [NSFont boundingRectForGlyph] and
2516 [NSString sizeWithAttributes] both return an advancement-sized
2517 rectangle, not a rectangle completely enclosing the glyph's ink.
2519 advancement.x = advancement.y = 0;
2520 [bpath removeAllPoints];
2521 [bpath moveToPoint:advancement];
2522 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2523 advancement = [bpath currentPoint];
2524 bbox = [bpath bounds];
2526 # else // USE_IPHONE
2528 /* There is no way to get "lbearing", "rbearing" or "descent" out of
2529 NSFont. 'sizeWithFont' gives us "width" and "height" only.
2530 Likewise, 'drawAtPoint' (to an offscreen CGContext) gives us the
2531 width of the character and the ascent of the font.
2533 Maybe we could use CGFontGetGlyphBBoxes() and avoid linking in
2534 the CoreText library, but there's no non-CoreText way to turn a
2535 unichar into a CGGlyph.
2537 UniChar uchar = [nsstr characterAtIndex: 0];
2538 CGGlyph cgglyph = 0;
2540 if (CTFontGetGlyphsForCharacters (ctfont, &uchar, &cgglyph, 1))
2542 bbox = CTFontGetBoundingRectsForGlyphs (ctfont,
2543 kCTFontDefaultOrientation,
2545 CGSize adv = { 0, };
2546 CTFontGetAdvancesForGlyphs (ctfont, kCTFontDefaultOrientation,
2548 advancement.x = adv.width;
2549 advancement.y = adv.height;
2551 /* A bug that existed was that the GL FPS display was truncating
2552 characters slightly: commas looked like periods.
2554 At one point, I believed the bounding box was being rounded
2555 wrong and we needed to add padding to it here.
2557 I think what was actually going on was, I was computing rbearing
2558 wrong. Also there was an off-by-one error in texfont.c, displaying
2559 too little of the bitmap.
2561 Adding arbitrarily large padding to the bbox is fine in fontglide
2562 and FPS display, but screws up BSOD. Increasing bbox width makes
2563 inverted text print too wide; decreasing origin makes characters
2566 I think that all 3 states are correct now with the new lbearing
2567 computation plus the texfont fix.
2571 bbox.origin.x -= kludge;
2572 bbox.origin.y -= kludge;
2573 bbox.size.width += kludge;
2574 bbox.size.height += kludge;
2577 # endif // USE_IPHONE
2579 /* Now that we know the advancement and bounding box, we can compute
2580 the lbearing and rbearing.
2582 XCharStruct *cs = &f->per_char[i-first];
2584 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2585 cs->descent = CEIL(-bbox.origin.y);
2586 cs->lbearing = floor (bbox.origin.x);
2587 // cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2588 cs->rbearing = CEIL (bbox.origin.x + bbox.size.width) - cs->lbearing;
2589 cs->width = CEIL (advancement.x);
2591 // Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2593 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2596 max->width = MAX (max->width, cs->width);
2597 max->ascent = MAX (max->ascent, cs->ascent);
2598 max->descent = MAX (max->descent, cs->descent);
2599 max->lbearing = MAX (max->lbearing, cs->lbearing);
2600 max->rbearing = MAX (max->rbearing, cs->rbearing);
2602 min->width = MIN (min->width, cs->width);
2603 min->ascent = MIN (min->ascent, cs->ascent);
2604 min->descent = MIN (min->descent, cs->descent);
2605 min->lbearing = MIN (min->lbearing, cs->lbearing);
2606 min->rbearing = MIN (min->rbearing, cs->rbearing);
2611 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2612 " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n",
2613 i, i, cs->width, cs->lbearing, cs->rbearing,
2614 cs->ascent, cs->descent,
2615 bbox.size.width, bbox.size.height,
2616 bbox.origin.x, bbox.origin.y,
2617 advancement.x, advancement.y);
2627 // Since 'Font' includes the metrics, this just makes a copy of that.
2630 XQueryFont (Display *dpy, Font fid)
2633 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2636 // copy XCharStruct array
2637 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2638 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2639 memcpy (f->per_char, fid->metrics.per_char,
2640 size * sizeof (XCharStruct));
2647 copy_font (Font fid)
2649 // copy 'Font' struct
2650 Font fid2 = (Font) malloc (sizeof(*fid2));
2653 // copy XCharStruct array
2654 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2655 fid2->metrics.per_char = (XCharStruct *)
2656 malloc ((size + 2) * sizeof (XCharStruct));
2657 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2658 size * sizeof (XCharStruct));
2660 // copy the other pointers
2661 fid2->ps_name = strdup (fid->ps_name);
2662 // [fid2->nsfont retain];
2663 fid2->metrics.fid = fid2;
2670 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2673 Assert (size > 0, "zero font size");
2678 // "Monaco" only exists in plain.
2679 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2681 if (bold && ital) name = "Courier-BoldOblique";
2682 else if (bold) name = "Courier-Bold";
2683 else if (ital) name = "Courier-Oblique";
2684 else name = "Courier";
2688 // "Georgia" looks better than "Times".
2690 if (bold && ital) name = "Georgia-BoldItalic";
2691 else if (bold) name = "Georgia-Bold";
2692 else if (ital) name = "Georgia-Italic";
2693 else name = "Georgia";
2697 // "Geneva" only exists in plain.
2698 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2699 // "Verdana" renders smoother than "Helvetica" for some reason.
2701 if (bold && ital) name = "Verdana-BoldItalic";
2702 else if (bold) name = "Verdana-Bold";
2703 else if (ital) name = "Verdana-Italic";
2704 else name = "Verdana";
2707 NSString *nsname = [NSString stringWithCString:name
2708 encoding:NSUTF8StringEncoding];
2709 NSFont *f = [NSFont fontWithName:nsname size:size];
2711 *name_ret = strdup(name);
2716 try_native_font (const char *name, float scale,
2717 char **name_ret, float *size_ret)
2719 if (!name) return 0;
2720 const char *spc = strrchr (name, ' ');
2723 if (1 != sscanf (spc, " %d ", &dsize)) return 0;
2726 if (size <= 4) return 0;
2730 char *name2 = strdup (name);
2731 name2[strlen(name2) - strlen(spc)] = 0;
2732 NSString *nsname = [NSString stringWithCString:name2
2733 encoding:NSUTF8StringEncoding];
2734 NSFont *f = [NSFont fontWithName:nsname size:size];
2746 /* Returns a random font in the given size and face.
2749 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2752 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2753 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2754 NSArray *fonts = [[NSFontManager sharedFontManager]
2755 availableFontNamesWithTraits:mask];
2756 if (!fonts) return 0;
2758 int n = [fonts count];
2759 if (n <= 0) return 0;
2762 for (j = 0; j < n; j++) {
2763 int i = random() % n;
2764 NSString *name = [fonts objectAtIndex:i];
2765 NSFont *f = [NSFont fontWithName:name size:size];
2768 /* Don't use this font if it (probably) doesn't include ASCII characters.
2770 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2771 if (! (enc == NSUTF8StringEncoding ||
2772 enc == NSISOLatin1StringEncoding ||
2773 enc == NSNonLossyASCIIStringEncoding ||
2774 enc == NSISOLatin2StringEncoding ||
2775 enc == NSUnicodeStringEncoding ||
2776 enc == NSWindowsCP1250StringEncoding ||
2777 enc == NSWindowsCP1252StringEncoding ||
2778 enc == NSMacOSRomanStringEncoding)) {
2779 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2782 // NSLog(@"using \"%@\": %d", name, enc);
2784 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2788 // None of the fonts support ASCII?
2791 # else // USE_IPHONE
2793 NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:100];
2794 NSArray *families = [UIFont familyNames];
2795 NSMutableDictionary *famdict = [NSMutableDictionary
2796 dictionaryWithCapacity:100];
2797 NSObject *y = [NSNumber numberWithBool:YES];
2798 for (NSString *name in families) {
2799 // There are many dups in the families array -- uniquify it.
2800 [famdict setValue:y forKey:name];
2803 for (NSString *name in famdict) {
2804 for (NSString *fn in [UIFont fontNamesForFamilyName:name]) {
2807 ([fn rangeOfString:X options:NSCaseInsensitiveSearch].location \
2810 BOOL bb = MATCH(@"Bold");
2811 BOOL ii = MATCH(@"Italic") || MATCH(@"Oblique");
2813 if (!bold != !bb) continue;
2814 if (!ital != !ii) continue;
2816 /* Check if it can do ASCII. No good way to accomplish this!
2817 These are fonts present in iPhone Simulator as of June 2012
2818 that don't include ASCII.
2820 if (MATCH(@"AppleGothic") || // Korean
2821 MATCH(@"Dingbats") || // Dingbats
2822 MATCH(@"Emoji") || // Emoticons
2823 MATCH(@"Geeza") || // Arabic
2824 MATCH(@"Hebrew") || // Hebrew
2825 MATCH(@"HiraKaku") || // Japanese
2826 MATCH(@"HiraMin") || // Japanese
2827 MATCH(@"Kailasa") || // Tibetan
2828 MATCH(@"Ornaments") || // Dingbats
2829 MATCH(@"STHeiti") // Chinese
2833 [fonts addObject:fn];
2838 if (! [fonts count]) return 0; // Nothing suitable?
2840 int i = random() % [fonts count];
2841 NSString *name = [fonts objectAtIndex:i];
2842 UIFont *ff = [UIFont fontWithName:name size:size];
2843 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2847 # endif // USE_IPHONE
2852 try_xlfd_font (const char *name, float scale,
2853 char **name_ret, float *size_ret)
2864 const char *s = (name ? name : "");
2866 while (*s && (*s == '*' || *s == '-'))
2869 while (*s2 && (*s2 != '*' && *s2 != '-'))
2875 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2876 else if (CMP ("random")) rand = YES;
2877 else if (CMP ("bold")) bold = YES;
2878 else if (CMP ("i")) ital = YES;
2879 else if (CMP ("o")) ital = YES;
2880 else if (CMP ("courier")) fixed = YES;
2881 else if (CMP ("fixed")) fixed = YES;
2882 else if (CMP ("m")) fixed = YES;
2883 else if (CMP ("times")) serif = YES;
2884 else if (CMP ("6x10")) fixed = YES, size = 8;
2885 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2886 else if (CMP ("9x15")) fixed = YES, size = 12;
2887 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2888 else if (CMP ("vga")) fixed = YES, size = 12;
2889 else if (CMP ("console")) fixed = YES, size = 12;
2890 else if (CMP ("gallant")) fixed = YES, size = 12;
2892 else if (size == 0) {
2894 if (1 == sscanf (s, " %d ", &n))
2901 if (size < 6 || size > 1000)
2907 nsfont = random_font (bold, ital, size, &ps_name);
2910 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2912 // if that didn't work, turn off attibutes until it does
2913 // (e.g., there is no "Monaco-Bold".)
2915 if (!nsfont && serif) {
2917 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2919 if (!nsfont && ital) {
2921 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2923 if (!nsfont && bold) {
2925 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2927 if (!nsfont && fixed) {
2929 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2933 *name_ret = ps_name;
2943 XLoadFont (Display *dpy, const char *name)
2945 Font fid = (Font) calloc (1, sizeof(*fid));
2950 // Scale up fonts on Retina displays.
2951 scale = dpy->main_window->window.view.contentScaleFactor;
2954 fid->nsfont = try_native_font (name, scale, &fid->ps_name, &fid->size);
2956 if (!fid->nsfont && name &&
2957 strchr (name, ' ') &&
2958 !strchr (name, '*')) {
2959 // If name contains a space but no stars, it is a native font spec --
2960 // return NULL so that we know it really didn't exist. Else, it is an
2961 // XLFD font, so keep trying.
2962 XUnloadFont (dpy, fid);
2967 fid->nsfont = try_xlfd_font (name, scale, &fid->ps_name, &fid->size);
2969 // We should never return NULL for XLFD fonts.
2971 Assert (0, "no font");
2974 CFRetain (fid->nsfont); // needed for garbage collection?
2976 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2985 XLoadQueryFont (Display *dpy, const char *name)
2987 Font fid = XLoadFont (dpy, name);
2989 return XQueryFont (dpy, fid);
2993 XUnloadFont (Display *dpy, Font fid)
2996 free (fid->ps_name);
2997 if (fid->metrics.per_char)
2998 free (fid->metrics.per_char);
3000 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
3001 // crashes in [NSFont ascender] <- query_font, and it seems to go away
3002 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
3003 // They're probably not very big...
3005 // [fid->nsfont release];
3006 // CFRelease (fid->nsfont);
3013 XFreeFontInfo (char **names, XFontStruct *info, int n)
3017 for (i = 0; i < n; i++)
3018 if (names[i]) free (names[i]);
3022 for (i = 0; i < n; i++)
3023 if (info[i].per_char)
3024 free (info[i].per_char);
3031 XFreeFont (Display *dpy, XFontStruct *f)
3034 XFreeFontInfo (0, f, 1);
3035 XUnloadFont (dpy, fid);
3041 XSetFont (Display *dpy, GC gc, Font fid)
3044 XUnloadFont (dpy, gc->gcv.font);
3045 gc->gcv.font = copy_font (fid);
3046 [gc->gcv.font->nsfont retain];
3047 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3052 XTextExtents (XFontStruct *f, const char *s, int length,
3053 int *dir_ret, int *ascent_ret, int *descent_ret,
3056 memset (cs, 0, sizeof(*cs));
3058 for (i = 0; i < length; i++) {
3059 unsigned char c = (unsigned char) s[i];
3060 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
3061 c = f->default_char;
3062 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
3066 cs->ascent = MAX (cs->ascent, cc->ascent);
3067 cs->descent = MAX (cs->descent, cc->descent);
3068 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
3069 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
3070 cs->width += cc->width;
3074 *ascent_ret = f->ascent;
3075 *descent_ret = f->descent;
3080 XTextWidth (XFontStruct *f, const char *s, int length)
3082 int ascent, descent, dir;
3084 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
3090 set_font (Display *dpy, CGContextRef cgc, GC gc)
3092 Font font = gc->gcv.font;
3094 font = XLoadFont (dpy, 0);
3095 gc->gcv.font = font;
3096 [gc->gcv.font->nsfont retain];
3097 CFRetain (gc->gcv.font->nsfont); // needed for garbage collection?
3099 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
3100 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
3105 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
3106 const char *str, int len, BOOL clear_background_p)
3108 if (clear_background_p) {
3109 int ascent, descent, dir;
3111 XTextExtents (&gc->gcv.font->metrics, str, len,
3112 &dir, &ascent, &descent, &cs);
3113 draw_rect (dpy, d, gc,
3114 x + MIN (0, cs.lbearing),
3115 y - MAX (0, ascent),
3116 MAX (MAX (0, cs.rbearing) -
3117 MIN (0, cs.lbearing),
3119 MAX (0, ascent) + MAX (0, descent),
3123 CGRect wr = d->frame;
3126 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
3127 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
3130 CGContextRef cgc = d->cgc;
3131 push_fg_gc (d, gc, YES);
3132 set_font (dpy, cgc, gc);
3134 CGContextSetTextDrawingMode (cgc, kCGTextFill);
3135 if (gc->gcv.antialias_p)
3136 CGContextSetShouldAntialias (cgc, YES);
3137 CGContextShowTextAtPoint (cgc,
3139 wr.origin.y + wr.size.height - y,
3148 unsigned long argb = gc->gcv.foreground;
3149 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
3150 float a = ((argb >> 24) & 0xFF) / 255.0;
3151 float r = ((argb >> 16) & 0xFF) / 255.0;
3152 float g = ((argb >> 8) & 0xFF) / 255.0;
3153 float b = ((argb ) & 0xFF) / 255.0;
3154 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
3155 NSDictionary *attr =
3156 [NSDictionary dictionaryWithObjectsAndKeys:
3157 gc->gcv.font->nsfont, NSFontAttributeName,
3158 fg, NSForegroundColorAttributeName,
3160 char *s2 = (char *) malloc (len + 1);
3161 strncpy (s2, str, len);
3163 NSString *nsstr = [NSString stringWithCString:s2
3164 encoding:NSISOLatin1StringEncoding];
3167 pos.x = wr.origin.x + x;
3168 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
3169 [nsstr drawAtPoint:pos withAttributes:attr];
3173 invalidate_drawable_cache (d);
3179 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
3180 const char *str, int len)
3182 return draw_string (dpy, d, gc, x, y, str, len, NO);
3186 XDrawImageString (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, YES);
3194 XSetForeground (Display *dpy, GC gc, unsigned long fg)
3196 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
3197 gc->gcv.foreground = fg;
3203 XSetBackground (Display *dpy, GC gc, unsigned long bg)
3205 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
3206 gc->gcv.background = bg;
3211 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
3213 gc->gcv.alpha_allowed_p = allowed;
3218 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
3220 gc->gcv.antialias_p = antialias_p;
3226 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
3227 int line_style, int cap_style, int join_style)
3229 gc->gcv.line_width = line_width;
3230 Assert (line_style == LineSolid, "only LineSolid implemented");
3231 // gc->gcv.line_style = line_style;
3232 gc->gcv.cap_style = cap_style;
3233 gc->gcv.join_style = join_style;
3238 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
3244 XSetFunction (Display *dpy, GC gc, int which)
3246 gc->gcv.function = which;
3251 XSetSubwindowMode (Display *dpy, GC gc, int which)
3253 gc->gcv.subwindow_mode = which;
3258 XSetClipMask (Display *dpy, GC gc, Pixmap m)
3260 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
3262 if (gc->gcv.clip_mask) {
3263 XFreePixmap (dpy, gc->gcv.clip_mask);
3264 CGImageRelease (gc->clip_mask);
3267 gc->gcv.clip_mask = copy_pixmap (dpy, m);
3268 if (gc->gcv.clip_mask)
3270 CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
3278 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
3280 gc->gcv.clip_x_origin = x;
3281 gc->gcv.clip_y_origin = y;
3287 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
3288 int *root_x_ret, int *root_y_ret,
3289 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
3291 Assert (w && w->type == WINDOW, "not a window");
3294 int x = w->window.last_mouse_x;
3295 int y = w->window.last_mouse_y;
3296 if (root_x_ret) *root_x_ret = x;
3297 if (root_y_ret) *root_y_ret = y;
3298 if (win_x_ret) *win_x_ret = x;
3299 if (win_y_ret) *win_y_ret = y;
3301 # else // !USE_IPHONE
3303 NSWindow *nsw = [w->window.view window];
3305 // get bottom left of window on screen, from bottom left
3306 wpos.x = wpos.y = 0;
3307 wpos = [nsw convertBaseToScreen:wpos];
3310 // get bottom left of view on window, from bottom left
3311 vpos.x = vpos.y = 0;
3312 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3314 // get bottom left of view on screen, from bottom left
3318 // get top left of view on screen, from bottom left
3319 vpos.y += w->frame.size.height;
3321 // get top left of view on screen, from top left
3322 NSArray *screens = [NSScreen screens];
3323 NSScreen *screen = (screens && [screens count] > 0
3324 ? [screens objectAtIndex:0]
3325 : [NSScreen mainScreen]);
3327 double s = w->window.view.contentScaleFactor;
3331 NSRect srect = [screen frame];
3332 vpos.y = (s * srect.size.height) - vpos.y;
3334 // get the mouse position on window, from bottom left
3335 NSEvent *e = [NSApp currentEvent];
3336 NSPoint p = [e locationInWindow];
3338 // get mouse position on screen, from bottom left
3342 // get mouse position on screen, from top left
3343 p.y = srect.size.height - p.y;
3345 if (root_x_ret) *root_x_ret = (int) p.x;
3346 if (root_y_ret) *root_y_ret = (int) p.y;
3347 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
3348 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
3349 # endif // !USE_IPHONE
3351 if (mask_ret) *mask_ret = 0; // #### poll the keyboard modifiers?
3352 if (root_ret) *root_ret = 0;
3353 if (child_ret) *child_ret = 0;
3358 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
3359 int src_x, int src_y,
3360 int *dest_x_ret, int *dest_y_ret,
3363 Assert (w && w->type == WINDOW, "not a window");
3371 # else // !USE_IPHONE
3373 NSWindow *nsw = [w->window.view window];
3375 // get bottom left of window on screen, from bottom left
3376 wpos.x = wpos.y = 0;
3377 wpos = [nsw convertBaseToScreen:wpos];
3380 // get bottom left of view on window, from bottom left
3381 vpos.x = vpos.y = 0;
3382 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
3384 // get bottom left of view on screen, from bottom left
3388 // get top left of view on screen, from bottom left
3389 vpos.y += w->frame.size.height;
3391 // get top left of view on screen, from top left
3392 NSArray *screens = [NSScreen screens];
3393 NSScreen *screen = (screens && [screens count] > 0
3394 ? [screens objectAtIndex:0]
3395 : [NSScreen mainScreen]);
3397 double s = w->window.view.contentScaleFactor;
3401 NSRect srect = [screen frame];
3402 vpos.y = (s * srect.size.height) - vpos.y;
3404 // point starts out relative to top left of view
3409 // get point relative to top left of screen
3412 # endif // !USE_IPHONE
3423 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
3429 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
3432 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
3434 // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
3435 if ((unsigned int) ks <= 255)
3438 // Put control characters in the string. Not meta.
3439 if (e->state & ControlMask) {
3440 if (c >= 'a' && c <= 'z') // Upcase control.
3442 if (c >= '@' && c <= '_') // Shift to control page.
3444 if (c == ' ') // C-SPC is NULL.
3448 if (k_ret) *k_ret = ks;
3449 if (size > 0) buf[0] = c;
3450 if (size > 1) buf[1] = 0;
3451 return (size > 0 ? 1 : 0);
3456 XFlush (Display *dpy)
3458 // Just let the event loop take care of this on its own schedule.
3463 XSync (Display *dpy, Bool flush)
3465 return XFlush (dpy);
3469 // declared in utils/visual.h
3471 has_writable_cells (Screen *s, Visual *v)
3477 visual_depth (Screen *s, Visual *v)
3483 visual_cells (Screen *s, Visual *v)
3489 visual_class (Screen *s, Visual *v)
3494 // declared in utils/grabclient.h
3496 use_subwindow_mode_p (Screen *screen, Window window)