Skip to content

Commit c3109d4

Browse files
committed
use a closure to hold the callback
sigh
1 parent 30174d0 commit c3109d4

File tree

2 files changed

+44
-101
lines changed

2 files changed

+44
-101
lines changed

src/FFI.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ private static function init(): void
802802
self::$ctypes = [
803803
"GObject" => self::$gobject->type("GObject*"),
804804
"GClosure" => self::$gobject->type("GClosure"),
805+
"GCallback" => self::$gobject->type("GCallback"),
805806
"GParamSpec" => self::$gobject->type("GParamSpec*"),
806807
"VipsObject" => self::$vips->type("VipsObject*"),
807808
"VipsOperation" => self::$vips->type("VipsOperation*"),

src/GObject.php

Lines changed: 43 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,6 @@ abstract class GObject
7070
*/
7171
private array $_references = [];
7272

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-
8273
/**
8374
* Wrap a GObject around an underlying vips resource. The GObject takes
8475
* ownership of the pointer and will unref it on finalize.
@@ -116,87 +107,65 @@ public function unref(): void
116107
FFI::gobject()->g_object_unref($this->pointer);
117108
}
118109

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
159119
{
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);
167121

168122
$id = FFI::gobject()->g_signal_connect_data($this->pointer,
169123
$name,
170124
$marshal,
171-
//$user,
172-
null,
125+
null,
173126
null,
174127
0);
175128
if ($id === 0) {
176129
throw new Exception("unable to connect signal $name");
177130
}
178131

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;
183135
}
184136

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.
189142
*/
190-
public function signalConnect(string $name, Closure $callback): void
143+
private static function buildMarshal(string $name, Closure $callback):
144+
Closure
191145
{
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+
};
196165

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+
}
200169
}
201170

202171
private static function getMarshaler(string $name, Closure $callback): ?Closure
@@ -229,34 +198,7 @@ private static function getMarshaler(string $name, Closure $callback): ?Closure
229198
);
230199
$callback($image, $pr);
231200
};
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;
249201

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;
260202
case 'seek':
261203
if (FFI::atLeast(8, 9)) {
262204
return static function (

0 commit comments

Comments
 (0)