/* * Nuklear - 1.32.0 - public domain * no warranty implied; use at your own risk. * based on allegro5 version authored from 2015-2016 by Micha Mettke * quickdraw version camhenlin 2021 * * v1 intent: * - only default system font support * - no graphics/images support - quickdraw has very limited support for this */ /* * ============================================================== * * API * * =============================================================== */ #ifndef NK_QUICKDRAW_H_ #define NK_QUICKDRAW_H_ #include #include #include #include #include #include "SerialHelper.h" #define ENABLED_DOUBLE_BUFFERING #define COMMAND_CACHING // #define NK_QUICKDRAW_GRAPHICS_DEBUGGING Boolean lastInputWasBackspace; typedef struct NkQuickDrawFont NkQuickDrawFont; NK_API struct nk_context* nk_quickdraw_init(unsigned int width, unsigned int height); NK_API int nk_quickdraw_handle_event(EventRecord *event, struct nk_context *nuklear_context); NK_API void nk_quickdraw_shutdown(void); NK_API void nk_quickdraw_render(WindowPtr window, struct nk_context *ctx); NK_API struct nk_image* nk_quickdraw_create_image(const char* file_name); NK_API void nk_quickdraw_del_image(struct nk_image* image); NK_API NkQuickDrawFont* nk_quickdraw_font_create_from_file(); #endif /* * ============================================================== * * IMPLEMENTATION * * =============================================================== */ #define MAX_MEMORY_IN_KB 6 #ifdef NK_QUICKDRAW_IMPLEMENTATION #ifndef NK_QUICKDRAW_TEXT_MAX #define NK_QUICKDRAW_TEXT_MAX 256 #endif #endif struct NkQuickDrawFont { struct nk_user_font nk; char *font; }; int lastEventWasKey = 0; void *last; void *buf; // constant keyboard mappings for convenenience // See Inside Macintosh: Text pg A-7, A-8 int homeKey = (int)0x01; int enterKey = (int)0x03; int endKey = (int)0x04; int helpKey = (int)0x05; int backspaceKey = (int)0x08; int deleteKey = (int)0x7F; int tabKey = (int)0x09; int pageUpKey = (int)0x0B; int pageDownKey = (int)0x0C; int returnKey = (int)0x0D; int rightArrowKey = (int)0x1D; int leftArrowKey = (int)0x1C; int downArrowKey = (int)0x1F; int upArrowKey = (int)0x1E; int eitherShiftKey = (int)0x0F; int escapeKey = (int)0x1B; // #define NK_QUICKDRAW_GRAPHICS_DEBUGGING // #def#ifdef NK_QUICKDRAW_EVENTS_DEBUGG typedef struct { Ptr Address; long RowBytes; GrafPtr bits; Rect bounds; BitMap BWBits; GrafPort BWPort; Handle OrigBits; } ShockBitmap; void NewShockBitmap(ShockBitmap *theMap, short width, short height) { theMap->bits = 0L; SetRect(&theMap->bounds, 0, 0, width, height); theMap->BWBits.bounds = theMap->bounds; theMap->BWBits.rowBytes = ((width+15) >> 4)<<1; // round to even theMap->BWBits.baseAddr = NewPtr(((long) height * (long) theMap->BWBits.rowBytes)); theMap->BWBits.baseAddr = StripAddress(theMap->BWBits.baseAddr); OpenPort(&theMap->BWPort); SetPort(&theMap->BWPort); SetPortBits(&theMap->BWBits); SetRectRgn(theMap->BWPort.visRgn, theMap->bounds.left, theMap->bounds.top, theMap->bounds.right, theMap->bounds.bottom); SetRectRgn(theMap->BWPort.clipRgn, theMap->bounds.left, theMap->bounds.top, theMap->bounds.right, theMap->bounds.bottom); EraseRect(&theMap->bounds); theMap->Address = theMap->BWBits.baseAddr; theMap->RowBytes = (long) theMap->BWBits.rowBytes; theMap->bits = (GrafPtr) &theMap->BWPort; } ShockBitmap gMainOffScreen; // bezier code is from http://preserve.mactech.com/articles/mactech/Vol.05/05.01/BezierCurve/index.html // as it is not built in to quickdraw like other "modern" graphics environments /* The greater the number of curve segments, the smoother the curve, and the longer it takes to generate and draw. The number below was pulled out of a hat, and seems to work o.k. */ #define SEGMENTS 16 static Fixed weight1[SEGMENTS + 1]; static Fixed weight2[SEGMENTS + 1]; #define w1(s) weight1[s] #define w2(s) weight2[s] #define w3(s) weight2[SEGMENTS - s] #define w4(s) weight1[SEGMENTS - s] /* * SetupBezier -- one-time setup code. * Compute the weights for the Bezier function. * For the those concerned with space, the tables can be precomputed. Setup is done here for purposes of illustration. */ void setupBezier() { Fixed t, zero, one; int s; zero = FixRatio(0, 1); one = FixRatio(1, 1); weight1[0] = one; weight2[0] = zero; for (s = 1; s < SEGMENTS; ++s) { t = FixRatio(s, SEGMENTS); weight1[s] = FixMul(one - t, FixMul(one - t, one - t)); weight2[s] = 3 * FixMul(t, FixMul(t - one, t - one)); } weight1[SEGMENTS] = zero; weight2[SEGMENTS] = zero; } /* * computeSegments -- compute segments for the Bezier curve * Compute the segments along the curve. * The curve touches the endpoints, so don’t bother to compute them. */ static void computeSegments(p1, p2, p3, p4, segment) Point p1, p2, p3, p4; Point segment[]; { int s; segment[0] = p1; for (s = 1; s < SEGMENTS; ++s) { segment[s].v = FixRound(w1(s) * p1.v + w2(s) * p2.v + w3(s) * p3.v + w4(s) * p4.v); segment[s].h = FixRound(w1(s) * p1.h + w2(s) * p2.h + w3(s) * p3.h + w4(s) * p4.h); } segment[SEGMENTS] = p4; } /* * BezierCurve -- Draw a Bezier Curve * Draw a curve with the given endpoints (p1, p4), and the given * control points (p2, p3). * Note that we make no assumptions about pen or pen mode. */ void BezierCurve(p1, p2, p3, p4) Point p1, p2, p3, p4; { int s; Point segment[SEGMENTS + 1]; computeSegments(p1, p2, p3, p4, segment); MoveTo(segment[0].h, segment[0].v); for (s = 1 ; s <= SEGMENTS ; ++s) { if (segment[s].h != segment[s - 1].h || segment[s].v != segment[s - 1].v) { LineTo(segment[s].h, segment[s].v); } } } // ex usage: // Point control[4] = {{144,72}, {72,144}, {216,144}, {144,216}}; // BezierCurve(c[0], c[1], c[2], c[3]); static struct nk_quickdraw { unsigned int width; unsigned int height; struct nk_context nuklear_context; struct nk_buffer cmds; } quickdraw; // TODO: maybe V2 - skipping images for first pass NK_API struct nk_image* nk_quickdraw_create_image(const char* file_name) { // TODO: just read the file to a char, we can draw it using quickdraw // b&w bitmaps are pretty easy to draw... // for us to do this, we need to figure out the format, then figure out if we can make it in to a quickdraw rect // and set the buffer to the image handle pointer in the image struct // i assume this gets consumed elsewhere in the code, thinking NK_COMMAND_IMAGE char* bitmap = ""; //al_load_bitmap(file_name); // TODO: https://www.allegro.cc/manual/5/al_load_bitmap, loads file to in-memory buffer understood by allegro if (bitmap == NULL) { fprintf(stdout, "Unable to load image file: %s\n", file_name); return NULL; } struct nk_image *image = (struct nk_image*)calloc(1, sizeof(struct nk_image)); image->handle.ptr = bitmap; image->w = 0; // al_get_bitmap_width(bitmap); // TODO: this can be retrieved from a bmp file image->h = 0; // al_get_bitmap_height(bitmap); // TODO: this can be retrieved from a bmp file return image; } // TODO: maybe V2 - skipping images for first pass NK_API void nk_quickdraw_del_image(struct nk_image* image) { if (!image) { return; } // al_destroy_bitmap(image->handle.ptr); // TODO: https://www.allegro.cc/manual/5/al_destroy_bitmap //this is just de-allocating the memory from a loaded bitmap free(image); } // NOTE: you will need to generate this yourself! short widthFor12ptFont[128] = { 0, 10, 10, 10, 10, 10, 10, 10, 10, 8, 10, 10, 10, 0, 10, 10, 10, 11, 11, 9, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 6, 7, 10, 7, 11, 10, 3, 5, 5, 7, 7, 4, 7, 4, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 6, 8, 6, 8, 11, 8, 8, 8, 8, 7, 7, 8, 8, 6, 7, 9, 7, 12, 9, 8, 8, 8, 8, 7, 6, 8, 8, 12, 8, 8, 8, 5, 7, 5, 8, 8, 6, 8, 8, 7, 8, 8, 6, 8, 8, 4, 6, 8, 4, 12, 8, 8, 8, 8, 6, 7, 6, 8, 8, 12, 8, 8, 8, 5, 5, 5, 8, 8 }; // doing this in a "fast" way by using a precomputed table for a 12pt font static short nk_quickdraw_font_get_text_width(nk_handle handle, short height, const char *text, short len) { if (!text || len == 0) { return 0; } short width = 0; for (short i = 0; i < len; i++) { width += widthFor12ptFont[text[i]]; } return width; } static int _get_text_width(const char *text, int len) { if (!text || len == 0) { return 0; } int width = 0; for (int i = 0; i < len; i++) { width += widthFor12ptFont[(int)text[i]]; } return width; } /* Flags are identical to al_load_font() flags argument */ NK_API NkQuickDrawFont* nk_quickdraw_font_create_from_file() { NkQuickDrawFont *font = (NkQuickDrawFont*)calloc(1, sizeof(NkQuickDrawFont)); font->font = calloc(1, 1024); if (font->font == NULL) { fprintf(stdout, "Unable to load font file\n"); return NULL; } font->nk.userdata = nk_handle_ptr(font); font->nk.height = (int)12; font->nk.width = nk_quickdraw_font_get_text_width; return font; } // used for bounds checking int mostLeft = 1; int mostBottom = 1; int mostTop = WINDOW_HEIGHT; int mostRight = WINDOW_WIDTH; void updateBounds(int top, int bottom, int left, int right) { if (left < mostLeft) { mostLeft = left; } if (right > mostRight) { mostRight = right; } if (top < mostTop) { mostTop = top; } if (bottom > mostBottom) { mostBottom = bottom; } } #ifdef COMMAND_CACHING void runDrawCommand(const struct nk_command *cmd, const struct nk_command *lastCmd) { #else void runDrawCommand(const struct nk_command *cmd) { #endif switch (cmd->type) { case NK_COMMAND_NOP: #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_NOP"); #endif break; case NK_COMMAND_SCISSOR: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_SCISSOR"); #endif const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; // there is no point in supressing scissor commands because they only affect // where we can actually draw to: // #ifdef COMMAND_CACHING // if (memcmp(s, lastCmd, sizeof(struct nk_command_scissor)) == 0) { // #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING // writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_scissor"); // #endif // break; // } // #endif Rect quickDrawRectangle; quickDrawRectangle.top = s->y; quickDrawRectangle.left = s->x; quickDrawRectangle.bottom = s->y + s->h; quickDrawRectangle.right = s->x + s->w; #ifdef ENABLED_DOUBLE_BUFFERING // we use "-8192" here to filter out nuklear "nk_null_rect" which we do not want updating bounds if (quickDrawRectangle.top != -8192) { updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); } #endif ClipRect(&quickDrawRectangle); } break; case NK_COMMAND_RECT: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT"); #endif // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-102.html#MARKER-9-372 // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-103.html#HEADING103-0 const struct nk_command_rect *r = (const struct nk_command_rect *)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(r, lastCmd, sizeof(struct nk_command_rect)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_rect"); #endif break; } #endif ForeColor(r->color); PenSize(r->line_thickness, r->line_thickness); Rect quickDrawRectangle; quickDrawRectangle.top = r->y; quickDrawRectangle.left = r->x; quickDrawRectangle.bottom = r->y + r->h; quickDrawRectangle.right = r->x + r->w; #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif FrameRoundRect(&quickDrawRectangle, r->rounding, r->rounding); } break; case NK_COMMAND_RECT_FILLED: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT_FILLED"); #endif const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd; if (r->allowCache == false) { Rect quickDrawRectangle; quickDrawRectangle.top = r->y; quickDrawRectangle.left = r->x; quickDrawRectangle.bottom = r->y + r->h; quickDrawRectangle.right = r->x + r->w; #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif FillRoundRect(&quickDrawRectangle, r->rounding, r->rounding, &qd.white); break; } #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(r, lastCmd, sizeof(struct nk_command_rect_filled)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_rect_filled"); #endif break; } #endif // TODO: to support coloring the lines, we need to map from qd Pattern types to integer colors // color = nk_color_to_quickdraw_bw_color(r->color); // ForeColor(color); ForeColor(blackColor); PenSize(1.0, 1.0); Rect quickDrawRectangle; quickDrawRectangle.top = r->y; quickDrawRectangle.left = r->x; quickDrawRectangle.bottom = r->y + r->h; quickDrawRectangle.right = r->x + r->w; #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif FillRoundRect(&quickDrawRectangle, r->rounding, r->rounding, &r->color); FrameRoundRect(&quickDrawRectangle, r->rounding, r->rounding); // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-105.html#HEADING105-0 } break; case NK_COMMAND_TEXT: { const struct nk_command_text *t = (const struct nk_command_text*)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && t->allowCache && cmd->type == lastCmd->type && memcmp(t, lastCmd, sizeof(struct nk_command_text)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING char log[255]; sprintf(log, "ALREADY DREW CMD nk_command_text string: \"%s\", height: %d, length: %d, x: %d, y: %d, allowCache: %d", t->string, t->height, t->length, t->x, t->y, t->allowCache); writeSerialPortDebug(boutRefNum, log); #endif break; } #endif #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING char log[255]; sprintf(log, "NK_COMMAND_TEXT string: \"%s\", height: %d, length: %d, x: %d, y: %d, allowCache: %d", t->string, t->height, t->length, t->x, t->y, t->allowCache); writeSerialPortDebug(boutRefNum, log); #endif Rect quickDrawRectangle; quickDrawRectangle.top = t->y; quickDrawRectangle.left = t->x; quickDrawRectangle.bottom = t->y + 15; quickDrawRectangle.right = t->x + _get_text_width((const char*)t->string, (int)t->length); #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif #ifdef COMMAND_CACHING EraseRect(&quickDrawRectangle); #endif ForeColor(t->foreground); MoveTo(t->x, t->y + t->height); PenSize(1.0, 1.0); DrawText((const char*)t->string, 0, t->length); } break; case NK_COMMAND_LINE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_LINE"); #endif const struct nk_command_line *l = (const struct nk_command_line *)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(l, lastCmd, sizeof(struct nk_command_line)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_line"); #endif break; } #endif // great reference: http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-60.html ForeColor(l->color); PenSize(l->line_thickness, l->line_thickness); MoveTo(l->begin.x, l->begin.y); LineTo(l->end.x, l->end.y); } break; case NK_COMMAND_CIRCLE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_CIRCLE"); #endif const struct nk_command_circle *c = (const struct nk_command_circle *)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(c, lastCmd, sizeof(struct nk_command_circle)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_circle"); #endif break; } #endif ForeColor(c->color); Rect quickDrawRectangle; quickDrawRectangle.top = c->y; quickDrawRectangle.left = c->x; quickDrawRectangle.bottom = c->y + c->h; quickDrawRectangle.right = c->x + c->w; #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif FrameOval(&quickDrawRectangle); // An oval is a circular or elliptical shape defined by the bounding rectangle that encloses it. inside macintosh: imaging with quickdraw 3-25 } break; case NK_COMMAND_CIRCLE_FILLED: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_CIRCLE_FILLED"); #endif const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(c, lastCmd, sizeof(struct nk_command_circle_filled)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_circle_filled"); #endif break; } #endif ForeColor(blackColor); // BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48 PenSize(1.0, 1.0); Rect quickDrawRectangle; quickDrawRectangle.top = c->y; quickDrawRectangle.left = c->x; quickDrawRectangle.bottom = c->y + c->h; quickDrawRectangle.right = c->x + c->w; #ifdef ENABLED_DOUBLE_BUFFERING updateBounds(quickDrawRectangle.top, quickDrawRectangle.bottom, quickDrawRectangle.left, quickDrawRectangle.right); #endif FillOval(&quickDrawRectangle, &c->color); FrameOval(&quickDrawRectangle);// An oval is a circular or elliptical shape defined by the bounding rectangle that encloses it. inside macintosh: imaging with quickdraw 3-25 // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-111.html#HEADING111-0 } break; case NK_COMMAND_TRIANGLE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_TRIANGLE"); #endif const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(t, lastCmd, sizeof(struct nk_command_triangle)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_triangle"); #endif break; } #endif ForeColor(t->color); PenSize(t->line_thickness, t->line_thickness); MoveTo(t->a.x, t->a.y); LineTo(t->b.x, t->b.y); LineTo(t->c.x, t->c.y); LineTo(t->a.x, t->a.y); } break; case NK_COMMAND_TRIANGLE_FILLED: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_TRIANGLE_FILLED"); #endif const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(t, lastCmd, sizeof(struct nk_command_triangle_filled)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_triangle_filled"); #endif break; } #endif PenSize(1.0, 1.0); // BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48 ForeColor(blackColor); PolyHandle trianglePolygon = OpenPoly(); MoveTo(t->a.x, t->a.y); LineTo(t->b.x, t->b.y); LineTo(t->c.x, t->c.y); LineTo(t->a.x, t->a.y); ClosePoly(); FillPoly(trianglePolygon, &t->color); KillPoly(trianglePolygon); } break; case NK_COMMAND_POLYGON: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYGON"); #endif const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(p, lastCmd, sizeof(struct nk_command_polygon)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_polygon"); #endif break; } #endif ForeColor(p->color); int i; for (i = 0; i < p->point_count; i++) { if (i == 0) { MoveTo(p->points[i].x, p->points[i].y); } LineTo(p->points[i].x, p->points[i].y); if (i == p->point_count - 1) { LineTo(p->points[0].x, p->points[0].y); } } } break; case NK_COMMAND_POLYGON_FILLED: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYGON_FILLED"); #endif const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(p, lastCmd, sizeof(struct nk_command_polygon_filled)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_polygon_filled"); #endif break; } #endif // BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48 -- but might actually need PenPat -- look into this ForeColor(blackColor); int i; PolyHandle trianglePolygon = OpenPoly(); for (i = 0; i < p->point_count; i++) { if (i == 0) { MoveTo(p->points[i].x, p->points[i].y); } LineTo(p->points[i].x, p->points[i].y); if (i == p->point_count - 1) { LineTo(p->points[0].x, p->points[0].y); } } ClosePoly(); FillPoly(trianglePolygon, &p->color); KillPoly(trianglePolygon); } break; case NK_COMMAND_POLYLINE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYLINE"); #endif // this is similar to polygons except the polygon does not get closed to the 0th point // check out the slight difference in the for loop const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd; #ifdef COMMAND_CACHING if (!forceRedraw && cmd->type == lastCmd->type && memcmp(p, lastCmd, sizeof(struct nk_command_polygon)) == 0) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "ALREADY DREW CMD nk_command_polygon"); #endif break; } #endif ForeColor(p->color); int i; for (i = 0; i < p->point_count; i++) { if (i == 0) { MoveTo(p->points[i].x, p->points[i].y); } LineTo(p->points[i].x, p->points[i].y); } } break; case NK_COMMAND_CURVE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_CURVE"); #endif const struct nk_command_curve *q = (const struct nk_command_curve *)cmd; ForeColor(q->color); Point p1 = { (int)q->begin.x, (int)q->begin.y}; Point p2 = { (int)q->ctrl[0].x, (int)q->ctrl[0].y}; Point p3 = { (int)q->ctrl[1].x, (int)q->ctrl[1].y}; Point p4 = { (int)q->end.x, (int)q->end.y}; BezierCurve(p1, p2, p3, p4); } break; case NK_COMMAND_ARC: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_ARC"); #endif const struct nk_command_arc *a = (const struct nk_command_arc *)cmd; ForeColor(a->color); Rect arcBoundingBoxRectangle; // this is kind of silly because the cx is at the center of the arc and we need to create a rectangle around it // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-60.html#MARKER-2-116 int x1 = (int)a->cx - (int)a->r; int y1 = (int)a->cy - (int)a->r; int x2 = (int)a->cx + (int)a->r; int y2 = (int)a->cy + (int)a->r; SetRect(&arcBoundingBoxRectangle, x1, y1, x2, y2); FrameArc(&arcBoundingBoxRectangle, a->a[0], a->a[1]); } break; case NK_COMMAND_IMAGE: { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_IMAGE"); #endif // const struct nk_command_image *i = (const struct nk_command_image *)cmd; // al_draw_bitmap_region(i->img.handle.ptr, 0, 0, i->w, i->h, i->x, i->y, 0); // TODO: look up and convert al_draw_bitmap_region // TODO: consider implementing a bitmap drawing routine. we could iterate pixel by pixel and draw // here is some super naive code that could work, used for another project that i was working on with a custom format but would be // easy to modify for standard bitmap files (just need to know how many bytes represent each pixel and iterate from there): // // for (int i = 0; i < strlen(string); i++) { // printf("\nchar: %c", string[i]); // char pixel[1]; // memcpy(pixel, &string[i], 1); // if (strcmp(pixel, "0") == 0) { // white pixel // MoveTo(++x, y); // } else if (strcmp(pixel, "1") == 0) { // black pixel // // advance the pen and draw a 1px x 1px "line" // MoveTo(++x, y); // LineTo(x, y); // } else if (strcmp(pixel, "|") == 0) { // next line // x = 1; // MoveTo(x, ++y); // } else if (strcmp(pixel, "/") == 0) { // end // } // } } break; // why are these cases not implemented? case NK_COMMAND_RECT_MULTI_COLOR: case NK_COMMAND_ARC_FILLED: default: #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT_MULTI_COLOR/NK_COMMAND_ARC_FILLED/default"); #endif break; } #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NK_COMMAND_* draw complete"); #endif } int lastCalls = 0; int currentCalls; NK_API void nk_quickdraw_render(WindowPtr window, struct nk_context *ctx) { currentCalls = 1; #ifdef PROFILING PROFILE_START("IN nk_quickdraw_render"); #endif #ifdef PROFILING PROFILE_START("get cmds and memcmp them"); #endif void *cmds = nk_buffer_memory(&ctx->memory); // do not render if the buffer did not change from the previous rendering run if (!memcmp(cmds, last, ctx->memory.allocated)) { #ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING writeSerialPortDebug(boutRefNum, "NO RENDER BUFFER CHANGE, ABORT"); #endif return; } #ifdef PROFILING PROFILE_END("get cmds and memcmp them"); #endif const struct nk_command *cmd = 0; #ifdef ENABLED_DOUBLE_BUFFERING OpenPort(&gMainOffScreen.BWPort); SetPort(&gMainOffScreen.BWPort); SetPortBits(&gMainOffScreen.BWBits); #endif #ifdef PROFILING PROFILE_START("rendering loop and switch"); #endif #ifdef COMMAND_CACHING const struct nk_command *lastCmd; lastCmd = nk_ptr_add_const(struct nk_command, last, 0); #endif nk_foreach(cmd, ctx) { #ifdef COMMAND_CACHING runDrawCommand(cmd, lastCmd); #else runDrawCommand(cmd); #endif #ifdef COMMAND_CACHING if (currentCalls <= lastCalls && lastCmd && lastCmd->next && lastCmd->next < ctx->memory.allocated) { lastCmd = nk_ptr_add_const(struct nk_command, last, lastCmd->next); } currentCalls++; #endif } #ifdef PROFILING PROFILE_START("memcpy commands"); #endif memcpy(last, cmds, ctx->memory.allocated); lastCalls = ctx->memory.calls; #ifdef PROFILING PROFILE_END("memcpy commands"); #endif #ifdef PROFILING PROFILE_END("rendering loop and switch"); #endif #ifdef COMMAND_CACHING lastInputWasBackspace = false; #endif #ifdef ENABLED_DOUBLE_BUFFERING #ifdef PROFILING PROFILE_START("copy bits"); #endif SetPort(window); Rect quickDrawRectangle; quickDrawRectangle.top = mostTop; quickDrawRectangle.left = mostLeft; quickDrawRectangle.bottom = mostBottom; quickDrawRectangle.right = mostRight; CopyBits(&gMainOffScreen.bits->portBits, &window->portBits, &quickDrawRectangle, &quickDrawRectangle, srcCopy, 0L); mostLeft = 1; mostBottom = 1; mostTop = WINDOW_HEIGHT; mostRight = WINDOW_WIDTH; #ifdef PROFILING PROFILE_END("copy bits"); #endif #endif #ifdef PROFILING PROFILE_END("IN nk_quickdraw_render"); #endif } NK_API int nk_quickdraw_handle_event(EventRecord *event, struct nk_context *nuklear_context) { // see: inside macintosh: toolbox essentials 2-4 // and inside macintosh toolbox essentials 2-79 WindowPtr window; FindWindow(event->where, &window); // char logb[255]; // sprintf(logb, "nk_quickdraw_handle_event event %d", event->what); // writeSerialPortDebug(boutRefNum, logb); switch (event->what) { case updateEvt: { return 1; } break; case osEvt: { // the quicktime osEvts are supposed to cover mouse movement events // notice that we are actually calling nk_input_motion in the EventLoop for the program // instead, as handling this event directly does not appear to work for whatever reason // TODO: research this // writeSerialPortDebug(boutRefNum, "osEvt"); switch (event->message) { case mouseMovedMessage: { #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "mouseMovedMessage"); #endif // event->where should have coordinates??? or is it just a pointer to what the mouse is over? // TODO need to figure this out nk_input_motion(nuklear_context, event->where.h, event->where.v); // TODO figure out mouse coordinates - not sure if this is right break; } return 1; } } break; case mouseUp: #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "mouseUp!!!"); #endif case mouseDown: { #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "mouseUp/Down"); #endif short part = FindWindow(event->where, &window); switch (part) { case inContent: { // event->where should have coordinates??? or is it just a pointer to what the mouse is over? // TODO need to figure this out #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "mouseUp/Down IN DEFAULT ZONE!!!!"); #endif // this converts the offset of the window to the actual location of the mouse within the window Point tempPoint; SetPt(&tempPoint, event->where.h, event->where.v); GlobalToLocal(&tempPoint); if (!event->where.h) { #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "no event location for mouse!!!!"); #endif return 1; } int x = tempPoint.h; int y = tempPoint.v; #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING char logx[255]; sprintf(logx, "mouse location at time of click h: %d, v: %d, is mouse down: %d", x, y, event->what == mouseDown); writeSerialPortDebug(boutRefNum, logx); #endif // nk_input_motion(nuklear_context, x, y); // you can enable this if you don't want to use motion tracking // in the Mac event loop handler as in the nuklear quickdraw sample, and this will allow mouse clicks to // work properly, but will not allow hover states to work nk_input_motion(nuklear_context, x, y); nk_input_button(nuklear_context, NK_BUTTON_LEFT, x, y, event->what == mouseDown); } break; return 1; } break; case keyDown: case autoKey: {/* check for menukey equivalents */ char charKey = event->message & charCodeMask; int key = (int)charKey; #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "keyDown/autoKey"); char logy[255]; sprintf(logy, "key pressed: key: '%c', 02x: '%02X', return: '%02X', %d == %d ??", key, key, returnKey, (int)(key), (int)(returnKey)); writeSerialPortDebug(boutRefNum, logy); #endif const Boolean isKeyDown = event->what == keyDown; if (event->modifiers & cmdKey) {/* Command key down */ if (isKeyDown) { // AdjustMenus(); /* enable/disable/check menu items properly */ // DoMenuCommand(MenuKey(key)); } if (key == 'c') { nk_input_key(nuklear_context, NK_KEY_COPY, 1); } else if (key == 'v') { nk_input_key(nuklear_context, NK_KEY_PASTE, 1); } else if (key == 'x') { nk_input_key(nuklear_context, NK_KEY_CUT, 1); } else if (key == 'z') { nk_input_key(nuklear_context, NK_KEY_TEXT_UNDO, 1); } else if (key == 'r') { nk_input_key(nuklear_context, NK_KEY_TEXT_REDO, 1); } } else if (key == eitherShiftKey) { nk_input_key(nuklear_context, NK_KEY_SHIFT, isKeyDown); } else if (key == deleteKey && isKeyDown) { nk_input_key(nuklear_context, NK_KEY_DEL, isKeyDown); } else if (key == enterKey) { nk_input_key(nuklear_context, NK_KEY_ENTER, isKeyDown); } else if (key == returnKey) { nk_input_key(nuklear_context, NK_KEY_ENTER, isKeyDown); } else if (key == tabKey) { nk_input_key(nuklear_context, NK_KEY_TAB, isKeyDown); } else if (key == leftArrowKey) { nk_input_key(nuklear_context, NK_KEY_LEFT, isKeyDown); } else if (key == rightArrowKey) { nk_input_key(nuklear_context, NK_KEY_RIGHT, isKeyDown); } else if (key == upArrowKey) { nk_input_key(nuklear_context, NK_KEY_UP, isKeyDown); } else if (key == downArrowKey) { nk_input_key(nuklear_context, NK_KEY_DOWN, isKeyDown); } else if (key == backspaceKey) { #ifdef COMMAND_CACHING lastInputWasBackspace = true; #endif nk_input_key(nuklear_context, NK_KEY_BACKSPACE, isKeyDown); } else if (key == escapeKey) { // nk_input_key(nuklear_context, NK_KEY_TEXT_RESET_MODE, isKeyDown); } else if (key == pageUpKey) { nk_input_key(nuklear_context, NK_KEY_SCROLL_UP, isKeyDown); } else if (key == pageDownKey) { nk_input_key(nuklear_context, NK_KEY_SCROLL_DOWN, isKeyDown); } else if (key == homeKey) { // nk_input_key(nuklear_context, NK_KEY_TEXT_START, isKeyDown); nk_input_key(nuklear_context, NK_KEY_SCROLL_START, isKeyDown); } else if (key == endKey) { // nk_input_key(nuklear_context, NK_KEY_TEXT_END, isKeyDown); nk_input_key(nuklear_context, NK_KEY_SCROLL_END, isKeyDown); } else { #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "default keydown/autokey event"); #endif nk_input_char(nuklear_context, charKey); } lastEventWasKey = 1; return 1; } break; default: { #ifdef NK_QUICKDRAW_EVENTS_DEBUGGING writeSerialPortDebug(boutRefNum, "default unhandled event"); #endif return 1; } break; } } return 1; } // i think these functions are close to correct, but throw an error around invalid storage class NK_INTERN void nk_quickdraw_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) { Handle hDest = NewHandle(0); // { automatically resizes it} HLock(hDest); // {put data into memory referenced thru hDest handle} int sizeOfSurfData = GetScrap(hDest, 'TEXT', 0); HUnlock(hDest); nk_textedit_paste(edit, (char *)hDest, sizeOfSurfData); DisposeHandle(hDest); } NK_INTERN void nk_quickdraw_clipboard_copy(nk_handle usr, const char *text, short len) { // in Macintosh Toolbox the clipboard is referred to as "scrap manager" PutScrap(len, 'TEXT', text); } // it us up to our "main" function to call this code NK_API struct nk_context* nk_quickdraw_init(unsigned int width, unsigned int height) { // needed to calculate bezier info, see mactech article. setupBezier(); #ifdef ENABLED_DOUBLE_BUFFERING NewShockBitmap(&gMainOffScreen, width, height); #else TextFont(0); TextSize(12); TextFace(0); #endif NkQuickDrawFont *quickdrawfont = nk_quickdraw_font_create_from_file(); struct nk_user_font *font = &quickdrawfont->nk; last = calloc(1, MAX_MEMORY_IN_KB * 1024); buf = calloc(1, MAX_MEMORY_IN_KB * 1024); nk_init_fixed(&quickdraw.nuklear_context, buf, MAX_MEMORY_IN_KB * 1024, font); // nk_init_default(&quickdraw.nuklear_context, font); nk_style_push_font(&quickdraw.nuklear_context, font); // this is pascal code but i think we would need to do something like this if we want this function // to be responsible for setting the window size // Region locUpdateRgn = NewRgn(); // SetRect(limitRect, kMinDocSize, kMinDocSize, kMaxDocSize, kMaxDocSize); // // {call Window Manager to let user drag size box} // growSize = GrowWindow(thisWindow, event.where, limitRect); // SizeWindow(thisWindow, LoWord(growSize), HiWord(growSize), TRUE); // SectRect(oldViewRect, myData^^.editRec^^.viewRect, oldViewRect); // // {validate the intersection (don't update)} // ValidRect(oldViewRect); // // {invalidate any prior update region} // InvalRgn(locUpdateRgn); // DisposeRgn(locUpdateRgn); quickdraw.nuklear_context.clip.copy = nk_quickdraw_clipboard_copy; quickdraw.nuklear_context.clip.paste = nk_quickdraw_clipboard_paste; quickdraw.nuklear_context.clip.userdata = nk_handle_ptr(0); // // fix styles to be more "mac-like" // struct nk_style *style; // struct nk_style_toggle *toggle; // struct nk_style_button *button; // style = &quickdraw.nuklear_context.style; // /* checkbox toggle */ // toggle = &style->checkbox; // nk_zero_struct(*toggle); // // toggle->normal = nk_style_item_color(nk_rgba(45, 45, 45, 255)); // // toggle->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255)); // this is the "background" hover state regardless of checked status - we want light gray // // toggle->active = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // i can't tell what this does yet // // toggle->cursor_normal = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the "checked" box itself - we want "black" // // toggle->cursor_hover = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the hover state of a "checked" box - anything lighter than black is ok // toggle->userdata = nk_handle_ptr(0); // toggle->text_background = qd.black; // // toggle->text_normal = nk_rgba(70, 70, 70, 255); // // toggle->text_hover = nk_rgba(70, 70, 70, 255); // // toggle->text_active = nk_rgba(70, 70, 70, 255); // toggle->padding = nk_vec2(3, 3); // toggle->touch_padding = nk_vec2(0,0); // // toggle->border_color = nk_rgba(0,0,0,0); // toggle->border = 0; // toggle->spacing = 5; // /* option toggle */ // toggle = &style->option; // nk_zero_struct(*toggle); // // toggle->normal = nk_style_item_color(nk_rgba(45, 45, 45, 255)); // // toggle->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255)); // this is the "background" hover state regardless of checked status - we want light gray // // toggle->active = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // i can't tell what this does yet // // toggle->cursor_normal = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the "checked" box itself - we want "black" // // toggle->cursor_hover = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the hover state of a "checked" box - anything lighter than black is ok // toggle->userdata = nk_handle_ptr(0); // toggle->text_background = qd.black; // // toggle->text_normal = nk_rgba(70, 70, 70, 255); // // toggle->text_hover = nk_rgba(70, 70, 70, 255); // // toggle->text_active = nk_rgba(70, 70, 70, 255); // toggle->padding = nk_vec2(3, 3); // toggle->touch_padding = nk_vec2(0,0); // // toggle->border_color = nk_rgba(0,0,0,0); // toggle->border = 0; // toggle->spacing = 5; // // button // button = &style->button; // nk_zero_struct(*button); // // button->normal = nk_style_item_color(nk_rgba(0, 0, 0, 255)); // // button->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255)); // // button->active = nk_style_item_color(nk_rgba(150, 150, 150, 255)); // // button->border_color = nk_rgba(255, 255, 255, 255); // button->text_background = qd.black; // // button->text_normal = nk_rgba(70, 70, 70, 255); // // button->text_hover = nk_rgba(70, 70, 70, 255); // // button->text_active = nk_rgba(0, 0, 0, 255); // button->padding = nk_vec2(2,2); // button->image_padding = nk_vec2(0,0); // button->touch_padding = nk_vec2(0, 0); // button->userdata = nk_handle_ptr(0); // button->text_alignment = NK_TEXT_CENTERED; // button->border = 1; // button->rounding = 10; // button->draw_begin = 0; // button->draw_end = 0; ForeColor(blackColor); return &quickdraw.nuklear_context; } NK_API void nk_quickdraw_shutdown(void) { nk_free(&quickdraw.nuklear_context); memset(&quickdraw, 0, sizeof(quickdraw)); }