@@ -70,15 +70,6 @@ abstract class GObject
70
70
*/
71
71
private array $ _references = [];
72
72
73
- /**
74
- * Marshalling functions for signal_connect.
75
- *
76
- * We build these once on first use, see get_marshal.
77
- *
78
- * @internal
79
- */
80
- private static ?array $ _marshal = null ;
81
-
82
73
/**
83
74
* Wrap a GObject around an underlying vips resource. The GObject takes
84
75
* ownership of the pointer and will unref it on finalize.
@@ -116,87 +107,65 @@ public function unref(): void
116
107
FFI ::gobject ()->g_object_unref ($ this ->pointer );
117
108
}
118
109
119
- private static function get_marshal (string $ name ): ?Closure
120
- {
121
- if (self ::$ _marshal == null ) {
122
- $ progress = function (CData $ image_pointer ,
123
- CData $ progress_pointer ,
124
- ?CData $ user ) {
125
- $ image = new Image ($ image_pointer );
126
- // Image() will unref on gc, so we must ref
127
- FFI ::gobject ()->g_object_ref ($ image_pointer );
128
- $ progress = \FFI ::cast (FFI ::ctypes ("VipsProgress " ),
129
- $ progress_pointer );
130
- // Closure $callback = $user;
131
-
132
- echo "marshal progress: \n" ;
133
- echo " image = $ image \n" ;
134
- echo " progress->run = $ progress ->run \n" ;
135
- echo " progress->eta = $ progress ->eta \n" ;
136
- echo " progress->tpels = $ progress ->tpels \n" ;
137
- echo " progress->npels = $ progress ->npels \n" ;
138
- echo " progress->percent = $ progress ->percent \n" ;
139
- echo " data = " ;
140
- print_r ($ user );
141
- echo "\n" ;
142
- };
143
-
144
- self ::$ _marshal = [
145
- "preeval " => $ progress ,
146
- "eval " => $ progress ,
147
- "posteval " => $ progress ,
148
- ];
149
- }
150
-
151
- if (!array_key_exists ($ name , self ::$ _marshal )) {
152
- throw new Exception ("unsupported signal $ name " );
153
- }
154
-
155
- return self ::$ _marshal [$ name ];
156
- }
157
-
158
- public function signal_connect2 (string $ name , $ callback ): void
110
+ /**
111
+ * Connect to a signal on this object.
112
+ *
113
+ * The callback will be triggered every time this signal is issued on this
114
+ * instance.
115
+ *
116
+ * @throws Exception
117
+ */
118
+ public function signalConnect (string $ name , Closure $ callback ): void
159
119
{
160
- $ marshal = self ::get_marshal ($ name );
161
- if ($ marshal === null ) {
162
- throw new Exception ("unsupported signal $ name " );
163
- }
164
-
165
- // php-ffi is expecting a void* for the user pointer
166
- //$user = \FFI::cast("void*", $callback);
120
+ $ marshal = self ::buildMarshal ($ name , $ callback );
167
121
168
122
$ id = FFI ::gobject ()->g_signal_connect_data ($ this ->pointer ,
169
123
$ name ,
170
124
$ marshal ,
171
- //$user,
172
- null ,
125
+ null ,
173
126
null ,
174
127
0 );
175
128
if ($ id === 0 ) {
176
129
throw new Exception ("unable to connect signal $ name " );
177
130
}
178
131
179
- // the closure we were passed may well be dynamically created and
180
- // contain objects which must not be GCd ... we must hold a reference
181
- // to it to keep it alive
182
- $ this ->_references [] = $ callback ;
132
+ // the marshaller must not be GCed
133
+ // FIXME ... _references must be copied to all child objects in call()
134
+ $ this ->_references [] = $ marshal ;
183
135
}
184
136
185
- /**
186
- * Connect to a signal on this object.
187
- * The callback will be triggered every time this signal is issued on this instance.
188
- * @throws Exception
137
+ /* Ideally, we'd use the "user" pointer of signal_connect to hold the
138
+ * callback closure, but unfortunately php-ffi has no way (I think) to
139
+ * turn a C pointer to function back into a php function, so we have to
140
+ * build a custom closure for every signal connect with the callback
141
+ * embedded within it.
189
142
*/
190
- public function signalConnect (string $ name , Closure $ callback ): void
143
+ private static function buildMarshal (string $ name , Closure $ callback ):
144
+ Closure
191
145
{
192
- $ marshaler = self ::getMarshaler ($ name , $ callback );
193
- if ($ marshaler === null ) {
194
- throw new Exception ("unsupported signal $ name " );
195
- }
146
+ switch ($ name ) {
147
+ case 'preeval ' :
148
+ case 'eval ' :
149
+ case 'posteval ' :
150
+ return static function (
151
+ CData $ image_pointer ,
152
+ CData $ progress_pointer ,
153
+ ?CData $ user
154
+ ) use (&$ callback ): void {
155
+ $ image = new Image ($ image_pointer );
156
+ // Image() will unref on gc, so we must ref
157
+ FFI ::gobject ()->g_object_ref ($ image_pointer );
158
+
159
+ // FIXME ... maybe wrap VipsProgress as a php class?
160
+ $ progress = \FFI ::cast (FFI ::ctypes ("VipsProgress " ),
161
+ $ progress_pointer );
162
+
163
+ $ callback ($ image , $ progress );
164
+ };
196
165
197
- $ gc = FFI :: gobject ()-> g_closure_new_simple (\ FFI :: sizeof ( FFI :: ctypes ( ' GClosure ' )), null );
198
- $ gc -> marshal = $ marshaler ;
199
- FFI :: gobject ()-> g_signal_connect_closure ( $ this -> pointer , $ name , $ gc , 0 );
166
+ default :
167
+ throw new Exception ( " unsupported signal $ name " ) ;
168
+ }
200
169
}
201
170
202
171
private static function getMarshaler (string $ name , Closure $ callback ): ?Closure
@@ -229,34 +198,7 @@ private static function getMarshaler(string $name, Closure $callback): ?Closure
229
198
);
230
199
$ callback ($ image , $ pr );
231
200
};
232
- case 'read ' :
233
- if (FFI ::atLeast (8 , 9 )) {
234
- return static function (
235
- CData $ gClosure ,
236
- CData $ returnValue ,
237
- int $ numberOfParams ,
238
- CData $ params ,
239
- CData $ hint ,
240
- ?CData $ data
241
- ) use (&$ callback ): void {
242
- assert ($ numberOfParams === 4 );
243
- /*
244
- * Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle)
245
- */
246
- $ bufferLength = (int )FFI ::gobject ()->g_value_get_int64 (\FFI ::addr ($ params [2 ]));
247
- $ returnBuffer = $ callback ($ bufferLength );
248
- $ returnBufferLength = 0 ;
249
201
250
- if ($ returnBuffer !== null ) {
251
- $ returnBufferLength = strlen ($ returnBuffer );
252
- $ bufferPointer = FFI ::gobject ()->g_value_get_pointer (\FFI ::addr ($ params [1 ]));
253
- \FFI ::memcpy ($ bufferPointer , $ returnBuffer , $ returnBufferLength );
254
- }
255
- FFI ::gobject ()->g_value_set_int64 ($ returnValue , $ returnBufferLength );
256
- };
257
- }
258
-
259
- return null ;
260
202
case 'seek ' :
261
203
if (FFI ::atLeast (8 , 9 )) {
262
204
return static function (
0 commit comments