Skip to content

Combined PRs into large update#87

Draft
timothyschoen wants to merge 70 commits intoagraef:masterfrom
timothyschoen:pdlua-combined-prs
Draft

Combined PRs into large update#87
timothyschoen wants to merge 70 commits intoagraef:masterfrom
timothyschoen:pdlua-combined-prs

Conversation

@timothyschoen
Copy link
Contributor

@timothyschoen timothyschoen commented Mar 16, 2026

Work in progress to take some work away from @agraef for the next update.

I've merged most of the opened PRs into a new branch, resolving conflicts and adding missing plugdata implementations if needed. It includes:

#84: SVG rendering by @timothyschoen
#82: Makefile improvement by @umlaeute
#80: Clock deprecation fix by @ben-wes
#79: Github actions runner fix by @ben-wes
#77: Property menu support by @charlesneimog
#76: Fix compilation against new Pd version by @ben-wes
#70: Add mouse enter/exit callbacks by @ben-wes
#69: Fix path:close() documentation error by @ben-wes
#66: Make some gfx arguments optional by @ben-wes
#65: Add alignment feature to text drawing by @ben-wes
timothyschoen#7: Fix missing return value by @KottV

On top of that:

  • plugdata fixes:
    • Minor bugfixes (mostly improving stability in DAW)
    • Ported new property menu feature
    • Ported new text alignment feature
    • Ported mouse enter/exit callbacks
  • Cleaned up properties implementation
    • Remove unused variables and unnecessary checks
    • Moved to separate file, since it works independently from pdlua graphics
    • Also allow properties on non-gui objects
    • Fix properties dialog title
    • Fix properties button greying out when no properties() function is available

Planned:

  • Add more property panel options: number input (float/int), combo box
  • Add option to render regular images
  • Ensure all new features and changes are properly documented
  • Runtime choice between lua and luajit:
  • Port features to purr-data
    • SVG rendering
    • Image rendering
    • Mouse enter/exit
    • Text alignment
    • Properties panels -> I've attempted to write the basic structure for it using the gui_dialog_external API

I'm aware that this is gonna be a big set of changes. To ensure stability, I was hoping to get some assistance with testing from @ben-wes and @charlesneimog, once I get this out of the draft stage.

ben-wes and others added 30 commits October 22, 2024 22:38
with proxy function for canvas messages and clock for cleanup
…f warnings, clean up buffered tcl/tk images when object is deleted
…addcheckbox`, `_properties_addtextinput` and `_properties_addcolorpicker`
pd-lib-builder has means to install entire directories,
so we should probably use them, rather than build our own
(and broken) install replacement.

Closes: agraef#81
@timothyschoen
Copy link
Contributor Author

timothyschoen commented Mar 17, 2026

  • Image rendering support for many formats (using stb_image)
  • Removed the "width" property for add_text because none of the other properties allow setting width
  • Made colour property show visual feedback and preserve last state
  • Added proper documentation for:
    • Image/svg rendering
    • Properties
    • Text alignment options
    • Default arguments
Screenshot 2026-03-17 at 11 50 09 Screenshot 2026-03-17 at 12 46 01

Note that the diff looks so large because of the added svg and image libraries, the actual changes to pdlua's code are not that big.

@timothyschoen
Copy link
Contributor Author

@charlesneimog It seems like right now, all properties apply immediately on change. We could either make it wait for the "apply" command, or we could replace the cancel/ok/apply buttons with just an ok button.

My preference might be to just have one button, I fear it would become quite complex otherwise

@timothyschoen
Copy link
Contributor Author

@agraef Regarding the purr-data implementation: SVGs, images, mouse enter/exit and text alignment should be trivial, just a matter of passing the data over to purr-data and handling it in JS.

There is already a basic implementation for purr-data external properties here, but it's missing a few features like:

  • Colour picker
  • Range and type limits for numbers
  • "Frame" layouting, though this could be simplified into just having labels for each section like we do for plugdata

All in all, I think it's quite doable to port this over

@charlesneimog
Copy link

@charlesneimog It seems like right now, all properties apply immediately on change. We could either make it wait for the "apply" command, or we could replace the cancel/ok/apply buttons with just an ok button.

My preference might be to just have one button, I fear it would become quite complex otherwise
Here’s a polished version of your text:

Yes, I agree. I think saving everything in variables and sending it all at once is better. I also don’t like Pd’s ApplyOk workflow, but I understand why it’s used.

If the user just closes the window, nothing will be applied, right? Do you think this might feel odd for users use to Pd? When I implemented it the first time, I found it could be a bit unexpected.

@timothyschoen
Copy link
Contributor Author

timothyschoen commented Mar 17, 2026

@charlesneimog It seems like right now, all properties apply immediately on change. We could either make it wait for the "apply" command, or we could replace the cancel/ok/apply buttons with just an ok button.
My preference might be to just have one button, I fear it would become quite complex otherwise
Here’s a polished version of your text:

Yes, I agree. I think saving everything in variables and sending it all at once is better. I also don’t like Pd’s ApplyOk workflow, but I understand why it’s used.

If the user just closes the window, nothing will be applied, right? Do you think this might feel odd for users use to Pd? When I implemented it the first time, I found it could be a bit unexpected.

Ah, so you would prefer it if the properties don't apply immediately? I'll work on that

Done, properties now get sent only after clicking "Apply" or "OK", and are discarded with "Cancel".

@umlaeute
Copy link
Contributor

Ah, so you would prefer it if the properties don't apply immediately? I'll work on that

Done, properties now get sent only after clicking "Apply" or "OK", and are discarded with "Cancel".

I think this is a matter of design guidelines for different OSs (rather than personal preferences).

eg on macOSX, properties should apply immediately (whenever a value changes).
on Windows and Linux (even though there are no design guidelines imposed) the general expectation is to be able to tell the system, when you want to apply (by clicking a button).

that's the reason why Pd's dialogs look they do

@timothyschoen
Copy link
Contributor Author

timothyschoen commented Mar 17, 2026

Ah, so you would prefer it if the properties don't apply immediately? I'll work on that
Done, properties now get sent only after clicking "Apply" or "OK", and are discarded with "Cancel".

I think this is a matter of design guidelines for different OSs (rather than personal preferences).

eg on macOSX, properties should apply immediately (whenever a value changes). on Windows and Linux (even though there are no design guidelines imposed) the general expectation is to be able to tell the system, when you want to apply (by clicking a button).

that's the reason why Pd's dialogs look they do

Thanks, that's good to know. So from my experimenting so far, I've found:

Windows/Linux:

  • Only apply properties after clicking "Apply"
  • Hitting return key applies and closes window

macOS:

  • Color properties apply immediately when set
  • Other properties apply on hitting return key, hitting return twice closes the window as well
  • There is no apply button

Does that sound right?

@timothyschoen
Copy link
Contributor Author

timothyschoen commented Mar 18, 2026

I managed to build a version that combines both lua and luajit into one external:

https://github.com/timothyschoen/pd-lua/tree/luajit

Here's how it works:

Luajit side:

  • Use macros to rename all pdlua_* functions to pdluajit_*
  • Use the compat53 library to make some of the modern lua features we use available to luajit, which is normally based on Lua 5.1. This includes both a C header to expand the C API, and a Lua library which we inject on startup
  • Define LUA_FILE_EXTENSION as ".pd_luajit"
  • Then, include "pdlua.c" and link against luajit

Regular lua side:

  • Use macros to prefix all lua functions with "lua54_"
  • Define LUA_FILE_EXTENSION as ".pd_lua"
  • Then, include both onelua.c and pdlua.c
  • Call pdluajit_setup() at the end of pdlua_setup()

After doing that, it works perfectly: both get compiled into a single external. You decide which Lua gets used based on whether the lua file ends with .pd_lua or .pd_luajit

Here's a benchmark comparing the two:

pdlua:
-----------------------------------
empty loop           0.013680 sec
arithmetic           0.060547 sec
function calls       0.076619 sec
table access         0.044508 sec
math.sin             0.152804 sec

pdluajit:
-----------------------------------
empty loop           0.002288 sec   ->  6x faster
arithmetic           0.010526 sec   ->  6x faster
function calls       0.002264 sec   ->  33x faster
table access         0.012697 sec   ->  3.5x faster
math.sin             0.035742 sec    ->  4.25x faster

Personally I'd be in favour of merging this, since the performance difference is enormous. This also makes pdlua much more interesting for DSP purposes. I understand that it's a big change though, and the build system is admittedly a bit hacky.

@charlesneimog
Copy link

charlesneimog commented Mar 18, 2026

This is really really cool, I am trying to compile it on arch linux, I am getting this:

++++ info: making luas/luajit.pd_linux.o in lib pdlua
cc -DPD -I "/usr/include/pd"  -DUNIX  -fPIC -DMAKE_LIB -Iluas/luajit/src -DLUA_USE_LINUX -Iluas/lua-compat-5.3 -DPDLUA_VERSION="plugdata/0.10.2-482-gce59fff" -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing -O3 -ffast-math -funroll-loops -fomit-frame-pointer -march=core2 -mfpmath=sse -msse -msse2 -msse3 -o luas/luajit.pd_linux.o -c luas/luajit.c
luas/luajit.c: In function ‘preload_compat53’:
luas/luajit.c:19:37: error: ‘luas_lua_compat_5_3_compat53_init_lua’ undeclared (first use in this function); did you mean ‘luas_lua_compat_5_3_compat53_file_mt_lua’?
   19 |     luaL_loadbuffer(L, (const char*)luas_lua_compat_5_3_compat53_init_lua,
      |                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                     luas_lua_compat_5_3_compat53_file_mt_lua
luas/luajit.c:19:37: note: each undeclared identifier is reported only once for each function it appears in
luas/luajit.c:20:24: error: ‘luas_lua_compat_5_3_compat53_init_lua_len’ undeclared (first use in this function); did you mean ‘luas_lua_compat_5_3_compat53_file_mt_lua_len’?
   20 |                        luas_lua_compat_5_3_compat53_init_lua_len, "compat53");
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                        luas_lua_compat_5_3_compat53_file_mt_lua_len
In file included from luas/../pdlua.c:117,

Some ideia?

EDIT: Now works!!

@timothyschoen
Copy link
Contributor Author

This is really really cool, I am trying to compile it on arch linux, I am getting this:

++++ info: making luas/luajit.pd_linux.o in lib pdlua
cc -DPD -I "/usr/include/pd"  -DUNIX  -fPIC -DMAKE_LIB -Iluas/luajit/src -DLUA_USE_LINUX -Iluas/lua-compat-5.3 -DPDLUA_VERSION="plugdata/0.10.2-482-gce59fff" -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing -O3 -ffast-math -funroll-loops -fomit-frame-pointer -march=core2 -mfpmath=sse -msse -msse2 -msse3 -o luas/luajit.pd_linux.o -c luas/luajit.c
luas/luajit.c: In function ‘preload_compat53’:
luas/luajit.c:19:37: error: ‘luas_lua_compat_5_3_compat53_init_lua’ undeclared (first use in this function); did you mean ‘luas_lua_compat_5_3_compat53_file_mt_lua’?
   19 |     luaL_loadbuffer(L, (const char*)luas_lua_compat_5_3_compat53_init_lua,
      |                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                     luas_lua_compat_5_3_compat53_file_mt_lua
luas/luajit.c:19:37: note: each undeclared identifier is reported only once for each function it appears in
luas/luajit.c:20:24: error: ‘luas_lua_compat_5_3_compat53_init_lua_len’ undeclared (first use in this function); did you mean ‘luas_lua_compat_5_3_compat53_file_mt_lua_len’?
   20 |                        luas_lua_compat_5_3_compat53_init_lua_len, "compat53");
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                        luas_lua_compat_5_3_compat53_file_mt_lua_len
In file included from luas/../pdlua.c:117,

Some ideia?

Can you try a clean build now? I was still messing around a bit to get the github actions to work on every platform, but it should be good now :)

@charlesneimog
Copy link

@timothyschoen, on properties yet! (pdluajit works here). I was trying to do this

function lplot:properties(p)
	local i = 0
	p:new_frame("Colors", #self.draws)

	for k, v in pairs(self.draws) do
		local method = "update_color_" .. i
		self[method] = function(obj, color)
			obj.draws[k].color = color[1]
		end
		p:add_color(k, method, v.color)
		i = i + 1
	end
end

I believe you need to add a

    pdlua->properties.property_count++;

on static int pdlua_properties_addcolor(lua_State *L) for Pure Data.

without this we have

(Tcl) UNHANDLED ERROR: window name "color0" already exists in parent
    while executing
"{frame} {.0x7f10243cc200.main.frame1.content.color0} "
    ("uplevel" body line 41)
    invoked from within
"uplevel #0 $docmds"

@timothyschoen
Copy link
Contributor Author

@timothyschoen, on properties yet! (pdluajit works here). I was trying to do this

function lplot:properties(p)
	local i = 0
	p:new_frame("Colors", #self.draws)

	for k, v in pairs(self.draws) do
		local method = "update_color_" .. i
		self[method] = function(obj, color)
			obj.draws[k].color = color[1]
		end
		p:add_color(k, method, v.color)
		i = i + 1
	end
end

I believe you need to add a

    pdlua->properties.property_count++;

on static int pdlua_properties_addcolor(lua_State *L) for Pure Data.

without this we have

(Tcl) UNHANDLED ERROR: window name "color0" already exists in parent
    while executing
"{frame} {.0x7f10243cc200.main.frame1.content.color0} "
    ("uplevel" body line 41)
    invoked from within
"uplevel #0 $docmds"

Good catch, fixed that for both branches.

@timothyschoen
Copy link
Contributor Author

Added documentation as well:

Screenshot 2026-03-18 at 19 50 33

Another cool thing LuaJIT can do, is interface with C functions:

local ffi = require("ffi")

ffi.cdef[[
void post(const char *fmt, ...);
float sys_getsr(void);
double clock_getlogicaltime(void);
]]

ffi.C.post("=== Pd via LuaJIT FFI ===")
ffi.C.post("Samplerate: " .. tostring(ffi.C.sys_getsr()))
ffi.C.post("Block size: " .. tostring(ffi.C.sys_getblksize()))
ffi.C.post("Time: " ..  tostring(ffi.C.clock_getlogicaltime()))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants