Skip to content

Commit f6287c1

Browse files
committed
fix: Callbacks are working now
1 parent a852510 commit f6287c1

File tree

4 files changed

+130
-38
lines changed

4 files changed

+130
-38
lines changed

src/FFI.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ private static function init(): void
302302
typedef int32_t gint32;
303303
typedef uint64_t guint64;
304304
typedef int64_t gint64;
305+
typedef void* gpointer;
305306
306307
typedef $gtype GType;
307308
@@ -366,6 +367,7 @@ private static function init(): void
366367
void g_value_set_flags (GValue* value, unsigned int f);
367368
void g_value_set_string (GValue* value, const char* str);
368369
void g_value_set_object (GValue* value, void* object);
370+
void g_value_set_pointer (GValue* value, gpointer pointer);
369371
370372
bool g_value_get_boolean (const GValue* value);
371373
int g_value_get_int (GValue* value);
@@ -376,6 +378,7 @@ private static function init(): void
376378
unsigned int g_value_get_flags (GValue* value);
377379
const char* g_value_get_string (GValue* value);
378380
void* g_value_get_object (GValue* value);
381+
gpointer g_value_get_pointer (GValue* value);
379382
380383
typedef struct _GEnumValue {
381384
int value;
@@ -429,6 +432,17 @@ private static function init(): void
429432
int connect_flags);
430433
431434
const char* g_param_spec_get_blurb (GParamSpec* psp);
435+
436+
typedef struct _GClosure GClosure;
437+
typedef void (*marshaler)(struct GClosure* closure, GValue* return_value, int n_param_values, const GValue* param_values, void* invocation_hint, void* marshal_data);
438+
struct _GClosure {
439+
int in_marshal : 1;
440+
int is_invalid : 1;
441+
marshaler marshal;
442+
};
443+
long g_signal_connect_closure(GObject* object, const char* detailed_signal, GClosure *closure, bool after);
444+
GClosure* g_closure_ref(GClosure* closure);
445+
GClosure* g_closure_new_simple (int sizeof_closure, void* data);
432446
EOS;
433447

434448
# the whole libvips API, mostly adapted from pyvips
@@ -746,6 +760,7 @@ private static function init(): void
746760
// look these up in advance
747761
self::$ctypes = [
748762
"GObject" => self::$gobject->type("GObject*"),
763+
"GClosure" => self::$gobject->type("GClosure"),
749764
"GParamSpec" => self::$gobject->type("GParamSpec*"),
750765
"VipsObject" => self::$vips->type("VipsObject*"),
751766
"VipsOperation" => self::$vips->type("VipsOperation*"),
@@ -780,7 +795,7 @@ private static function init(): void
780795
"GObject" => self::$gobject->g_type_from_name("GObject"),
781796
"VipsImage" => self::$gobject->g_type_from_name("VipsImage"),
782797

783-
"GCallback" => self::$gobject->g_type_from_name("GCallback"),
798+
"GClosure" => self::$gobject->g_type_from_name("GClosure"),
784799
];
785800

786801
// map vips format names to c type names

src/GObject.php

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ abstract class GObject
6767
* Don't call this yourself, users should stick to (for example)
6868
* Image::newFromFile().
6969
*
70-
* @param FFI\CData $pointer The underlying pointer that this
70+
* @param CData $pointer The underlying pointer that this
7171
* object should wrap.
7272
*
7373
* @internal
@@ -99,47 +99,121 @@ public function unref(): void
9999

100100
public function signalConnect(string $name, Closure $callback): void
101101
{
102-
static $marshalers = null;
103-
104-
if ($marshalers === null) {
105-
$imageProgressCb = static function (CData $vi, CData $progress, CData $handle) {
106-
FFI::gobject()->g_object_ref($vi);
107-
$image = new Image($vi);
108-
$progress = \FFI::cast(FFI::ctypes('VipsProgress'), $progress);
109-
$handle($image, $progress);
102+
$imageProgressCb = static function (
103+
CData $gClosure,
104+
?CData $returnValue,
105+
int $numberOfParams,
106+
CData $params,
107+
CData $hint,
108+
?CData $data
109+
) use ($callback) {
110+
assert($numberOfParams === 3);
111+
/**
112+
* Marshal-Signature: void(VipsImage*, void*, void*)
113+
*/
114+
$vi = \FFI::cast(FFI::ctypes('GObject'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1])));
115+
FFI::gobject()->g_object_ref($vi);
116+
$image = new Image($vi);
117+
$pr = \FFI::cast(FFI::ctypes('VipsProgress'), FFI::gobject()->g_value_get_pointer(\FFI::addr($params[2])));
118+
$callback($image, $pr);
119+
};
120+
$marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb];
121+
122+
if (FFI::atLeast(8, 9)) {
123+
$marshalers['read'] = static function (
124+
CData $gClosure,
125+
CData $returnValue,
126+
int $numberOfParams,
127+
CData $params,
128+
CData $hint,
129+
?CData $data
130+
) use (&$callback): void {
131+
assert($numberOfParams === 4);
132+
/*
133+
* Marshal-Signature: gint64(VipsSourceCustom*, void*, gint64, void*)
134+
*/
135+
$bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]));
136+
$bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2]));
137+
$buffer = \FFI::string($bufferPointer, $bufferLength);
138+
$returnBufferLength = $callback($buffer);
139+
\FFI::memcpy($bufferPointer, $buffer, $returnBufferLength);
140+
FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength);
141+
FFI::gobject()->g_value_set_pointer(\FFI::addr($params[1]), $bufferPointer);
110142
};
111-
$marshalers = ['preeval' => $imageProgressCb, 'eval' => $imageProgressCb, 'posteval' => $imageProgressCb];
112-
113-
if (FFI::atLeast(8, 9)) {
114-
$marshalers['read'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int {
115-
$buffer = \FFI::string($pointer, $length);
116-
return $handle($buffer);
117-
};
118-
$marshalers['seek'] = static function (CData $gObject, int $offset, int $whence, CData $handle): int {
119-
return $handle($offset, $whence);
120-
};
121-
$marshalers['write'] = static function (CData $gObject, CData $pointer, int $length, CData $handle): int {
122-
$buffer = \FFI::string($pointer, $length);
123-
return $handle($buffer);
124-
};
125-
$marshalers['finish'] = static function (CData $gObject, CData $handle): void {
126-
$handle();
127-
};
128-
}
143+
$marshalers['seek'] = static function (
144+
CData $gClosure,
145+
CData $returnValue,
146+
int $numberOfParams,
147+
CData $params,
148+
CData $hint,
149+
?CData $data
150+
) use (&$callback): void {
151+
assert($numberOfParams === 4);
152+
/*
153+
* Marshal-Signature: gint64(VipsSourceCustom*, gint64, int, void*)
154+
*/
155+
$offset = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[1]));
156+
$whence = (int) FFI::gobject()->g_value_get_int(\FFI::addr($params[2]));
157+
FFI::gobject()->g_value_set_int64($returnValue, $callback($offset, $whence));
158+
};
159+
$marshalers['write'] = static function (
160+
CData $gClosure,
161+
CData $returnValue,
162+
int $numberOfParams,
163+
CData $params,
164+
CData $hint,
165+
?CData $data
166+
) use (&$callback): void {
167+
assert($numberOfParams === 4);
168+
/*
169+
* Marshal-Signature: gint64(VipsTargetCustom*, void*, gint64, void*)
170+
*/
171+
$bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]));
172+
$bufferLength = (int) FFI::gobject()->g_value_get_int64(\FFI::addr($params[2]));
173+
$buffer = \FFI::string($bufferPointer, $bufferLength);
174+
FFI::gobject()->g_value_set_int64($returnValue, $callback($buffer));
175+
};
176+
$marshalers['finish'] = static function (
177+
CData $gClosure,
178+
?CData $returnValue,
179+
int $numberOfParams,
180+
CData $params,
181+
CData $hint,
182+
?CData $data
183+
) use (&$callback): void {
184+
assert($numberOfParams === 2);
185+
/**
186+
* Marshal-Signature: void(VipsTargetCustom*, void*)
187+
*/
188+
$callback();
189+
};
190+
}
129191

130-
if (FFI::atLeast(8, 13)) {
131-
$marshalers['end'] = static function (CData $gObject, CData $handle): int {
132-
return $handle();
133-
};
134-
}
192+
if (FFI::atLeast(8, 13)) {
193+
$marshalers['end'] = static function (
194+
CData $gClosure,
195+
CData $returnValue,
196+
int $numberOfParams,
197+
CData $params,
198+
CData $hint,
199+
?CData $data
200+
) use (&$callback): void {
201+
assert($numberOfParams === 2);
202+
/**
203+
* Marshal-Signature: int(VipsTargetCustom*, void*)
204+
*/
205+
FFI::gobject()->g_value_set_int($returnValue, $callback());
206+
};
135207
}
136208

137209
if (!isset($marshalers[$name])) {
138210
throw new Exception("unsupported signal $name");
139211
}
140212

141213
$go = \FFI::cast(FFI::ctypes('GObject'), $this->pointer);
142-
FFI::gobject()->g_signal_connect_data($go, $name, $marshalers[$name], $callback, null, 0);
214+
$gc = FFI::gobject()->g_closure_new_simple(64, null);
215+
$gc->marshal = $marshalers[$name];
216+
FFI::gobject()->g_signal_connect_closure($go, $name, $gc, 0);
143217
}
144218
}
145219

src/GValue.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,7 @@ public function set($value): void
188188
# can own and free
189189
$n = strlen($value);
190190
$memory = \FFI::new("char[$n]", false, true);
191-
for ($i = 0; $i < $n; $i++) {
192-
$memory[$i] = $value[$i];
193-
}
191+
\FFI::memcpy($memory, $value, $n);
194192
FFI::vips()->
195193
vips_value_set_blob_free($this->pointer, $memory, $n);
196194
break;

src/VipsSource.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ public static function newFromFile(string $filename): self
6161
*/
6262
public static function newFromMemory(string $data): self
6363
{
64-
$pointer = FFI::vips()->vips_source_new_from_memory($data, strlen($data));
64+
# we need to set the memory to a copy of the data that vips_lib
65+
# can own and free
66+
$n = strlen($data);
67+
$memory = \FFI::new("char[$n]", false, true);
68+
\FFI::memcpy($memory, $data, $n);
69+
$pointer = FFI::vips()->vips_source_new_from_memory($memory, $n);
6570

6671
if (\FFI::isNull($pointer)) {
6772
throw new Exception("can't create source from memory");

0 commit comments

Comments
 (0)