diff --git a/pdlua.c b/pdlua.c index d03c611..ba27502 100644 --- a/pdlua.c +++ b/pdlua.c @@ -703,11 +703,17 @@ static void pdlua_delete(t_gobj *z, t_glist *glist){ canvas_deletelinesfor(glist, (t_text *)z); } +#ifdef PURR_DATA // Purr Data uses an older version of this API +static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy) +#else static void pdlua_motion(t_gobj *z, t_floatarg dx, t_floatarg dy, t_floatarg up) +#endif { #if !PLUGDATA +#ifndef PURR_DATA if (!up) +#endif { t_pdlua *x = (t_pdlua *)z; x->gfx.mouse_drag_x = x->gfx.mouse_drag_x + dx; @@ -783,6 +789,25 @@ static void pdlua_displace(t_gobj *z, t_glist *glist, int dx, int dy){ canvas_fixlinesfor(glist, (t_text*)x); } +#ifdef PURR_DATA +static void pdlua_displace_wtag(t_gobj *z, t_glist *glist, int dx, int dy){ + t_pdlua *x = (t_pdlua *)z; + + + if(x->has_gui) + { + x->pd.te_xpix += dx, x->pd.te_ypix += dy; + //gfx_displace((t_pdlua*)z, glist, dx, dy); + } + else { + text_widgetbehavior.w_displacefnwtag(z, glist, dx, dy); + } + + + canvas_fixlinesfor(glist, (t_text*)x); +} +#endif + static void pdlua_activate(t_gobj *z, t_glist *glist, int state) { if(!((t_pdlua *)z)->has_gui) @@ -796,11 +821,16 @@ static void pdlua_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp { t_pdlua *x = (t_pdlua *)z; if(x->has_gui) { +#ifndef PURR_DATA + int zoom = glist->gl_zoom; +#else + int zoom = 1; +#endif float x1 = text_xpix((t_text *)x, glist), y1 = text_ypix((t_text *)x, glist); *xp1 = x1; *yp1 = y1; - *xp2 = x1 + x->gfx.width * glist->gl_zoom; - *yp2 = y1 + x->gfx.height * glist->gl_zoom; + *xp2 = x1 + x->gfx.width * zoom; + *yp2 = y1 + x->gfx.height * zoom; } else { // Bypass to text widgetbehaviour if we're not a GUI @@ -1113,12 +1143,6 @@ static int pdlua_class_new(lua_State *L) plugdata_register_class(name); #endif -#ifndef PURR_DATA - /* Vanilla Pd and plugdata require this for the gfx routines, but this - interferes with Purr Data's handling of canvas events and is thus - disabled there. XXXTODO: When we add the gfx API in Purr Data, we'll - have to figure out how to tie into Purr Data's JavaScript GUI in order - to implement these callbacks. -ag */ // Set custom widgetbehaviour for GUIs pdlua_widgetbehavior.w_getrectfn = pdlua_getrect; pdlua_widgetbehavior.w_displacefn = pdlua_displace; @@ -1127,8 +1151,12 @@ static int pdlua_class_new(lua_State *L) pdlua_widgetbehavior.w_clickfn = pdlua_click; pdlua_widgetbehavior.w_visfn = pdlua_vis; pdlua_widgetbehavior.w_activatefn = pdlua_activate; - class_setwidget(c, &pdlua_widgetbehavior); +#ifdef PURR_DATA + // Purr Data only, this seems to be preferred over w_displacefn and is + // actually needed to make text_widgetbehavior.w_selectfn happy. + pdlua_widgetbehavior.w_displacefnwtag = pdlua_displace_wtag; #endif + class_setwidget(c, &pdlua_widgetbehavior); if (c) { /* a class with a "menu-open" method will have the "Open" item highlighted in the right-click menu */ @@ -1197,16 +1225,8 @@ static int pdlua_object_new(lua_State *L) static int pdlua_object_creategui(lua_State *L) { t_pdlua *o = lua_touserdata(L, 1); -#ifndef PURR_DATA o->has_gui = 1; gfx_initialize(o); -#else - // We avoid the gfx initalization here, since it produces unwanted - // artifacts on the canvas and at present none of the graphics routines - // will work in Purr Data anyway. -ag - o->has_gui = 0; - gfx_not_implemented(); -#endif return 0; } diff --git a/pdlua_gfx.h b/pdlua_gfx.h index 4868a29..e49a419 100644 --- a/pdlua_gfx.h +++ b/pdlua_gfx.h @@ -23,40 +23,20 @@ #ifdef PURR_DATA -// Purr Data doesn't currently define these vanilla API routines, and the -// graphics interface needs them, so for now we're providing dummy versions -// here. XXXFIXME: This needs to go once the graphics interface has been -// implemented in Purr Data. At present you'll just get a warning when the -// graphics interface is utilized. -ag +// Port of the vanilla gfx interface to Purr Data. There are some differences +// in the zoom API which Purr Data does directly on the canvas, so we can just +// always assume a zoom factor of 1. Other API differences are dealt with on +// the spot below and in pdlua.c (look for #ifdef/#ifndef PURR_DATA). -static void gfx_not_implemented(void) -{ - static int init = 0; - if (!init) { - post("pd-lua[gfx]: WARNING: graphics interface not yet implemented!"); - init = 1; - } -} - -int glist_getzoom(t_glist *x) -{ - gfx_not_implemented(); - return 1; -} +#define glist_getzoom(x) 1 -void pdgui_vmess(const char* message, const char* format, ...) -{ - gfx_not_implemented(); -} - -// this has an extra argument in vanilla - -int wrap_hostfontsize(int fontsize, int zoom) +// this has an extra argument in vanilla (which we ignore) +int xxsys_hostfontsize(int fontsize, int zoom) { return sys_hostfontsize(fontsize); } -#define sys_hostfontsize wrap_hostfontsize +#define sys_hostfontsize xxsys_hostfontsize #endif @@ -597,9 +577,116 @@ static void transform_point_float(t_pdlua_gfx *gfx, float* x, float* y) { } } +#ifdef PURR_DATA +// Purr Data's glist_drawiofor and glist_eraseiofor aren't compatible with +// vanilla's, so they won't work for what we're doing here. We replace them +// with something that's more like the vanilla routines but calling into the +// nw.js GUI. + +#define EXTRAPIX 2 +#define IOWIDTH 7 + +#define glist_drawiofor xxglist_drawiofor +#define glist_eraseiofor xxglist_eraseiofor + + /* draw inlets and outlets for a text object or for a graph. */ +void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, + char *tag, int x1, int y1, int x2, int y2) +{ + t_canvas *canvas = glist_getcanvas(glist); + int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i; + int width = x2 - x1; + int issignal; + for (i = 0; i < n; i++) { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) { + issignal = obj_issignaloutlet(ob,i); + + /* need to send issignal and is_iemgui here... */ + gui_vmess("gui_gobj_draw_io", "xssiiiiiisiii", + canvas, + tag, + tag, + onset, + y2 - 2, + onset + IOWIDTH, + y2, + x1, + y1, + "o", + i, + issignal, + 0); + } else { + gui_vmess("gui_gobj_redraw_io", "xssiisiii", + canvas, + tag, + tag, + onset, + y2 - 2, + "o", + i, + x1, + y1); + } + } + n = obj_ninlets(ob); + nplus = (n == 1 ? 1 : n-1); + for (i = 0; i < n; i++) { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) { + issignal = obj_issignalinlet(ob,i); + gui_vmess("gui_gobj_draw_io", "xssiiiiiisiii", + canvas, + tag, + tag, + onset, + y1, + onset + IOWIDTH, + y1 + EXTRAPIX, + x1, + y1, + "i", + i, + issignal, + 0); + } else { + gui_vmess("gui_gobj_redraw_io", "xssiisiii", + canvas, + tag, + tag, + onset, + y1, + "i", + i, + x1, + y1); + } + } +} + +void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag) +{ + char tagbuf[MAXPDSTRING]; + t_canvas *canvas = glist_getcanvas(glist); + int i, n; + n = obj_noutlets(ob); + for (i = 0; i < n; i++) { + sprintf(tagbuf, "%so%d", tag, i); + gui_vmess("gui_gobj_erase_io", "xs", canvas, tagbuf); + } + n = obj_ninlets(ob); + for (i = 0; i < n; i++) { + sprintf(tagbuf, "%si%d", tag, i); + gui_vmess("gui_gobj_erase_io", "xs", canvas, tagbuf); + } +} +#endif + static void pdlua_gfx_clear(t_pdlua *obj, int removed) { t_pdlua_gfx *gfx = &obj->gfx; t_canvas *cnv = glist_getcanvas(obj->canvas); +#ifndef PURR_DATA pdgui_vmess(0, "crs", cnv, "delete", gfx->object_tag); if(removed && gfx->order_tag[0] != '\0') @@ -607,6 +694,18 @@ static void pdlua_gfx_clear(t_pdlua *obj, int removed) { pdgui_vmess(0, "crs", cnv, "delete", gfx->order_tag); gfx->order_tag[0] = '\0'; } +#else // PURR_DATA + if (removed) { + // nuke the gobj container, this gets rid of everything + gui_vmess("gui_gobj_erase", "xs", cnv, gfx->object_tag); + } else { + // this just clears the gobj container + gui_vmess("gui_luagfx_clear_contents", "xs", cnv, gfx->object_tag); + // redraw the border rectangle + gui_vmess("gui_luagfx_draw_border", "xsii", cnv, gfx->object_tag, + gfx->width, gfx->height); + } +#endif glist_eraseiofor(glist_getcanvas(cnv), &obj->pd, gfx->object_tag); } @@ -633,7 +732,11 @@ static void get_bounds_args(lua_State* L, t_pdlua *obj, int* x1, int* y1, int* x static void gfx_displace(t_pdlua *x, t_glist *glist, int dx, int dy) { +#ifndef PURR_DATA sys_vgui(".x%lx.c move .x%lx %d %d\n", glist_getcanvas(x->canvas), (long)x, dx, dy); +#else + gui_vmess("gui_text_displace", "xsii", glist_getcanvas(x->canvas), x->gfx.object_tag, dx, dy); +#endif canvas_fixlinesfor(glist, (t_text*)x); int scale = glist_getzoom(glist_getcanvas(x->canvas)); @@ -653,8 +756,14 @@ static int gfx_initialize(t_pdlua *obj) { t_pdlua_gfx *gfx = &obj->gfx; + t_object *ob = (t_object*)obj; +#ifndef PURR_DATA snprintf(gfx->object_tag, 128, ".x%lx", (long)obj); gfx->object_tag[127] = '\0'; +#else // PURR_DATA + // deferred until the object is fully initialized + strcpy(gfx->object_tag, "*"); +#endif gfx->order_tag[0] = '\0'; gfx->object = obj; gfx->transforms = NULL; @@ -694,6 +803,22 @@ static int start_paint(lua_State* L) { return 1; } +#ifdef PURR_DATA + if (gfx->object_tag[0] == '*') { + // late initialization, deferred until glist_findrtext will work + t_rtext *y = glist_findrtext(obj->canvas, (t_text*)obj); + if (y) { + const char *s = rtext_gettag(y); + strcpy(gfx->object_tag, s); + } else { + // this shouldn't happen, but if it does, we fall back to the + // vanilla-style tag + snprintf(gfx->object_tag, 128, ".x%lx", (long)obj); + gfx->object_tag[127] = '\0'; + } + } +#endif + // Check if: // 1. The canvas and object are visible // 2. This is the first repaint since "vis" was called @@ -708,6 +833,7 @@ static int start_paint(lua_State* L) { lua_pushlightuserdata(L, gfx); luaL_setmetatable(L, "GraphicsContext"); +#ifndef PURR_DATA // clear anything that was painted before if(strlen(gfx->object_tag)) pdlua_gfx_clear(obj, 0); @@ -723,6 +849,20 @@ static int start_paint(lua_State* L) { pdgui_vmess(0, "crr iiii ri rS", cnv, "create", "line", 0, 0, 0, 0, "-width", 1, "-tags", 1, tags); } +#else // PURR_DATA + if(gfx->first_draw) { + t_canvas *cnv = glist_getcanvas(obj->canvas); + int xpos = text_xpix((t_object*)obj, obj->canvas); + int ypos = text_ypix((t_object*)obj, obj->canvas); + // create a gobj container in the GUI + gui_vmess("gui_gobj_new", "xssiii", cnv, gfx->object_tag, "obj", xpos, ypos, + glist_istoplevel(obj->canvas)); + // draw a border rectangle + gui_vmess("gui_luagfx_draw_border", "xsii", cnv, gfx->object_tag, + gfx->width, gfx->height); + } else if (strlen(gfx->object_tag)) + pdlua_gfx_clear(obj, 0); +#endif return 1; } @@ -744,10 +884,12 @@ static int end_paint(lua_State* L) { glist_drawiofor(glist_getcanvas(obj->canvas), (t_object*)obj, 1, gfx->object_tag, xpos, ypos, xpos + (gfx->width * scale), ypos + (gfx->height * scale)); +#ifndef PURR_DATA if(!gfx->first_draw && gfx->order_tag[0] != '\0') { // Move everything to below the order marker, to make sure redrawn stuff isn't always on top pdgui_vmess(0, "crss", cnv, "lower", gfx->object_tag, gfx->order_tag); } +#endif return 0; } @@ -794,7 +936,17 @@ static int fill_ellipse(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii rs ri rS", cnv, "create", "oval", x1, y1, x2, y2, "-fill", gfx->current_color, "-width", 0, "-tags", 2, tags); +#else // PURR_DATA + // in Purr Data, the coordinates of the graphical objects are all relative + // to the gobj container + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_fill_ellipse", "xsssiiiii", cnv, tags[0], tags[1], + gfx->current_color, 0, + x1-x0, y1-y0, x2-x0, y2-y0); +#endif return 0; } @@ -812,7 +964,15 @@ static int stroke_ellipse(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x1, y1, x2, y2, "-width", line_width, "-outline", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_stroke_ellipse", "xsssiiiii", cnv, tags[0], tags[1], + gfx->current_color, line_width, + x1-x0, y1-y0, x2-x0, y2-y0); +#endif return 0; } @@ -830,7 +990,13 @@ static int fill_all(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + gui_vmess("gui_luagfx_fill_all", "xsssiiii", cnv, tags[0], tags[1], + gfx->current_color, + 0, 0, x2-x1, y2-y1); +#endif return 0; } @@ -846,7 +1012,15 @@ static int fill_rect(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii rs ri rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-fill", gfx->current_color, "-width", 0, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_fill_rect", "xsssiiiii", cnv, tags[0], tags[1], + gfx->current_color, 0, + x1-x0, y1-y0, x2-x0, y2-y0); +#endif return 0; } @@ -864,7 +1038,15 @@ static int stroke_rect(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1, y1, x2, y2, "-width", line_width, "-outline", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_stroke_rect", "xsssiiiii", cnv, tags[0], tags[1], + gfx->current_color, line_width, + x1-x0, y1-y0, x2-x0, y2-y0); +#endif return 0; } @@ -886,6 +1068,7 @@ static int fill_rounded_rect(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA // Tcl/tk can't fill rounded rectangles, so we draw 2 smaller rectangles with 4 ovals over the corners pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x1, y1, x1 + radius_x * 2, y1 + radius_y * 2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x2 - radius_x * 2 , y1, x2, y1 + radius_y * 2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); @@ -893,6 +1076,14 @@ static int fill_rounded_rect(lua_State* L) { pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "oval", x2 - radius_x * 2, y2 - radius_y * 2, x2, y2, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1 + radius_x, y1, x2 - radius_x, y2, "-width", 0, "-fill", gfx->current_color, "-tag", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "rectangle", x1, y1 + radius_y, x2, y2 - radius_y, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_fill_rounded_rect", "xsssiiiiiii", cnv, tags[0], tags[1], + gfx->current_color, 0, + x1-x0, y1-y0, x2-x0, y2-y0, + radius_x, radius_y); +#endif return 0; } @@ -914,6 +1105,7 @@ static int stroke_rounded_rect(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA // Tcl/tk can't stroke rounded rectangles either, so we draw 2 lines connecting with 4 arcs at the corners pdgui_vmess(0, "crr iiii ri ri ri ri rs rs rS", cnv, "create", "arc", x1, y1 + radius_y*2, x1 + radius_x*2, y1, "-start", 0, "-extent", 90, "-width", line_width, "-start", 90, "-outline", gfx->current_color, "-style", "arc", "-tags", 2, tags); @@ -933,6 +1125,14 @@ static int stroke_rounded_rect(lua_State* L) { "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x2 , y1 + radius_y, x2, y2 - radius_y, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_stroke_rounded_rect", "xsssiiiiiii", cnv, tags[0], tags[1], + gfx->current_color, line_width, + x1-x0, y1-y0, x2-x0, y2-y0, + radius_x, radius_y); +#endif return 0; } @@ -967,8 +1167,16 @@ static int draw_line(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", x1, y1, x2, y2, "-width", line_width, "-fill", gfx->current_color, "-tags", 2, tags); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_draw_line", "xsssiiiii", cnv, tags[0], tags[1], + gfx->current_color, line_width, + x1-x0, y1-y0, x2-x0, y2-y0); +#endif return 0; } @@ -999,6 +1207,7 @@ static int draw_text(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr ii rs ri rs rS", cnv, "create", "text", 0, 0, "-anchor", "nw", "-width", w, "-text", text, "-tags", 2, tags); @@ -1013,6 +1222,12 @@ static int draw_text(lua_State* L) { "-justify", "left"); pdgui_vmess(0, "crs ii", cnv, "coords", tags[1], x, y); +#else // PURR_DATA + int x0 = text_xpix((t_object*)obj, obj->canvas); + int y0 = text_ypix((t_object*)obj, obj->canvas); + gui_vmess("gui_luagfx_draw_text", "xsssiiiis", cnv, tags[0], tags[1], + gfx->current_color, w, font_height, x-x0, y-y0, text); +#endif return 0; } @@ -1036,6 +1251,7 @@ static int stroke_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "line", 0, 0, 0, 0, "-width", stroke_width, "-fill", gfx->current_color, "-tags", 2, tags); sys_vgui(".x%lx.c coords %s", cnv, tags[1]); @@ -1045,6 +1261,19 @@ static int stroke_path(lua_State* L) { sys_vgui(" %f %f", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); } sys_vgui("\n"); +#else // PURR_DATA + gui_start_vmess("gui_luagfx_stroke_path", "xsssi", cnv, tags[0], tags[1], + gfx->current_color, stroke_width); + gui_start_array(); + for (int i = 0; i < path->num_path_segments; i++) { + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; + transform_point_float(gfx, &x, &y); + gui_s(i==0?"M":"L"); + gui_f(x); gui_f(y); + } + gui_end_array(); + gui_end_vmess(); +#endif return 0; } @@ -1068,6 +1297,7 @@ static int fill_path(lua_State* L) { const char* tags[] = { gfx->object_tag, register_drawing(gfx) }; +#ifndef PURR_DATA pdgui_vmess(0, "crr iiii ri rs rS", cnv, "create", "polygon", 0, 0, 0, 0, "-width", 0, "-fill", gfx->current_color, "-tags", 2, tags); sys_vgui(".x%lx.c coords %s", cnv, tags[1]); @@ -1077,6 +1307,19 @@ static int fill_path(lua_State* L) { sys_vgui(" %f %f", (x * canvas_zoom) + obj_x, (y * canvas_zoom) + obj_y); } sys_vgui("\n"); +#else // PURR_DATA + gui_start_vmess("gui_luagfx_fill_path", "xsssi", cnv, tags[0], tags[1], + gfx->current_color, 0); + gui_start_array(); + for (int i = 0; i < path->num_path_segments; i++) { + float x = path->path_segments[i * 2], y = path->path_segments[i * 2 + 1]; + transform_point_float(gfx, &x, &y); + gui_s(i==0?"M":"L"); + gui_f(x); gui_f(y); + } + gui_end_array(); + gui_end_vmess(); +#endif return 0; }