Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
--[[
pdx.lua: useful extensions to pd.lua
Copyright (C) 2020 Albert Gräf <aggraef@gmail.com>
To use this in your pd-lua scripts: local pdx = require 'pdx'
Currently there's only the pdx.reload() function, which implements a kind of
remote reload functionality based on dofile and receivers, as explained in the
pd-lua tutorial. More may be added in the future.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--]]
local pdx = {}
--[[
Reload functionality. Call pdx.reload() on your object to enable, and
pdx.unreload() to disable this functionality again.
pdx.reload installs a "pdluax" receiver which reloads the object's script file
when it receives the "reload" message without any arguments, or a "reload
class" message with a matching class name.
We have to go to some lengths here since we only want the script to be
reloaded *once* for each class, not for every object in the class. This
complicates things quite a bit. In particular, we need to keep track of object
deletions to perform the necessary bookkeeping, in order to ensure that at any
time there's exactly one participating object in the class which receives the
message. Which means that we also need to keep track of the object's finalize
method, so that we can chain to it in our own finalizer.
--]]
local reloadables = {}
-- finalize method, here we perform the necessary bookkeeping when an object
-- in the reloadables table gets deleted
local function finalize(self)
if reloadables[self._name] then
-- remove the object from the reloadables table, and restore its
-- original finalizer
pdx.unreload(self)
-- call the object's own finalizer, if any
if self.finalize then
self.finalize(self)
end
end
end
-- Our receiver. In the future, more functionality may be added here, but at
-- present this only recognizes the "reload" message and checks the class
-- name, if given.
local function pdluax(self, sel, atoms)
if sel == "reload" then
-- reload message, check that any extra argument matches the class name
if atoms[1] == nil or atoms[1] == self._name then
pd.post(string.format("pdx: reloading %s", self._name))
self:dofile(self._scriptname)
-- update the object's finalizer and restore our own, in case
-- anything has changed there
if self.finalize ~= finalize then
reloadables[self._name][self].finalize = self.finalize
self.finalize = finalize
end
end
end
end
-- purge an object from the reloadables table
function pdx.unreload(self)
if reloadables[self._name] then
if reloadables[self._name].current == self then
-- self is the current receiver, find another one
local current = nil
for obj, data in pairs(reloadables[self._name]) do
if type(obj) == "table" and obj ~= self then
-- install the receiver
data.recv = pd.Receive:new():register(obj, "pdluax", "_pdluax")
obj._pdluax = pdluax
-- record that we have a new receiver and bail out
current = obj
break
end
end
reloadables[self._name].current = current
-- get rid of the old receiver
reloadables[self._name][self].recv:destruct()
self._pdluax = nil
end
-- restore the object's finalize method
self.finalize = reloadables[self._name][self].finalize
-- purge the object from the reloadables table
reloadables[self._name][self] = nil
-- if the list of reloadables in this class is now empty, purge the
-- entire class from the reloadables table
if reloadables[self._name].current == nil then
reloadables[self._name] = nil
end
end
end
-- register a new object in the reloadables table
function pdx.reload(self)
if reloadables[self._name] then
-- We already have an object for this class, simply record the new one
-- and install our finalizer so that we can perform the necessary
-- cleanup when the object gets deleted. Also check that we don't
-- register the same object twice (this won't really do any harm, but
-- would be a waste of time).
if reloadables[self._name][self] == nil then
reloadables[self._name][self] = { finalize = self.finalize }
self.finalize = finalize
end
else
-- New class, make this the default receiver.
reloadables[self._name] = { current = self }
reloadables[self._name][self] = { finalize = self.finalize }
-- install our finalizer
self.finalize = finalize
-- add the receiver
reloadables[self._name][self].recv =
pd.Receive:new():register(self, "pdluax", "_pdluax")
self._pdluax = pdluax
end
end
return pdx