[basic x3view impl for carbon
raph.levien@gmail.com**20060513215949] {
hunk ./x3.h 35
+typedef struct _x3dc x3dc;
+
+struct _x3viewclient {
+    void (*destroy)(x3viewclient *self);
+    void (*mouse)(x3viewclient *self, int buttons, int mods,
+		  double x, double y);
+    int (*key)(x3viewclient *self, char *keyname, int mods, int key);
+    void (*draw)(x3viewclient *self, x3dc *dc);
+};
+
hunk ./x3.h 51
-typedef struct {
+struct _x3dc {
hunk ./x3.h 63
-} x3dc;
-
-struct _x3viewclient {
-    void (*destroy)(x3viewclient *self);
-    void (*mouse)(x3viewclient *self, int buttons, int mods,
-		  double x, double y);
-    int (*key)(x3viewclient *self, char *keyname, int mods, int key);
-    void (*draw)(x3viewclient *self, x3dc *dc);
-
-/* to delete */
-#ifdef X3_CAIRO
-    void (*cairo)(x3viewclient *self, cairo_t *cr,
-		  int x, int y, int width, int height);
-#endif
-    void (*rgb)(x3viewclient *self,
-		char *buf, int x, int y, int width, int height, int rowstride);
hunk ./x3.h 81
+struct _x3dc {
+    /* move to x3rect structure? */
+    int x;
+    int y;
+    int width;
+    int height;
+
+    CGContextRef ctx;
+    CGMutablePathRef path;
+
+    /* for rgb drawing */
+    char *buf;
+    int rowstride;
+};
+
hunk ./x3carbon.c 5
+#define VERBOSE
+
hunk ./x3carbon.c 15
+#define kX3ViewClassID CFSTR("com.levien.x3.X3View")
+#define kX3ViewPrivate 'X3_v'
+
hunk ./x3carbon.c 567
+#ifdef VERBOSE
hunk ./x3carbon.c 571
+#endif
hunk ./x3carbon.c 620
+typedef struct
+{
+    HIViewRef view;
+    x3viewflags flags;
+    x3viewclient *vc;
+} x3view_data;
+
+static OSStatus
+x3view_construct(EventRef inEvent)
+{
+    OSStatus err;
+    x3view_data *data;
+
+    data = (x3view_data *)malloc(sizeof(x3view_data));
+    require_action(data != NULL, CantMalloc, err = memFullErr);
+    err = GetEventParameter(inEvent, kEventParamHIObjectInstance,
+			    typeHIObjectRef, NULL, sizeof(HIObjectRef), NULL,
+			    (HIObjectRef *)&data->view);
+    require_noerr(err, ParameterMissing);
+    err = SetEventParameter(inEvent, kEventParamHIObjectInstance,
+			    typeVoidPtr, sizeof(x3view_data *), &data);
+
+    data->vc = NULL;
+
+ ParameterMissing:
+    if (err != noErr)
+	free(data);
+
+ CantMalloc:
+    return err;
+}
+
+static OSStatus
+x3view_destruct(EventRef inEvent, x3view_data *inData)
+{
+    free(inData);
+    return noErr;
+}
+
+static OSStatus
+x3view_initialize(EventHandlerCallRef inCallRef, EventRef inEvent,
+		  x3view_data *inData)
+{
+    OSStatus err;
+    HIRect bounds;
+
+    err = CallNextEventHandler(inCallRef, inEvent);
+    require_noerr(err, TroubleInSuperClass);
+
+    err = GetEventParameter(inEvent, 'Boun', typeHIRect,
+			    NULL, sizeof(HIRect), NULL, &bounds);
+    require_noerr(err, ParameterMissing);
+
+    HIViewSetFrame(inData->view, &bounds);
+
+ ParameterMissing:
+ TroubleInSuperClass:
+    return err;
+}
+
+static OSStatus
+x3view_draw(EventRef inEvent, x3view_data *inData)
+{
+    OSStatus err;
+    CGContextRef ctx;
+
+    err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef,
+			    NULL, sizeof(CGContextRef), NULL, &ctx);
+    require_noerr(err, ParameterMissing);
+
+#ifdef VERBOSE
+    printf("x3view_draw!\n");
+#endif
+
+    if (inData->vc && inData->vc->draw) {
+	x3dc dc;
+
+	/* set up bounds */
+	if (inData->flags & x3view_2d) {
+	    dc.ctx = ctx;
+	    dc.path = NULL;
+	    dc.buf = NULL;
+
+	    inData->vc->draw(inData->vc, &dc);
+	    if (dc.path) {
+		CGPathRelease(dc.path);
+	    }
+	} else if (inData->flags & x3view_rgb) {
+	    /* todo */
+	}
+    }
+
+ ParameterMissing:
+    return err;
+}
+
+static OSStatus
+x3view_get_data(EventRef inEvent, x3view_data *inData)
+{
+    OSStatus err;
+    OSType tag;
+    Ptr ptr;
+    Size outSize;
+
+    /* Probably could use a bit more error checking here, for type
+       and size match. Also, just returning an x3view_data seems a
+       little hacky. */
+    err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration,
+			    NULL, sizeof(OSType), NULL, &tag);
+    require_noerr(err, ParameterMissing);
+
+    err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr,
+			    NULL, sizeof(Ptr), NULL, &ptr);
+
+    if (tag == kX3ViewPrivate) {
+	*((x3view_data **)ptr) = inData;
+	outSize = sizeof(x3view_data *);
+    } else
+	err = errDataNotSupported;
+
+    if (err == noErr)
+	err = SetEventParameter(inEvent, kEventParamControlDataBufferSize, typeLongInteger,
+				sizeof(Size), &outSize);
+
+ ParameterMissing:
+    return err;
+}
+
+static OSStatus
+x3view_set_data(EventRef inEvent, x3view_data *inData)
+{
+    OSStatus err;
+    Ptr ptr;
+    OSType tag;
+
+    err = GetEventParameter(inEvent, kEventParamControlDataTag, typeEnumeration,
+			    NULL, sizeof(OSType), NULL, &tag);
+    require_noerr(err, ParameterMissing);
+
+    err = GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr,
+			    NULL, sizeof(Ptr), NULL, &ptr);
+    require_noerr(err, ParameterMissing);
+
+    if (tag == 'X3vc') {
+	inData->vc = *(x3viewclient **)ptr;
+    } else if (tag == 'X3vf') {
+	inData->flags = *(x3viewflags *)ptr;
+    } else
+	err = errDataNotSupported;
+
+ ParameterMissing:
+    return err;
+}
+
+static OSStatus
+x3view_hittest(EventRef inEvent, x3view_data *inData)
+{
+    OSStatus err;
+    HIPoint where;
+    HIRect bounds;
+    ControlPartCode part;
+
+    err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint,
+			    NULL, sizeof(HIPoint), NULL, &where);
+    require_noerr(err, ParameterMissing);
+
+    err = HIViewGetBounds(inData->view, &bounds);
+    require_noerr(err, ParameterMissing);
+
+    if (CGRectContainsPoint(bounds, where))
+	part = 1;
+    else
+	part = kControlNoPart;
+    err = SetEventParameter(inEvent, kEventParamControlPart,
+			    typeControlPartCode, sizeof(ControlPartCode),
+			    &part);
+    printf("hittest %g, %g!\n", where.x, where.y);
+
+ ParameterMissing:
+    return err;
+}
+
+/*
+  If we need more sophisticated tracking (like mixing key events), there's
+  a good discussion here:
+  http://lists.apple.com/archives/Carbon-development/2001/Apr/msg01688.html
+*/
+static OSStatus
+x3view_track(EventRef inEvent, x3view_data *inData)
+{
+    OSStatus err;
+    HIPoint where;
+    MouseTrackingResult mouseStatus;
+    HIRect bounds;
+    Rect windBounds;
+    Point theQDPoint;
+    UInt32 mods;
+
+    err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint,
+			    NULL, sizeof(HIPoint), NULL, &where);
+    require_noerr(err, ParameterMissing);
+
+    err = GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32,
+			    NULL, sizeof(UInt32), NULL, &mods);
+    require_noerr(err, ParameterMissing);
+
+    err = HIViewGetBounds(inData->view, &bounds);
+    require_noerr(err, ParameterMissing);
+
+    GetWindowBounds(GetControlOwner(inData->view), kWindowStructureRgn, &windBounds);
+
+    if (inData->vc && inData->vc->mouse)
+	inData->vc->mouse(inData->vc, 1, mods, where.x, where.y);
+
+#ifdef VERBOSE
+    printf("press: %g, %g!\n", where.x, where.y);
+#endif
+    //pe_view_button_press(inData, where.x, where.y, 0);
+
+    mouseStatus = kMouseTrackingMouseDown;
+    while (mouseStatus != kMouseTrackingMouseUp) {
+	TrackMouseLocation(NULL, &theQDPoint, &mouseStatus);
+	where.x = theQDPoint.h - windBounds.left;
+	where.y = theQDPoint.v - windBounds.top;
+	HIViewConvertPoint(&where, NULL, inData->view);
+#ifdef VERBOSE
+	printf("track %d: %g, %g!\n", mouseStatus, where.x, where.y);
+#endif
+	if (mouseStatus == kMouseTrackingMouseUp) {
+	    if (inData->vc && inData->vc->mouse)
+		inData->vc->mouse(inData->vc, -1, mods, where.x, where.y);
+	} else if (mouseStatus == kMouseTrackingKeyModifiersChanged) {
+	    /* todo: get new mods */
+	} else {
+	    if (inData->vc && inData->vc->mouse)
+		inData->vc->mouse(inData->vc, 0, mods, where.x, where.y);
+	}
+    }
+
+ ParameterMissing:
+    return err;
+}
+
+pascal OSStatus
+x3view_handler(EventHandlerCallRef inCallRef,
+		EventRef inEvent,
+		void* inUserData )
+{
+    OSStatus err = eventNotHandledErr;
+    UInt32 eventClass = GetEventClass(inEvent);
+    UInt32 eventKind = GetEventKind(inEvent);
+    x3view_data *data = (x3view_data *)inUserData;
+
+    printf("view handler %c%c%c%c %d\n",
+	   (eventClass >> 24) & 0xff, 
+	   (eventClass >> 16) & 0xff, 
+	   (eventClass >> 8) & 0xff, 
+	   (eventClass >> 0) & 0xff, 
+	   eventKind);
+    switch (eventClass) {
+    case kEventClassHIObject:
+	switch (eventKind) {
+	case kEventHIObjectConstruct:
+	    err = x3view_construct(inEvent);
+	    break;
+	case kEventHIObjectInitialize:
+	    err = x3view_initialize(inCallRef, inEvent, data);
+	    break;
+	case kEventHIObjectDestruct:
+	    err = x3view_destruct(inEvent, data);
+	    break;
+	}
+	break;
+    case kEventClassControl:
+	switch (eventKind) {
+	case kEventControlInitialize:
+	    err = noErr;
+	    break;
+	case kEventControlDraw:
+	    err = x3view_draw(inEvent, data);
+	    break;
+	case kEventControlGetData:
+	    err = x3view_get_data(inEvent, data);
+	    break;
+	case kEventControlSetData:
+	    err = x3view_set_data(inEvent, data);
+	    break;
+	case kEventControlTrack:
+	    err = x3view_track(inEvent, data);
+	    break;
+	case kEventControlHitTest:
+	    err = x3view_hittest(inEvent, data);
+	    break;
+	    /*...*/
+	}
+	break;
+    }
+    return err;
+}
+
+static OSStatus
+x3view_register(void)
+{
+    OSStatus err = noErr;
+    static HIObjectClassRef x3view_ClassRef = NULL;
+
+    if (x3view_ClassRef == NULL) {
+	EventTypeSpec eventList[] = {
+	    { kEventClassHIObject, kEventHIObjectConstruct },
+	    { kEventClassHIObject, kEventHIObjectInitialize },
+	    { kEventClassHIObject, kEventHIObjectDestruct },
+
+	    { kEventClassControl, kEventControlActivate },
+	    { kEventClassControl, kEventControlDeactivate },
+	    { kEventClassControl, kEventControlDraw },
+	    { kEventClassControl, kEventControlHiliteChanged },
+	    { kEventClassControl, kEventControlHitTest },
+	    { kEventClassControl, kEventControlInitialize },
+	    { kEventClassControl, kEventControlGetData },
+	    { kEventClassControl, kEventControlSetData },
+	    { kEventClassControl, kEventControlTrack }
+	};
+	err = HIObjectRegisterSubclass(kX3ViewClassID,
+				       kHIViewClassID,
+				       0,
+				       x3view_handler,
+				       GetEventTypeCount(eventList),
+				       eventList,
+				       NULL,
+				       &x3view_ClassRef);
+    }
+    return err;
+}
+
+OSStatus x3view_create(
+	WindowRef		inWindow,
+	const HIRect*		inBounds,
+	HIViewRef*		outView)
+{
+    OSStatus err;
+    EventRef event;
+
+    err = x3view_register();
+    require_noerr(err, CantRegister);
+
+    err = CreateEvent(NULL, kEventClassHIObject, kEventHIObjectInitialize,
+		      GetCurrentEventTime(), 0, &event);
+    require_noerr(err, CantCreateEvent);
+
+    if (inBounds != NULL) {
+	err = SetEventParameter(event, 'Boun', typeHIRect, sizeof(HIRect),
+				inBounds);
+	require_noerr(err, CantSetParameter);
+    }
+
+    err = HIObjectCreate(kX3ViewClassID, event, (HIObjectRef*)outView);
+    require_noerr(err, CantCreate);
+
+    if (inWindow != NULL) {
+	HIViewRef root;
+	err = GetRootControl(inWindow, &root);
+	require_noerr(err, CantGetRootView);
+	err = HIViewAddSubview(root, *outView);
+    }
+ CantCreate:
+ CantGetRootView:
+ CantSetParameter:
+ CantCreateEvent:
+    ReleaseEvent(event);
+ CantRegister:
+    return err;
+}
+
+x3widget *x3view(x3widget *parent, x3viewflags flags, x3viewclient *vc)
+{
+    WindowRef window = x3window_of(parent);
+    HIRect r = { {10, 10}, {30, 100} };
+    OSStatus err;
+    HIViewRef view;
+
+    err = x3view_create(window, &r, &view);
+    err = SetControlData(view, 1, 'X3vc', sizeof(x3view_data *), &vc);
+    err = SetControlData(view, 1, 'X3vf', sizeof(x3viewflags), &flags);
+    HIViewSetVisible(view, true);
+    return x3widget_new_hiview(parent, NULL, &x3buttontype, view);
+}
+
+void
+x3view_dirty(x3widget *w)
+{
+    if (w->var == x3carbonhiview)
+	HIViewSetNeedsDisplay(w->u.hiview, true);
+}
+
+void x3viewclient_init(x3viewclient *vc)
+{
+    vc->destroy = NULL;
+    vc->mouse = NULL;
+    vc->key = NULL;
+    vc->draw = NULL;
+}
+
hunk ./x3carbon.c 1040
+
+/* 2d drawing functions, implemented using Quartz */
+
+void
+x3moveto(x3dc *dc, double x, double y)
+{
+    if (dc->path == NULL) {
+	dc->path = CGPathCreateMutable();
+    }
+    CGPathMoveToPoint(dc->path, NULL, x, y);
+}
+
+void 
+x3lineto(x3dc *dc, double x, double y)
+{
+    CGPathAddLineToPoint(dc->path, NULL, x, y);
+}
+
+void 
+x3curveto(x3dc *dc,
+	  double x1, double y1,
+	  double x2, double y2,
+	  double x3, double y3)
+{
+    CGPathAddCurveToPoint(dc->path, NULL, x1, y1, x2, y2, x3, y3);
+}
+
+void 
+x3closepath(x3dc *dc)
+{
+    CGPathCloseSubpath(dc->path);
+}
+
+void 
+x3rectangle(x3dc *dc, double x, double y, double width, double height)
+{
+    CGRect rect;
+
+    rect.origin.x = x;
+    rect.origin.y = y;
+    rect.size.width = width;
+    rect.size.height = height;
+    CGPathAddRect(dc->path, NULL, rect);
+}
+
+void
+x3getcurrentpoint(x3dc *dc, double *px, double *py)
+{
+    CGPoint point = CGPathGetCurrentPoint(dc->path);
+
+    *px = point.x;
+    *py = point.y;
+}
+
+void
+x3setrgba(x3dc *dc, unsigned int rgba)
+{
+    CGContextSetRGBFillColor(dc->ctx,
+			     ((rgba >> 24) & 0xff) * (1.0/255),
+			     ((rgba >> 16) & 0xff) * (1.0/255),
+			     ((rgba >> 8) & 0xff) * (1.0/255),
+			     (rgba & 0xff) * (1.0/255));
+    CGContextSetRGBStrokeColor(dc->ctx,
+			       ((rgba >> 24) & 0xff) * (1.0/255),
+			       ((rgba >> 16) & 0xff) * (1.0/255),
+			       ((rgba >> 8) & 0xff) * (1.0/255),
+			       (rgba & 0xff) * (1.0/255));
+}
+
+void
+x3setlinewidth(x3dc *dc, double w)
+{
+    CGContextSetLineWidth(dc->ctx, w);
+}
+
+void
+x3fill(x3dc *dc)
+{
+    CGContextAddPath(dc->ctx, dc->path);
+    CGPathRelease(dc->path);
+    dc->path = NULL;
+    CGContextFillPath(dc->ctx);
+}
+
+void
+x3stroke(x3dc *dc)
+{
+    CGContextAddPath(dc->ctx, dc->path);
+    CGPathRelease(dc->path);
+    dc->path = NULL;
+    CGContextStrokePath(dc->ctx);
+}
}
