1 /* xscreensaver, Copyright (c) 1991-2009 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.
21 #import <Cocoa/Cocoa.h>
23 #import "jwxyz-timers.h"
26 #define Assert(C,S) do { \
34 # define MAX(a,b) ((a)>(b)?(a):(b))
35 # define MIN(a,b) ((a)<(b)?(a):(b))
38 struct jwxyz_Drawable {
39 enum { WINDOW, PIXMAP } type;
45 unsigned long background;
53 struct jwxyz_Display {
56 struct jwxyz_sources_data *timers_data;
58 CGDirectDisplayID cgdpy; /* ...of the one and only Window, main_window.
59 This can change if the window is dragged to
60 a different screen. */
62 CGColorSpaceRef colorspace; /* Color space of this screen. We tag all of
63 our images with this to avoid translation
75 CGImageRef clip_mask; // CGImage copy of the Pixmap in gcv.clip_mask
83 // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
84 // But we need the metrics on both of them, so they go here.
90 jwxyz_make_display (void *nsview_arg)
92 NSView *view = (NSView *) nsview_arg;
95 Display *d = (Display *) calloc (1, sizeof(*d));
96 d->screen = (Screen *) calloc (1, sizeof(Screen));
99 Visual *v = (Visual *) calloc (1, sizeof(Visual));
100 v->class = TrueColor;
101 v->red_mask = 0x00FF0000;
102 v->green_mask = 0x0000FF00;
103 v->blue_mask = 0x000000FF;
105 d->screen->visual = v;
107 d->timers_data = jwxyz_sources_init (XtDisplayToApplicationContext (d));
110 Window w = (Window) calloc (1, sizeof(*w));
112 // kludge! this needs to be set late, so we do it in XClearWindow!
113 // w->cgc = [[[view window] graphicsContext] graphicsPort];
115 w->window.view = view;
116 w->window.background = BlackPixel(0,0);
121 w->cgc = [[[view window] graphicsContext] graphicsPort];
124 jwxyz_window_resized (d, w);
130 jwxyz_free_display (Display *dpy)
132 jwxyz_XtRemoveInput_all (dpy);
133 // #### jwxyz_XtRemoveTimeOut_all ();
135 free (dpy->screen->visual);
137 free (dpy->main_window);
143 jwxyz_window_view (Window w)
145 Assert (w->type == WINDOW, "not a window");
146 return w->window.view;
149 /* Call this when the View changes size or position.
152 jwxyz_window_resized (Display *dpy, Window w)
154 Assert (w->type == WINDOW, "not a window");
155 NSRect r = [w->window.view frame];
156 w->frame.origin.x = r.origin.x; // NSRect -> CGRect
157 w->frame.origin.y = r.origin.y;
158 w->frame.size.width = r.size.width;
159 w->frame.size.height = r.size.height;
161 // Figure out which screen the window is currently on.
164 XTranslateCoordinates (dpy, w, NULL, 0, 0, &wx, &wy, NULL);
170 CGGetDisplaysWithPoint (p, 1, &dpy->cgdpy, &n);
171 Assert (dpy->cgdpy, "unable to find CGDisplay");
176 // Figure out this screen's colorspace, and use that for every CGImage.
178 CMProfileRef profile = 0;
179 CMGetProfileByAVID ((CMDisplayIDType) dpy->cgdpy, &profile);
180 Assert (profile, "unable to find colorspace profile");
181 dpy->colorspace = CGColorSpaceCreateWithPlatformColorSpace (profile);
182 Assert (dpy->colorspace, "unable to find colorspace");
186 // WTF? It's faster if we *do not* use the screen's colorspace!
188 dpy->colorspace = CGColorSpaceCreateDeviceRGB();
194 display_sources_data (Display *dpy)
196 return dpy->timers_data;
201 XRootWindow (Display *dpy, int screen)
203 return dpy->main_window;
207 XDefaultScreenOfDisplay (Display *dpy)
213 XDefaultVisualOfScreen (Screen *screen)
215 return screen->visual;
219 XDisplayOfScreen (Screen *s)
225 XDisplayNumberOfScreen (Screen *s)
231 XScreenNumberOfScreen (Screen *s)
237 XDisplayWidth (Display *dpy, int screen)
239 return (int) dpy->main_window->frame.size.width;
243 XDisplayHeight (Display *dpy, int screen)
245 return (int) dpy->main_window->frame.size.height;
250 validate_pixel (unsigned long pixel, unsigned int depth, BOOL alpha_allowed_p)
253 Assert ((pixel == 0 || pixel == 1), "bogus mono pixel");
254 else if (!alpha_allowed_p)
255 Assert (((pixel & BlackPixel(0,0)) == BlackPixel(0,0)),
256 "bogus color pixel");
261 set_color (CGContextRef cgc, unsigned long argb, unsigned int depth,
262 BOOL alpha_allowed_p, BOOL fill_p)
264 validate_pixel (argb, depth, alpha_allowed_p);
267 CGContextSetGrayFillColor (cgc, (argb ? 1.0 : 0.0), 1.0);
269 CGContextSetGrayStrokeColor (cgc, (argb ? 1.0 : 0.0), 1.0);
271 float a = ((argb >> 24) & 0xFF) / 255.0;
272 float r = ((argb >> 16) & 0xFF) / 255.0;
273 float g = ((argb >> 8) & 0xFF) / 255.0;
274 float b = ((argb ) & 0xFF) / 255.0;
276 CGContextSetRGBFillColor (cgc, r, g, b, a);
278 CGContextSetRGBStrokeColor (cgc, r, g, b, a);
283 set_line_mode (CGContextRef cgc, XGCValues *gcv)
285 CGContextSetLineWidth (cgc, gcv->line_width ? gcv->line_width : 1);
286 CGContextSetLineJoin (cgc,
287 gcv->join_style == JoinMiter ? kCGLineJoinMiter :
288 gcv->join_style == JoinRound ? kCGLineJoinRound :
290 CGContextSetLineCap (cgc,
291 gcv->cap_style == CapNotLast ? kCGLineCapButt :
292 gcv->cap_style == CapButt ? kCGLineCapButt :
293 gcv->cap_style == CapRound ? kCGLineCapRound :
298 set_clip_mask (Drawable d, GC gc)
300 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
302 Pixmap p = gc->gcv.clip_mask;
304 Assert (p->type == PIXMAP, "not a pixmap");
306 CGRect wr = d->frame;
308 to.origin.x = wr.origin.x + gc->gcv.clip_x_origin;
309 to.origin.y = wr.origin.y + wr.size.height - gc->gcv.clip_y_origin
310 - p->frame.size.height;
311 to.size.width = p->frame.size.width;
312 to.size.height = p->frame.size.height;
314 CGContextClipToMask (d->cgc, to, gc->clip_mask);
318 /* Pushes a GC context; sets BlendMode and ClipMask.
321 push_gc (Drawable d, GC gc)
323 CGContextRef cgc = d->cgc;
324 CGContextSaveGState (cgc);
326 switch (gc->gcv.function) {
329 case GXcopy:/*CGContextSetBlendMode (cgc, kCGBlendModeNormal);*/ break;
330 case GXxor: CGContextSetBlendMode (cgc, kCGBlendModeDifference); break;
331 case GXor: CGContextSetBlendMode (cgc, kCGBlendModeLighten); break;
332 case GXand: CGContextSetBlendMode (cgc, kCGBlendModeDarken); break;
333 default: abort(); break;
336 if (gc->gcv.clip_mask)
337 set_clip_mask (d, gc);
340 #define pop_gc(d,gc) CGContextRestoreGState ((d)->cgc)
343 /* Pushes a GC context; sets BlendMode, ClipMask, Fill, and Stroke colors.
346 push_color_gc (Drawable d, GC gc, unsigned long color,
347 BOOL antialias_p, Bool fill_p)
351 int depth = gc->depth;
352 switch (gc->gcv.function) {
353 case GXset: color = (depth == 1 ? 1 : WhitePixel(0,0)); break;
354 case GXclear: color = (depth == 1 ? 0 : BlackPixel(0,0)); break;
357 CGContextRef cgc = d->cgc;
359 set_color (cgc, color, depth, gc->gcv.alpha_allowed_p, fill_p);
360 CGContextSetShouldAntialias (cgc, antialias_p);
364 /* Pushes a GC context; sets Fill and Stroke colors to the foreground color.
367 push_fg_gc (Drawable d, GC gc, Bool fill_p)
369 push_color_gc (d, gc, gc->gcv.foreground, gc->gcv.antialias_p, fill_p);
372 /* Pushes a GC context; sets Fill and Stroke colors to the background color.
375 push_bg_gc (Drawable d, GC gc, Bool fill_p)
377 push_color_gc (d, gc, gc->gcv.background, gc->gcv.antialias_p, fill_p);
382 /* You've got to be fucking kidding me!
384 It is *way* faster to draw points by creating and drawing a 1x1 CGImage
385 with repeated calls to CGContextDrawImage than it is to make a single
386 call to CGContextFillRects()!
388 I still wouldn't call it *fast*, however...
390 #define XDRAWPOINTS_IMAGES
393 XDrawPoints (Display *dpy, Drawable d, GC gc,
394 XPoint *points, int count, int mode)
397 CGRect wr = d->frame;
399 push_fg_gc (d, gc, YES);
401 # ifdef XDRAWPOINTS_IMAGES
403 unsigned int argb = gc->gcv.foreground;
404 validate_pixel (argb, gc->depth, gc->gcv.alpha_allowed_p);
406 argb = (gc->gcv.foreground ? WhitePixel(0,0) : BlackPixel(0,0));
408 CGDataProviderRef prov = CGDataProviderCreateWithData (NULL, &argb, 4, NULL);
409 CGImageRef cgi = CGImageCreate (1, 1,
412 /* Host-ordered, since we're using the
413 address of an int as the color data. */
414 (kCGImageAlphaNoneSkipFirst |
415 kCGBitmapByteOrder32Host),
418 NO, /* interpolate */
419 kCGRenderingIntentDefault);
420 CGDataProviderRelease (prov);
422 CGContextRef cgc = d->cgc;
424 rect.size.width = rect.size.height = 1;
425 for (i = 0; i < count; i++) {
426 if (i > 0 && mode == CoordModePrevious) {
427 rect.origin.x += points->x;
428 rect.origin.x -= points->y;
430 rect.origin.x = wr.origin.x + points->x;
431 rect.origin.y = wr.origin.y + wr.size.height - points->y - 1;
434 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
435 CGContextDrawImage (cgc, rect, cgi);
439 CGImageRelease (cgi);
441 # else /* ! XDRAWPOINTS_IMAGES */
443 CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
446 for (i = 0; i < count; i++) {
447 r->size.width = r->size.height = 1;
448 if (i > 0 && mode == CoordModePrevious) {
449 r->origin.x = r[-1].origin.x + points->x;
450 r->origin.y = r[-1].origin.x - points->y;
452 r->origin.x = wr.origin.x + points->x;
453 r->origin.y = wr.origin.y + wr.size.height - points->y;
459 CGContextFillRects (d->cgc, rects, count);
462 # endif /* ! XDRAWPOINTS_IMAGES */
471 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
476 return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
480 static void draw_rect (Display *, Drawable, GC,
481 int x, int y, unsigned int width, unsigned int height,
482 BOOL foreground_p, BOOL fill_p);
485 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc,
486 int src_x, int src_y,
487 unsigned int width, unsigned int height,
488 int dst_x, int dst_y)
490 Assert ((width < 65535), "improbably large width");
491 Assert ((height < 65535), "improbably large height");
492 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
493 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
494 Assert ((dst_x < 65535 && dst_x > -65535), "improbably large dst_x");
495 Assert ((dst_y < 65535 && dst_y > -65535), "improbably large dst_y");
497 if (width == 0 || height == 0)
500 if (gc && (gc->gcv.function == GXset ||
501 gc->gcv.function == GXclear)) {
502 // "set" and "clear" are dumb drawing modes that ignore the source
503 // bits and just draw solid rectangles.
504 set_color (dst->cgc, (gc->gcv.function == GXset
505 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
506 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
507 gc->depth, gc->gcv.alpha_allowed_p, YES);
508 draw_rect (dpy, dst, 0, dst_x, dst_y, width, height, YES, YES);
512 CGRect src_frame, dst_frame; // Sizes and origins of the two drawables
513 CGRect src_rect, dst_rect; // The two rects to draw, clipped to the
514 // bounds of their drawables.
515 BOOL clipped = NO; // Whether we did any clipping of the rects.
517 src_frame = src->frame;
518 dst_frame = dst->frame;
520 // Initialize src_rect...
522 src_rect.origin.x = src_frame.origin.x + src_x;
523 src_rect.origin.y = src_frame.origin.y + src_frame.size.height
525 if (src_rect.origin.y < -65535) Assert(0, "src.origin.y went nuts");
526 src_rect.size.width = width;
527 src_rect.size.height = height;
529 // Initialize dst_rect...
531 dst_rect.origin.x = dst_frame.origin.x + dst_x;
532 dst_rect.origin.y = dst_frame.origin.y + dst_frame.size.height
534 if (dst_rect.origin.y < -65535) Assert(0, "dst.origin.y went nuts");
535 dst_rect.size.width = width;
536 dst_rect.size.height = height;
538 // Clip rects to frames...
540 // CGRect orig_src_rect = src_rect;
541 CGRect orig_dst_rect = dst_rect;
543 # define CLIP(THIS,THAT,VAL,SIZE) do { \
544 float off = THIS##_rect.origin.VAL; \
547 THIS##_rect.size.SIZE += off; \
548 THAT##_rect.size.SIZE += off; \
549 THIS##_rect.origin.VAL -= off; \
550 THAT##_rect.origin.VAL -= off; \
552 off = (( THIS##_rect.origin.VAL + THIS##_rect.size.SIZE) - \
553 (THIS##_frame.origin.VAL + THIS##_frame.size.SIZE)); \
556 THIS##_rect.size.SIZE -= off; \
557 THAT##_rect.size.SIZE -= off; \
560 CLIP (dst, src, x, width);
561 CLIP (dst, src, y, height);
562 CLIP (src, dst, x, width);
563 CLIP (src, dst, y, height);
567 Assert (src_rect.size.width == dst_rect.size.width, "width out of sync");
568 Assert (src_rect.size.height == dst_rect.size.height, "height out of sync");
569 Assert (src_rect.origin.x >= 0 && src_rect.origin.y >= 0, "clip failed src_x");
570 Assert (dst_rect.origin.x >= 0 && dst_rect.origin.y >= 0, "clip failed dst_x");
571 Assert (src_rect.origin.y >= 0 && src_rect.origin.y >= 0, "clip failed src_y");
572 Assert (dst_rect.origin.y >= 0 && dst_rect.origin.y >= 0, "clip failed dst_y");
573 Assert (src_rect.origin.x + src_rect.size.width <=
574 src_frame.origin.x + src_frame.size.width, "clip failed src_width");
577 if (src_rect.size.width <= 0 || src_rect.size.height <= 0)
580 NSObject *releaseme = 0;
584 if (src->type == PIXMAP) {
586 // get a CGImage out of the pixmap CGContext -- it's the whole pixmap,
587 // but it presumably shares the data pointer instead of copying it.
588 cgi = CGBitmapContextCreateImage (src->cgc);
590 // if doing a sub-rect, trim it down.
591 if (src_rect.origin.x != src_frame.origin.x ||
592 src_rect.origin.y != src_frame.origin.y ||
593 src_rect.size.width != src_frame.size.width ||
594 src_rect.size.height != src_frame.size.height) {
595 // #### I don't understand why this is needed...
596 src_rect.origin.y = (src_frame.size.height -
597 src_rect.size.height - src_rect.origin.y);
598 // This does not copy image data, so it should be fast.
599 CGImageRef cgi2 = CGImageCreateWithImageInRect (cgi, src_rect);
600 CGImageRelease (cgi);
604 if (src->pixmap.depth == 1)
607 } else { /* (src->type == WINDOW) */
610 nsfrom.origin.x = src_rect.origin.x;
611 nsfrom.origin.y = src_rect.origin.y;
612 nsfrom.size.width = src_rect.size.width;
613 nsfrom.size.height = src_rect.size.height;
616 // get the bits (desired sub-rectangle) out of the NSView via Cocoa.
618 NSBitmapImageRep *bm = [NSBitmapImageRep alloc];
619 [bm initWithFocusedViewRect:nsfrom];
620 unsigned char *data = [bm bitmapData];
621 int bps = [bm bitsPerSample];
622 int bpp = [bm bitsPerPixel];
623 int bpl = [bm bytesPerRow];
628 // QuickDraw way (doesn't work, need NSQuickDrawView)
629 PixMapHandle pix = GetPortPixMap([src->window.view qdPort]);
630 char **data = GetPortPixMap (pix);
633 int bpl = GetPixRowBytes (pix) & 0x3FFF;
637 // get the bits (desired sub-rectangle) out of the raw frame buffer.
638 // (This renders wrong, and appears to be even slower anyway.)
640 int window_x, window_y;
641 XTranslateCoordinates (dpy, src, NULL, 0, 0, &window_x, &window_y, NULL);
642 window_x += nsfrom.origin.x;
643 window_y += (dst->frame.size.height
644 - (nsfrom.origin.y + nsfrom.size.height));
646 unsigned char *data = (unsigned char *)
647 CGDisplayAddressForPosition (dpy->cgdpy, window_x, window_y);
648 int bps = CGDisplayBitsPerSample (dpy->cgdpy);
649 int bpp = CGDisplayBitsPerPixel (dpy->cgdpy);
650 int bpl = CGDisplayBytesPerRow (dpy->cgdpy);
654 // create a CGImage from those bits
656 CGDataProviderRef prov =
657 CGDataProviderCreateWithData (NULL, data, bpl * nsfrom.size.height,
659 cgi = CGImageCreate (src_rect.size.width, src_rect.size.height,
662 /* Use whatever default bit ordering we got from
663 initWithFocusedViewRect. I would have assumed
664 that it was (kCGImageAlphaNoneSkipFirst |
665 kCGBitmapByteOrder32Host), but on Intel,
671 NO, /* interpolate */
672 kCGRenderingIntentDefault);
673 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
674 CGDataProviderRelease (prov);
677 if (mask_p) { // src depth == 1
679 push_bg_gc (dst, gc, YES);
681 // fill the destination rectangle with solid background...
682 CGContextFillRect (dst->cgc, orig_dst_rect);
684 // then fill in a solid rectangle of the fg color, using the image as an
685 // alpha mask. (the image has only values of BlackPixel or WhitePixel.)
686 set_color (dst->cgc, gc->gcv.foreground, gc->depth,
687 gc->gcv.alpha_allowed_p, YES);
688 CGContextClipToMask (dst->cgc, dst_rect, cgi);
689 CGContextFillRect (dst->cgc, dst_rect);
693 } else { // src depth > 1
697 // If either the src or dst rects did not lie within their drawables,
698 // then we have adjusted both the src and dst rects to account for
699 // the clipping; that means we need to first clear to the background,
700 // so that clipped bits end up in the bg color instead of simply not
704 set_color (dst->cgc, gc->gcv.background, gc->depth,
705 gc->gcv.alpha_allowed_p, YES);
706 CGContextFillRect (dst->cgc, orig_dst_rect);
709 // copy the CGImage onto the destination CGContext
710 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
711 CGContextDrawImage (dst->cgc, dst_rect, cgi);
716 CGImageRelease (cgi);
717 if (releaseme) [releaseme release];
723 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
724 int src_x, int src_y,
725 unsigned width, int height,
726 int dest_x, int dest_y, unsigned long plane)
728 Assert ((gc->depth == 1 || plane == 1), "hairy plane mask!");
730 // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
731 // not to white/black.
732 return XCopyArea (dpy, src, dest, gc,
733 src_x, src_y, width, height, dest_x, dest_y);
738 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
740 // when drawing a zero-length line, obey line-width and cap-style.
741 if (x1 == x2 && y1 == y2) {
742 int w = gc->gcv.line_width;
745 if (gc->gcv.line_width > 1 && gc->gcv.cap_style == CapRound)
746 return XFillArc (dpy, d, gc, x1, y1, w, w, 0, 360*64);
748 return XFillRectangle (dpy, d, gc, x1, y1, w, w);
751 CGRect wr = d->frame;
753 p.x = wr.origin.x + x1;
754 p.y = wr.origin.y + wr.size.height - y1;
756 push_fg_gc (d, gc, NO);
758 set_line_mode (d->cgc, &gc->gcv);
759 CGContextBeginPath (d->cgc);
760 CGContextMoveToPoint (d->cgc, p.x, p.y);
761 p.x = wr.origin.x + x2;
762 p.y = wr.origin.y + wr.size.height - y2;
763 CGContextAddLineToPoint (d->cgc, p.x, p.y);
764 CGContextStrokePath (d->cgc);
770 XDrawLines (Display *dpy, Drawable d, GC gc, XPoint *points, int count,
775 CGRect wr = d->frame;
776 push_fg_gc (d, gc, NO);
777 set_line_mode (d->cgc, &gc->gcv);
779 // if the first and last points coincide, use closepath to get
780 // the proper line-joining.
781 BOOL closed_p = (points[0].x == points[count-1].x &&
782 points[0].y == points[count-1].y);
783 if (closed_p) count--;
785 p.x = wr.origin.x + points->x;
786 p.y = wr.origin.y + wr.size.height - points->y;
788 CGContextBeginPath (d->cgc);
789 CGContextMoveToPoint (d->cgc, p.x, p.y);
790 for (i = 1; i < count; i++) {
791 if (mode == CoordModePrevious) {
795 p.x = wr.origin.x + points->x;
796 p.y = wr.origin.y + wr.size.height - points->y;
798 CGContextAddLineToPoint (d->cgc, p.x, p.y);
801 if (closed_p) CGContextClosePath (d->cgc);
802 CGContextStrokePath (d->cgc);
809 XDrawSegments (Display *dpy, Drawable d, GC gc, XSegment *segments, int count)
812 CGRect wr = d->frame;
814 push_fg_gc (d, gc, NO);
815 set_line_mode (d->cgc, &gc->gcv);
816 CGContextBeginPath (d->cgc);
817 for (i = 0; i < count; i++) {
818 CGContextMoveToPoint (d->cgc,
819 wr.origin.x + segments->x1,
820 wr.origin.y + wr.size.height - segments->y1);
821 CGContextAddLineToPoint (d->cgc,
822 wr.origin.x + segments->x2,
823 wr.origin.y + wr.size.height - segments->y2);
826 CGContextStrokePath (d->cgc);
833 XClearWindow (Display *dpy, Window win)
835 Assert (win->type == WINDOW, "not a window");
836 CGRect wr = win->frame;
837 return XClearArea (dpy, win, 0, 0, wr.size.width, wr.size.height, 0);
841 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
843 Assert (w->type == WINDOW, "not a window");
844 validate_pixel (pixel, 32, NO);
845 w->window.background = pixel;
850 draw_rect (Display *dpy, Drawable d, GC gc,
851 int x, int y, unsigned int width, unsigned int height,
852 BOOL foreground_p, BOOL fill_p)
854 CGRect wr = d->frame;
856 r.origin.x = wr.origin.x + x;
857 r.origin.y = wr.origin.y + wr.size.height - y - height;
858 r.size.width = width;
859 r.size.height = height;
863 push_fg_gc (d, gc, fill_p);
865 push_bg_gc (d, gc, fill_p);
869 CGContextFillRect (d->cgc, r);
872 set_line_mode (d->cgc, &gc->gcv);
873 CGContextStrokeRect (d->cgc, r);
882 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
883 unsigned int width, unsigned int height)
885 draw_rect (dpy, d, gc, x, y, width, height, YES, YES);
890 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y,
891 unsigned int width, unsigned int height)
893 draw_rect (dpy, d, gc, x, y, width, height, YES, NO);
898 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
900 CGRect wr = d->frame;
902 push_fg_gc (d, gc, YES);
903 for (i = 0; i < n; i++) {
905 r.origin.x = wr.origin.x + rects->x;
906 r.origin.y = wr.origin.y + wr.size.height - rects->y - rects->height;
907 r.size.width = rects->width;
908 r.size.height = rects->height;
909 CGContextFillRect (d->cgc, r);
918 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
920 Assert (win->type == WINDOW, "not a window");
921 set_color (win->cgc, win->window.background, 32, NO, YES);
922 draw_rect (dpy, win, 0, x, y, w, h, NO, YES);
928 XFillPolygon (Display *dpy, Drawable d, GC gc,
929 XPoint *points, int npoints, int shape, int mode)
931 CGRect wr = d->frame;
933 push_fg_gc (d, gc, YES);
934 CGContextBeginPath (d->cgc);
935 for (i = 0; i < npoints; i++) {
937 if (i > 0 && mode == CoordModePrevious) {
941 x = wr.origin.x + points[i].x;
942 y = wr.origin.y + wr.size.height - points[i].y;
946 CGContextMoveToPoint (d->cgc, x, y);
948 CGContextAddLineToPoint (d->cgc, x, y);
950 CGContextClosePath (d->cgc);
951 if (gc->gcv.fill_rule == EvenOddRule)
952 CGContextEOFillPath (d->cgc);
954 CGContextFillPath (d->cgc);
959 #define radians(DEG) ((DEG) * M_PI / 180.0)
960 #define degrees(RAD) ((RAD) * 180.0 / M_PI)
963 draw_arc (Display *dpy, Drawable d, GC gc, int x, int y,
964 unsigned int width, unsigned int height, int angle1, int angle2,
967 CGRect wr = d->frame;
969 bound.origin.x = wr.origin.x + x;
970 bound.origin.y = wr.origin.y + wr.size.height - y - height;
971 bound.size.width = width;
972 bound.size.height = height;
975 ctr.x = bound.origin.x + bound.size.width /2;
976 ctr.y = bound.origin.y + bound.size.height/2;
978 float r1 = radians (angle1/64.0);
979 float r2 = radians (angle2/64.0) + r1;
980 BOOL clockwise = angle2 < 0;
981 BOOL closed_p = (angle2 >= 360*64 || angle2 <= -360*64);
983 push_fg_gc (d, gc, fill_p);
985 CGContextBeginPath (d->cgc);
987 CGContextSaveGState(d->cgc);
988 CGContextTranslateCTM (d->cgc, ctr.x, ctr.y);
989 CGContextScaleCTM (d->cgc, width/2.0, height/2.0);
991 CGContextMoveToPoint (d->cgc, 0, 0);
993 CGContextAddArc (d->cgc, 0.0, 0.0, 1, r1, r2, clockwise);
994 CGContextRestoreGState (d->cgc); // restore before stroke, for line width
997 CGContextClosePath (d->cgc); // for proper line joining
1000 CGContextFillPath (d->cgc);
1002 set_line_mode (d->cgc, &gc->gcv);
1003 CGContextStrokePath (d->cgc);
1011 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
1012 unsigned int width, unsigned int height, int angle1, int angle2)
1014 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, NO);
1018 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
1019 unsigned int width, unsigned int height, int angle1, int angle2)
1021 return draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, YES);
1025 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1028 for (i = 0; i < narcs; i++)
1029 draw_arc (dpy, d, gc,
1030 arcs[i].x, arcs[i].y,
1031 arcs[i].width, arcs[i].height,
1032 arcs[i].angle1, arcs[i].angle2,
1038 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
1041 for (i = 0; i < narcs; i++)
1042 draw_arc (dpy, d, gc,
1043 arcs[i].x, arcs[i].y,
1044 arcs[i].width, arcs[i].height,
1045 arcs[i].angle1, arcs[i].angle2,
1052 gcv_defaults (XGCValues *gcv, int depth)
1054 memset (gcv, 0, sizeof(*gcv));
1055 gcv->function = GXcopy;
1056 gcv->foreground = (depth == 1 ? 1 : WhitePixel(0,0));
1057 gcv->background = (depth == 1 ? 0 : BlackPixel(0,0));
1058 gcv->line_width = 1;
1059 gcv->cap_style = CapNotLast;
1060 gcv->join_style = JoinMiter;
1061 gcv->fill_rule = EvenOddRule;
1063 gcv->alpha_allowed_p = NO;
1064 gcv->antialias_p = YES;
1068 set_gcv (GC gc, XGCValues *from, unsigned long mask)
1070 if (mask & GCFunction) gc->gcv.function = from->function;
1071 if (mask & GCForeground) gc->gcv.foreground = from->foreground;
1072 if (mask & GCBackground) gc->gcv.background = from->background;
1073 if (mask & GCLineWidth) gc->gcv.line_width = from->line_width;
1074 if (mask & GCCapStyle) gc->gcv.cap_style = from->cap_style;
1075 if (mask & GCJoinStyle) gc->gcv.join_style = from->join_style;
1076 if (mask & GCFillRule) gc->gcv.fill_rule = from->fill_rule;
1077 if (mask & GCClipXOrigin) gc->gcv.clip_x_origin = from->clip_x_origin;
1078 if (mask & GCClipYOrigin) gc->gcv.clip_y_origin = from->clip_y_origin;
1079 if (mask & GCSubwindowMode) gc->gcv.subwindow_mode = from->subwindow_mode;
1081 if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask);
1082 if (mask & GCFont) XSetFont (0, gc, from->font);
1084 if (mask & GCForeground) validate_pixel (from->foreground, gc->depth,
1085 gc->gcv.alpha_allowed_p);
1086 if (mask & GCBackground) validate_pixel (from->background, gc->depth,
1087 gc->gcv.alpha_allowed_p);
1089 if (mask & GCLineStyle) abort();
1090 if (mask & GCPlaneMask) abort();
1091 if (mask & GCFillStyle) abort();
1092 if (mask & GCTile) abort();
1093 if (mask & GCStipple) abort();
1094 if (mask & GCTileStipXOrigin) abort();
1095 if (mask & GCTileStipYOrigin) abort();
1096 if (mask & GCGraphicsExposures) abort();
1097 if (mask & GCDashOffset) abort();
1098 if (mask & GCDashList) abort();
1099 if (mask & GCArcMode) abort();
1104 XCreateGC (Display *dpy, Drawable d, unsigned long mask, XGCValues *xgcv)
1106 struct jwxyz_GC *gc = (struct jwxyz_GC *) calloc (1, sizeof(*gc));
1107 if (d->type == WINDOW) {
1109 } else { /* (d->type == PIXMAP) */
1110 gc->depth = d->pixmap.depth;
1113 gcv_defaults (&gc->gcv, gc->depth);
1114 set_gcv (gc, xgcv, mask);
1119 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *gcv)
1121 set_gcv (gc, gcv, mask);
1127 XFreeGC (Display *dpy, GC gc)
1130 XUnloadFont (dpy, gc->gcv.font);
1132 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
1134 if (gc->gcv.clip_mask) {
1135 XFreePixmap (dpy, gc->gcv.clip_mask);
1136 CGImageRelease (gc->clip_mask);
1144 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
1146 Assert (w->type == WINDOW, "not a window");
1147 memset (xgwa, 0, sizeof(*xgwa));
1148 xgwa->x = w->frame.origin.x;
1149 xgwa->y = w->frame.origin.y;
1150 xgwa->width = w->frame.size.width;
1151 xgwa->height = w->frame.size.height;
1153 xgwa->screen = dpy->screen;
1154 xgwa->visual = dpy->screen->visual;
1159 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
1160 int *x_ret, int *y_ret,
1161 unsigned int *w_ret, unsigned int *h_ret,
1162 unsigned int *bw_ret, unsigned int *d_ret)
1164 *x_ret = d->frame.origin.x;
1165 *y_ret = d->frame.origin.y;
1166 *w_ret = d->frame.size.width;
1167 *h_ret = d->frame.size.height;
1168 *d_ret = (d->type == WINDOW ? 32 : d->pixmap.depth);
1169 *root_ret = RootWindow (dpy, 0);
1176 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
1178 // store 32 bit ARGB in the pixel field.
1179 // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
1180 color->pixel = (uint32_t)
1182 (((color->red >> 8) & 0xFF) << 16) |
1183 (((color->green >> 8) & 0xFF) << 8) |
1184 (((color->blue >> 8) & 0xFF) ));
1189 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
1190 unsigned long *pmret, unsigned int npl,
1191 unsigned long *pxret, unsigned int npx)
1197 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
1199 Assert(0, "XStoreColors called");
1204 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
1206 Assert(0, "XStoreColor called");
1211 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
1212 unsigned long planes)
1218 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
1220 unsigned char r=0, g=0, b=0;
1221 if (*spec == '#' && strlen(spec) == 7) {
1222 static unsigned const char hex[] = { // yeah yeah, shoot me.
1223 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,
1224 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,
1225 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,
1226 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,
1227 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,
1228 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,
1229 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,
1230 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};
1231 r = (hex[spec[1]] << 4) | hex[spec[2]];
1232 g = (hex[spec[3]] << 4) | hex[spec[4]];
1233 b = (hex[spec[5]] << 4) | hex[spec[6]];
1234 } else if (!strcasecmp(spec,"black")) {
1236 } else if (!strcasecmp(spec,"white")) {
1238 } else if (!strcasecmp(spec,"red")) {
1240 } else if (!strcasecmp(spec,"green")) {
1242 } else if (!strcasecmp(spec,"blue")) {
1244 } else if (!strcasecmp(spec,"cyan")) {
1246 } else if (!strcasecmp(spec,"magenta")) {
1248 } else if (!strcasecmp(spec,"yellow")) {
1254 ret->red = (r << 8) | r;
1255 ret->green = (g << 8) | g;
1256 ret->blue = (b << 8) | b;
1257 ret->flags = DoRed|DoGreen|DoBlue;
1262 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
1263 XColor *screen_ret, XColor *exact_ret)
1265 if (! XParseColor (dpy, cmap, name, screen_ret))
1267 *exact_ret = *screen_ret;
1268 return XAllocColor (dpy, cmap, screen_ret);
1272 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
1274 validate_pixel (color->pixel, 32, NO);
1275 unsigned char r = ((color->pixel >> 16) & 0xFF);
1276 unsigned char g = ((color->pixel >> 8) & 0xFF);
1277 unsigned char b = ((color->pixel ) & 0xFF);
1278 color->red = (r << 8) | r;
1279 color->green = (g << 8) | g;
1280 color->blue = (b << 8) | b;
1281 color->flags = DoRed|DoGreen|DoBlue;
1286 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
1289 for (i = 0; i < n; i++)
1290 XQueryColor (dpy, cmap, &c[i]);
1295 static unsigned long
1296 ximage_getpixel_1 (XImage *ximage, int x, int y)
1298 return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
1302 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
1305 ximage->data [y * ximage->bytes_per_line + (x>>3)] |= (1 << (x & 7));
1307 ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
1312 static unsigned long
1313 ximage_getpixel_32 (XImage *ximage, int x, int y)
1315 return ((unsigned long)
1316 *((uint32_t *) ximage->data +
1317 (y * (ximage->bytes_per_line >> 2)) +
1322 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
1324 *((uint32_t *) ximage->data +
1325 (y * (ximage->bytes_per_line >> 2)) +
1326 x) = (uint32_t) pixel;
1332 XInitImage (XImage *ximage)
1334 if (!ximage->bytes_per_line)
1335 ximage->bytes_per_line = (ximage->depth == 1
1336 ? (ximage->width + 7) / 8
1337 : ximage->width * 4);
1339 if (ximage->depth == 1) {
1340 ximage->f.put_pixel = ximage_putpixel_1;
1341 ximage->f.get_pixel = ximage_getpixel_1;
1342 } else if (ximage->depth == 32 || ximage->depth == 24) {
1343 ximage->f.put_pixel = ximage_putpixel_32;
1344 ximage->f.get_pixel = ximage_getpixel_32;
1353 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
1354 int format, int offset, char *data,
1355 unsigned int width, unsigned int height,
1356 int bitmap_pad, int bytes_per_line)
1358 XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
1359 ximage->width = width;
1360 ximage->height = height;
1361 ximage->format = format;
1362 ximage->data = data;
1363 ximage->bitmap_unit = 8;
1364 ximage->byte_order = MSBFirst;
1365 ximage->bitmap_bit_order = ximage->byte_order;
1366 ximage->bitmap_pad = bitmap_pad;
1367 ximage->depth = depth;
1368 ximage->red_mask = (depth == 1 ? 0 : 0x00FF0000);
1369 ximage->green_mask = (depth == 1 ? 0 : 0x0000FF00);
1370 ximage->blue_mask = (depth == 1 ? 0 : 0x000000FF);
1371 ximage->bits_per_pixel = (depth == 1 ? 1 : 32);
1372 ximage->bytes_per_line = bytes_per_line;
1374 XInitImage (ximage);
1379 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
1381 XImage *to = XCreateImage (0, 0, from->depth, from->format, 0, 0,
1382 w, h, from->bitmap_pad, 0);
1383 to->data = (char *) malloc (h * to->bytes_per_line);
1385 if (x >= from->width)
1387 else if (x+w > from->width)
1388 w = from->width - x;
1390 if (y >= from->height)
1392 else if (y+h > from->height)
1393 h = from->height - y;
1396 for (ty = 0; ty < h; ty++)
1397 for (tx = 0; tx < w; tx++)
1398 XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
1403 XPixmapFormatValues *
1404 XListPixmapFormats (Display *dpy, int *n_ret)
1406 XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
1408 ret[0].bits_per_pixel = 32;
1409 ret[0].scanline_pad = 8;
1411 ret[1].bits_per_pixel = 1;
1412 ret[1].scanline_pad = 8;
1419 XGetPixel (XImage *ximage, int x, int y)
1421 return ximage->f.get_pixel (ximage, x, y);
1426 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
1428 return ximage->f.put_pixel (ximage, x, y, pixel);
1432 XDestroyImage (XImage *ximage)
1434 if (ximage->data) free (ximage->data);
1441 flipbits (unsigned const char *in, unsigned char *out, int length)
1443 static const unsigned char table[256] = {
1444 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
1445 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
1446 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
1447 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
1448 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
1449 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
1450 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
1451 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
1452 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
1453 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
1454 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
1455 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
1456 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
1457 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
1458 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
1459 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
1460 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
1461 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
1462 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
1463 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
1464 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
1465 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
1466 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
1467 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
1468 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
1469 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
1470 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
1471 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
1472 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
1473 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
1474 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
1475 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
1477 while (--length > 0)
1478 *out++ = table[*in++];
1483 XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
1484 int src_x, int src_y, int dest_x, int dest_y,
1485 unsigned int w, unsigned int h)
1487 CGRect wr = d->frame;
1489 Assert ((w < 65535), "improbably large width");
1490 Assert ((h < 65535), "improbably large height");
1491 Assert ((src_x < 65535 && src_x > -65535), "improbably large src_x");
1492 Assert ((src_y < 65535 && src_y > -65535), "improbably large src_y");
1493 Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
1494 Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
1496 // Clip width and height to the bounds of the Drawable
1498 if (dest_x + w > wr.size.width) {
1499 if (dest_x > wr.size.width)
1501 w = wr.size.width - dest_x;
1503 if (dest_y + h > wr.size.height) {
1504 if (dest_y > wr.size.height)
1506 h = wr.size.height - dest_y;
1508 if (w <= 0 || h <= 0)
1511 // Clip width and height to the bounds of the XImage
1513 if (src_x + w > ximage->width) {
1514 if (src_x > ximage->width)
1516 w = ximage->width - src_x;
1518 if (src_y + h > ximage->height) {
1519 if (src_y > ximage->height)
1521 h = ximage->height - src_y;
1523 if (w <= 0 || h <= 0)
1526 if (gc && (gc->gcv.function == GXset ||
1527 gc->gcv.function == GXclear)) {
1528 // "set" and "clear" are dumb drawing modes that ignore the source
1529 // bits and just draw solid rectangles.
1530 set_color (d->cgc, (gc->gcv.function == GXset
1531 ? (gc->depth == 1 ? 1 : WhitePixel(0,0))
1532 : (gc->depth == 1 ? 0 : BlackPixel(0,0))),
1533 gc->depth, gc->gcv.alpha_allowed_p, YES);
1534 draw_rect (dpy, d, 0, dest_x, dest_y, w, h, YES, YES);
1538 int bpl = ximage->bytes_per_line;
1539 int bpp = ximage->bits_per_pixel;
1540 int bsize = bpl * h;
1541 char *data = ximage->data;
1544 r.origin.x = wr.origin.x + dest_x;
1545 r.origin.y = wr.origin.y + wr.size.height - dest_y - h;
1551 /* Take advantage of the fact that it's ok for (bpl != w * bpp)
1552 to create a CGImage from a sub-rectagle of the XImage.
1554 data += (src_y * bpl) + (src_x * 4);
1555 CGDataProviderRef prov =
1556 CGDataProviderCreateWithData (NULL, data, bsize, NULL);
1558 CGImageRef cgi = CGImageCreate (w, h,
1561 /* Need this for XPMs to have the right
1562 colors, e.g. the logo in "maze". */
1563 (kCGImageAlphaNoneSkipFirst |
1564 kCGBitmapByteOrder32Host),
1566 NULL, /* decode[] */
1567 NO, /* interpolate */
1568 kCGRenderingIntentDefault);
1569 CGDataProviderRelease (prov);
1570 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1571 CGContextDrawImage (d->cgc, r, cgi);
1572 CGImageRelease (cgi);
1574 } else { // (bpp == 1)
1576 /* To draw a 1bpp image, we use it as a mask and fill two rectangles.
1578 #### However, the bit order within a byte in a 1bpp XImage is
1579 the wrong way around from what Quartz expects, so first we
1580 have to copy the data to reverse it. Shit! Maybe it
1581 would be worthwhile to go through the hacks and #ifdef
1582 each one that diddles 1bpp XImage->data directly...
1584 Assert ((src_x % 8) == 0,
1585 "XPutImage with non-byte-aligned 1bpp X offset not implemented");
1587 data += (src_y * bpl) + (src_x / 8); // move to x,y within the data
1588 unsigned char *flipped = (unsigned char *) malloc (bsize);
1590 flipbits ((unsigned char *) data, flipped, bsize);
1592 CGDataProviderRef prov =
1593 CGDataProviderCreateWithData (NULL, flipped, bsize, NULL);
1594 CGImageRef mask = CGImageMaskCreate (w, h,
1597 NULL, /* decode[] */
1598 NO); /* interpolate */
1599 push_fg_gc (d, gc, YES);
1601 CGContextFillRect (d->cgc, r); // foreground color
1602 CGContextClipToMask (d->cgc, r, mask);
1603 set_color (d->cgc, gc->gcv.background, gc->depth, NO, YES);
1604 CGContextFillRect (d->cgc, r); // background color
1608 CGDataProviderRelease (prov);
1609 CGImageRelease (mask);
1617 XGetImage (Display *dpy, Drawable d, int x, int y,
1618 unsigned int width, unsigned int height,
1619 unsigned long plane_mask, int format)
1621 const unsigned char *data = 0;
1622 int depth, ibpp, ibpl, alpha_first_p;
1623 NSBitmapImageRep *bm = 0;
1625 Assert ((width < 65535), "improbably large width");
1626 Assert ((height < 65535), "improbably large height");
1627 Assert ((x < 65535 && x > -65535), "improbably large x");
1628 Assert ((y < 65535 && y > -65535), "improbably large y");
1630 if (d->type == PIXMAP) {
1631 depth = d->pixmap.depth;
1632 ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
1633 ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
1634 data = CGBitmapContextGetData (d->cgc);
1635 Assert (data, "CGBitmapContextGetData failed");
1637 // get the bits (desired sub-rectangle) out of the NSView
1638 bm = [NSBitmapImageRep alloc];
1640 nsfrom.origin.x = x;
1641 nsfrom.origin.y = y;
1642 nsfrom.size.width = width;
1643 nsfrom.size.height = height;
1644 [bm initWithFocusedViewRect:nsfrom];
1646 alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
1647 ibpp = [bm bitsPerPixel];
1648 ibpl = [bm bytesPerRow];
1649 data = [bm bitmapData];
1650 Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
1653 // data points at (x,y) with ibpl rowstride. ignore x,y from now on.
1654 data += (y * ibpl) + (x * (ibpp/8));
1656 format = (depth == 1 ? XYPixmap : ZPixmap);
1657 XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
1659 image->data = (char *) malloc (height * image->bytes_per_line);
1661 int obpl = image->bytes_per_line;
1663 /* both PPC and Intel use word-ordered ARGB frame buffers, which
1664 means that on Intel it is BGRA when viewed by bytes (And BGR
1665 when using 24bpp packing).
1667 BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
1668 The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
1669 indicator of this latest kink.
1673 const unsigned char *iline = data;
1674 for (yy = 0; yy < height; yy++) {
1676 const unsigned char *iline2 = iline;
1677 for (xx = 0; xx < width; xx++) {
1679 iline2++; // ignore R or A or A or B
1680 iline2++; // ignore G or B or R or G
1681 unsigned char r = *iline2++; // use B or G or G or R
1682 if (ibpp == 32) iline2++; // ignore A or R or B or A
1684 XPutPixel (image, xx, yy, (r ? 1 : 0));
1689 Assert (ibpp == 24 || ibpp == 32, "weird obpp");
1690 const unsigned char *iline = data;
1691 unsigned char *oline = (unsigned char *) image->data;
1692 for (yy = 0; yy < height; yy++) {
1694 const unsigned char *iline2 = iline;
1695 unsigned char *oline2 = oline;
1697 if (alpha_first_p) // ARGB
1698 for (xx = 0; xx < width; xx++) {
1699 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1700 unsigned char r = *iline2++;
1701 unsigned char g = *iline2++;
1702 unsigned char b = *iline2++;
1703 uint32_t pixel = ((a << 24) |
1707 *((uint32_t *) oline2) = pixel;
1711 for (xx = 0; xx < width; xx++) {
1712 unsigned char r = *iline2++;
1713 unsigned char g = *iline2++;
1714 unsigned char b = *iline2++;
1715 unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
1716 uint32_t pixel = ((a << 24) |
1720 *((uint32_t *) oline2) = pixel;
1729 if (bm) [bm release];
1735 /* Returns a transformation matrix to do rotation as per the provided
1736 EXIF "Orientation" value.
1738 static CGAffineTransform
1739 exif_rotate (int rot, CGSize rect)
1741 CGAffineTransform trans = CGAffineTransformIdentity;
1743 case 2: // flip horizontal
1744 trans = CGAffineTransformMakeTranslation (rect.width, 0);
1745 trans = CGAffineTransformScale (trans, -1, 1);
1748 case 3: // rotate 180
1749 trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
1750 trans = CGAffineTransformRotate (trans, M_PI);
1753 case 4: // flip vertical
1754 trans = CGAffineTransformMakeTranslation (0, rect.height);
1755 trans = CGAffineTransformScale (trans, 1, -1);
1758 case 5: // transpose (UL-to-LR axis)
1759 trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
1760 trans = CGAffineTransformScale (trans, -1, 1);
1761 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1764 case 6: // rotate 90
1765 trans = CGAffineTransformMakeTranslation (0, rect.width);
1766 trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
1769 case 7: // transverse (UR-to-LL axis)
1770 trans = CGAffineTransformMakeScale (-1, 1);
1771 trans = CGAffineTransformRotate (trans, M_PI / 2);
1774 case 8: // rotate 270
1775 trans = CGAffineTransformMakeTranslation (rect.height, 0);
1776 trans = CGAffineTransformRotate (trans, M_PI / 2);
1788 jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
1789 XRectangle *geom_ret, int exif_rotation)
1791 NSImage *nsimg = (NSImage *) nsimg_arg;
1793 // convert the NSImage to a CGImage via the toll-free-bridging
1794 // of NSData and CFData...
1796 NSData *nsdata = [NSBitmapImageRep
1797 TIFFRepresentationOfImageRepsInArray:
1798 [nsimg representations]];
1799 CFDataRef cfdata = (CFDataRef) nsdata;
1800 CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
1801 CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
1803 NSSize imgr = [nsimg size];
1804 Bool rot_p = (exif_rotation >= 5);
1807 imgr = NSMakeSize (imgr.height, imgr.width);
1809 CGRect winr = d->frame;
1810 float rw = winr.size.width / imgr.width;
1811 float rh = winr.size.height / imgr.height;
1812 float r = (rw < rh ? rw : rh);
1815 dst.size.width = imgr.width * r;
1816 dst.size.height = imgr.height * r;
1817 dst.origin.x = (winr.size.width - dst.size.width) / 2;
1818 dst.origin.y = (winr.size.height - dst.size.height) / 2;
1820 dst2.origin.x = dst2.origin.y = 0;
1822 dst2.size.width = dst.size.height;
1823 dst2.size.height = dst.size.width;
1825 dst2.size = dst.size;
1828 // Clear the part not covered by the image to background or black.
1830 if (d->type == WINDOW)
1831 XClearWindow (dpy, d);
1833 set_color (d->cgc, BlackPixel(dpy,0), 32, NO, YES);
1834 draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
1837 CGAffineTransform trans =
1838 exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
1840 CGContextSaveGState (d->cgc);
1841 CGContextConcatCTM (d->cgc,
1842 CGAffineTransformMakeTranslation (dst.origin.x,
1844 CGContextConcatCTM (d->cgc, trans);
1845 //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
1846 CGContextDrawImage (d->cgc, dst2, cgi);
1847 CGContextRestoreGState (d->cgc);
1850 CGImageRelease (cgi);
1853 geom_ret->x = dst.origin.x;
1854 geom_ret->y = dst.origin.y;
1855 geom_ret->width = dst.size.width;
1856 geom_ret->height = dst.size.height;
1862 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
1864 unsigned int w, unsigned int h,
1865 unsigned long fg, unsigned int bg,
1868 Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
1869 XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
1870 (char *) data, w, h, 0, 0);
1872 gcv.foreground = fg;
1873 gcv.background = bg;
1874 GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
1875 XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
1878 XDestroyImage (image);
1883 XCreatePixmap (Display *dpy, Drawable d,
1884 unsigned int width, unsigned int height, unsigned int depth)
1886 char *data = (char *) malloc (width * height * 4);
1887 if (! data) return 0;
1889 Pixmap p = (Pixmap) calloc (1, sizeof(*p));
1891 p->frame.size.width = width;
1892 p->frame.size.height = height;
1893 p->pixmap.depth = depth;
1895 /* Quartz doesn't have a 1bpp image type.
1896 We used to use 8bpp gray images instead of 1bpp, but some Mac video
1897 don't support that! So we always use 32bpp, regardless of depth. */
1899 p->cgc = CGBitmapContextCreate (data, width, height,
1900 8, /* bits per component */
1901 width * 4, /* bpl */
1903 // Without this, it returns 0...
1904 kCGImageAlphaNoneSkipFirst
1906 Assert (p->cgc, "could not create CGBitmapContext");
1912 XFreePixmap (Display *d, Pixmap p)
1914 Assert (p->type == PIXMAP, "not a pixmap");
1915 CGContextRelease (p->cgc);
1922 copy_pixmap (Pixmap p)
1925 Assert (p->type == PIXMAP, "not a pixmap");
1926 Pixmap p2 = (Pixmap) malloc (sizeof (*p2));
1928 CGContextRetain (p2->cgc); // #### is this ok? need to copy it instead?
1933 /* Font metric terminology, as used by X11:
1935 "lbearing" is the distance from the logical origin to the leftmost pixel.
1936 If a character's ink extends to the left of the origin, it is negative.
1938 "rbearing" is the distance from the logical origin to the rightmost pixel.
1940 "descent" is the distance from the logical origin to the bottommost pixel.
1941 For characters with descenders, it is negative.
1943 "ascent" is the distance from the logical origin to the topmost pixel.
1944 It is the number of pixels above the baseline.
1946 "width" is the distance from the logical origin to the position where
1947 the logical origin of the next character should be placed.
1949 If "rbearing" is greater than "width", then this character overlaps the
1950 following character. If smaller, then there is trailing blank space.
1954 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
1957 query_font (Font fid)
1959 if (!fid || !fid->nsfont) {
1960 NSLog(@"no NSFont in fid");
1963 if (![fid->nsfont fontName]) {
1964 NSLog(@"broken NSFont in fid");
1971 XFontStruct *f = &fid->metrics;
1972 XCharStruct *min = &f->min_bounds;
1973 XCharStruct *max = &f->max_bounds;
1975 #define CEIL(F) ((F) < 0 ? floor(F) : ceil(F))
1978 f->min_char_or_byte2 = first;
1979 f->max_char_or_byte2 = last;
1980 f->default_char = 'M';
1981 f->ascent = CEIL ([fid->nsfont ascender]);
1982 f->descent = -CEIL ([fid->nsfont descender]);
1984 min->width = 255; // set to smaller values in the loop
1987 min->lbearing = 255;
1988 min->rbearing = 255;
1990 f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
1993 NSBezierPath *bpath = [NSBezierPath bezierPath];
1995 for (i = first; i <= last; i++) {
1996 unsigned char str[2];
2000 NSString *nsstr = [NSString stringWithCString:(char *) str
2001 encoding:NSISOLatin1StringEncoding];
2003 /* I can't believe we have to go through this bullshit just to
2004 convert a 'char' to an NSGlyph!!
2006 You might think that we could do
2007 NSGlyph glyph = [fid->nsfont glyphWithName:nsstr];
2008 but that doesn't work; my guess is that glyphWithName expects
2009 full Unicrud names like "LATIN CAPITAL LETTER A WITH ACUTE".
2013 NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
2014 [ts setFont:fid->nsfont];
2015 NSLayoutManager *lm = [[NSLayoutManager alloc] init];
2016 NSTextContainer *tc = [[NSTextContainer alloc] init];
2017 [lm addTextContainer:tc];
2018 [tc release]; // lm retains tc
2019 [ts addLayoutManager:lm];
2020 [lm release]; // ts retains lm
2021 glyph = [lm glyphAtIndex:0];
2025 /* Compute the bounding box and advancement by converting the glyph
2026 to a bezier path. There appears to be *no other way* to find out
2027 the bounding box of a character: [NSFont boundingRectForGlyph] and
2028 [NSString sizeWithAttributes] both return an advancement-sized
2029 rectangle, not a rectangle completely enclosing the glyph's ink.
2031 NSPoint advancement;
2033 advancement.x = advancement.y = 0;
2034 [bpath removeAllPoints];
2035 [bpath moveToPoint:advancement];
2036 [bpath appendBezierPathWithGlyph:glyph inFont:fid->nsfont];
2037 advancement = [bpath currentPoint];
2038 bbox = [bpath bounds];
2040 /* Now that we know the advancement and bounding box, we can compute
2041 the lbearing and rbearing.
2043 XCharStruct *cs = &f->per_char[i-first];
2045 cs->ascent = CEIL (bbox.origin.y) + CEIL (bbox.size.height);
2046 cs->descent = CEIL(-bbox.origin.y);
2047 cs->lbearing = CEIL (bbox.origin.x);
2048 cs->rbearing = CEIL (bbox.origin.x) + CEIL (bbox.size.width);
2049 cs->width = CEIL (advancement.x);
2051 Assert (cs->rbearing - cs->lbearing == CEIL(bbox.size.width),
2053 Assert (cs->ascent + cs->descent == CEIL(bbox.size.height),
2056 max->width = MAX (max->width, cs->width);
2057 max->ascent = MAX (max->ascent, cs->ascent);
2058 max->descent = MAX (max->descent, cs->descent);
2059 max->lbearing = MAX (max->lbearing, cs->lbearing);
2060 max->rbearing = MAX (max->rbearing, cs->rbearing);
2062 min->width = MIN (min->width, cs->width);
2063 min->ascent = MIN (min->ascent, cs->ascent);
2064 min->descent = MIN (min->descent, cs->descent);
2065 min->lbearing = MIN (min->lbearing, cs->lbearing);
2066 min->rbearing = MIN (min->rbearing, cs->rbearing);
2071 fprintf(stderr, " %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
2072 " bb=%3d x %3d @ %3d %3d adv=%3d %3d\n",
2073 i, i, cs->width, cs->lbearing, cs->rbearing,
2074 cs->ascent, cs->descent,
2075 (int) bbox.size.width, (int) bbox.size.height,
2076 (int) bbox.origin.x, (int) bbox.origin.y,
2077 (int) advancement.x, (int) advancement.y);
2084 // Since 'Font' includes the metrics, this just makes a copy of that.
2087 XQueryFont (Display *dpy, Font fid)
2090 XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
2093 // copy XCharStruct array
2094 int size = f->max_char_or_byte2 - f->min_char_or_byte2;
2095 f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
2096 memcpy (f->per_char, fid->metrics.per_char,
2097 size * sizeof (XCharStruct));
2104 copy_font (Font fid)
2106 // copy 'Font' struct
2107 Font fid2 = (Font) malloc (sizeof(*fid2));
2110 // copy XCharStruct array
2111 int size = fid->metrics.max_char_or_byte2 - fid->metrics.min_char_or_byte2;
2112 fid2->metrics.per_char = (XCharStruct *)
2113 malloc ((size + 2) * sizeof (XCharStruct));
2114 memcpy (fid2->metrics.per_char, fid->metrics.per_char,
2115 size * sizeof (XCharStruct));
2117 // copy the other pointers
2118 fid2->ps_name = strdup (fid->ps_name);
2119 // [fid2->nsfont retain];
2120 fid2->metrics.fid = fid2;
2127 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
2130 Assert (size > 0, "zero font size");
2135 // "Monaco" only exists in plain.
2136 // "LucidaSansTypewriterStd" gets an AGL bad value error.
2138 if (bold && ital) name = "Courier-BoldOblique";
2139 else if (bold) name = "Courier-Bold";
2140 else if (ital) name = "Courier-Oblique";
2141 else name = "Courier";
2145 // "Georgia" looks better than "Times".
2147 if (bold && ital) name = "Georgia-BoldItalic";
2148 else if (bold) name = "Georgia-Bold";
2149 else if (ital) name = "Georgia-Italic";
2150 else name = "Georgia";
2154 // "Geneva" only exists in plain.
2155 // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
2156 // "Verdana" renders smoother than "Helvetica" for some reason.
2158 if (bold && ital) name = "Verdana-BoldItalic";
2159 else if (bold) name = "Verdana-Bold";
2160 else if (ital) name = "Verdana-Italic";
2161 else name = "Verdana";
2164 NSString *nsname = [NSString stringWithCString:name
2165 encoding:NSUTF8StringEncoding];
2166 NSFont *f = [NSFont fontWithName:nsname size:size];
2168 *name_ret = strdup(name);
2173 try_native_font (const char *name, char **name_ret, float *size_ret)
2175 if (!name) return 0;
2176 const char *spc = strrchr (name, ' ');
2179 if (1 != sscanf (spc, " %d ", &size)) return 0;
2180 if (size <= 4) return 0;
2182 char *name2 = strdup (name);
2183 name2[strlen(name2) - strlen(spc)] = 0;
2184 NSString *nsname = [NSString stringWithCString:name2
2185 encoding:NSUTF8StringEncoding];
2186 NSFont *f = [NSFont fontWithName:nsname size:size];
2198 /* Returns a random font in the given size and face.
2201 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
2203 NSFontTraitMask mask = ((bold ? NSBoldFontMask : NSUnboldFontMask) |
2204 (ital ? NSItalicFontMask : NSUnitalicFontMask));
2205 NSArray *fonts = [[NSFontManager sharedFontManager]
2206 availableFontNamesWithTraits:mask];
2207 if (!fonts) return 0;
2209 int n = [fonts count];
2210 if (n <= 0) return 0;
2213 for (j = 0; j < n; j++) {
2214 int i = random() % n;
2215 NSString *name = [fonts objectAtIndex:i];
2216 NSFont *f = [NSFont fontWithName:name size:size];
2219 /* Don't use this font if it (probably) doesn't include ASCII characters.
2221 NSStringEncoding enc = [f mostCompatibleStringEncoding];
2222 if (! (enc == NSUTF8StringEncoding ||
2223 enc == NSISOLatin1StringEncoding ||
2224 enc == NSNonLossyASCIIStringEncoding ||
2225 enc == NSISOLatin2StringEncoding ||
2226 enc == NSUnicodeStringEncoding ||
2227 enc == NSWindowsCP1250StringEncoding ||
2228 enc == NSWindowsCP1252StringEncoding ||
2229 enc == NSMacOSRomanStringEncoding)) {
2230 // NSLog(@"skipping \"%@\": encoding = %d", name, enc);
2233 // NSLog(@"using \"%@\": %d", name, enc);
2235 *name_ret = strdup ([name cStringUsingEncoding:NSUTF8StringEncoding]);
2239 // None of the fonts support ASCII?
2245 try_xlfd_font (const char *name, char **name_ret, float *size_ret)
2256 const char *s = (name ? name : "");
2258 while (*s && (*s == '*' || *s == '-'))
2261 while (*s2 && (*s2 != '*' && *s2 != '-'))
2267 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
2268 else if (CMP ("random")) rand = YES;
2269 else if (CMP ("bold")) bold = YES;
2270 else if (CMP ("i")) ital = YES;
2271 else if (CMP ("o")) ital = YES;
2272 else if (CMP ("courier")) fixed = YES;
2273 else if (CMP ("fixed")) fixed = YES;
2274 else if (CMP ("m")) fixed = YES;
2275 else if (CMP ("times")) serif = YES;
2276 else if (CMP ("6x10")) fixed = YES, size = 8;
2277 else if (CMP ("6x10bold")) fixed = YES, size = 8, bold = YES;
2278 else if (CMP ("9x15")) fixed = YES, size = 12;
2279 else if (CMP ("9x15bold")) fixed = YES, size = 12, bold = YES;
2280 else if (CMP ("vga")) fixed = YES, size = 12;
2281 else if (CMP ("console")) fixed = YES, size = 12;
2282 else if (CMP ("gallant")) fixed = YES, size = 12;
2284 else if (size == 0) {
2286 if (1 == sscanf (s, " %d ", &n))
2293 if (size < 6 || size > 1000)
2297 nsfont = random_font (bold, ital, size, &ps_name);
2300 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2302 // if that didn't work, turn off attibutes until it does
2303 // (e.g., there is no "Monaco-Bold".)
2305 if (!nsfont && serif) {
2307 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2309 if (!nsfont && ital) {
2311 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2313 if (!nsfont && bold) {
2315 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2317 if (!nsfont && fixed) {
2319 nsfont = try_font (fixed, bold, ital, serif, size, &ps_name);
2323 *name_ret = ps_name;
2333 XLoadFont (Display *dpy, const char *name)
2335 Font fid = (Font) calloc (1, sizeof(*fid));
2337 fid->nsfont = try_native_font (name, &fid->ps_name, &fid->size);
2339 fid->nsfont = try_xlfd_font (name, &fid->ps_name, &fid->size);
2341 NSLog(@"no NSFont for \"%s\"", name);
2345 //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
2354 XLoadQueryFont (Display *dpy, const char *name)
2356 Font fid = XLoadFont (dpy, name);
2357 return XQueryFont (dpy, fid);
2361 XUnloadFont (Display *dpy, Font fid)
2363 free (fid->ps_name);
2364 free (fid->metrics.per_char);
2366 // #### DAMMIT! I can't tell what's going wrong here, but I keep getting
2367 // crashes in [NSFont ascender] <- query_font, and it seems to go away
2368 // if I never release the nsfont. So, fuck it, we'll just leak fonts.
2369 // They're probably not very big...
2371 // [fid->nsfont release];
2378 XFreeFontInfo (char **names, XFontStruct *info, int n)
2382 for (i = 0; i < n; i++)
2383 if (names[i]) free (names[i]);
2387 for (i = 0; i < n; i++)
2388 if (info[i].per_char)
2389 free (info[i].per_char);
2396 XFreeFont (Display *dpy, XFontStruct *f)
2399 XFreeFontInfo (0, f, 1);
2400 XUnloadFont (dpy, fid);
2406 XSetFont (Display *dpy, GC gc, Font fid)
2409 XUnloadFont (dpy, gc->gcv.font);
2410 gc->gcv.font = copy_font (fid);
2411 [gc->gcv.font->nsfont retain];
2416 XTextExtents (XFontStruct *f, const char *s, int length,
2417 int *dir_ret, int *ascent_ret, int *descent_ret,
2420 memset (cs, 0, sizeof(*cs));
2422 for (i = 0; i < length; i++) {
2423 unsigned char c = (unsigned char) s[i];
2424 if (c < f->min_char_or_byte2 || c > f->max_char_or_byte2)
2425 c = f->default_char;
2426 const XCharStruct *cc = &f->per_char[c - f->min_char_or_byte2];
2430 cs->ascent = MAX (cs->ascent, cc->ascent);
2431 cs->descent = MAX (cs->descent, cc->descent);
2432 cs->lbearing = MIN (cs->lbearing, cs->width + cc->lbearing);
2433 cs->rbearing = MAX (cs->rbearing, cs->width + cc->rbearing);
2434 cs->width += cc->width;
2438 *ascent_ret = f->ascent;
2439 *descent_ret = f->descent;
2444 XTextWidth (XFontStruct *f, const char *s, int length)
2446 int ascent, descent, dir;
2448 XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
2454 set_font (CGContextRef cgc, GC gc)
2456 Font font = gc->gcv.font;
2458 font = XLoadFont (0, 0);
2459 gc->gcv.font = font;
2460 [gc->gcv.font->nsfont retain];
2462 CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
2463 CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
2468 draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
2469 const char *str, int len, BOOL clear_background_p)
2471 if (clear_background_p) {
2472 int ascent, descent, dir;
2474 XTextExtents (&gc->gcv.font->metrics, str, len,
2475 &dir, &ascent, &descent, &cs);
2476 draw_rect (dpy, d, gc,
2477 x + MIN (0, cs.lbearing),
2478 y - MAX (0, ascent),
2479 MAX (MAX (0, cs.rbearing) -
2480 MIN (0, cs.lbearing),
2482 MAX (0, ascent) + MAX (0, descent),
2486 CGRect wr = d->frame;
2489 /* The Quartz way is probably faster, but doesn't draw Latin1 properly.
2490 But the Cocoa way only works on NSView, not on CGContextRef (pixmaps)!
2493 push_fg_gc (d, gc, YES);
2494 set_font (d->cgc, gc);
2496 CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
2497 if (gc->gcv.antialias_p)
2498 CGContextSetShouldAntialias (d->cgc, YES);
2499 CGContextShowTextAtPoint (d->cgc,
2501 wr.origin.y + wr.size.height - y,
2510 unsigned long argb = gc->gcv.foreground;
2511 if (gc->depth == 1) argb = (argb ? WhitePixel(dpy,0) : BlackPixel(dpy,0));
2512 float a = ((argb >> 24) & 0xFF) / 255.0;
2513 float r = ((argb >> 16) & 0xFF) / 255.0;
2514 float g = ((argb >> 8) & 0xFF) / 255.0;
2515 float b = ((argb ) & 0xFF) / 255.0;
2516 NSColor *fg = [NSColor colorWithDeviceRed:r green:g blue:b alpha:a];
2517 NSDictionary *attr =
2518 [NSDictionary dictionaryWithObjectsAndKeys:
2519 gc->gcv.font->nsfont, NSFontAttributeName,
2520 fg, NSForegroundColorAttributeName,
2522 char *s2 = (char *) malloc (len + 1);
2523 strncpy (s2, str, len);
2525 NSString *nsstr = [NSString stringWithCString:s2
2526 encoding:NSISOLatin1StringEncoding];
2529 pos.x = wr.origin.x + x;
2530 pos.y = wr.origin.y + wr.size.height - y - gc->gcv.font->metrics.descent;
2531 [nsstr drawAtPoint:pos withAttributes:attr];
2540 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
2541 const char *str, int len)
2543 return draw_string (dpy, d, gc, x, y, str, len, NO);
2547 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
2548 const char *str, int len)
2550 return draw_string (dpy, d, gc, x, y, str, len, YES);
2555 XSetForeground (Display *dpy, GC gc, unsigned long fg)
2557 validate_pixel (fg, gc->depth, gc->gcv.alpha_allowed_p);
2558 gc->gcv.foreground = fg;
2564 XSetBackground (Display *dpy, GC gc, unsigned long bg)
2566 validate_pixel (bg, gc->depth, gc->gcv.alpha_allowed_p);
2567 gc->gcv.background = bg;
2572 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
2574 gc->gcv.alpha_allowed_p = allowed;
2579 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
2581 gc->gcv.antialias_p = antialias_p;
2587 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
2588 int line_style, int cap_style, int join_style)
2590 gc->gcv.line_width = line_width;
2591 Assert (line_style == LineSolid, "only LineSolid implemented");
2592 // gc->gcv.line_style = line_style;
2593 gc->gcv.cap_style = cap_style;
2594 gc->gcv.join_style = join_style;
2599 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
2605 XSetFunction (Display *dpy, GC gc, int which)
2607 gc->gcv.function = which;
2612 XSetSubwindowMode (Display *dpy, GC gc, int which)
2614 gc->gcv.subwindow_mode = which;
2619 XSetClipMask (Display *dpy, GC gc, Pixmap m)
2621 Assert (!!gc->gcv.clip_mask == !!gc->clip_mask, "GC clip mask mixup");
2623 if (gc->gcv.clip_mask) {
2624 XFreePixmap (dpy, gc->gcv.clip_mask);
2625 CGImageRelease (gc->clip_mask);
2628 gc->gcv.clip_mask = copy_pixmap (m);
2629 if (gc->gcv.clip_mask)
2630 gc->clip_mask = CGBitmapContextCreateImage (gc->gcv.clip_mask->cgc);
2638 XSetClipOrigin (Display *dpy, GC gc, int x, int y)
2640 gc->gcv.clip_x_origin = x;
2641 gc->gcv.clip_y_origin = y;
2647 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
2648 int *root_x_ret, int *root_y_ret,
2649 int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
2651 Assert (w->type == WINDOW, "not a window");
2652 NSWindow *nsw = [w->window.view window];
2654 // get bottom left of window on screen, from bottom left
2655 wpos.x = wpos.y = 0;
2656 wpos = [nsw convertBaseToScreen:wpos];
2659 // get bottom left of view on window, from bottom left
2660 vpos.x = vpos.y = 0;
2661 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2663 // get bottom left of view on screen, from bottom left
2667 // get top left of view on screen, from bottom left
2668 vpos.y += w->frame.size.height;
2670 // get top left of view on screen, from top left
2671 NSArray *screens = [NSScreen screens];
2672 NSScreen *screen = (screens && [screens count] > 0
2673 ? [screens objectAtIndex:0]
2674 : [NSScreen mainScreen]);
2675 NSRect srect = [screen frame];
2676 vpos.y = srect.size.height - vpos.y;
2678 // get the mouse position on window, from bottom left
2679 NSEvent *e = [NSApp currentEvent];
2680 NSPoint p = [e locationInWindow];
2682 // get mouse position on screen, from bottom left
2686 // get mouse position on screen, from top left
2687 p.y = srect.size.height - p.y;
2689 if (root_x_ret) *root_x_ret = (int) p.x;
2690 if (root_y_ret) *root_y_ret = (int) p.y;
2691 if (win_x_ret) *win_x_ret = (int) (p.x - vpos.x);
2692 if (win_y_ret) *win_y_ret = (int) (p.y - vpos.y);
2694 if (mask_ret) *mask_ret = 0; // ####
2695 if (root_ret) *root_ret = 0;
2696 if (child_ret) *child_ret = 0;
2701 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
2702 int src_x, int src_y,
2703 int *dest_x_ret, int *dest_y_ret,
2706 Assert (w->type == WINDOW, "not a window");
2707 NSWindow *nsw = [w->window.view window];
2709 // get bottom left of window on screen, from bottom left
2710 wpos.x = wpos.y = 0;
2711 wpos = [nsw convertBaseToScreen:wpos];
2714 // get bottom left of view on window, from bottom left
2715 vpos.x = vpos.y = 0;
2716 vpos = [w->window.view convertPoint:vpos toView:[nsw contentView]];
2718 // get bottom left of view on screen, from bottom left
2722 // get top left of view on screen, from bottom left
2723 vpos.y += w->frame.size.height;
2725 // get top left of view on screen, from top left
2726 NSArray *screens = [NSScreen screens];
2727 NSScreen *screen = (screens && [screens count] > 0
2728 ? [screens objectAtIndex:0]
2729 : [NSScreen mainScreen]);
2730 NSRect srect = [screen frame];
2731 vpos.y = srect.size.height - vpos.y;
2733 // point starts out relative to top left of view
2738 // get point relative to top left of screen
2751 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
2757 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
2760 KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
2761 char c = (char) ks; // could be smarter about modifiers here...
2762 if (k_ret) *k_ret = ks;
2763 if (size > 0) buf[0] = c;
2764 if (size > 1) buf[1] = 0;
2770 XFlush (Display *dpy)
2772 // Just let the event loop take care of this on its own schedule.
2777 XSync (Display *dpy, Bool flush)
2779 return XFlush (dpy);
2783 // declared in utils/visual.h
2785 has_writable_cells (Screen *s, Visual *v)
2791 visual_depth (Screen *s, Visual *v)
2797 visual_cells (Screen *s, Visual *v)
2803 visual_class (Screen *s, Visual *v)
2808 // declared in utils/grabclient.h
2810 use_subwindow_mode_p (Screen *screen, Window window)